├── .gitignore
├── LICENSE
├── README.md
├── nginx
├── LICENSE
├── conf
│ └── nginx.conf
└── src
│ └── http
│ └── modules
│ └── ngx_http_fastcgi_module.c
└── servers
└── afcgitest
├── Makefile
├── afcgitest.c
├── sockutil.c
└── sockutil.h
/.gitignore:
--------------------------------------------------------------------------------
1 | servers/afcgitest/afcgitest
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Rasmus Andersson
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # afcgi
2 |
3 | Asynchronous FastCGI.
4 |
5 | Primarily a modified version of the [Nginx FastCGI module](http://wiki.nginx.org/NginxHttpFcgiModule) which implements multiplexing of connections, allowing a single FastCGI server to handle many concurrent requests.
6 |
7 | This paves the way for long-lived connections in web apps without wasting resources -- i.e. optimally you only need to run one server process per CPU (or one server with one thread per CPU) instead of one process per request.
8 |
9 | ## Files
10 |
11 | - `nginx/` contains the modified `ngx_http_fastcgi_module.c` along with a "prefix" (conf, logs, etc) for running a test server.
12 |
13 | - `servers/` contains a set of asynchronous FastCGI server implementations, most notably the original libevent-based server written with the purpose to test afcgi.
14 |
15 | [
](http://hunch.se/s/cx/e1rg8i8bkgwks.png)
16 |
17 | ## Building
18 |
19 | ### Nginx module
20 |
21 | The nginx module is built by *replacing* the original `ngx_http_fastcgi_module.c`:
22 |
23 | wget http://sysoev.ru/nginx/nginx-0.8.29.tar.gz
24 | tar xzf nginx-0.8.29.tar.gz
25 | cd nginx-0.8.29/src/http/modules
26 | mv ngx_http_fastcgi_module.c ngx_http_fastcgi_module.c.dist
27 | cp /path/to/afcgi/nginx/src/http/modules/ngx_http_fastcgi_module.c .
28 | cd ../../..
29 | ./configure
30 | make
31 |
32 | This module has been developed for and tested with nginx version 0.8.29 and is still in an experimental stage. No warranties, no guarantees -- use at own risk ;)
33 |
34 | ### Server reference implementation
35 |
36 | The FCGI server reference implementation in `servers/afcgitest` is built by using regular make and linking against [libevent](http://monkey.org/~provos/libevent/):
37 |
38 | cd /path/to/afcgi/servers/afcgitest
39 | make
40 | ./afcgitest 127.0.0.1:5000
41 |
42 | > The last line assumes a FastCGI client (i.e. nginx) is connecting to port `5000` on `127.0.0.1`.
43 |
44 | At the moment the `Makefile` is prepared for Mac OS X and contains a few lines which will not work in other environments. Simply remove or comment-out these lines when building on another platform:
45 |
46 | # If Mac OS X:
47 | CFLAGS += -arch i386
48 | LDFLAGS += -arch i386
49 |
50 | becomes:
51 |
52 | # If Mac OS X:
53 | #CFLAGS += -arch i386
54 | #LDFLAGS += -arch i386
55 |
56 | > **OS X users:** This assumes libevent is built for the i386 architecture. Your libevent library might use another architecture and then you should change the `-arch` argument value to match the architecture of libevent.
57 |
58 | ## License & redistribution
59 |
60 | Most parts of this software is licensed under the MIT license (see the `LICENSE` file for details) while the nginx module is licensed under the nginx license (details in the file `nginx/LICENSE`).
61 |
62 | In short; you are free to use this software for commercial and non-commercial applictions as long as the license(s) and copyright notice(s) are properly reproduced. Read the LICENSEs files for details.
63 |
64 | ## Authors
65 |
66 | - Rasmus Andersson
67 |
68 | - Igor Sysoev (author of the original, synchronous fastcgi module)
69 |
--------------------------------------------------------------------------------
/nginx/LICENSE:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2002-2009 Igor Sysoev
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions
6 | * are met:
7 | * 1. Redistributions of source code must retain the above copyright
8 | * notice, this list of conditions and the following disclaimer.
9 | * 2. Redistributions in binary form must reproduce the above copyright
10 | * notice, this list of conditions and the following disclaimer in the
11 | * documentation and/or other materials provided with the distribution.
12 | *
13 | * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 | * SUCH DAMAGE.
24 | */
25 |
--------------------------------------------------------------------------------
/nginx/conf/nginx.conf:
--------------------------------------------------------------------------------
1 | worker_processes 1;
2 | daemon off;
3 | error_log nginx.log debug;
4 |
5 | events {
6 | worker_connections 256;
7 | }
8 |
9 | http {
10 | default_type application/octet-stream;
11 | sendfile on;
12 | keepalive_timeout 30;
13 |
14 | server {
15 | listen 8888;
16 | location /fcgi {
17 | fastcgi_pass 127.0.0.1:5000;
18 | fastcgi_param SCRIPT_FILENAME /tmp$fastcgi_script_name;
19 | fastcgi_param QUERY_STRING $query_string;
20 | fastcgi_param REQUEST_METHOD $request_method;
21 | fastcgi_param CONTENT_TYPE $content_type;
22 | fastcgi_param CONTENT_LENGTH $content_length;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/nginx/src/http/modules/ngx_http_fastcgi_module.c:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Copyright (C) Igor Sysoev
4 | */
5 |
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | #define NGX_FASTCGI_MPX_REQLIMIT __SHRT_MAX__+__SHRT_MAX__
12 | #define NGX_HTTP_FASTCGI_KEEP_CONN 1
13 | //#define NGX_FASTCGI_MPX_DEBUG 1
14 |
15 | #ifndef NGX_FASTCGI_MPX_DEBUG
16 | #define NGX_FASTCGI_MPX_DEBUG 0
17 | #endif
18 |
19 | #if ((NGX_DEBUG) || (NGX_FASTCGI_MPX_DEBUG))
20 | #define DLOG(r, fmt, ...) \
21 | ngx_log_error(NGX_LOG_DEBUG, (r)->connection->log, 0, \
22 | "[ngx_http_fastcgi_module:%d] " fmt, __LINE__, ##__VA_ARGS__)
23 | #else
24 | #define DLOG(...)
25 | #endif
26 |
27 |
28 | typedef struct {
29 | ngx_http_upstream_conf_t upstream;
30 |
31 | ngx_str_t index;
32 |
33 | ngx_array_t *flushes;
34 | ngx_array_t *params_len;
35 | ngx_array_t *params;
36 | ngx_array_t *params_source;
37 | ngx_array_t *catch_stderr;
38 |
39 | ngx_array_t *fastcgi_lengths;
40 | ngx_array_t *fastcgi_values;
41 |
42 | #if (NGX_HTTP_CACHE)
43 | ngx_http_complex_value_t cache_key;
44 | #endif
45 |
46 | #if (NGX_PCRE)
47 | ngx_regex_t *split_regex;
48 | ngx_str_t split_name;
49 | #endif
50 |
51 | ngx_http_request_t *requests[NGX_FASTCGI_MPX_REQLIMIT];
52 | } ngx_http_fastcgi_loc_conf_t;
53 |
54 |
55 | typedef enum {
56 | ngx_http_fastcgi_st_version = 0,
57 | ngx_http_fastcgi_st_type,
58 | ngx_http_fastcgi_st_request_id_hi,
59 | ngx_http_fastcgi_st_request_id_lo,
60 | ngx_http_fastcgi_st_content_length_hi,
61 | ngx_http_fastcgi_st_content_length_lo,
62 | ngx_http_fastcgi_st_padding_length,
63 | ngx_http_fastcgi_st_reserved,
64 | ngx_http_fastcgi_st_data,
65 | ngx_http_fastcgi_st_padding
66 | } ngx_http_fastcgi_state_e;
67 |
68 |
69 | typedef struct {
70 | u_char *start;
71 | u_char *end;
72 | } ngx_http_fastcgi_split_part_t;
73 |
74 |
75 | typedef struct {
76 | ngx_http_fastcgi_state_e state;
77 | u_char *pos;
78 | u_char *last;
79 | ngx_uint_t type;
80 | size_t length;
81 | size_t padding;
82 |
83 | ngx_uint_t fastcgi_stdout; /* unsigned :1 */
84 |
85 | ngx_array_t *split_parts;
86 |
87 | ngx_str_t script_name;
88 | ngx_str_t path_info;
89 | short rid;
90 | } ngx_http_fastcgi_ctx_t;
91 |
92 |
93 | #define NGX_HTTP_FASTCGI_RESPONDER 1
94 |
95 | #define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1
96 | #define NGX_HTTP_FASTCGI_ABORT_REQUEST 2
97 | #define NGX_HTTP_FASTCGI_END_REQUEST 3
98 | #define NGX_HTTP_FASTCGI_PARAMS 4
99 | #define NGX_HTTP_FASTCGI_STDIN 5
100 | #define NGX_HTTP_FASTCGI_STDOUT 6
101 | #define NGX_HTTP_FASTCGI_STDERR 7
102 | #define NGX_HTTP_FASTCGI_DATA 8
103 |
104 |
105 | typedef struct {
106 | u_char version;
107 | u_char type;
108 | u_char request_id_hi;
109 | u_char request_id_lo;
110 | u_char content_length_hi;
111 | u_char content_length_lo;
112 | u_char padding_length;
113 | u_char reserved;
114 | } ngx_http_fastcgi_header_t;
115 |
116 |
117 | typedef struct {
118 | u_char role_hi;
119 | u_char role_lo;
120 | u_char flags;
121 | u_char reserved[5];
122 | } ngx_http_fastcgi_begin_request_t;
123 |
124 |
125 | typedef struct {
126 | u_char version;
127 | u_char type;
128 | u_char request_id_hi;
129 | u_char request_id_lo;
130 | } ngx_http_fastcgi_header_small_t;
131 |
132 |
133 | typedef struct {
134 | ngx_http_fastcgi_header_t h0;
135 | ngx_http_fastcgi_begin_request_t br;
136 | ngx_http_fastcgi_header_small_t h1;
137 | } ngx_http_fastcgi_request_start_t;
138 |
139 |
140 | static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,
141 | ngx_http_fastcgi_loc_conf_t *flcf);
142 | #if (NGX_HTTP_CACHE)
143 | static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);
144 | #endif
145 | static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
146 | static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
147 | static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
148 | static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
149 | ngx_buf_t *buf);
150 | static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,
151 | ngx_http_fastcgi_ctx_t *f);
152 | static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);
153 | static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,
154 | ngx_int_t rc);
155 |
156 | static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);
157 | static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);
158 | static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,
159 | void *parent, void *child);
160 | static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
161 | ngx_http_variable_value_t *v, uintptr_t data);
162 | static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
163 | ngx_http_variable_value_t *v, uintptr_t data);
164 | static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,
165 | ngx_http_fastcgi_loc_conf_t *flcf);
166 |
167 | static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
168 | void *conf);
169 | static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,
170 | ngx_command_t *cmd, void *conf);
171 | static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
172 | void *conf);
173 | #if (NGX_HTTP_CACHE)
174 | static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
175 | void *conf);
176 | static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
177 | void *conf);
178 | #endif
179 |
180 | static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
181 | void *data);
182 |
183 | static char *ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
184 | ngx_command_t *cmd, void *conf);
185 | static char *ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
186 | ngx_command_t *cmd, void *conf);
187 |
188 |
189 | static ngx_conf_post_t ngx_http_fastcgi_lowat_post =
190 | { ngx_http_fastcgi_lowat_check };
191 |
192 |
193 | static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = {
194 | { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
195 | { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
196 | { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
197 | { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
198 | { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
199 | { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
200 | { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
201 | { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
202 | { ngx_null_string, 0 }
203 | };
204 |
205 |
206 | static ngx_conf_bitmask_t ngx_http_fastcgi_ignore_headers_masks[] = {
207 | { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
208 | { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
209 | { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
210 | { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
211 | { ngx_null_string, 0 }
212 | };
213 |
214 |
215 | ngx_module_t ngx_http_fastcgi_module;
216 |
217 |
218 | static ngx_command_t ngx_http_fastcgi_commands[] = {
219 |
220 | { ngx_string("fastcgi_pass"),
221 | NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
222 | ngx_http_fastcgi_pass,
223 | NGX_HTTP_LOC_CONF_OFFSET,
224 | 0,
225 | NULL },
226 |
227 | { ngx_string("fastcgi_index"),
228 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
229 | ngx_conf_set_str_slot,
230 | NGX_HTTP_LOC_CONF_OFFSET,
231 | offsetof(ngx_http_fastcgi_loc_conf_t, index),
232 | NULL },
233 |
234 | { ngx_string("fastcgi_split_path_info"),
235 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
236 | ngx_http_fastcgi_split_path_info,
237 | NGX_HTTP_LOC_CONF_OFFSET,
238 | 0,
239 | NULL },
240 |
241 | { ngx_string("fastcgi_store"),
242 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
243 | ngx_http_fastcgi_store,
244 | NGX_HTTP_LOC_CONF_OFFSET,
245 | 0,
246 | NULL },
247 |
248 | { ngx_string("fastcgi_store_access"),
249 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
250 | ngx_conf_set_access_slot,
251 | NGX_HTTP_LOC_CONF_OFFSET,
252 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),
253 | NULL },
254 |
255 | { ngx_string("fastcgi_ignore_client_abort"),
256 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
257 | ngx_conf_set_flag_slot,
258 | NGX_HTTP_LOC_CONF_OFFSET,
259 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),
260 | NULL },
261 |
262 | { ngx_string("fastcgi_bind"),
263 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
264 | ngx_http_upsteam_bind_set_slot,
265 | NGX_HTTP_LOC_CONF_OFFSET,
266 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local),
267 | NULL },
268 |
269 | { ngx_string("fastcgi_connect_timeout"),
270 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
271 | ngx_conf_set_msec_slot,
272 | NGX_HTTP_LOC_CONF_OFFSET,
273 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),
274 | NULL },
275 |
276 | { ngx_string("fastcgi_send_timeout"),
277 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
278 | ngx_conf_set_msec_slot,
279 | NGX_HTTP_LOC_CONF_OFFSET,
280 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),
281 | NULL },
282 |
283 | { ngx_string("fastcgi_send_lowat"),
284 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
285 | ngx_conf_set_size_slot,
286 | NGX_HTTP_LOC_CONF_OFFSET,
287 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),
288 | &ngx_http_fastcgi_lowat_post },
289 |
290 | { ngx_string("fastcgi_buffer_size"),
291 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
292 | ngx_conf_set_size_slot,
293 | NGX_HTTP_LOC_CONF_OFFSET,
294 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
295 | NULL },
296 |
297 | { ngx_string("fastcgi_pass_request_headers"),
298 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
299 | ngx_conf_set_flag_slot,
300 | NGX_HTTP_LOC_CONF_OFFSET,
301 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),
302 | NULL },
303 |
304 | { ngx_string("fastcgi_pass_request_body"),
305 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
306 | ngx_conf_set_flag_slot,
307 | NGX_HTTP_LOC_CONF_OFFSET,
308 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),
309 | NULL },
310 |
311 | { ngx_string("fastcgi_intercept_errors"),
312 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
313 | ngx_conf_set_flag_slot,
314 | NGX_HTTP_LOC_CONF_OFFSET,
315 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),
316 | NULL },
317 |
318 | { ngx_string("fastcgi_read_timeout"),
319 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
320 | ngx_conf_set_msec_slot,
321 | NGX_HTTP_LOC_CONF_OFFSET,
322 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),
323 | NULL },
324 |
325 | { ngx_string("fastcgi_buffers"),
326 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
327 | ngx_conf_set_bufs_slot,
328 | NGX_HTTP_LOC_CONF_OFFSET,
329 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),
330 | NULL },
331 |
332 | { ngx_string("fastcgi_busy_buffers_size"),
333 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
334 | ngx_conf_set_size_slot,
335 | NGX_HTTP_LOC_CONF_OFFSET,
336 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),
337 | NULL },
338 |
339 | #if (NGX_HTTP_CACHE)
340 |
341 | { ngx_string("fastcgi_cache"),
342 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
343 | ngx_http_fastcgi_cache,
344 | NGX_HTTP_LOC_CONF_OFFSET,
345 | 0,
346 | NULL },
347 |
348 | { ngx_string("fastcgi_cache_key"),
349 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
350 | ngx_http_fastcgi_cache_key,
351 | NGX_HTTP_LOC_CONF_OFFSET,
352 | 0,
353 | NULL },
354 |
355 | { ngx_string("fastcgi_cache_path"),
356 | NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
357 | ngx_http_file_cache_set_slot,
358 | 0,
359 | 0,
360 | &ngx_http_fastcgi_module },
361 |
362 | { ngx_string("fastcgi_cache_valid"),
363 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
364 | ngx_http_file_cache_valid_set_slot,
365 | NGX_HTTP_LOC_CONF_OFFSET,
366 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid),
367 | NULL },
368 |
369 | { ngx_string("fastcgi_cache_min_uses"),
370 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
371 | ngx_conf_set_num_slot,
372 | NGX_HTTP_LOC_CONF_OFFSET,
373 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses),
374 | NULL },
375 |
376 | { ngx_string("fastcgi_cache_use_stale"),
377 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
378 | ngx_conf_set_bitmask_slot,
379 | NGX_HTTP_LOC_CONF_OFFSET,
380 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_use_stale),
381 | &ngx_http_fastcgi_next_upstream_masks },
382 |
383 | { ngx_string("fastcgi_cache_methods"),
384 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
385 | ngx_conf_set_bitmask_slot,
386 | NGX_HTTP_LOC_CONF_OFFSET,
387 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods),
388 | &ngx_http_upstream_cache_method_mask },
389 |
390 | #endif
391 |
392 | { ngx_string("fastcgi_temp_path"),
393 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
394 | ngx_conf_set_path_slot,
395 | NGX_HTTP_LOC_CONF_OFFSET,
396 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),
397 | NULL },
398 |
399 | { ngx_string("fastcgi_max_temp_file_size"),
400 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
401 | ngx_conf_set_size_slot,
402 | NGX_HTTP_LOC_CONF_OFFSET,
403 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),
404 | NULL },
405 |
406 | { ngx_string("fastcgi_temp_file_write_size"),
407 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
408 | ngx_conf_set_size_slot,
409 | NGX_HTTP_LOC_CONF_OFFSET,
410 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),
411 | NULL },
412 |
413 | { ngx_string("fastcgi_next_upstream"),
414 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
415 | ngx_conf_set_bitmask_slot,
416 | NGX_HTTP_LOC_CONF_OFFSET,
417 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream),
418 | &ngx_http_fastcgi_next_upstream_masks },
419 |
420 | { ngx_string("fastcgi_upstream_max_fails"),
421 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
422 | ngx_http_fastcgi_upstream_max_fails_unsupported,
423 | 0,
424 | 0,
425 | NULL },
426 |
427 | { ngx_string("fastcgi_upstream_fail_timeout"),
428 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
429 | ngx_http_fastcgi_upstream_fail_timeout_unsupported,
430 | 0,
431 | 0,
432 | NULL },
433 |
434 | { ngx_string("fastcgi_param"),
435 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
436 | ngx_conf_set_keyval_slot,
437 | NGX_HTTP_LOC_CONF_OFFSET,
438 | offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
439 | NULL },
440 |
441 | { ngx_string("fastcgi_pass_header"),
442 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
443 | ngx_conf_set_str_array_slot,
444 | NGX_HTTP_LOC_CONF_OFFSET,
445 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),
446 | NULL },
447 |
448 | { ngx_string("fastcgi_hide_header"),
449 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
450 | ngx_conf_set_str_array_slot,
451 | NGX_HTTP_LOC_CONF_OFFSET,
452 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
453 | NULL },
454 |
455 | { ngx_string("fastcgi_ignore_headers"),
456 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
457 | ngx_conf_set_bitmask_slot,
458 | NGX_HTTP_LOC_CONF_OFFSET,
459 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers),
460 | &ngx_http_fastcgi_ignore_headers_masks },
461 |
462 | { ngx_string("fastcgi_catch_stderr"),
463 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
464 | ngx_conf_set_str_array_slot,
465 | NGX_HTTP_LOC_CONF_OFFSET,
466 | offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),
467 | NULL },
468 |
469 | ngx_null_command
470 | };
471 |
472 |
473 | static ngx_http_module_t ngx_http_fastcgi_module_ctx = {
474 | ngx_http_fastcgi_add_variables, /* preconfiguration */
475 | NULL, /* postconfiguration */
476 |
477 | NULL, /* create main configuration */
478 | NULL, /* init main configuration */
479 |
480 | NULL, /* create server configuration */
481 | NULL, /* merge server configuration */
482 |
483 | ngx_http_fastcgi_create_loc_conf, /* create location configuration */
484 | ngx_http_fastcgi_merge_loc_conf /* merge location configuration */
485 | };
486 |
487 |
488 | ngx_module_t ngx_http_fastcgi_module = {
489 | NGX_MODULE_V1,
490 | &ngx_http_fastcgi_module_ctx, /* module context */
491 | ngx_http_fastcgi_commands, /* module directives */
492 | NGX_HTTP_MODULE, /* module type */
493 | NULL, /* init master */
494 | NULL, /* init module */
495 | NULL, /* init process */
496 | NULL, /* init thread */
497 | NULL, /* exit thread */
498 | NULL, /* exit process */
499 | NULL, /* exit master */
500 | NGX_MODULE_V1_PADDING
501 | };
502 |
503 |
504 | static ngx_http_fastcgi_request_start_t ngx_http_fastcgi_request_start = {
505 | { 1, /* version */
506 | NGX_HTTP_FASTCGI_BEGIN_REQUEST, /* type */
507 | 0, /* request_id_hi */
508 | 1, /* request_id_lo */
509 | 0, /* content_length_hi */
510 | sizeof(ngx_http_fastcgi_begin_request_t), /* content_length_lo */
511 | 0, /* padding_length */
512 | 0 }, /* reserved */
513 |
514 | { 0, /* role_hi */
515 | NGX_HTTP_FASTCGI_RESPONDER, /* role_lo */
516 | NGX_HTTP_FASTCGI_KEEP_CONN, /* flags */
517 | { 0, 0, 0, 0, 0 } }, /* reserved[5] */
518 |
519 | { 1, /* version */
520 | NGX_HTTP_FASTCGI_PARAMS, /* type */
521 | 0, /* request_id_hi */
522 | 1 }, /* request_id_lo */
523 |
524 | };
525 |
526 |
527 | static ngx_http_variable_t ngx_http_fastcgi_vars[] = {
528 |
529 | { ngx_string("fastcgi_script_name"), NULL,
530 | ngx_http_fastcgi_script_name_variable, 0,
531 | NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
532 |
533 | { ngx_string("fastcgi_path_info"), NULL,
534 | ngx_http_fastcgi_path_info_variable, 0,
535 | NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
536 |
537 | { ngx_null_string, NULL, NULL, 0, 0, 0 }
538 | };
539 |
540 |
541 | static ngx_str_t ngx_http_fastcgi_hide_headers[] = {
542 | ngx_string("Status"),
543 | ngx_string("X-Accel-Expires"),
544 | ngx_string("X-Accel-Redirect"),
545 | ngx_string("X-Accel-Limit-Rate"),
546 | ngx_string("X-Accel-Buffering"),
547 | ngx_string("X-Accel-Charset"),
548 | ngx_null_string
549 | };
550 |
551 |
552 | #if (NGX_HTTP_CACHE)
553 |
554 | static ngx_str_t ngx_http_fastcgi_hide_cache_headers[] = {
555 | ngx_string("Status"),
556 | ngx_string("X-Accel-Expires"),
557 | ngx_string("X-Accel-Redirect"),
558 | ngx_string("X-Accel-Limit-Rate"),
559 | ngx_string("X-Accel-Buffering"),
560 | ngx_string("X-Accel-Charset"),
561 | ngx_string("Set-Cookie"),
562 | ngx_string("P3P"),
563 | ngx_null_string
564 | };
565 |
566 | #endif
567 |
568 |
569 | static ngx_path_init_t ngx_http_fastcgi_temp_path = {
570 | ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 }
571 | };
572 |
573 |
574 | static ngx_inline short
575 | ngx_http_fastcgi_retain_request(ngx_http_request_t *r,
576 | ngx_http_fastcgi_ctx_t *f, ngx_http_fastcgi_loc_conf_t *c)
577 | {
578 | if (f == NULL) {
579 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
580 | }
581 | if (f->rid != 0) {
582 | // should assert this?
583 | return 0;
584 | }
585 | if (c == NULL) {
586 | c = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
587 | }
588 |
589 | int i;
590 |
591 | f->rid = 0;
592 |
593 | for (i=0; irequests[i] == NULL) {
595 | f->rid = (short)i+1;
596 | DLOG(r, "RETAIN rid %d", f->rid);
597 | c->requests[i] = r;
598 | break;
599 | }
600 | }
601 |
602 | return f->rid;
603 | }
604 |
605 |
606 | static ngx_inline void
607 | ngx_http_fastcgi_release_request(ngx_http_request_t *r,
608 | ngx_http_fastcgi_ctx_t *f, ngx_http_fastcgi_loc_conf_t *c)
609 | {
610 | if (f == NULL) {
611 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
612 | }
613 | if (f->rid < 1)
614 | return;
615 | if (c == NULL) {
616 | c = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
617 | }
618 |
619 | if (f != NULL) {
620 | DLOG(r, "RELEASE rid %d", f->rid);
621 | if (c != NULL) {
622 | c->requests[f->rid-1] = NULL;
623 | }
624 | f->rid = 0;
625 | }
626 | }
627 |
628 |
629 | static ngx_int_t
630 | ngx_http_fastcgi_handler(ngx_http_request_t *r)
631 | {
632 | ngx_int_t rc;
633 | ngx_http_upstream_t *u;
634 | ngx_http_fastcgi_ctx_t *f;
635 | ngx_http_fastcgi_loc_conf_t *flcf;
636 |
637 | if (r->subrequest_in_memory) {
638 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
639 | "ngx_http_fastcgi_module does not support "
640 | "subrequest in memory");
641 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
642 | }
643 |
644 | if (ngx_http_upstream_create(r) != NGX_OK) {
645 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
646 | }
647 |
648 | f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
649 | if (f == NULL) {
650 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
651 | }
652 |
653 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
654 |
655 | u = r->upstream;
656 |
657 | /* todo: store requests[] map per upstream peer, not in location scope */
658 |
659 | if (ngx_http_fastcgi_retain_request(r, f, flcf) == 0) {
660 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
661 | "ngx_http_fastcgi_module: max number of concurrent "
662 | "requests hit (%d requests)", NGX_FASTCGI_MPX_REQLIMIT);
663 | return NGX_HTTP_SERVICE_UNAVAILABLE;
664 | }
665 |
666 | ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
667 |
668 | if (flcf->fastcgi_lengths) {
669 | if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {
670 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
671 | }
672 | }
673 |
674 | u->schema.len = sizeof("fastcgi://") - 1;
675 | u->schema.data = (u_char *) "fastcgi://";
676 |
677 | u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;
678 |
679 | u->conf = &flcf->upstream;
680 |
681 | #if (NGX_HTTP_CACHE)
682 | u->create_key = ngx_http_fastcgi_create_key;
683 | #endif
684 | u->create_request = ngx_http_fastcgi_create_request;
685 | u->reinit_request = ngx_http_fastcgi_reinit_request;
686 | u->process_header = ngx_http_fastcgi_process_header;
687 | u->abort_request = ngx_http_fastcgi_abort_request;
688 | u->finalize_request = ngx_http_fastcgi_finalize_request;
689 |
690 | u->buffering = 1;
691 |
692 | u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
693 | if (u->pipe == NULL) {
694 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
695 | }
696 |
697 | u->pipe->input_filter = ngx_http_fastcgi_input_filter;
698 | u->pipe->input_ctx = r;
699 | // end
700 |
701 | rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
702 |
703 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
704 | return rc;
705 | }
706 |
707 | return NGX_DONE;
708 | }
709 |
710 |
711 | static ngx_int_t
712 | ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
713 | {
714 | ngx_url_t u;
715 |
716 | ngx_memzero(&u, sizeof(ngx_url_t));
717 |
718 | if (ngx_http_script_run(r, &u.url, flcf->fastcgi_lengths->elts, 0,
719 | flcf->fastcgi_values->elts)
720 | == NULL)
721 | {
722 | return NGX_ERROR;
723 | }
724 |
725 | u.no_resolve = 1;
726 |
727 | if (ngx_parse_url(r->pool, &u) != NGX_OK) {
728 | if (u.err) {
729 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
730 | "%s in upstream \"%V\"", u.err, &u.url);
731 | }
732 |
733 | return NGX_ERROR;
734 | }
735 |
736 | if (u.no_port) {
737 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
738 | "no port in upstream \"%V\"", &u.url);
739 | return NGX_ERROR;
740 | }
741 |
742 | r->upstream->resolved = ngx_pcalloc(r->pool,
743 | sizeof(ngx_http_upstream_resolved_t));
744 | if (r->upstream->resolved == NULL) {
745 | return NGX_ERROR;
746 | }
747 |
748 | if (u.addrs && u.addrs[0].sockaddr) {
749 | r->upstream->resolved->sockaddr = u.addrs[0].sockaddr;
750 | r->upstream->resolved->socklen = u.addrs[0].socklen;
751 | r->upstream->resolved->naddrs = 1;
752 | r->upstream->resolved->host = u.addrs[0].name;
753 |
754 | } else {
755 | r->upstream->resolved->host = u.host;
756 | r->upstream->resolved->port = u.port;
757 | }
758 |
759 | return NGX_OK;
760 | }
761 |
762 |
763 | #if (NGX_HTTP_CACHE)
764 |
765 | static ngx_int_t
766 | ngx_http_fastcgi_create_key(ngx_http_request_t *r)
767 | {
768 | ngx_str_t *key;
769 | ngx_http_fastcgi_loc_conf_t *flcf;
770 |
771 | key = ngx_array_push(&r->cache->keys);
772 | if (key == NULL) {
773 | return NGX_ERROR;
774 | }
775 |
776 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
777 |
778 | if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) {
779 | return NGX_ERROR;
780 | }
781 |
782 | return NGX_OK;
783 | }
784 |
785 | #endif
786 |
787 |
788 | static ngx_int_t
789 | ngx_http_fastcgi_create_request(ngx_http_request_t *r)
790 | {
791 | off_t file_pos;
792 | u_char ch, *pos;
793 | size_t size, len, key_len, val_len, padding;
794 | ngx_uint_t i, n, next;
795 | ngx_buf_t *b;
796 | ngx_chain_t *cl, *body;
797 | ngx_list_part_t *part;
798 | ngx_table_elt_t *header;
799 | ngx_http_script_code_pt code;
800 | ngx_http_script_engine_t e, le;
801 | ngx_http_fastcgi_header_t *h;
802 | ngx_http_fastcgi_loc_conf_t *flcf;
803 | ngx_http_script_len_code_pt lcode;
804 | ngx_http_fastcgi_ctx_t *f;
805 |
806 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
807 | //DLOG(r, "create rid %d", f->rid);
808 |
809 | len = 0;
810 |
811 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
812 |
813 | if (flcf->params_len) {
814 | ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
815 |
816 | ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes);
817 | le.flushed = 1;
818 |
819 | le.ip = flcf->params_len->elts;
820 | le.request = r;
821 |
822 | while (*(uintptr_t *) le.ip) {
823 |
824 | lcode = *(ngx_http_script_len_code_pt *) le.ip;
825 | key_len = lcode(&le);
826 |
827 | for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
828 | lcode = *(ngx_http_script_len_code_pt *) le.ip;
829 | }
830 | le.ip += sizeof(uintptr_t);
831 |
832 | len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
833 | }
834 | }
835 |
836 | if (flcf->upstream.pass_request_headers) {
837 |
838 | part = &r->headers_in.headers.part;
839 | header = part->elts;
840 |
841 | for (i = 0; /* void */; i++) {
842 |
843 | if (i >= part->nelts) {
844 | if (part->next == NULL) {
845 | break;
846 | }
847 |
848 | part = part->next;
849 | header = part->elts;
850 | i = 0;
851 | }
852 |
853 | len += ((sizeof("HTTP_") - 1 + header[i].key.len > 127) ? 4 : 1)
854 | + ((header[i].value.len > 127) ? 4 : 1)
855 | + sizeof("HTTP_") - 1 + header[i].key.len + header[i].value.len;
856 | }
857 | }
858 |
859 |
860 | if (len > 65535) {
861 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
862 | "fastcgi request record is too big: %uz", len);
863 | return NGX_ERROR;
864 | }
865 |
866 |
867 | padding = 8 - len % 8;
868 | padding = (padding == 8) ? 0 : padding;
869 |
870 |
871 | size = sizeof(ngx_http_fastcgi_header_t)
872 | + sizeof(ngx_http_fastcgi_begin_request_t)
873 |
874 | + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
875 | + len + padding
876 | + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
877 |
878 | + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
879 |
880 |
881 | b = ngx_create_temp_buf(r->pool, size);
882 | if (b == NULL) {
883 | return NGX_ERROR;
884 | }
885 |
886 | cl = ngx_alloc_chain_link(r->pool);
887 | if (cl == NULL) {
888 | return NGX_ERROR;
889 | }
890 |
891 | cl->buf = b;
892 |
893 | ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
894 | sizeof(ngx_http_fastcgi_request_start_t));
895 |
896 | ((ngx_http_fastcgi_request_start_t *)b->pos)->h0.request_id_hi =
897 | (u_char) ((f->rid >> 8) & 0xff);
898 | ((ngx_http_fastcgi_request_start_t *)b->pos)->h0.request_id_lo =
899 | (u_char) (f->rid & 0xff);
900 |
901 | h = (ngx_http_fastcgi_header_t *)
902 | (b->pos + sizeof(ngx_http_fastcgi_header_t)
903 | + sizeof(ngx_http_fastcgi_begin_request_t));
904 |
905 | h->content_length_hi = (u_char) ((len >> 8) & 0xff);
906 | h->content_length_lo = (u_char) (len & 0xff);
907 | h->padding_length = (u_char) padding;
908 | h->reserved = 0;
909 |
910 | b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
911 | + sizeof(ngx_http_fastcgi_begin_request_t)
912 | + sizeof(ngx_http_fastcgi_header_t);
913 |
914 |
915 | if (flcf->params_len) {
916 | ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
917 |
918 | e.ip = flcf->params->elts;
919 | e.pos = b->last;
920 | e.request = r;
921 | e.flushed = 1;
922 |
923 | le.ip = flcf->params_len->elts;
924 |
925 | while (*(uintptr_t *) le.ip) {
926 |
927 | lcode = *(ngx_http_script_len_code_pt *) le.ip;
928 | key_len = (u_char) lcode(&le);
929 |
930 | for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
931 | lcode = *(ngx_http_script_len_code_pt *) le.ip;
932 | }
933 | le.ip += sizeof(uintptr_t);
934 |
935 | *e.pos++ = (u_char) key_len;
936 |
937 | if (val_len > 127) {
938 | *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
939 | *e.pos++ = (u_char) ((val_len >> 16) & 0xff);
940 | *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
941 | *e.pos++ = (u_char) (val_len & 0xff);
942 |
943 | } else {
944 | *e.pos++ = (u_char) val_len;
945 | }
946 |
947 | while (*(uintptr_t *) e.ip) {
948 | code = *(ngx_http_script_code_pt *) e.ip;
949 | code((ngx_http_script_engine_t *) &e);
950 | }
951 | e.ip += sizeof(uintptr_t);
952 |
953 | ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
954 | "fastcgi param: \"%*s: %*s\"",
955 | key_len, e.pos - (key_len + val_len),
956 | val_len, e.pos - val_len);
957 | }
958 |
959 | b->last = e.pos;
960 | }
961 |
962 |
963 | if (flcf->upstream.pass_request_headers) {
964 |
965 | part = &r->headers_in.headers.part;
966 | header = part->elts;
967 |
968 | for (i = 0; /* void */; i++) {
969 |
970 | if (i >= part->nelts) {
971 | if (part->next == NULL) {
972 | break;
973 | }
974 |
975 | part = part->next;
976 | header = part->elts;
977 | i = 0;
978 | }
979 |
980 | len = sizeof("HTTP_") - 1 + header[i].key.len;
981 | if (len > 127) {
982 | *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80);
983 | *b->last++ = (u_char) ((len >> 16) & 0xff);
984 | *b->last++ = (u_char) ((len >> 8) & 0xff);
985 | *b->last++ = (u_char) (len & 0xff);
986 |
987 | } else {
988 | *b->last++ = (u_char) len;
989 | }
990 |
991 | len = header[i].value.len;
992 | if (len > 127) {
993 | *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80);
994 | *b->last++ = (u_char) ((len >> 16) & 0xff);
995 | *b->last++ = (u_char) ((len >> 8) & 0xff);
996 | *b->last++ = (u_char) (len & 0xff);
997 |
998 | } else {
999 | *b->last++ = (u_char) len;
1000 | }
1001 |
1002 | b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);
1003 |
1004 | for (n = 0; n < header[i].key.len; n++) {
1005 | ch = header[i].key.data[n];
1006 |
1007 | if (ch >= 'a' && ch <= 'z') {
1008 | ch &= ~0x20;
1009 |
1010 | } else if (ch == '-') {
1011 | ch = '_';
1012 | }
1013 |
1014 | *b->last++ = ch;
1015 | }
1016 |
1017 | b->last = ngx_copy(b->last, header[i].value.data,
1018 | header[i].value.len);
1019 | }
1020 | }
1021 |
1022 |
1023 | if (padding) {
1024 | ngx_memzero(b->last, padding);
1025 | b->last += padding;
1026 | }
1027 |
1028 |
1029 | h = (ngx_http_fastcgi_header_t *) b->last;
1030 | b->last += sizeof(ngx_http_fastcgi_header_t);
1031 |
1032 | h->version = 1;
1033 | h->type = NGX_HTTP_FASTCGI_PARAMS;
1034 |
1035 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff);
1036 | h->request_id_lo = (u_char) (f->rid & 0xff);
1037 |
1038 | h->content_length_hi = 0;
1039 | h->content_length_lo = 0;
1040 | h->padding_length = 0;
1041 | h->reserved = 0;
1042 |
1043 | h = (ngx_http_fastcgi_header_t *) b->last;
1044 | b->last += sizeof(ngx_http_fastcgi_header_t);
1045 |
1046 | if (flcf->upstream.pass_request_body) {
1047 | body = r->upstream->request_bufs;
1048 | r->upstream->request_bufs = cl;
1049 |
1050 | #if (NGX_SUPPRESS_WARN)
1051 | file_pos = 0;
1052 | pos = NULL;
1053 | #endif
1054 |
1055 | while (body) {
1056 |
1057 | if (body->buf->in_file) {
1058 | file_pos = body->buf->file_pos;
1059 |
1060 | } else {
1061 | pos = body->buf->pos;
1062 | }
1063 |
1064 | next = 0;
1065 |
1066 | do {
1067 | b = ngx_alloc_buf(r->pool);
1068 | if (b == NULL) {
1069 | return NGX_ERROR;
1070 | }
1071 |
1072 | ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
1073 |
1074 | if (body->buf->in_file) {
1075 | b->file_pos = file_pos;
1076 | file_pos += 32 * 1024;
1077 |
1078 | if (file_pos >= body->buf->file_last) {
1079 | file_pos = body->buf->file_last;
1080 | next = 1;
1081 | }
1082 |
1083 | b->file_last = file_pos;
1084 | len = (ngx_uint_t) (file_pos - b->file_pos);
1085 |
1086 | } else {
1087 | b->pos = pos;
1088 | pos += 32 * 1024;
1089 |
1090 | if (pos >= body->buf->last) {
1091 | pos = body->buf->last;
1092 | next = 1;
1093 | }
1094 |
1095 | b->last = pos;
1096 | len = (ngx_uint_t) (pos - b->pos);
1097 | }
1098 |
1099 | padding = 8 - len % 8;
1100 | padding = (padding == 8) ? 0 : padding;
1101 |
1102 | h->version = 1;
1103 | h->type = NGX_HTTP_FASTCGI_STDIN;
1104 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff);
1105 | h->request_id_lo = (u_char) (f->rid & 0xff);
1106 | h->content_length_hi = (u_char) ((len >> 8) & 0xff);
1107 | h->content_length_lo = (u_char) (len & 0xff);
1108 | h->padding_length = (u_char) padding;
1109 | h->reserved = 0;
1110 |
1111 | cl->next = ngx_alloc_chain_link(r->pool);
1112 | if (cl->next == NULL) {
1113 | return NGX_ERROR;
1114 | }
1115 |
1116 | cl = cl->next;
1117 | cl->buf = b;
1118 |
1119 | b = ngx_create_temp_buf(r->pool,
1120 | sizeof(ngx_http_fastcgi_header_t)
1121 | + padding);
1122 | if (b == NULL) {
1123 | return NGX_ERROR;
1124 | }
1125 |
1126 | if (padding) {
1127 | ngx_memzero(b->last, padding);
1128 | b->last += padding;
1129 | }
1130 |
1131 | h = (ngx_http_fastcgi_header_t *) b->last;
1132 | b->last += sizeof(ngx_http_fastcgi_header_t);
1133 |
1134 | cl->next = ngx_alloc_chain_link(r->pool);
1135 | if (cl->next == NULL) {
1136 | return NGX_ERROR;
1137 | }
1138 |
1139 | cl = cl->next;
1140 | cl->buf = b;
1141 |
1142 | } while (!next);
1143 |
1144 | body = body->next;
1145 | }
1146 |
1147 | } else {
1148 | r->upstream->request_bufs = cl;
1149 | }
1150 |
1151 | h->version = 1;
1152 | h->type = NGX_HTTP_FASTCGI_STDIN;
1153 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff);
1154 | h->request_id_lo = (u_char) (f->rid & 0xff);
1155 | h->content_length_hi = 0;
1156 | h->content_length_lo = 0;
1157 | h->padding_length = 0;
1158 | h->reserved = 0;
1159 |
1160 | cl->next = NULL;
1161 |
1162 | return NGX_OK;
1163 | }
1164 |
1165 |
1166 | static ngx_int_t
1167 | ngx_http_fastcgi_reinit_request(ngx_http_request_t *r)
1168 | {
1169 | ngx_http_fastcgi_ctx_t *f;
1170 |
1171 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1172 |
1173 | if (f == NULL) {
1174 | return NGX_OK;
1175 | }
1176 |
1177 | DLOG(r, "reinit rid %d", f->rid);
1178 |
1179 | f->state = ngx_http_fastcgi_st_version;
1180 | f->fastcgi_stdout = 0;
1181 | f->rid = 0;
1182 |
1183 | return NGX_OK;
1184 | }
1185 |
1186 |
1187 | static ngx_int_t
1188 | ngx_http_fastcgi_process_header(ngx_http_request_t *r)
1189 | {
1190 | u_char *p, *msg, *start, *last,
1191 | *part_start, *part_end;
1192 | size_t size;
1193 | ngx_str_t *status_line, *pattern;
1194 | ngx_int_t rc, status;
1195 | ngx_buf_t buf;
1196 | ngx_uint_t i;
1197 | ngx_table_elt_t *h;
1198 | ngx_http_upstream_t *u;
1199 | ngx_http_fastcgi_ctx_t *f;
1200 | ngx_http_upstream_header_t *hh;
1201 | ngx_http_fastcgi_loc_conf_t *flcf;
1202 | ngx_http_fastcgi_split_part_t *part;
1203 | ngx_http_upstream_main_conf_t *umcf;
1204 |
1205 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1206 |
1207 | umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
1208 |
1209 | u = r->upstream;
1210 |
1211 | for ( ;; ) {
1212 |
1213 | if (f->state < ngx_http_fastcgi_st_data) {
1214 |
1215 | f->pos = u->buffer.pos;
1216 | f->last = u->buffer.last;
1217 |
1218 | rc = ngx_http_fastcgi_process_record(r, f);
1219 |
1220 | u->buffer.pos = f->pos;
1221 | u->buffer.last = f->last;
1222 |
1223 | if (rc == NGX_AGAIN) {
1224 | return NGX_AGAIN;
1225 | }
1226 |
1227 | if (rc == NGX_ERROR) {
1228 | return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1229 | }
1230 |
1231 | if (f->type != NGX_HTTP_FASTCGI_STDOUT
1232 | && f->type != NGX_HTTP_FASTCGI_STDERR)
1233 | {
1234 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1235 | "upstream sent unexpected FastCGI record: %d",
1236 | f->type);
1237 |
1238 | return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1239 | }
1240 |
1241 | if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1242 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1243 | "upstream closed prematurely FastCGI stdout");
1244 |
1245 | return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1246 | }
1247 | }
1248 |
1249 | if (f->state == ngx_http_fastcgi_st_padding) {
1250 |
1251 | if (u->buffer.pos + f->padding < u->buffer.last) {
1252 | f->state = ngx_http_fastcgi_st_version;
1253 | u->buffer.pos += f->padding;
1254 |
1255 | continue;
1256 | }
1257 |
1258 | if (u->buffer.pos + f->padding == u->buffer.last) {
1259 | f->state = ngx_http_fastcgi_st_version;
1260 | u->buffer.pos = u->buffer.last;
1261 |
1262 | return NGX_AGAIN;
1263 | }
1264 |
1265 | f->padding -= u->buffer.last - u->buffer.pos;
1266 | u->buffer.pos = u->buffer.last;
1267 |
1268 | return NGX_AGAIN;
1269 | }
1270 |
1271 |
1272 | /* f->state == ngx_http_fastcgi_st_data */
1273 |
1274 | if (f->type == NGX_HTTP_FASTCGI_STDERR) {
1275 |
1276 | if (f->length) {
1277 | msg = u->buffer.pos;
1278 |
1279 | if (u->buffer.pos + f->length <= u->buffer.last) {
1280 | u->buffer.pos += f->length;
1281 | f->length = 0;
1282 | f->state = ngx_http_fastcgi_st_padding;
1283 |
1284 | } else {
1285 | f->length -= u->buffer.last - u->buffer.pos;
1286 | u->buffer.pos = u->buffer.last;
1287 | }
1288 |
1289 | for (p = u->buffer.pos - 1; msg < p; p--) {
1290 | if (*p != LF && *p != CR && *p != '.' && *p != ' ') {
1291 | break;
1292 | }
1293 | }
1294 |
1295 | p++;
1296 |
1297 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1298 | "FastCGI sent in stderr: \"%*s\"", p - msg, msg);
1299 |
1300 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
1301 |
1302 | if (flcf->catch_stderr) {
1303 | pattern = flcf->catch_stderr->elts;
1304 |
1305 | for (i = 0; i < flcf->catch_stderr->nelts; i++) {
1306 | if (ngx_strnstr(msg, (char *) pattern[i].data,
1307 | p - msg)
1308 | != NULL)
1309 | {
1310 | return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1311 | }
1312 | }
1313 | }
1314 |
1315 | if (u->buffer.pos == u->buffer.last) {
1316 |
1317 | if (!f->fastcgi_stdout) {
1318 |
1319 | /*
1320 | * the special handling the large number
1321 | * of the PHP warnings to not allocate memory
1322 | */
1323 |
1324 | u->buffer.pos = u->buffer.start;
1325 | u->buffer.last = u->buffer.start;
1326 | }
1327 |
1328 | return NGX_AGAIN;
1329 | }
1330 |
1331 | } else {
1332 | f->state = ngx_http_fastcgi_st_version;
1333 | }
1334 |
1335 | continue;
1336 | }
1337 |
1338 |
1339 | /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1340 |
1341 | f->fastcgi_stdout = 1;
1342 |
1343 | start = u->buffer.pos;
1344 |
1345 | if (u->buffer.pos + f->length < u->buffer.last) {
1346 |
1347 | /*
1348 | * set u->buffer.last to the end of the FastCGI record data
1349 | * for ngx_http_parse_header_line()
1350 | */
1351 |
1352 | last = u->buffer.last;
1353 | u->buffer.last = u->buffer.pos + f->length;
1354 |
1355 | } else {
1356 | last = NULL;
1357 | }
1358 |
1359 | for ( ;; ) {
1360 |
1361 | part_start = u->buffer.pos;
1362 | part_end = u->buffer.last;
1363 |
1364 | rc = ngx_http_parse_header_line(r, &u->buffer, 1);
1365 |
1366 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1367 | "http fastcgi parser: %d", rc);
1368 |
1369 | if (rc == NGX_AGAIN) {
1370 | break;
1371 | }
1372 |
1373 | if (rc == NGX_OK) {
1374 |
1375 | /* a header line has been parsed successfully */
1376 |
1377 | h = ngx_list_push(&u->headers_in.headers);
1378 | if (h == NULL) {
1379 | return NGX_ERROR;
1380 | }
1381 |
1382 | if (f->split_parts && f->split_parts->nelts) {
1383 |
1384 | part = f->split_parts->elts;
1385 | size = u->buffer.pos - part_start;
1386 |
1387 | for (i = 0; i < f->split_parts->nelts; i++) {
1388 | size += part[i].end - part[i].start;
1389 | }
1390 |
1391 | p = ngx_pnalloc(r->pool, size);
1392 | if (p == NULL) {
1393 | return NGX_ERROR;
1394 | }
1395 |
1396 | buf.pos = p;
1397 |
1398 | for (i = 0; i < f->split_parts->nelts; i++) {
1399 | p = ngx_cpymem(p, part[i].start,
1400 | part[i].end - part[i].start);
1401 | }
1402 |
1403 | p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);
1404 |
1405 | buf.last = p;
1406 |
1407 | f->split_parts->nelts = 0;
1408 |
1409 | rc = ngx_http_parse_header_line(r, &buf, 1);
1410 |
1411 | h->key.len = r->header_name_end - r->header_name_start;
1412 | h->key.data = r->header_name_start;
1413 | h->key.data[h->key.len] = '\0';
1414 |
1415 | h->value.len = r->header_end - r->header_start;
1416 | h->value.data = r->header_start;
1417 | h->value.data[h->value.len] = '\0';
1418 |
1419 | h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
1420 | if (h->lowcase_key == NULL) {
1421 | return NGX_ERROR;
1422 | }
1423 |
1424 | } else {
1425 |
1426 | h->key.len = r->header_name_end - r->header_name_start;
1427 | h->value.len = r->header_end - r->header_start;
1428 |
1429 | h->key.data = ngx_pnalloc(r->pool,
1430 | h->key.len + 1 + h->value.len + 1
1431 | + h->key.len);
1432 | if (h->key.data == NULL) {
1433 | return NGX_ERROR;
1434 | }
1435 |
1436 | h->value.data = h->key.data + h->key.len + 1;
1437 | h->lowcase_key = h->key.data + h->key.len + 1
1438 | + h->value.len + 1;
1439 |
1440 | ngx_cpystrn(h->key.data, r->header_name_start,
1441 | h->key.len + 1);
1442 | ngx_cpystrn(h->value.data, r->header_start,
1443 | h->value.len + 1);
1444 | }
1445 |
1446 | h->hash = r->header_hash;
1447 |
1448 | if (h->key.len == r->lowcase_index) {
1449 | ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
1450 |
1451 | } else {
1452 | ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
1453 | }
1454 |
1455 | hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
1456 | h->lowcase_key, h->key.len);
1457 |
1458 | if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
1459 | return NGX_ERROR;
1460 | }
1461 |
1462 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1463 | "http fastcgi header: \"%V: %V\"",
1464 | &h->key, &h->value);
1465 |
1466 | if (u->buffer.pos < u->buffer.last) {
1467 | continue;
1468 | }
1469 |
1470 | /* the end of the FastCGI record */
1471 |
1472 | break;
1473 | }
1474 |
1475 | if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1476 |
1477 | /* a whole header has been parsed successfully */
1478 |
1479 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1480 | "http fastcgi header done");
1481 |
1482 | if (u->headers_in.status) {
1483 | status_line = &u->headers_in.status->value;
1484 |
1485 | status = ngx_atoi(status_line->data, 3);
1486 |
1487 | if (status == NGX_ERROR) {
1488 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1489 | "upstream sent invalid status \"%V\"",
1490 | status_line);
1491 | return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1492 | }
1493 |
1494 | u->headers_in.status_n = status;
1495 | u->headers_in.status_line = *status_line;
1496 |
1497 | } else if (u->headers_in.location) {
1498 | u->headers_in.status_n = 302;
1499 | u->headers_in.status_line.len =
1500 | sizeof("302 Moved Temporarily") - 1;
1501 | u->headers_in.status_line.data =
1502 | (u_char *) "302 Moved Temporarily";
1503 |
1504 | } else {
1505 | u->headers_in.status_n = 200;
1506 | u->headers_in.status_line.len = sizeof("200 OK") - 1;
1507 | u->headers_in.status_line.data = (u_char *) "200 OK";
1508 | }
1509 |
1510 | if (u->state) {
1511 | u->state->status = u->headers_in.status_n;
1512 | }
1513 |
1514 | break;
1515 | }
1516 |
1517 | /* there was error while a header line parsing */
1518 |
1519 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1520 | "upstream sent invalid header");
1521 |
1522 | return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1523 | }
1524 |
1525 | if (last) {
1526 | u->buffer.last = last;
1527 | }
1528 |
1529 | f->length -= u->buffer.pos - start;
1530 |
1531 | if (f->length == 0) {
1532 | if (f->padding) {
1533 | f->state = ngx_http_fastcgi_st_padding;
1534 | } else {
1535 | f->state = ngx_http_fastcgi_st_version;
1536 | }
1537 | }
1538 |
1539 | if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1540 | return NGX_OK;
1541 | }
1542 |
1543 | if (rc == NGX_OK) {
1544 | continue;
1545 | }
1546 |
1547 | /* rc == NGX_AGAIN */
1548 |
1549 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1550 | "upstream split a header line in FastCGI records");
1551 |
1552 | if (f->split_parts == NULL) {
1553 | f->split_parts = ngx_array_create(r->pool, 1,
1554 | sizeof(ngx_http_fastcgi_split_part_t));
1555 | if (f->split_parts == NULL) {
1556 | return NGX_ERROR;
1557 | }
1558 | }
1559 |
1560 | part = ngx_array_push(f->split_parts);
1561 |
1562 | part->start = part_start;
1563 | part->end = part_end;
1564 |
1565 | if (u->buffer.pos < u->buffer.last) {
1566 | continue;
1567 | }
1568 |
1569 | return NGX_AGAIN;
1570 | }
1571 | }
1572 |
1573 |
1574 | static ngx_int_t
1575 | ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
1576 | {
1577 | u_char *m, *msg;
1578 | ngx_int_t rc;
1579 | ngx_buf_t *b, **prev;
1580 | ngx_chain_t *cl;
1581 | ngx_http_request_t *r;
1582 | ngx_http_fastcgi_ctx_t *f;
1583 |
1584 | if (buf->pos == buf->last) {
1585 | return NGX_OK;
1586 | }
1587 |
1588 | r = p->input_ctx;
1589 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1590 |
1591 | b = NULL;
1592 | prev = &buf->shadow;
1593 |
1594 | f->pos = buf->pos;
1595 | f->last = buf->last;
1596 |
1597 | for ( ;; ) {
1598 | if (f->state < ngx_http_fastcgi_st_data) {
1599 |
1600 | rc = ngx_http_fastcgi_process_record(r, f);
1601 |
1602 | if (rc == NGX_AGAIN) {
1603 | break;
1604 | }
1605 |
1606 | if (rc == NGX_ERROR) {
1607 | return NGX_ERROR;
1608 | }
1609 |
1610 | if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1611 | f->state = ngx_http_fastcgi_st_version;
1612 | p->upstream_done = 1;
1613 | // only done in finalize_request:
1614 | //ngx_http_fastcgi_release_request(r, f, NULL);
1615 |
1616 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1617 | "http fastcgi closed stdout");
1618 |
1619 | continue;
1620 | }
1621 |
1622 | if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
1623 | f->state = ngx_http_fastcgi_st_version;
1624 | p->upstream_done = 1;
1625 | // only done in finalize_request:
1626 | //ngx_http_fastcgi_release_request(r, f, NULL);
1627 |
1628 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1629 | "http fastcgi sent end request");
1630 |
1631 | break;
1632 | }
1633 | }
1634 |
1635 |
1636 | if (f->state == ngx_http_fastcgi_st_padding) {
1637 |
1638 | if (f->pos + f->padding < f->last) {
1639 | f->state = ngx_http_fastcgi_st_version;
1640 | f->pos += f->padding;
1641 |
1642 | continue;
1643 | }
1644 |
1645 | if (f->pos + f->padding == f->last) {
1646 | f->state = ngx_http_fastcgi_st_version;
1647 |
1648 | break;
1649 | }
1650 |
1651 | f->padding -= f->last - f->pos;
1652 |
1653 | break;
1654 | }
1655 |
1656 |
1657 | /* f->state == ngx_http_fastcgi_st_data */
1658 |
1659 | if (f->type == NGX_HTTP_FASTCGI_STDERR) {
1660 |
1661 | if (f->length) {
1662 |
1663 | if (f->pos == f->last) {
1664 | break;
1665 | }
1666 |
1667 | msg = f->pos;
1668 |
1669 | if (f->pos + f->length <= f->last) {
1670 | f->pos += f->length;
1671 | f->length = 0;
1672 | f->state = ngx_http_fastcgi_st_padding;
1673 |
1674 | } else {
1675 | f->length -= f->last - f->pos;
1676 | f->pos = f->last;
1677 | }
1678 |
1679 | for (m = f->pos - 1; msg < m; m--) {
1680 | if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
1681 | break;
1682 | }
1683 | }
1684 |
1685 | ngx_log_error(NGX_LOG_ERR, p->log, 0,
1686 | "FastCGI sent in stderr: \"%*s\"",
1687 | m + 1 - msg, msg);
1688 |
1689 | if (f->pos == f->last) {
1690 | break;
1691 | }
1692 |
1693 | } else {
1694 | f->state = ngx_http_fastcgi_st_version;
1695 | }
1696 |
1697 | continue;
1698 | }
1699 |
1700 |
1701 | /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1702 |
1703 | if (f->pos == f->last) {
1704 | break;
1705 | }
1706 |
1707 | if (p->free) {
1708 | b = p->free->buf;
1709 | p->free = p->free->next;
1710 |
1711 | } else {
1712 | b = ngx_alloc_buf(p->pool);
1713 | if (b == NULL) {
1714 | return NGX_ERROR;
1715 | }
1716 | }
1717 |
1718 | ngx_memzero(b, sizeof(ngx_buf_t));
1719 |
1720 | b->pos = f->pos;
1721 | b->start = buf->start;
1722 | b->end = buf->end;
1723 | b->tag = p->tag;
1724 | b->temporary = 1;
1725 | b->recycled = 1;
1726 |
1727 | *prev = b;
1728 | prev = &b->shadow;
1729 |
1730 | cl = ngx_alloc_chain_link(p->pool);
1731 | if (cl == NULL) {
1732 | return NGX_ERROR;
1733 | }
1734 |
1735 | cl->buf = b;
1736 | cl->next = NULL;
1737 |
1738 | if (p->in) {
1739 | *p->last_in = cl;
1740 | } else {
1741 | p->in = cl;
1742 | }
1743 | p->last_in = &cl->next;
1744 |
1745 |
1746 | /* STUB */ b->num = buf->num;
1747 |
1748 | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1749 | "input buf #%d %p", b->num, b->pos);
1750 |
1751 | if (f->pos + f->length < f->last) {
1752 |
1753 | if (f->padding) {
1754 | f->state = ngx_http_fastcgi_st_padding;
1755 | } else {
1756 | f->state = ngx_http_fastcgi_st_version;
1757 | }
1758 |
1759 | f->pos += f->length;
1760 | b->last = f->pos;
1761 |
1762 | continue;
1763 | }
1764 |
1765 | if (f->pos + f->length == f->last) {
1766 |
1767 | if (f->padding) {
1768 | f->state = ngx_http_fastcgi_st_padding;
1769 | } else {
1770 | f->state = ngx_http_fastcgi_st_version;
1771 | }
1772 |
1773 | b->last = f->last;
1774 |
1775 | break;
1776 | }
1777 |
1778 | f->length -= f->last - f->pos;
1779 |
1780 | b->last = f->last;
1781 |
1782 | break;
1783 |
1784 | }
1785 |
1786 | if (b) {
1787 | b->shadow = buf;
1788 | b->last_shadow = 1;
1789 |
1790 | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1791 | "input buf %p %z", b->pos, b->last - b->pos);
1792 |
1793 | return NGX_OK;
1794 | }
1795 |
1796 | /* there is no data record in the buf, add it to free chain */
1797 |
1798 | if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
1799 | return NGX_ERROR;
1800 | }
1801 |
1802 | return NGX_OK;
1803 | }
1804 |
1805 |
1806 | static ngx_int_t
1807 | ngx_http_fastcgi_process_record(ngx_http_request_t *r,
1808 | ngx_http_fastcgi_ctx_t *f)
1809 | {
1810 | u_char ch, *p;
1811 | //u_char rid_hi;
1812 | ngx_http_fastcgi_state_e state;
1813 |
1814 | state = f->state;
1815 |
1816 | for (p = f->pos; p < f->last; p++) {
1817 |
1818 | ch = *p;
1819 |
1820 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1821 | "http fastcgi record byte: %02Xd", ch);
1822 |
1823 | switch (state) {
1824 |
1825 | case ngx_http_fastcgi_st_version:
1826 | if (ch != 1) {
1827 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1828 | "upstream sent unsupported FastCGI "
1829 | "protocol version: %d", ch);
1830 | return NGX_ERROR;
1831 | }
1832 | state = ngx_http_fastcgi_st_type;
1833 | break;
1834 |
1835 | case ngx_http_fastcgi_st_type:
1836 | switch (ch) {
1837 | case NGX_HTTP_FASTCGI_STDOUT:
1838 | case NGX_HTTP_FASTCGI_STDERR:
1839 | case NGX_HTTP_FASTCGI_END_REQUEST:
1840 | f->type = (ngx_uint_t) ch;
1841 | break;
1842 | default:
1843 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1844 | "upstream sent invalid FastCGI "
1845 | "record type: %d", ch);
1846 | return NGX_ERROR;
1847 |
1848 | }
1849 | state = ngx_http_fastcgi_st_request_id_hi;
1850 | break;
1851 |
1852 | /* we support the single request per connection */
1853 |
1854 | case ngx_http_fastcgi_st_request_id_hi:
1855 | //rid_hi = ch;
1856 | state = ngx_http_fastcgi_st_request_id_lo;
1857 | break;
1858 |
1859 | case ngx_http_fastcgi_st_request_id_lo:
1860 | //DLOG(r, "r rid %d, rec rid %d", f->rid, (rid_hi << 8) + ch);
1861 | state = ngx_http_fastcgi_st_content_length_hi;
1862 | break;
1863 |
1864 | case ngx_http_fastcgi_st_content_length_hi:
1865 | f->length = ch << 8;
1866 | state = ngx_http_fastcgi_st_content_length_lo;
1867 | break;
1868 |
1869 | case ngx_http_fastcgi_st_content_length_lo:
1870 | f->length |= (size_t) ch;
1871 | state = ngx_http_fastcgi_st_padding_length;
1872 | break;
1873 |
1874 | case ngx_http_fastcgi_st_padding_length:
1875 | f->padding = (size_t) ch;
1876 | state = ngx_http_fastcgi_st_reserved;
1877 | break;
1878 |
1879 | case ngx_http_fastcgi_st_reserved:
1880 | state = ngx_http_fastcgi_st_data;
1881 |
1882 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1883 | "http fastcgi record length: %z", f->length);
1884 |
1885 | f->pos = p + 1;
1886 | f->state = state;
1887 |
1888 | return NGX_OK;
1889 |
1890 | /* suppress warning */
1891 | case ngx_http_fastcgi_st_data:
1892 | case ngx_http_fastcgi_st_padding:
1893 | break;
1894 | }
1895 | }
1896 |
1897 | f->state = state;
1898 |
1899 | return NGX_AGAIN;
1900 | }
1901 |
1902 |
1903 | static void
1904 | ngx_http_fastcgi_abort_request(ngx_http_request_t *r)
1905 | {
1906 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1907 | "abort http fastcgi request");
1908 | return;
1909 | }
1910 |
1911 |
1912 | static void
1913 | ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t status)
1914 | {
1915 | if (status == NGX_HTTP_CLIENT_CLOSED_REQUEST) {
1916 | DLOG(r, "ABORTED");
1917 | ngx_http_fastcgi_header_t *h;
1918 | ngx_buf_t *b;
1919 | ngx_chain_t *cl;
1920 | size_t size;
1921 |
1922 | size = sizeof(ngx_http_fastcgi_header_t);
1923 |
1924 | b = ngx_create_temp_buf(r->pool, size);
1925 | if (b == NULL) {
1926 | return;
1927 | }
1928 |
1929 | cl = ngx_alloc_chain_link(r->pool);
1930 | if (cl == NULL) {
1931 | return;
1932 | }
1933 |
1934 | cl->buf = b;
1935 | cl->next = NULL;
1936 |
1937 | r->upstream->request_bufs = cl;
1938 | b->last = b->pos + size;
1939 |
1940 | ngx_http_fastcgi_ctx_t *f;
1941 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1942 |
1943 | h = ((ngx_http_fastcgi_header_t *)b->pos);
1944 | h->version = 1;
1945 | h->type = NGX_HTTP_FASTCGI_ABORT_REQUEST;
1946 | h->request_id_hi = (u_char) ((f->rid >> 8) & 0xff);
1947 | h->request_id_lo = (u_char) (f->rid & 0xff);
1948 | h->content_length_hi = 0;
1949 | h->content_length_lo = 0;
1950 | h->padding_length = 0;
1951 | h->reserved = 0;
1952 | }
1953 | ngx_http_fastcgi_release_request(r, NULL, NULL);
1954 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1955 | "finalize http fastcgi request");
1956 |
1957 | return;
1958 | }
1959 |
1960 |
1961 | static ngx_int_t
1962 | ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
1963 | {
1964 | ngx_http_variable_t *var, *v;
1965 |
1966 | for (v = ngx_http_fastcgi_vars; v->name.len; v++) {
1967 | var = ngx_http_add_variable(cf, &v->name, v->flags);
1968 | if (var == NULL) {
1969 | return NGX_ERROR;
1970 | }
1971 |
1972 | var->get_handler = v->get_handler;
1973 | var->data = v->data;
1974 | }
1975 |
1976 | return NGX_OK;
1977 | }
1978 |
1979 |
1980 | static void *
1981 | ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
1982 | {
1983 | ngx_http_fastcgi_loc_conf_t *conf;
1984 |
1985 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));
1986 | if (conf == NULL) {
1987 | return NULL;
1988 | }
1989 |
1990 | int i;
1991 | for (i=0; irequests[i] = NULL;
1993 |
1994 | /*
1995 | * set by ngx_pcalloc():
1996 | *
1997 | * conf->upstream.bufs.num = 0;
1998 | * conf->upstream.ignore_headers = 0;
1999 | * conf->upstream.next_upstream = 0;
2000 | * conf->upstream.cache_use_stale = 0;
2001 | * conf->upstream.cache_methods = 0;
2002 | * conf->upstream.temp_path = NULL;
2003 | * conf->upstream.hide_headers_hash = { NULL, 0 };
2004 | * conf->upstream.uri = { 0, NULL };
2005 | * conf->upstream.location = NULL;
2006 | * conf->upstream.store_lengths = NULL;
2007 | * conf->upstream.store_values = NULL;
2008 | *
2009 | * conf->index.len = 0;
2010 | * conf->index.data = NULL;
2011 | */
2012 |
2013 | conf->upstream.store = NGX_CONF_UNSET;
2014 | conf->upstream.store_access = NGX_CONF_UNSET_UINT;
2015 | conf->upstream.buffering = NGX_CONF_UNSET;
2016 | conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
2017 |
2018 | conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
2019 | conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
2020 | conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
2021 |
2022 | conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
2023 | conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
2024 |
2025 | conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
2026 | conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
2027 | conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
2028 |
2029 | conf->upstream.pass_request_headers = NGX_CONF_UNSET;
2030 | conf->upstream.pass_request_body = NGX_CONF_UNSET;
2031 |
2032 | #if (NGX_HTTP_CACHE)
2033 | conf->upstream.cache = NGX_CONF_UNSET_PTR;
2034 | conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
2035 | conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
2036 | #endif
2037 |
2038 | conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
2039 | conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
2040 |
2041 | conf->upstream.intercept_errors = NGX_CONF_UNSET;
2042 |
2043 | /* "fastcgi_cyclic_temp_file" is disabled */
2044 | conf->upstream.cyclic_temp_file = 0;
2045 |
2046 | conf->catch_stderr = NGX_CONF_UNSET_PTR;
2047 |
2048 | return conf;
2049 | }
2050 |
2051 |
2052 | static char *
2053 | ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
2054 | {
2055 | ngx_http_fastcgi_loc_conf_t *prev = parent;
2056 | ngx_http_fastcgi_loc_conf_t *conf = child;
2057 |
2058 | u_char *p;
2059 | size_t size;
2060 | uintptr_t *code;
2061 | ngx_str_t *h;
2062 | ngx_uint_t i;
2063 | ngx_keyval_t *src;
2064 | ngx_hash_init_t hash;
2065 | ngx_http_script_compile_t sc;
2066 | ngx_http_script_copy_code_t *copy;
2067 |
2068 | if (conf->upstream.store != 0) {
2069 | ngx_conf_merge_value(conf->upstream.store,
2070 | prev->upstream.store, 0);
2071 |
2072 | if (conf->upstream.store_lengths == NULL) {
2073 | conf->upstream.store_lengths = prev->upstream.store_lengths;
2074 | conf->upstream.store_values = prev->upstream.store_values;
2075 | }
2076 | }
2077 |
2078 | ngx_conf_merge_uint_value(conf->upstream.store_access,
2079 | prev->upstream.store_access, 0600);
2080 |
2081 | ngx_conf_merge_value(conf->upstream.buffering,
2082 | prev->upstream.buffering, 1);
2083 |
2084 | ngx_conf_merge_value(conf->upstream.ignore_client_abort,
2085 | prev->upstream.ignore_client_abort, 0);
2086 |
2087 | ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
2088 | prev->upstream.connect_timeout, 60000);
2089 |
2090 | ngx_conf_merge_msec_value(conf->upstream.send_timeout,
2091 | prev->upstream.send_timeout, 60000);
2092 |
2093 | ngx_conf_merge_msec_value(conf->upstream.read_timeout,
2094 | prev->upstream.read_timeout, 60000);
2095 |
2096 | ngx_conf_merge_size_value(conf->upstream.send_lowat,
2097 | prev->upstream.send_lowat, 0);
2098 |
2099 | ngx_conf_merge_size_value(conf->upstream.buffer_size,
2100 | prev->upstream.buffer_size,
2101 | (size_t) ngx_pagesize);
2102 |
2103 |
2104 | ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
2105 | 8, ngx_pagesize);
2106 |
2107 | if (conf->upstream.bufs.num < 2) {
2108 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2109 | "there must be at least 2 \"fastcgi_buffers\"");
2110 | return NGX_CONF_ERROR;
2111 | }
2112 |
2113 |
2114 | size = conf->upstream.buffer_size;
2115 | if (size < conf->upstream.bufs.size) {
2116 | size = conf->upstream.bufs.size;
2117 | }
2118 |
2119 |
2120 | ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
2121 | prev->upstream.busy_buffers_size_conf,
2122 | NGX_CONF_UNSET_SIZE);
2123 |
2124 | if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
2125 | conf->upstream.busy_buffers_size = 2 * size;
2126 | } else {
2127 | conf->upstream.busy_buffers_size =
2128 | conf->upstream.busy_buffers_size_conf;
2129 | }
2130 |
2131 | if (conf->upstream.busy_buffers_size < size) {
2132 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2133 | "\"fastcgi_busy_buffers_size\" must be equal or bigger than "
2134 | "maximum of the value of \"fastcgi_buffer_size\" and "
2135 | "one of the \"fastcgi_buffers\"");
2136 |
2137 | return NGX_CONF_ERROR;
2138 | }
2139 |
2140 | if (conf->upstream.busy_buffers_size
2141 | > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
2142 | {
2143 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2144 | "\"fastcgi_busy_buffers_size\" must be less than "
2145 | "the size of all \"fastcgi_buffers\" minus one buffer");
2146 |
2147 | return NGX_CONF_ERROR;
2148 | }
2149 |
2150 |
2151 | ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
2152 | prev->upstream.temp_file_write_size_conf,
2153 | NGX_CONF_UNSET_SIZE);
2154 |
2155 | if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
2156 | conf->upstream.temp_file_write_size = 2 * size;
2157 | } else {
2158 | conf->upstream.temp_file_write_size =
2159 | conf->upstream.temp_file_write_size_conf;
2160 | }
2161 |
2162 | if (conf->upstream.temp_file_write_size < size) {
2163 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2164 | "\"fastcgi_temp_file_write_size\" must be equal or bigger than "
2165 | "maximum of the value of \"fastcgi_buffer_size\" and "
2166 | "one of the \"fastcgi_buffers\"");
2167 |
2168 | return NGX_CONF_ERROR;
2169 | }
2170 |
2171 |
2172 | ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
2173 | prev->upstream.max_temp_file_size_conf,
2174 | NGX_CONF_UNSET_SIZE);
2175 |
2176 | if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
2177 | conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
2178 | } else {
2179 | conf->upstream.max_temp_file_size =
2180 | conf->upstream.max_temp_file_size_conf;
2181 | }
2182 |
2183 | if (conf->upstream.max_temp_file_size != 0
2184 | && conf->upstream.max_temp_file_size < size)
2185 | {
2186 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2187 | "\"fastcgi_max_temp_file_size\" must be equal to zero to disable "
2188 | "the temporary files usage or must be equal or bigger than "
2189 | "maximum of the value of \"fastcgi_buffer_size\" and "
2190 | "one of the \"fastcgi_buffers\"");
2191 |
2192 | return NGX_CONF_ERROR;
2193 | }
2194 |
2195 |
2196 | ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
2197 | prev->upstream.ignore_headers,
2198 | NGX_CONF_BITMASK_SET);
2199 |
2200 |
2201 | ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
2202 | prev->upstream.next_upstream,
2203 | (NGX_CONF_BITMASK_SET
2204 | |NGX_HTTP_UPSTREAM_FT_ERROR
2205 | |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
2206 |
2207 | if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
2208 | conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
2209 | |NGX_HTTP_UPSTREAM_FT_OFF;
2210 | }
2211 |
2212 | if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
2213 | prev->upstream.temp_path,
2214 | &ngx_http_fastcgi_temp_path)
2215 | != NGX_OK)
2216 | {
2217 | return NGX_CONF_ERROR;
2218 | }
2219 |
2220 | #if (NGX_HTTP_CACHE)
2221 |
2222 | ngx_conf_merge_ptr_value(conf->upstream.cache,
2223 | prev->upstream.cache, NULL);
2224 |
2225 | if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
2226 | ngx_shm_zone_t *shm_zone;
2227 |
2228 | shm_zone = conf->upstream.cache;
2229 |
2230 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2231 | "\"fastcgi_cache\" zone \"%V\" is unknown",
2232 | &shm_zone->shm.name);
2233 |
2234 | return NGX_CONF_ERROR;
2235 | }
2236 |
2237 | ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
2238 | prev->upstream.cache_min_uses, 1);
2239 |
2240 | ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
2241 | prev->upstream.cache_use_stale,
2242 | (NGX_CONF_BITMASK_SET
2243 | |NGX_HTTP_UPSTREAM_FT_OFF));
2244 |
2245 | if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
2246 | conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
2247 | |NGX_HTTP_UPSTREAM_FT_OFF;
2248 | }
2249 |
2250 | if (conf->upstream.cache_methods == 0) {
2251 | conf->upstream.cache_methods = prev->upstream.cache_methods;
2252 | }
2253 |
2254 | conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
2255 |
2256 | ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
2257 | prev->upstream.cache_valid, NULL);
2258 |
2259 | if (conf->cache_key.value.data == NULL) {
2260 | conf->cache_key = prev->cache_key;
2261 | }
2262 |
2263 | #endif
2264 |
2265 | ngx_conf_merge_value(conf->upstream.pass_request_headers,
2266 | prev->upstream.pass_request_headers, 1);
2267 | ngx_conf_merge_value(conf->upstream.pass_request_body,
2268 | prev->upstream.pass_request_body, 1);
2269 |
2270 | ngx_conf_merge_value(conf->upstream.intercept_errors,
2271 | prev->upstream.intercept_errors, 0);
2272 |
2273 | ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);
2274 |
2275 |
2276 | ngx_conf_merge_str_value(conf->index, prev->index, "");
2277 |
2278 | hash.max_size = 512;
2279 | hash.bucket_size = ngx_align(64, ngx_cacheline_size);
2280 | hash.name = "fastcgi_hide_headers_hash";
2281 |
2282 | #if (NGX_HTTP_CACHE)
2283 |
2284 | h = conf->upstream.cache ? ngx_http_fastcgi_hide_cache_headers:
2285 | ngx_http_fastcgi_hide_headers;
2286 | #else
2287 |
2288 | h = ngx_http_fastcgi_hide_headers;
2289 |
2290 | #endif
2291 |
2292 | if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
2293 | &prev->upstream, h, &hash)
2294 | != NGX_OK)
2295 | {
2296 | return NGX_CONF_ERROR;
2297 | }
2298 |
2299 | if (conf->upstream.upstream == NULL) {
2300 | conf->upstream.upstream = prev->upstream.upstream;
2301 | }
2302 |
2303 | if (conf->fastcgi_lengths == NULL) {
2304 | conf->fastcgi_lengths = prev->fastcgi_lengths;
2305 | conf->fastcgi_values = prev->fastcgi_values;
2306 | }
2307 |
2308 | #if (NGX_PCRE)
2309 | if (conf->split_regex == NULL) {
2310 | conf->split_regex = prev->split_regex;
2311 | conf->split_name = prev->split_name;
2312 | }
2313 | #endif
2314 |
2315 | if (conf->params_source == NULL) {
2316 | conf->flushes = prev->flushes;
2317 | conf->params_len = prev->params_len;
2318 | conf->params = prev->params;
2319 | conf->params_source = prev->params_source;
2320 |
2321 | if (conf->params_source == NULL) {
2322 | return NGX_CONF_OK;
2323 | }
2324 | }
2325 |
2326 | conf->params_len = ngx_array_create(cf->pool, 64, 1);
2327 | if (conf->params_len == NULL) {
2328 | return NGX_CONF_ERROR;
2329 | }
2330 |
2331 | conf->params = ngx_array_create(cf->pool, 512, 1);
2332 | if (conf->params == NULL) {
2333 | return NGX_CONF_ERROR;
2334 | }
2335 |
2336 | src = conf->params_source->elts;
2337 | for (i = 0; i < conf->params_source->nelts; i++) {
2338 |
2339 | if (ngx_http_script_variables_count(&src[i].value) == 0) {
2340 | copy = ngx_array_push_n(conf->params_len,
2341 | sizeof(ngx_http_script_copy_code_t));
2342 | if (copy == NULL) {
2343 | return NGX_CONF_ERROR;
2344 | }
2345 |
2346 | copy->code = (ngx_http_script_code_pt)
2347 | ngx_http_script_copy_len_code;
2348 | copy->len = src[i].key.len;
2349 |
2350 |
2351 | copy = ngx_array_push_n(conf->params_len,
2352 | sizeof(ngx_http_script_copy_code_t));
2353 | if (copy == NULL) {
2354 | return NGX_CONF_ERROR;
2355 | }
2356 |
2357 | copy->code = (ngx_http_script_code_pt)
2358 | ngx_http_script_copy_len_code;
2359 | copy->len = src[i].value.len;
2360 |
2361 |
2362 | size = (sizeof(ngx_http_script_copy_code_t)
2363 | + src[i].key.len + src[i].value.len
2364 | + sizeof(uintptr_t) - 1)
2365 | & ~(sizeof(uintptr_t) - 1);
2366 |
2367 | copy = ngx_array_push_n(conf->params, size);
2368 | if (copy == NULL) {
2369 | return NGX_CONF_ERROR;
2370 | }
2371 |
2372 | copy->code = ngx_http_script_copy_code;
2373 | copy->len = src[i].key.len + src[i].value.len;
2374 |
2375 | p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
2376 |
2377 | p = ngx_cpymem(p, src[i].key.data, src[i].key.len);
2378 | ngx_memcpy(p, src[i].value.data, src[i].value.len);
2379 |
2380 | } else {
2381 | copy = ngx_array_push_n(conf->params_len,
2382 | sizeof(ngx_http_script_copy_code_t));
2383 | if (copy == NULL) {
2384 | return NGX_CONF_ERROR;
2385 | }
2386 |
2387 | copy->code = (ngx_http_script_code_pt)
2388 | ngx_http_script_copy_len_code;
2389 | copy->len = src[i].key.len;
2390 |
2391 |
2392 | size = (sizeof(ngx_http_script_copy_code_t)
2393 | + src[i].key.len + sizeof(uintptr_t) - 1)
2394 | & ~(sizeof(uintptr_t) - 1);
2395 |
2396 | copy = ngx_array_push_n(conf->params, size);
2397 | if (copy == NULL) {
2398 | return NGX_CONF_ERROR;
2399 | }
2400 |
2401 | copy->code = ngx_http_script_copy_code;
2402 | copy->len = src[i].key.len;
2403 |
2404 | p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
2405 | ngx_memcpy(p, src[i].key.data, src[i].key.len);
2406 |
2407 |
2408 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2409 |
2410 | sc.cf = cf;
2411 | sc.source = &src[i].value;
2412 | sc.flushes = &conf->flushes;
2413 | sc.lengths = &conf->params_len;
2414 | sc.values = &conf->params;
2415 |
2416 | if (ngx_http_script_compile(&sc) != NGX_OK) {
2417 | return NGX_CONF_ERROR;
2418 | }
2419 | }
2420 |
2421 | code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
2422 | if (code == NULL) {
2423 | return NGX_CONF_ERROR;
2424 | }
2425 |
2426 | *code = (uintptr_t) NULL;
2427 |
2428 |
2429 | code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
2430 | if (code == NULL) {
2431 | return NGX_CONF_ERROR;
2432 | }
2433 |
2434 | *code = (uintptr_t) NULL;
2435 | }
2436 |
2437 | code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
2438 | if (code == NULL) {
2439 | return NGX_CONF_ERROR;
2440 | }
2441 |
2442 | *code = (uintptr_t) NULL;
2443 |
2444 | return NGX_CONF_OK;
2445 | }
2446 |
2447 |
2448 | static ngx_int_t
2449 | ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
2450 | ngx_http_variable_value_t *v, uintptr_t data)
2451 | {
2452 | u_char *p;
2453 | ngx_http_fastcgi_ctx_t *f;
2454 | ngx_http_fastcgi_loc_conf_t *flcf;
2455 |
2456 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2457 |
2458 | f = ngx_http_fastcgi_split(r, flcf);
2459 |
2460 | if (f == NULL) {
2461 | return NGX_ERROR;
2462 | }
2463 |
2464 | if (f->script_name.len == 0
2465 | || f->script_name.data[f->script_name.len - 1] != '/')
2466 | {
2467 | v->len = f->script_name.len;
2468 | v->valid = 1;
2469 | v->no_cacheable = 0;
2470 | v->not_found = 0;
2471 | v->data = f->script_name.data;
2472 |
2473 | return NGX_OK;
2474 | }
2475 |
2476 | v->len = f->script_name.len + flcf->index.len;
2477 |
2478 | v->data = ngx_pnalloc(r->pool, v->len);
2479 | if (v->data == NULL) {
2480 | return NGX_ERROR;
2481 | }
2482 |
2483 | p = ngx_copy(v->data, f->script_name.data, f->script_name.len);
2484 | ngx_memcpy(p, flcf->index.data, flcf->index.len);
2485 |
2486 | return NGX_OK;
2487 | }
2488 |
2489 |
2490 | static ngx_int_t
2491 | ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
2492 | ngx_http_variable_value_t *v, uintptr_t data)
2493 | {
2494 | ngx_http_fastcgi_ctx_t *f;
2495 | ngx_http_fastcgi_loc_conf_t *flcf;
2496 |
2497 | flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2498 |
2499 | f = ngx_http_fastcgi_split(r, flcf);
2500 |
2501 | if (f == NULL) {
2502 | return NGX_ERROR;
2503 | }
2504 |
2505 | v->len = f->path_info.len;
2506 | v->valid = 1;
2507 | v->no_cacheable = 0;
2508 | v->not_found = 0;
2509 | v->data = f->path_info.data;
2510 |
2511 | return NGX_OK;
2512 | }
2513 |
2514 |
2515 | static ngx_http_fastcgi_ctx_t *
2516 | ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
2517 | {
2518 | ngx_http_fastcgi_ctx_t *f;
2519 | #if (NGX_PCRE)
2520 | ngx_int_t n;
2521 | int captures[(1 + 2) * 3];
2522 |
2523 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
2524 |
2525 | if (f == NULL) {
2526 | f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
2527 | if (f == NULL) {
2528 | return NULL;
2529 | }
2530 |
2531 | ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
2532 | }
2533 |
2534 | if (f->script_name.len) {
2535 | return f;
2536 | }
2537 |
2538 | if (flcf->split_regex == NULL) {
2539 | f->script_name = r->uri;
2540 | return f;
2541 | }
2542 |
2543 | n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);
2544 |
2545 | if (n >= 0) { /* match */
2546 | f->script_name.len = captures[3] - captures[2];
2547 | f->script_name.data = r->uri.data;
2548 |
2549 | f->path_info.len = captures[5] - captures[4];
2550 | f->path_info.data = r->uri.data + f->script_name.len;
2551 |
2552 | return f;
2553 | }
2554 |
2555 | if (n == NGX_REGEX_NO_MATCHED) {
2556 | f->script_name = r->uri;
2557 | return f;
2558 | }
2559 |
2560 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
2561 | ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
2562 | n, &r->uri, &flcf->split_name);
2563 | return NULL;
2564 |
2565 | #else
2566 |
2567 | f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
2568 |
2569 | if (f == NULL) {
2570 | f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
2571 | if (f == NULL) {
2572 | return NULL;
2573 | }
2574 |
2575 | ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
2576 | }
2577 |
2578 | f->script_name = r->uri;
2579 |
2580 | return f;
2581 |
2582 | #endif
2583 | }
2584 |
2585 |
2586 | static char *
2587 | ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2588 | {
2589 | ngx_http_fastcgi_loc_conf_t *flcf = conf;
2590 |
2591 | ngx_url_t u;
2592 | ngx_str_t *value, *url;
2593 | ngx_uint_t n;
2594 | ngx_http_core_loc_conf_t *clcf;
2595 | ngx_http_script_compile_t sc;
2596 |
2597 | if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
2598 | return "is duplicate";
2599 | }
2600 |
2601 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
2602 |
2603 | clcf->handler = ngx_http_fastcgi_handler;
2604 |
2605 | if (clcf->name.data[clcf->name.len - 1] == '/') {
2606 | clcf->auto_redirect = 1;
2607 | }
2608 |
2609 | value = cf->args->elts;
2610 |
2611 | url = &value[1];
2612 |
2613 | n = ngx_http_script_variables_count(url);
2614 |
2615 | if (n) {
2616 |
2617 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2618 |
2619 | sc.cf = cf;
2620 | sc.source = url;
2621 | sc.lengths = &flcf->fastcgi_lengths;
2622 | sc.values = &flcf->fastcgi_values;
2623 | sc.variables = n;
2624 | sc.complete_lengths = 1;
2625 | sc.complete_values = 1;
2626 |
2627 | if (ngx_http_script_compile(&sc) != NGX_OK) {
2628 | return NGX_CONF_ERROR;
2629 | }
2630 |
2631 | return NGX_CONF_OK;
2632 | }
2633 |
2634 | ngx_memzero(&u, sizeof(ngx_url_t));
2635 |
2636 | u.url = value[1];
2637 | u.no_resolve = 1;
2638 |
2639 | flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
2640 | if (flcf->upstream.upstream == NULL) {
2641 | return NGX_CONF_ERROR;
2642 | }
2643 |
2644 | return NGX_CONF_OK;
2645 | }
2646 |
2647 |
2648 | static char *
2649 | ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2650 | {
2651 | #if (NGX_PCRE)
2652 | ngx_http_fastcgi_loc_conf_t *flcf = conf;
2653 |
2654 | ngx_str_t *value;
2655 | ngx_regex_compile_t rc;
2656 | u_char errstr[NGX_MAX_CONF_ERRSTR];
2657 |
2658 | value = cf->args->elts;
2659 |
2660 | flcf->split_name = value[1];
2661 |
2662 | ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
2663 |
2664 | rc.pattern = value[1];
2665 | rc.pool = cf->pool;
2666 | rc.err.len = NGX_MAX_CONF_ERRSTR;
2667 | rc.err.data = errstr;
2668 |
2669 | if (ngx_regex_compile(&rc) != NGX_OK) {
2670 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
2671 | return NGX_CONF_ERROR;
2672 | }
2673 |
2674 | if (rc.captures != 2) {
2675 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2676 | "pattern \"%V\" must have 2 captures", &value[1]);
2677 | return NGX_CONF_ERROR;
2678 | }
2679 |
2680 | flcf->split_regex = rc.regex;
2681 |
2682 | return NGX_CONF_OK;
2683 |
2684 | #else
2685 |
2686 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2687 | "\"%V\" requires PCRE library", &cmd->name);
2688 | return NGX_CONF_ERROR;
2689 |
2690 | #endif
2691 | }
2692 |
2693 |
2694 | static char *
2695 | ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2696 | {
2697 | ngx_http_fastcgi_loc_conf_t *flcf = conf;
2698 |
2699 | ngx_str_t *value;
2700 | ngx_http_script_compile_t sc;
2701 |
2702 | if (flcf->upstream.store != NGX_CONF_UNSET
2703 | || flcf->upstream.store_lengths)
2704 | {
2705 | return "is duplicate";
2706 | }
2707 |
2708 | value = cf->args->elts;
2709 |
2710 | if (ngx_strcmp(value[1].data, "off") == 0) {
2711 | flcf->upstream.store = 0;
2712 | return NGX_CONF_OK;
2713 | }
2714 |
2715 | #if (NGX_HTTP_CACHE)
2716 |
2717 | if (flcf->upstream.cache != NGX_CONF_UNSET_PTR
2718 | && flcf->upstream.cache != NULL)
2719 | {
2720 | return "is incompatible with \"fastcgi_cache\"";
2721 | }
2722 |
2723 | #endif
2724 |
2725 | if (ngx_strcmp(value[1].data, "on") == 0) {
2726 | flcf->upstream.store = 1;
2727 | return NGX_CONF_OK;
2728 | }
2729 |
2730 | /* include the terminating '\0' into script */
2731 | value[1].len++;
2732 |
2733 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2734 |
2735 | sc.cf = cf;
2736 | sc.source = &value[1];
2737 | sc.lengths = &flcf->upstream.store_lengths;
2738 | sc.values = &flcf->upstream.store_values;
2739 | sc.variables = ngx_http_script_variables_count(&value[1]);
2740 | sc.complete_lengths = 1;
2741 | sc.complete_values = 1;
2742 |
2743 | if (ngx_http_script_compile(&sc) != NGX_OK) {
2744 | return NGX_CONF_ERROR;
2745 | }
2746 |
2747 | return NGX_CONF_OK;
2748 | }
2749 |
2750 |
2751 | #if (NGX_HTTP_CACHE)
2752 |
2753 | static char *
2754 | ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2755 | {
2756 | ngx_http_fastcgi_loc_conf_t *flcf = conf;
2757 |
2758 | ngx_str_t *value;
2759 |
2760 | value = cf->args->elts;
2761 |
2762 | if (flcf->upstream.cache != NGX_CONF_UNSET_PTR) {
2763 | return "is duplicate";
2764 | }
2765 |
2766 | if (ngx_strcmp(value[1].data, "off") == 0) {
2767 | flcf->upstream.cache = NULL;
2768 | return NGX_CONF_OK;
2769 | }
2770 |
2771 | if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) {
2772 | return "is incompatible with \"fastcgi_store\"";
2773 | }
2774 |
2775 | flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
2776 | &ngx_http_fastcgi_module);
2777 | if (flcf->upstream.cache == NULL) {
2778 | return NGX_CONF_ERROR;
2779 | }
2780 |
2781 | return NGX_CONF_OK;
2782 | }
2783 |
2784 |
2785 | static char *
2786 | ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2787 | {
2788 | ngx_http_fastcgi_loc_conf_t *flcf = conf;
2789 |
2790 | ngx_str_t *value;
2791 | ngx_http_compile_complex_value_t ccv;
2792 |
2793 | value = cf->args->elts;
2794 |
2795 | if (flcf->cache_key.value.len) {
2796 | return "is duplicate";
2797 | }
2798 |
2799 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
2800 |
2801 | ccv.cf = cf;
2802 | ccv.value = &value[1];
2803 | ccv.complex_value = &flcf->cache_key;
2804 |
2805 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
2806 | return NGX_CONF_ERROR;
2807 | }
2808 |
2809 | return NGX_CONF_OK;
2810 | }
2811 |
2812 | #endif
2813 |
2814 |
2815 | static char *
2816 | ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
2817 | {
2818 | #if (NGX_FREEBSD)
2819 | ssize_t *np = data;
2820 |
2821 | if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
2822 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2823 | "\"fastcgi_send_lowat\" must be less than %d "
2824 | "(sysctl net.inet.tcp.sendspace)",
2825 | ngx_freebsd_net_inet_tcp_sendspace);
2826 |
2827 | return NGX_CONF_ERROR;
2828 | }
2829 |
2830 | #elif !(NGX_HAVE_SO_SNDLOWAT)
2831 | ssize_t *np = data;
2832 |
2833 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
2834 | "\"fastcgi_send_lowat\" is not supported, ignored");
2835 |
2836 | *np = 0;
2837 |
2838 | #endif
2839 |
2840 | return NGX_CONF_OK;
2841 | }
2842 |
2843 |
2844 | static char *
2845 | ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
2846 | ngx_command_t *cmd, void *conf)
2847 | {
2848 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2849 | "\"fastcgi_upstream_max_fails\" is not supported, "
2850 | "use the \"max_fails\" parameter of the \"server\" directive ",
2851 | "inside the \"upstream\" block");
2852 |
2853 | return NGX_CONF_ERROR;
2854 | }
2855 |
2856 |
2857 | static char *
2858 | ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
2859 | ngx_command_t *cmd, void *conf)
2860 | {
2861 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2862 | "\"fastcgi_upstream_fail_timeout\" is not supported, "
2863 | "use the \"fail_timeout\" parameter of the \"server\" directive ",
2864 | "inside the \"upstream\" block");
2865 |
2866 | return NGX_CONF_ERROR;
2867 | }
2868 |
--------------------------------------------------------------------------------
/servers/afcgitest/Makefile:
--------------------------------------------------------------------------------
1 | INCDIRS = /opt/local/include
2 | LIBDIRS = /opt/local/lib
3 | LIBS = event
4 | SOURCES = sockutil.c afcgitest.c
5 | EXECUTABLE = afcgitest
6 |
7 | CFLAGS = -Wall $(addprefix -I, $(INCDIRS))
8 | LDLIBS = $(addprefix -l, $(LIBS) $(LIBS_$(notdir $*)))
9 | LDFLAGS = $(addprefix -L, $(LIBDIRS)) $(LDLIBS)
10 | OBJECTS = $(SOURCES:.c=.o)
11 |
12 | #-----------------------------------------------------
13 | # Options
14 |
15 | # Optimize
16 | #CFLAGS += -O3 -DNDEBUG
17 |
18 | # If Mac OS X:
19 | CFLAGS += -arch i386
20 | LDFLAGS += -arch i386
21 |
22 | #-----------------------------------------------------
23 |
24 | all: $(SOURCES) $(EXECUTABLE)
25 |
26 | $(EXECUTABLE): $(OBJECTS)
27 | $(CC) $(LDFLAGS) $(OBJECTS) -o $@
28 |
29 | .c.o:
30 | $(CC) $(CFLAGS) -c $< -o $@
31 |
32 | clean:
33 | rm -rf *.o $(EXECUTABLE)
34 |
35 | .PHONY: all clean
36 |
--------------------------------------------------------------------------------
/servers/afcgitest/afcgitest.c:
--------------------------------------------------------------------------------
1 | #include "sockutil.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include /* for inet_ntop */
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include
21 |
22 | #define DEBUG_RESPONSE_DELAY 5
23 |
24 | #ifndef DEBUG_RESPONSE_DELAY
25 | #define DEBUG_RESPONSE_DELAY 0
26 | #endif
27 |
28 | #define AZ(foo) assert((foo) == 0)
29 | #define AN(foo) assert((foo) != 0)
30 |
31 | #define FCGI_LISTENSOCK_FILENO 0
32 | #define FLAG_KEEP_CONN 1
33 | #define REQUESTS_MAX __SHRT_MAX__+__SHRT_MAX__
34 |
35 | #define ROLE_RESPONDER 1
36 | #define ROLE_AUTHORIZER 2
37 | #define ROLE_FILTER 3
38 |
39 |
40 | typedef struct {
41 | struct event ev;
42 | } server_t;
43 |
44 |
45 | typedef struct {
46 | uint16_t id;
47 | uint16_t role;
48 | uint8_t keepconn;
49 | uint8_t stdin_eof;
50 | uint8_t terminate;
51 | uint8_t padding;
52 | struct bufferevent *bev;
53 | } request_t;
54 |
55 |
56 | request_t *_requests[REQUESTS_MAX];
57 |
58 |
59 | enum {
60 | TYPE_BEGIN_REQUEST = 1,
61 | TYPE_ABORT_REQUEST = 2,
62 | TYPE_END_REQUEST = 3,
63 | TYPE_PARAMS = 4,
64 | TYPE_STDIN = 5,
65 | TYPE_STDOUT = 6,
66 | TYPE_STDERR = 7,
67 | TYPE_DATA = 8,
68 | TYPE_GET_VALUES = 9,
69 | TYPE_GET_VALUES_RESULT = 10,
70 | TYPE_UNKNOWN = 11
71 | };
72 |
73 |
74 | enum {
75 | PROTOST_REQUEST_COMPLETE = 0,
76 | PROTOST_CANT_MPX_CONN = 1,
77 | PROTOST_OVERLOADED = 2,
78 | PROTOST_UNKNOWN_ROLE = 3
79 | };
80 |
81 |
82 | typedef struct {
83 | uint8_t version;
84 | uint8_t type;
85 | uint8_t requestIdB1;
86 | uint8_t requestIdB0;
87 | uint8_t contentLengthB1;
88 | uint8_t contentLengthB0;
89 | uint8_t paddingLength;
90 | uint8_t reserved;
91 | } header_t; // 8
92 |
93 |
94 | typedef struct {
95 | uint8_t roleB1;
96 | uint8_t roleB0;
97 | uint8_t flags;
98 | uint8_t reserved[5];
99 | } begin_request_t; // 8
100 |
101 |
102 | typedef struct {
103 | header_t header;
104 | uint8_t appStatusB3;
105 | uint8_t appStatusB2;
106 | uint8_t appStatusB1;
107 | uint8_t appStatusB0;
108 | uint8_t protocolStatus;
109 | uint8_t reserved[3];
110 | } end_request_t; // 16
111 |
112 |
113 | typedef struct {
114 | header_t header;
115 | uint8_t type;
116 | uint8_t reserved[7];
117 | } unknown_type_t; // 16
118 |
119 |
120 | inline static void header_init(header_t *self, uint8_t t, uint16_t id, uint16_t len) {
121 | self->version = '\1';
122 | self->type = t;
123 | self->requestIdB1 = id >> 8;
124 | self->requestIdB0 = id & 0xff;
125 | self->contentLengthB1 = len >> 8;
126 | self->contentLengthB0 = len & 0xff;
127 | self->paddingLength = '\0';
128 | self->reserved = '\0';
129 | }
130 |
131 |
132 | inline static void end_request_init(end_request_t *self, uint16_t id, uint32_t ast, uint8_t protostatus) {
133 | header_init((header_t *)self, TYPE_END_REQUEST, id, sizeof(end_request_t)-sizeof(header_t));
134 | self->appStatusB3 = (ast >> 24) & 0xff;
135 | self->appStatusB3 = (ast >> 16) & 0xff;
136 | self->appStatusB3 = (ast >> 8) & 0xff;
137 | self->appStatusB3 = ast & 0xff;
138 | self->protocolStatus = protostatus;
139 | memset(self->reserved, 0, sizeof(self->reserved));
140 | }
141 |
142 |
143 | inline static void unknown_type_init(unknown_type_t *self, uint8_t unknown_type) {
144 | header_init((header_t *)self, TYPE_UNKNOWN, 0, sizeof(unknown_type_t)-sizeof(header_t));
145 | self->type = unknown_type;
146 | memset(self->reserved, 0, sizeof(self->reserved));
147 | }
148 |
149 |
150 | /////////// bev
151 |
152 |
153 | #define BEV_FD(p) ((p)->ev_read.ev_fd)
154 | #define BEV_FD_SET(p, v) ((p)->ev_read.ev_fd = (v))
155 |
156 |
157 | inline static void bev_disable(struct bufferevent *bev) {
158 | int events = 0;
159 | if (bev->readcb)
160 | events |= EV_READ;
161 | if (bev->writecb)
162 | events |= EV_WRITE;
163 | bufferevent_disable(bev, events);
164 | }
165 |
166 |
167 | inline static void bev_close(struct bufferevent *bev) {
168 | bev_disable(bev);
169 | close(BEV_FD(bev));
170 | BEV_FD_SET(bev, -1);
171 | }
172 |
173 |
174 | inline static void bev_drain(struct bufferevent *bev) {
175 | evbuffer_drain(bev->input, EVBUFFER_LENGTH(bev->input));
176 | evbuffer_drain(bev->output, EVBUFFER_LENGTH(bev->output));
177 | }
178 |
179 |
180 | void bev_abort(struct bufferevent *bev) {
181 | struct linger lingeropt;
182 | lingeropt.l_onoff = 1;
183 | lingeropt.l_linger = 0;
184 | AZ(setsockopt(BEV_FD(bev), SOL_SOCKET, SO_LINGER, (char *)&lingeropt, sizeof(lingeropt)));
185 | shutdown(BEV_FD(bev), SHUT_RDWR);
186 | bev_drain(bev);
187 | bev_close(bev);
188 | }
189 |
190 |
191 | inline static int bev_add(struct event *ev, int timeout) {
192 | struct timeval tv, *ptv = NULL;
193 | if (timeout) {
194 | evutil_timerclear(&tv);
195 | tv.tv_sec = timeout;
196 | ptv = &tv;
197 | }
198 | return event_add(ev, ptv);
199 | }
200 |
201 |
202 | /////////// request
203 |
204 | void app_handle_beginrequest(request_t *r);
205 | void app_handle_input(request_t *r, uint16_t length);
206 | void app_handle_requestaborted(request_t *r);
207 |
208 |
209 | // get a request by request id
210 | inline static request_t *request_get(uint16_t id) {
211 | request_t *r;
212 | r = _requests[id];
213 | if (r == NULL) {
214 | r = (request_t *)calloc(1, sizeof(request_t));
215 | _requests[id] = r;
216 | }
217 | printf("** get req %p\n", r);
218 | return r;
219 | }
220 |
221 | // restore a request object
222 | inline static void request_put(request_t *r) {
223 | printf("** put req %p\n", r);
224 | r->bev->cbarg = NULL;
225 | r->bev = NULL;
226 | }
227 |
228 | static inline bool request_is_active(request_t *r) {
229 | return ((r != NULL) && (r->bev != NULL));
230 | }
231 |
232 |
233 | void request_write(request_t *r, const char *buf, uint16_t len, uint8_t tostdout) {
234 | if (len == 0)
235 | return;
236 |
237 | if (!request_is_active(r)) {
238 | //warn("request_write(): request is not active");
239 | return;
240 | }
241 |
242 | header_t h;
243 | header_init(&h, tostdout ? TYPE_STDOUT : TYPE_STDERR, r->id, len);
244 |
245 | if (evbuffer_add(r->bev->output, (const void *)&h, sizeof(header_t)) != -1)
246 | evbuffer_add(r->bev->output, (const void *)buf, len);
247 |
248 | // schedule write
249 | if (r->bev->enabled & EV_WRITE)
250 | bev_add(&r->bev->ev_write, r->bev->timeout_write);
251 | }
252 |
253 |
254 | void request_end(request_t *r, uint32_t appstatus, uint8_t protostatus) {
255 | if (!request_is_active(r)) {
256 | //warn("request_end(): request is not active");
257 | return;
258 | }
259 |
260 | uint8_t buf[32]; // header + header + end_request_t
261 | uint8_t *p = buf;
262 |
263 | //assert(EVBUFFER_LENGTH(r->bev->output) == 0);
264 |
265 | // Terminate the stdout and stderr stream, and send the end-request message.
266 | header_init((header_t *)p, TYPE_STDOUT, r->id, 0);
267 | p += sizeof(header_t);
268 | header_init((header_t *)p, TYPE_STDERR, r->id, 0);
269 | p += sizeof(header_t);
270 | end_request_init((end_request_t *)p, r->id, appstatus, protostatus);
271 | p += sizeof(end_request_t);
272 |
273 | printf("sending END_REQUEST for id %d\n", r->id);
274 |
275 | bufferevent_write(r->bev, (const void *)buf, sizeof(buf));
276 |
277 | r->terminate = true;
278 | }
279 |
280 |
281 | inline static void process_abort_request(request_t *r) {
282 | assert(r->bev != NULL);
283 | printf("request %p aborted by client\n", r);
284 |
285 | app_handle_requestaborted(r);
286 |
287 | r->terminate = 1; // can we trust fcgiproto_writecb to be called?
288 | }
289 |
290 |
291 | void fcgiproto_errorcb(struct bufferevent *bev, short what, request_t *r) {
292 | if (what & EVBUFFER_EOF) {
293 | printf("request %p EOF\n", r);
294 | // we treat abrupt disconnect as abort
295 | process_abort_request(r);
296 | }
297 | else if (what & EVBUFFER_TIMEOUT)
298 | printf("request %p timeout\n", r);
299 | else
300 | printf("request %p error\n", r);
301 |
302 | bev_close(bev);
303 | if (r)
304 | request_put(r);
305 | }
306 |
307 |
308 | inline static void process_unknown(struct bufferevent *bev, uint8_t type, uint16_t len) {
309 | printf("process_unknown(%p, %d, %d)\n", bev, type, len);
310 | unknown_type_t msg;
311 | unknown_type_init(&msg, type);
312 | bufferevent_write(bev, (const void *)&msg, sizeof(unknown_type_t));
313 | evbuffer_drain(bev->input, sizeof(header_t) + len);
314 | }
315 |
316 |
317 | inline static void process_begin_request(struct bufferevent *bev, uint16_t id, const begin_request_t *br) {
318 | request_t *r;
319 |
320 | r = request_get(id);
321 | //assert(r->bev == NULL);
322 | if ((r->bev != NULL) && (EVBUFFER_LENGTH(r->bev->input) != 0)) {
323 | printf("warn: client sent already used req id %d -- skipping", id);
324 | // todo: respond with error
325 | bev_close(r->bev);
326 | return;
327 | }
328 |
329 |
330 | r->bev = bev;
331 | r->id = id;
332 | r->keepconn = (br->flags & FLAG_KEEP_CONN) == 1;
333 | r->role = (br->roleB1 << 8) + br->roleB0;
334 | r->stdin_eof = false;
335 | r->terminate = false;
336 | bev->cbarg = (void *)r;
337 |
338 | evbuffer_drain(bev->input, sizeof(header_t)+sizeof(begin_request_t));
339 | }
340 |
341 |
342 | inline static void process_params(struct bufferevent *bev, uint16_t id, const uint8_t *buf, uint16_t len) {
343 | request_t *r;
344 |
345 | r = request_get(id);
346 | //assert(r->bev != NULL); // this can actually happen and it's ok
347 |
348 | // Is this the last message to come? Then queue the request for the user.
349 | if (len == 0) {
350 | evbuffer_drain(bev->input, sizeof(header_t));
351 | app_handle_beginrequest(r);
352 | return;
353 | }
354 |
355 | // Process message.
356 |
357 | uint8_t const * const bufend = buf + len;
358 | uint32_t name_len;
359 | uint32_t data_len;
360 |
361 | while(buf != bufend) {
362 |
363 | if (*buf >> 7 == 0) {
364 | name_len = *(buf++);
365 | }
366 | else {
367 | name_len = ((buf[0] & 0x7F) << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
368 | buf += 4;
369 | }
370 |
371 | if (*buf >> 7 == 0) {
372 | data_len = *(buf++);
373 | }
374 | else {
375 | data_len = ((buf[0] & 0x7F) << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
376 | buf += 4;
377 | }
378 |
379 | assert(buf + name_len + data_len <= bufend);
380 |
381 | // todo replace with actual adding to req:
382 | char k[255], v[8192];
383 | strncpy(k, (const char *)buf, name_len); k[name_len] = '\0';
384 | buf += name_len;
385 | strncpy(v, (const char *)buf, data_len); v[data_len] = '\0';
386 | buf += data_len;
387 | printf("fcgiproto>> param>> '%s' => '%s'\n", k, v);
388 | // todo: req->second->params[name] = data;
389 | }
390 |
391 | evbuffer_drain(bev->input, sizeof(header_t) + len);
392 | }
393 |
394 |
395 | inline static void process_stdin(struct bufferevent *bev, uint16_t id, const uint8_t *buf, uint16_t len) {
396 | request_t *r;
397 |
398 | r = request_get(id);
399 |
400 | // left-over stdin on inactive request is drained and forgotten
401 | if (r->bev == NULL) {
402 | bev_drain(bev);
403 | return;
404 | }
405 |
406 | assert(r->bev != NULL);
407 | evbuffer_drain(bev->input, sizeof(header_t));
408 |
409 | // Is this the last message to come? Then set the eof flag.
410 | // Otherwise, add the data to the buffer in the request structure.
411 | if (len == 0) {
412 | r->stdin_eof = true;
413 | return;
414 | }
415 |
416 | app_handle_input(r, len);
417 | }
418 |
419 |
420 | void fcgiproto_readcb(struct bufferevent *bev, request_t *r) {
421 | printf("fcgiproto_readcb(%p, %p)\n", bev, r);
422 | //bufferevent_write_buffer(bev, bev->input);
423 |
424 | while(EVBUFFER_LENGTH(bev->input) >= sizeof(header_t)) {
425 | const header_t *hp = (const header_t *)EVBUFFER_DATA(bev->input);
426 |
427 | // Check whether our peer speaks the correct protocol version.
428 | if (hp->version != 1) {
429 | warnx("fcgiev: cannot handle protocol version %u", hp->version);
430 | bev_abort(bev);
431 | break;
432 | }
433 |
434 | // Check whether we have the whole message that follows the
435 | // headers in our buffer already. If not, we can't process it
436 | // yet.
437 | uint16_t msg_len = (hp->contentLengthB1 << 8) + hp->contentLengthB0;
438 | uint16_t msg_id = (hp->requestIdB1 << 8) + hp->requestIdB0;
439 |
440 | if (EVBUFFER_LENGTH(bev->input) < sizeof(header_t) + msg_len + hp->paddingLength)
441 | return;
442 |
443 | // Process the message.
444 | printf("fcgiproto>> received message: id: %d, bodylen: %d, padding: %d, type: %d\n",
445 | msg_id, msg_len, hp->paddingLength, (int)hp->type);
446 |
447 | switch (hp->type) {
448 | case TYPE_BEGIN_REQUEST:
449 | process_begin_request(bev, msg_id,
450 | (const begin_request_t *)(EVBUFFER_DATA(bev->input) + sizeof(header_t)) );
451 | break;
452 | case TYPE_ABORT_REQUEST:
453 | process_abort_request(request_get(msg_id));
454 | break;
455 | case TYPE_PARAMS:
456 | process_params(bev, msg_id, (const uint8_t *)EVBUFFER_DATA(bev->input) + sizeof(header_t), msg_len);
457 | break;
458 | case TYPE_STDIN:
459 | process_stdin(bev, msg_id, (const uint8_t *)EVBUFFER_DATA(bev->input) + sizeof(header_t), msg_len);
460 | break;
461 | //case TYPE_END_REQUEST:
462 | //case TYPE_STDOUT:
463 | //case TYPE_STDERR:
464 | //case TYPE_DATA:
465 | //case TYPE_GET_VALUES:
466 | //case TYPE_GET_VALUES_RESULT:
467 | //case TYPE_UNKNOWN:
468 | default:
469 | process_unknown(bev, hp->type, msg_len);
470 | }/* switch(hp->type) */
471 |
472 | if (hp->paddingLength)
473 | evbuffer_drain(bev->input, hp->paddingLength);
474 | }
475 | }
476 |
477 |
478 | void fcgiproto_writecb(struct bufferevent *bev, request_t *r) {
479 | // Invoked if bev->output is drained or below the low watermark.
480 | printf("fcgiproto_writecb(%p, %p)\n", bev, r);
481 |
482 | if (r != NULL && r->terminate) {
483 | bev_disable(r->bev);
484 | bev_drain(r->bev);
485 | bev_close(r->bev);
486 | request_put(r);
487 | if (r->keepconn == false) {
488 | printf("PUT connection (r->keepconn == false, in fcgiproto_writecb)\n");
489 | }
490 | }
491 | }
492 |
493 |
494 | /*void conn_init(request_t *r, int fd) {
495 | r->ident = 0;
496 | r->bev.readcb = (evbuffercb)fcgiproto_readcb;
497 | r->bev.writecb = (evbuffercb)fcgiproto_writecb;
498 | r->bev.errorcb = (everrorcb)fcgiproto_errorcb;
499 | r->bev.cbarg = (void *)r;
500 | event_set(&r->bev.ev_read, fd, EV_READ, bufferevent_readcb, (void *)&r->bev);
501 | event_set(&r->bev.ev_write, fd, EV_WRITE, bufferevent_writecb, (void *)&r->bev);
502 | r->bev.enabled = EV_WRITE;
503 | }*/
504 |
505 |
506 | void server_init(server_t *server) {
507 | server->ev.ev_fd = FCGI_LISTENSOCK_FILENO;
508 | }
509 |
510 |
511 | const sau_t *server_bind(server_t *server, const char *addrorpath) {
512 | static sau_t sa;
513 | if ((server->ev.ev_fd = sockutil_bind(addrorpath, SOMAXCONN, &sa)) < 0)
514 | return NULL;
515 | return (const sau_t *)(&sa);
516 | }
517 |
518 |
519 | static const char *sockaddr_host(const sau_t *sa) {
520 | static char *buf[SOCK_MAXADDRLEN+1];
521 | buf[0] = '\0';
522 | struct sockaddr_in *sk = (struct sockaddr_in *)sa;
523 | return inet_ntop(AF_INET, &(sk->sin_addr), (char *)buf, SOCK_MAXADDRLEN);
524 | }
525 |
526 |
527 | void server_accept(int fd, short ev, server_t *server) {
528 | struct bufferevent *bev;
529 | socklen_t saz;
530 | int on = 1, events, connfd;
531 | struct timeval *timeout;
532 | sau_t sa;
533 |
534 | saz = sizeof(sa);
535 | connfd = accept(fd, (struct sockaddr *)&sa, &saz);
536 | timeout = NULL;
537 |
538 | if (connfd < 0) {
539 | warn("accept failed");
540 | return;
541 | }
542 |
543 | // Disable Nagle -- better response times at the cost of more packets being sent.
544 | setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, (char *)&on, sizeof(on));
545 | // Set nonblocking
546 | AZ(ioctl(connfd, FIONBIO, (int *)&on));
547 |
548 | bev = bufferevent_new(connfd, (evbuffercb)fcgiproto_readcb,
549 | (evbuffercb)fcgiproto_writecb, (everrorcb)fcgiproto_errorcb, NULL);
550 |
551 | events = EV_READ;
552 | if (bev->writecb)
553 | events |= EV_WRITE;
554 | bufferevent_enable(bev, events);
555 |
556 | printf("GET connection\n");
557 | printf("fcgi client %s connected on fd %d\n", sockaddr_host(&sa), connfd);
558 | }
559 |
560 |
561 | void server_enable(server_t *server) {
562 | int on = 1;
563 | AZ(ioctl(server->ev.ev_fd, FIONBIO, (int *)&on));
564 | event_set(&server->ev, server->ev.ev_fd, EV_READ|EV_PERSIST,
565 | (void (*)(int,short,void*))server_accept, (void *)server);
566 | event_add(&server->ev, NULL/* no timeout */);
567 | }
568 |
569 |
570 | void fcgiev_init() {
571 | memset((void *)_requests, 0, sizeof(_requests));
572 | }
573 |
574 |
575 | static void app_test_delayed_finalize_request(int fd, short event, void *arg) {
576 | request_t *r = (request_t *)arg;
577 | if (request_is_active(r)) {
578 | printf("app_test_delayed_finalize_request %p\n", r);
579 | static const char hello[] = "Content-type: text/plain\r\n\r\nHello world\n";
580 | request_write(r, hello, sizeof(hello)-1, 1);
581 | request_end(r, 0, PROTOST_REQUEST_COMPLETE);
582 | }
583 | }
584 |
585 |
586 | void app_handle_beginrequest(request_t *r) {
587 | printf("app_handle_beginrequest %p\n", r);
588 |
589 | if (r->role != ROLE_RESPONDER) {
590 | request_write(r, "We can't handle any role but RESPONDER.", 39, 0);
591 | request_end(r, 1, PROTOST_UNKNOWN_ROLE);
592 | return;
593 | }
594 |
595 | #if DEBUG_RESPONSE_DELAY
596 | // DELAYED
597 | struct event *timer_ev;
598 | struct timeval tv;
599 | timer_ev = calloc(1,sizeof(struct event));
600 | tv.tv_sec = DEBUG_RESPONSE_DELAY;
601 | tv.tv_usec = 0;
602 | evtimer_set(timer_ev, app_test_delayed_finalize_request, (void *)r);
603 | evtimer_add(timer_ev, &tv);
604 | #else
605 | // DIRECT
606 | static const char hello[] = "Content-type: text/plain\r\n\r\nHello world\n";
607 | request_write(r, hello, sizeof(hello)-1, 1);
608 | request_end(r, 0, PROTOST_REQUEST_COMPLETE);
609 | #endif
610 | }
611 |
612 |
613 | void app_handle_input(request_t *r, uint16_t length) {
614 | printf("app_handle_input %p -- %d bytes\n", r, length);
615 | // simply drain it for now
616 | evbuffer_drain(r->bev->input, length);
617 | }
618 |
619 |
620 | void app_handle_requestaborted(request_t *r) {
621 | printf("app_handle_requestaborted %p\n", r);
622 | }
623 |
624 |
625 | int main(int argc, const char * const *argv) {
626 | server_t *server;
627 | int i;
628 |
629 | // initialize libraries
630 | event_init();
631 | fcgiev_init();
632 |
633 | // no argmuents: bind to stdin
634 | if (argc <= 1) {
635 | server = calloc(1,sizeof(server_t));
636 | server_init(server);
637 | server_enable(server);
638 | }
639 | // bind with every argument
640 | else {
641 | for (i=1; i
4 | #include
5 | #include
6 | #include
7 | #include /* for fcntl */
8 | #include
9 | #include /* for memchr() */
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | #ifndef PATH_MAX
24 | #ifdef MAXPATHLEN
25 | #define PATH_MAX MAXPATHLEN
26 | #else
27 | #define PATH_MAX 1024
28 | #endif
29 | #endif
30 |
31 | inline static int _build_un(const char *bindPath, struct sockaddr_un *servAddrPtr, int *servAddrLen) {
32 | int bindPathLen = strlen(bindPath);
33 | if(bindPathLen > sizeof(servAddrPtr->sun_path))
34 | return -1;
35 | memset((char *) servAddrPtr, 0, sizeof(*servAddrPtr));
36 | servAddrPtr->sun_family = AF_UNIX;
37 | memcpy(servAddrPtr->sun_path, bindPath, bindPathLen);
38 | *servAddrLen = sizeof(servAddrPtr->sun_family) + bindPathLen;
39 | return 0;
40 | }
41 |
42 |
43 | int sockutil_bind(const char *bindPath, int backlog, sau_t *sa) {
44 | int fd, servLen;
45 | bool tcp = false;
46 | unsigned long tcp_ia = 0;
47 | char *tp;
48 | short port = 0;
49 | char host[PATH_MAX];
50 |
51 | strcpy(host, bindPath);
52 |
53 | if((tp = strchr(host, ':')) != 0) {
54 | *tp++ = 0;
55 | if((port = atoi(tp)) == 0)
56 | *--tp = ':';
57 | else
58 | tcp = true;
59 | }
60 |
61 | if(tcp) {
62 | if (!*host || !strcmp(host,"*")) {
63 | tcp_ia = htonl(INADDR_ANY);
64 | }
65 | else {
66 | tcp_ia = inet_addr(host);
67 | if (tcp_ia == INADDR_NONE) {
68 | struct hostent * hep;
69 | hep = gethostbyname(host);
70 | if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
71 | warn("fcgiev: cannot resolve host name %s", host);
72 | return -1;
73 | }
74 | if (hep->h_addr_list[1]) {
75 | warn("fcgiev: host %s has multiple addresses -- choose one explicitly", host);
76 | return -1;
77 | }
78 | tcp_ia = ((struct in_addr *) (hep->h_addr))->s_addr;
79 | }
80 | }
81 |
82 | fd = socket(AF_INET, SOCK_STREAM, 0);
83 |
84 | if(fd >= 0) {
85 | int flag = 1;
86 | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) {
87 | warn("fcgiev: can't set SO_REUSEADDR.");
88 | return -1;
89 | }
90 | }
91 | }
92 | else { /* tcp == FALSE */
93 | fd = socket(AF_UNIX, SOCK_STREAM, 0);
94 | }
95 |
96 | if(fd < 0)
97 | return -1;
98 |
99 | // Bind the listening socket.
100 | if(tcp) {
101 | memset((char *)&sa->in, 0, sizeof(sa->in));
102 | sa->in.sin_family = AF_INET;
103 | sa->in.sin_addr.s_addr = tcp_ia;
104 | sa->in.sin_port = htons(port);
105 | servLen = sizeof(sa->in);
106 | }
107 | else {
108 | unlink(bindPath);
109 | if(_build_un(bindPath, &sa->un, &servLen)) {
110 | warn("fcgiev: listening socket's path name is too long.");
111 | return -1;
112 | }
113 | }
114 |
115 | if(bind(fd, (struct sockaddr *) &sa->un, servLen) < 0) {
116 | perror("fcgiev: bind");
117 | return -1;
118 | }
119 |
120 | if(listen(fd, backlog) < 0) {
121 | perror("fcgiev: listen");
122 | return -1;
123 | }
124 |
125 | return fd;
126 | }
127 |
--------------------------------------------------------------------------------
/servers/afcgitest/sockutil.h:
--------------------------------------------------------------------------------
1 | #ifndef _FCGIEV_SOCKUTIL_H_
2 | #define _FCGIEV_SOCKUTIL_H_
3 |
4 | #include
5 | #include
6 |
7 | typedef union {
8 | struct sockaddr_un un;
9 | struct sockaddr_in in;
10 | } sau_t;
11 |
12 | int sockutil_bind(const char *bindPath, int backlog, sau_t *sa);
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------