nginx 0.7.8
[nginx-catap.git] / src / http / modules / ngx_http_fastcgi_module.c
blob8d57a46a26b6204268b749b0e7aeaddcf65392a7
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10 #include <nginx.h>
13 typedef struct {
14 ngx_http_upstream_conf_t upstream;
16 ngx_str_t index;
18 ngx_array_t *flushes;
19 ngx_array_t *params_len;
20 ngx_array_t *params;
21 ngx_array_t *params_source;
22 ngx_array_t *catch_stderr;
23 } ngx_http_fastcgi_loc_conf_t;
26 typedef enum {
27 ngx_http_fastcgi_st_version = 0,
28 ngx_http_fastcgi_st_type,
29 ngx_http_fastcgi_st_request_id_hi,
30 ngx_http_fastcgi_st_request_id_lo,
31 ngx_http_fastcgi_st_content_length_hi,
32 ngx_http_fastcgi_st_content_length_lo,
33 ngx_http_fastcgi_st_padding_length,
34 ngx_http_fastcgi_st_reserved,
35 ngx_http_fastcgi_st_data,
36 ngx_http_fastcgi_st_padding
37 } ngx_http_fastcgi_state_e;
40 typedef struct {
41 u_char *start;
42 u_char *end;
43 } ngx_http_fastcgi_split_part_t;
46 typedef struct {
47 ngx_http_fastcgi_state_e state;
48 u_char *pos;
49 u_char *last;
50 ngx_uint_t type;
51 size_t length;
52 size_t padding;
54 ngx_uint_t fastcgi_stdout; /* unsigned :1 */
56 ngx_array_t *split_parts;
57 } ngx_http_fastcgi_ctx_t;
60 #define NGX_HTTP_FASTCGI_RESPONDER 1
62 #define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1
63 #define NGX_HTTP_FASTCGI_ABORT_REQUEST 2
64 #define NGX_HTTP_FASTCGI_END_REQUEST 3
65 #define NGX_HTTP_FASTCGI_PARAMS 4
66 #define NGX_HTTP_FASTCGI_STDIN 5
67 #define NGX_HTTP_FASTCGI_STDOUT 6
68 #define NGX_HTTP_FASTCGI_STDERR 7
69 #define NGX_HTTP_FASTCGI_DATA 8
72 typedef struct {
73 u_char version;
74 u_char type;
75 u_char request_id_hi;
76 u_char request_id_lo;
77 u_char content_length_hi;
78 u_char content_length_lo;
79 u_char padding_length;
80 u_char reserved;
81 } ngx_http_fastcgi_header_t;
84 typedef struct {
85 u_char role_hi;
86 u_char role_lo;
87 u_char flags;
88 u_char reserved[5];
89 } ngx_http_fastcgi_begin_request_t;
92 typedef struct {
93 u_char version;
94 u_char type;
95 u_char request_id_hi;
96 u_char request_id_lo;
97 } ngx_http_fastcgi_header_small_t;
100 typedef struct {
101 ngx_http_fastcgi_header_t h0;
102 ngx_http_fastcgi_begin_request_t br;
103 ngx_http_fastcgi_header_small_t h1;
104 } ngx_http_fastcgi_request_start_t;
107 static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
108 static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
109 static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
110 static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
111 ngx_buf_t *buf);
112 static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,
113 ngx_http_fastcgi_ctx_t *f);
114 static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);
115 static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,
116 ngx_int_t rc);
118 static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);
119 static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);
120 static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,
121 void *parent, void *child);
122 static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
123 ngx_http_variable_value_t *v, uintptr_t data);
125 static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
126 void *conf);
127 static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
128 void *conf);
129 static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
130 void *data);
132 static char *ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
133 ngx_command_t *cmd, void *conf);
134 static char *ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
135 ngx_command_t *cmd, void *conf);
138 static ngx_conf_post_t ngx_http_fastcgi_lowat_post =
139 { ngx_http_fastcgi_lowat_check };
142 static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = {
143 { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
144 { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
145 { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
146 { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
147 { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
148 { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
149 { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
150 { ngx_null_string, 0 }
154 static ngx_command_t ngx_http_fastcgi_commands[] = {
156 { ngx_string("fastcgi_pass"),
157 NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
158 ngx_http_fastcgi_pass,
159 NGX_HTTP_LOC_CONF_OFFSET,
161 NULL },
163 { ngx_string("fastcgi_index"),
164 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
165 ngx_conf_set_str_slot,
166 NGX_HTTP_LOC_CONF_OFFSET,
167 offsetof(ngx_http_fastcgi_loc_conf_t, index),
168 NULL },
170 { ngx_string("fastcgi_store"),
171 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
172 ngx_http_fastcgi_store,
173 NGX_HTTP_LOC_CONF_OFFSET,
175 NULL },
177 { ngx_string("fastcgi_store_access"),
178 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
179 ngx_conf_set_access_slot,
180 NGX_HTTP_LOC_CONF_OFFSET,
181 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),
182 NULL },
184 { ngx_string("fastcgi_ignore_client_abort"),
185 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
186 ngx_conf_set_flag_slot,
187 NGX_HTTP_LOC_CONF_OFFSET,
188 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),
189 NULL },
191 { ngx_string("fastcgi_connect_timeout"),
192 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
193 ngx_conf_set_msec_slot,
194 NGX_HTTP_LOC_CONF_OFFSET,
195 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),
196 NULL },
198 { ngx_string("fastcgi_send_timeout"),
199 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
200 ngx_conf_set_msec_slot,
201 NGX_HTTP_LOC_CONF_OFFSET,
202 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),
203 NULL },
205 { ngx_string("fastcgi_send_lowat"),
206 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
207 ngx_conf_set_size_slot,
208 NGX_HTTP_LOC_CONF_OFFSET,
209 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),
210 &ngx_http_fastcgi_lowat_post },
212 { ngx_string("fastcgi_buffer_size"),
213 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
214 ngx_conf_set_size_slot,
215 NGX_HTTP_LOC_CONF_OFFSET,
216 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
217 NULL },
219 { ngx_string("fastcgi_pass_request_headers"),
220 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
221 ngx_conf_set_flag_slot,
222 NGX_HTTP_LOC_CONF_OFFSET,
223 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),
224 NULL },
226 { ngx_string("fastcgi_pass_request_body"),
227 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
228 ngx_conf_set_flag_slot,
229 NGX_HTTP_LOC_CONF_OFFSET,
230 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),
231 NULL },
233 { ngx_string("fastcgi_intercept_errors"),
234 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
235 ngx_conf_set_flag_slot,
236 NGX_HTTP_LOC_CONF_OFFSET,
237 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),
238 NULL },
240 { ngx_string("fastcgi_read_timeout"),
241 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
242 ngx_conf_set_msec_slot,
243 NGX_HTTP_LOC_CONF_OFFSET,
244 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),
245 NULL },
247 { ngx_string("fastcgi_buffers"),
248 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
249 ngx_conf_set_bufs_slot,
250 NGX_HTTP_LOC_CONF_OFFSET,
251 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),
252 NULL },
254 { ngx_string("fastcgi_busy_buffers_size"),
255 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
256 ngx_conf_set_size_slot,
257 NGX_HTTP_LOC_CONF_OFFSET,
258 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),
259 NULL },
261 { ngx_string("fastcgi_temp_path"),
262 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
263 ngx_conf_set_path_slot,
264 NGX_HTTP_LOC_CONF_OFFSET,
265 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),
266 (void *) ngx_garbage_collector_temp_handler },
268 { ngx_string("fastcgi_max_temp_file_size"),
269 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
270 ngx_conf_set_size_slot,
271 NGX_HTTP_LOC_CONF_OFFSET,
272 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),
273 NULL },
275 { ngx_string("fastcgi_temp_file_write_size"),
276 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
277 ngx_conf_set_size_slot,
278 NGX_HTTP_LOC_CONF_OFFSET,
279 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),
280 NULL },
282 { ngx_string("fastcgi_next_upstream"),
283 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
284 ngx_conf_set_bitmask_slot,
285 NGX_HTTP_LOC_CONF_OFFSET,
286 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream),
287 &ngx_http_fastcgi_next_upstream_masks },
289 { ngx_string("fastcgi_upstream_max_fails"),
290 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
291 ngx_http_fastcgi_upstream_max_fails_unsupported,
294 NULL },
296 { ngx_string("fastcgi_upstream_fail_timeout"),
297 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
298 ngx_http_fastcgi_upstream_fail_timeout_unsupported,
301 NULL },
303 { ngx_string("fastcgi_param"),
304 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
305 ngx_conf_set_keyval_slot,
306 NGX_HTTP_LOC_CONF_OFFSET,
307 offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
308 NULL },
310 { ngx_string("fastcgi_pass_header"),
311 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
312 ngx_conf_set_str_array_slot,
313 NGX_HTTP_LOC_CONF_OFFSET,
314 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),
315 NULL },
317 { ngx_string("fastcgi_hide_header"),
318 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
319 ngx_conf_set_str_array_slot,
320 NGX_HTTP_LOC_CONF_OFFSET,
321 offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
322 NULL },
324 { ngx_string("fastcgi_catch_stderr"),
325 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
326 ngx_conf_set_str_array_slot,
327 NGX_HTTP_LOC_CONF_OFFSET,
328 offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),
329 NULL },
331 ngx_null_command
335 static ngx_http_module_t ngx_http_fastcgi_module_ctx = {
336 ngx_http_fastcgi_add_variables, /* preconfiguration */
337 NULL, /* postconfiguration */
339 NULL, /* create main configuration */
340 NULL, /* init main configuration */
342 NULL, /* create server configuration */
343 NULL, /* merge server configuration */
345 ngx_http_fastcgi_create_loc_conf, /* create location configuration */
346 ngx_http_fastcgi_merge_loc_conf /* merge location configuration */
350 ngx_module_t ngx_http_fastcgi_module = {
351 NGX_MODULE_V1,
352 &ngx_http_fastcgi_module_ctx, /* module context */
353 ngx_http_fastcgi_commands, /* module directives */
354 NGX_HTTP_MODULE, /* module type */
355 NULL, /* init master */
356 NULL, /* init module */
357 NULL, /* init process */
358 NULL, /* init thread */
359 NULL, /* exit thread */
360 NULL, /* exit process */
361 NULL, /* exit master */
362 NGX_MODULE_V1_PADDING
366 static ngx_http_fastcgi_request_start_t ngx_http_fastcgi_request_start = {
367 { 1, /* version */
368 NGX_HTTP_FASTCGI_BEGIN_REQUEST, /* type */
369 0, /* request_id_hi */
370 1, /* request_id_lo */
371 0, /* content_length_hi */
372 sizeof(ngx_http_fastcgi_begin_request_t), /* content_length_lo */
373 0, /* padding_length */
374 0 }, /* reserved */
376 { 0, /* role_hi */
377 NGX_HTTP_FASTCGI_RESPONDER, /* role_lo */
378 0, /* NGX_HTTP_FASTCGI_KEEP_CONN */ /* flags */
379 { 0, 0, 0, 0, 0 } }, /* reserved[5] */
381 { 1, /* version */
382 NGX_HTTP_FASTCGI_PARAMS, /* type */
383 0, /* request_id_hi */
384 1 }, /* request_id_lo */
389 static ngx_str_t ngx_http_fastcgi_script_name =
390 ngx_string("fastcgi_script_name");
393 static ngx_str_t ngx_http_fastcgi_hide_headers[] = {
394 ngx_string("Status"),
395 ngx_string("X-Accel-Expires"),
396 ngx_string("X-Accel-Redirect"),
397 ngx_string("X-Accel-Limit-Rate"),
398 ngx_string("X-Accel-Buffering"),
399 ngx_string("X-Accel-Charset"),
400 ngx_null_string
404 static ngx_int_t
405 ngx_http_fastcgi_handler(ngx_http_request_t *r)
407 ngx_int_t rc;
408 ngx_http_upstream_t *u;
409 ngx_http_fastcgi_loc_conf_t *flcf;
411 if (r->subrequest_in_memory) {
412 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
413 "ngx_http_fastcgi_module does not support "
414 "subrequest in memory");
415 return NGX_HTTP_INTERNAL_SERVER_ERROR;
418 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
420 u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
421 if (u == NULL) {
422 return NGX_HTTP_INTERNAL_SERVER_ERROR;
425 u->schema = flcf->upstream.schema;
427 u->peer.log = r->connection->log;
428 u->peer.log_error = NGX_ERROR_ERR;
429 #if (NGX_THREADS)
430 u->peer.lock = &r->connection->lock;
431 #endif
433 u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;
435 u->conf = &flcf->upstream;
437 u->create_request = ngx_http_fastcgi_create_request;
438 u->reinit_request = ngx_http_fastcgi_reinit_request;
439 u->process_header = ngx_http_fastcgi_process_header;
440 u->abort_request = ngx_http_fastcgi_abort_request;
441 u->finalize_request = ngx_http_fastcgi_finalize_request;
443 u->buffering = 1;
445 u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
446 if (u->pipe == NULL) {
447 return NGX_HTTP_INTERNAL_SERVER_ERROR;
450 u->pipe->input_filter = ngx_http_fastcgi_input_filter;
451 u->pipe->input_ctx = r;
453 r->upstream = u;
455 rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
457 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
458 return rc;
461 return NGX_DONE;
465 static ngx_int_t
466 ngx_http_fastcgi_create_request(ngx_http_request_t *r)
468 off_t file_pos;
469 u_char ch, *pos;
470 size_t size, len, key_len, val_len, padding;
471 ngx_uint_t i, n, next;
472 ngx_buf_t *b;
473 ngx_chain_t *cl, *body;
474 ngx_list_part_t *part;
475 ngx_table_elt_t *header;
476 ngx_http_script_code_pt code;
477 ngx_http_script_engine_t e, le;
478 ngx_http_fastcgi_header_t *h;
479 ngx_http_fastcgi_loc_conf_t *flcf;
480 ngx_http_script_len_code_pt lcode;
482 len = 0;
484 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
486 if (flcf->params_len) {
487 ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
489 ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes);
490 le.flushed = 1;
492 le.ip = flcf->params_len->elts;
493 le.request = r;
495 while (*(uintptr_t *) le.ip) {
497 lcode = *(ngx_http_script_len_code_pt *) le.ip;
498 key_len = lcode(&le);
500 for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
501 lcode = *(ngx_http_script_len_code_pt *) le.ip;
503 le.ip += sizeof(uintptr_t);
505 len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
509 if (flcf->upstream.pass_request_headers) {
511 part = &r->headers_in.headers.part;
512 header = part->elts;
514 for (i = 0; /* void */; i++) {
516 if (i >= part->nelts) {
517 if (part->next == NULL) {
518 break;
521 part = part->next;
522 header = part->elts;
523 i = 0;
526 len += ((sizeof("HTTP_") - 1 + header[i].key.len > 127) ? 4 : 1)
527 + ((header[i].value.len > 127) ? 4 : 1)
528 + sizeof("HTTP_") - 1 + header[i].key.len + header[i].value.len;
533 if (len > 65535) {
534 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
535 "fastcgi request record is too big: %uz", len);
536 return NGX_ERROR;
540 padding = 8 - len % 8;
541 padding = (padding == 8) ? 0 : padding;
544 size = sizeof(ngx_http_fastcgi_header_t)
545 + sizeof(ngx_http_fastcgi_begin_request_t)
547 + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
548 + len + padding
549 + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
551 + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
554 b = ngx_create_temp_buf(r->pool, size);
555 if (b == NULL) {
556 return NGX_ERROR;
559 cl = ngx_alloc_chain_link(r->pool);
560 if (cl == NULL) {
561 return NGX_ERROR;
564 cl->buf = b;
566 ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
567 sizeof(ngx_http_fastcgi_request_start_t));
569 h = (ngx_http_fastcgi_header_t *)
570 (b->pos + sizeof(ngx_http_fastcgi_header_t)
571 + sizeof(ngx_http_fastcgi_begin_request_t));
573 h->content_length_hi = (u_char) ((len >> 8) & 0xff);
574 h->content_length_lo = (u_char) (len & 0xff);
575 h->padding_length = (u_char) padding;
576 h->reserved = 0;
578 b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
579 + sizeof(ngx_http_fastcgi_begin_request_t)
580 + sizeof(ngx_http_fastcgi_header_t);
583 if (flcf->params_len) {
584 ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
586 e.ip = flcf->params->elts;
587 e.pos = b->last;
588 e.request = r;
589 e.flushed = 1;
591 le.ip = flcf->params_len->elts;
593 while (*(uintptr_t *) le.ip) {
595 lcode = *(ngx_http_script_len_code_pt *) le.ip;
596 key_len = (u_char) lcode(&le);
598 for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
599 lcode = *(ngx_http_script_len_code_pt *) le.ip;
601 le.ip += sizeof(uintptr_t);
603 *e.pos++ = (u_char) key_len;
605 if (val_len > 127) {
606 *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
607 *e.pos++ = (u_char) ((val_len >> 16) & 0xff);
608 *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
609 *e.pos++ = (u_char) (val_len & 0xff);
611 } else {
612 *e.pos++ = (u_char) val_len;
615 while (*(uintptr_t *) e.ip) {
616 code = *(ngx_http_script_code_pt *) e.ip;
617 code((ngx_http_script_engine_t *) &e);
619 e.ip += sizeof(uintptr_t);
621 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
622 "fastcgi param: \"%*s: %*s\"",
623 key_len, e.pos - (key_len + val_len),
624 val_len, e.pos - val_len);
627 b->last = e.pos;
631 if (flcf->upstream.pass_request_headers) {
633 part = &r->headers_in.headers.part;
634 header = part->elts;
636 for (i = 0; /* void */; i++) {
638 if (i >= part->nelts) {
639 if (part->next == NULL) {
640 break;
643 part = part->next;
644 header = part->elts;
645 i = 0;
648 len = sizeof("HTTP_") - 1 + header[i].key.len;
649 if (len > 127) {
650 *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80);
651 *b->last++ = (u_char) ((len >> 16) & 0xff);
652 *b->last++ = (u_char) ((len >> 8) & 0xff);
653 *b->last++ = (u_char) (len & 0xff);
655 } else {
656 *b->last++ = (u_char) len;
659 len = header[i].value.len;
660 if (len > 127) {
661 *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80);
662 *b->last++ = (u_char) ((len >> 16) & 0xff);
663 *b->last++ = (u_char) ((len >> 8) & 0xff);
664 *b->last++ = (u_char) (len & 0xff);
666 } else {
667 *b->last++ = (u_char) len;
670 b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);
672 for (n = 0; n < header[i].key.len; n++) {
673 ch = header[i].key.data[n];
675 if (ch >= 'a' && ch <= 'z') {
676 ch &= ~0x20;
678 } else if (ch == '-') {
679 ch = '_';
682 *b->last++ = ch;
685 b->last = ngx_copy(b->last, header[i].value.data,
686 header[i].value.len);
691 if (padding) {
692 ngx_memzero(b->last, padding);
693 b->last += padding;
697 h = (ngx_http_fastcgi_header_t *) b->last;
698 b->last += sizeof(ngx_http_fastcgi_header_t);
700 h->version = 1;
701 h->type = NGX_HTTP_FASTCGI_PARAMS;
702 h->request_id_hi = 0;
703 h->request_id_lo = 1;
704 h->content_length_hi = 0;
705 h->content_length_lo = 0;
706 h->padding_length = 0;
707 h->reserved = 0;
709 h = (ngx_http_fastcgi_header_t *) b->last;
710 b->last += sizeof(ngx_http_fastcgi_header_t);
712 if (flcf->upstream.pass_request_body) {
713 body = r->upstream->request_bufs;
714 r->upstream->request_bufs = cl;
716 #if (NGX_SUPPRESS_WARN)
717 file_pos = 0;
718 pos = NULL;
719 #endif
721 while (body) {
723 if (body->buf->in_file) {
724 file_pos = body->buf->file_pos;
726 } else {
727 pos = body->buf->pos;
730 next = 0;
732 do {
733 b = ngx_alloc_buf(r->pool);
734 if (b == NULL) {
735 return NGX_ERROR;
738 ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
740 if (body->buf->in_file) {
741 b->file_pos = file_pos;
742 file_pos += 32 * 1024;
744 if (file_pos >= body->buf->file_last) {
745 file_pos = body->buf->file_last;
746 next = 1;
749 b->file_last = file_pos;
750 len = (ngx_uint_t) (file_pos - b->file_pos);
752 } else {
753 b->pos = pos;
754 pos += 32 * 1024;
756 if (pos >= body->buf->last) {
757 pos = body->buf->last;
758 next = 1;
761 b->last = pos;
762 len = (ngx_uint_t) (pos - b->pos);
765 padding = 8 - len % 8;
766 padding = (padding == 8) ? 0 : padding;
768 h->version = 1;
769 h->type = NGX_HTTP_FASTCGI_STDIN;
770 h->request_id_hi = 0;
771 h->request_id_lo = 1;
772 h->content_length_hi = (u_char) ((len >> 8) & 0xff);
773 h->content_length_lo = (u_char) (len & 0xff);
774 h->padding_length = (u_char) padding;
775 h->reserved = 0;
777 cl->next = ngx_alloc_chain_link(r->pool);
778 if (cl->next == NULL) {
779 return NGX_ERROR;
782 cl = cl->next;
783 cl->buf = b;
785 b = ngx_create_temp_buf(r->pool,
786 sizeof(ngx_http_fastcgi_header_t)
787 + padding);
788 if (b == NULL) {
789 return NGX_ERROR;
792 if (padding) {
793 ngx_memzero(b->last, padding);
794 b->last += padding;
797 h = (ngx_http_fastcgi_header_t *) b->last;
798 b->last += sizeof(ngx_http_fastcgi_header_t);
800 cl->next = ngx_alloc_chain_link(r->pool);
801 if (cl->next == NULL) {
802 return NGX_ERROR;
805 cl = cl->next;
806 cl->buf = b;
808 } while (!next);
810 body = body->next;
813 } else {
814 r->upstream->request_bufs = cl;
817 h->version = 1;
818 h->type = NGX_HTTP_FASTCGI_STDIN;
819 h->request_id_hi = 0;
820 h->request_id_lo = 1;
821 h->content_length_hi = 0;
822 h->content_length_lo = 0;
823 h->padding_length = 0;
824 h->reserved = 0;
826 cl->next = NULL;
828 return NGX_OK;
832 static ngx_int_t
833 ngx_http_fastcgi_reinit_request(ngx_http_request_t *r)
835 ngx_http_fastcgi_ctx_t *f;
837 f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
839 if (f == NULL) {
840 return NGX_OK;
843 f->state = ngx_http_fastcgi_st_version;
844 f->fastcgi_stdout = 0;
846 return NGX_OK;
850 static ngx_int_t
851 ngx_http_fastcgi_process_header(ngx_http_request_t *r)
853 u_char *p, *start, *last, *part_start;
854 size_t size;
855 ngx_str_t *status_line, line, *pattern;
856 ngx_int_t rc, status;
857 ngx_buf_t buf;
858 ngx_uint_t i;
859 ngx_table_elt_t *h;
860 ngx_http_upstream_t *u;
861 ngx_http_fastcgi_ctx_t *f;
862 ngx_http_upstream_header_t *hh;
863 ngx_http_fastcgi_loc_conf_t *flcf;
864 ngx_http_fastcgi_split_part_t *part;
865 ngx_http_upstream_main_conf_t *umcf;
867 f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
869 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
871 if (f == NULL) {
872 f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
873 if (f == NULL) {
874 return NGX_ERROR;
877 ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
880 u = r->upstream;
882 for ( ;; ) {
884 if (f->state < ngx_http_fastcgi_st_data) {
886 f->pos = u->buffer.pos;
887 f->last = u->buffer.last;
889 rc = ngx_http_fastcgi_process_record(r, f);
891 u->buffer.pos = f->pos;
892 u->buffer.last = f->last;
894 if (rc == NGX_AGAIN) {
895 return NGX_AGAIN;
898 if (rc == NGX_ERROR) {
899 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
902 if (f->type != NGX_HTTP_FASTCGI_STDOUT
903 && f->type != NGX_HTTP_FASTCGI_STDERR)
905 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
906 "upstream sent unexpected FastCGI record: %d",
907 f->type);
909 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
912 if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
913 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
914 "upstream closed prematurely FastCGI stdout");
916 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
920 if (f->state == ngx_http_fastcgi_st_padding) {
922 if (u->buffer.pos + f->padding < u->buffer.last) {
923 f->state = ngx_http_fastcgi_st_version;
924 u->buffer.pos += f->padding;
926 continue;
929 if (u->buffer.pos + f->padding == u->buffer.last) {
930 f->state = ngx_http_fastcgi_st_version;
931 u->buffer.pos = u->buffer.last;
933 return NGX_AGAIN;
936 f->padding -= u->buffer.last - u->buffer.pos;
937 u->buffer.pos = u->buffer.last;
939 return NGX_AGAIN;
943 /* f->state == ngx_http_fastcgi_st_data */
945 if (f->type == NGX_HTTP_FASTCGI_STDERR) {
947 if (f->length) {
948 line.data = u->buffer.pos;
950 if (u->buffer.pos + f->length <= u->buffer.last) {
951 line.len = f->length;
952 u->buffer.pos += f->length;
953 f->length = 0;
954 f->state = ngx_http_fastcgi_st_padding;
956 } else {
957 line.len = u->buffer.last - u->buffer.pos;
958 f->length -= u->buffer.last - u->buffer.pos;
959 u->buffer.pos = u->buffer.last;
962 while (line.data[line.len - 1] == LF
963 || line.data[line.len - 1] == CR
964 || line.data[line.len - 1] == '.'
965 || line.data[line.len - 1] == ' ')
967 line.len--;
970 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
971 "FastCGI sent in stderr: \"%V\"", &line);
973 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
975 if (flcf->catch_stderr) {
976 pattern = flcf->catch_stderr->elts;
978 line.data[line.len - 1] = '\0';
980 for (i = 0; i < flcf->catch_stderr->nelts; i++) {
981 if (ngx_strstr(line.data, pattern[i].data)) {
982 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
987 if (u->buffer.pos == u->buffer.last) {
989 if (!f->fastcgi_stdout) {
992 * the special handling the large number
993 * of the PHP warnings to not allocate memory
996 u->buffer.pos = u->buffer.start;
997 u->buffer.last = u->buffer.start;
1000 return NGX_AGAIN;
1003 } else {
1004 f->state = ngx_http_fastcgi_st_version;
1007 continue;
1011 /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1013 f->fastcgi_stdout = 1;
1015 start = u->buffer.pos;
1017 if (u->buffer.pos + f->length < u->buffer.last) {
1020 * set u->buffer.last to the end of the FastCGI record data
1021 * for ngx_http_parse_header_line()
1024 last = u->buffer.last;
1025 u->buffer.last = u->buffer.pos + f->length;
1027 } else {
1028 last = NULL;
1031 for ( ;; ) {
1033 part_start = u->buffer.pos;
1035 rc = ngx_http_parse_header_line(r, &u->buffer);
1037 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1038 "http fastcgi parser: %d", rc);
1040 if (rc == NGX_AGAIN) {
1041 break;
1044 if (rc == NGX_OK) {
1046 /* a header line has been parsed successfully */
1048 h = ngx_list_push(&u->headers_in.headers);
1049 if (h == NULL) {
1050 return NGX_ERROR;
1053 if (f->split_parts && f->split_parts->nelts) {
1055 part = f->split_parts->elts;
1056 size = u->buffer.pos - part_start;
1058 for (i = 0; i < f->split_parts->nelts; i++) {
1059 size += part[i].end - part[i].start;
1062 p = ngx_pnalloc(r->pool, size);
1063 if (p == NULL) {
1064 return NGX_ERROR;
1067 buf.pos = p;
1069 for (i = 0; i < f->split_parts->nelts; i++) {
1070 p = ngx_cpymem(p, part[i].start,
1071 part[i].end - part[i].start);
1074 p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);
1076 buf.last = p;
1078 f->split_parts->nelts = 0;
1080 rc = ngx_http_parse_header_line(r, &buf);
1082 h->key.len = r->header_name_end - r->header_name_start;
1083 h->key.data = r->header_name_start;
1084 h->key.data[h->key.len] = '\0';
1086 h->value.len = r->header_end - r->header_start;
1087 h->value.data = r->header_start;
1088 h->value.data[h->value.len] = '\0';
1090 h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
1091 if (h->lowcase_key == NULL) {
1092 return NGX_ERROR;
1095 } else {
1097 h->key.len = r->header_name_end - r->header_name_start;
1098 h->value.len = r->header_end - r->header_start;
1100 h->key.data = ngx_pnalloc(r->pool,
1101 h->key.len + 1 + h->value.len + 1
1102 + h->key.len);
1103 if (h->key.data == NULL) {
1104 return NGX_ERROR;
1107 h->value.data = h->key.data + h->key.len + 1;
1108 h->lowcase_key = h->key.data + h->key.len + 1
1109 + h->value.len + 1;
1111 ngx_cpystrn(h->key.data, r->header_name_start,
1112 h->key.len + 1);
1113 ngx_cpystrn(h->value.data, r->header_start,
1114 h->value.len + 1);
1117 h->hash = r->header_hash;
1119 if (h->key.len == r->lowcase_index) {
1120 ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
1122 } else {
1123 ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
1126 hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
1127 h->lowcase_key, h->key.len);
1129 if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
1130 return NGX_ERROR;
1133 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1134 "http fastcgi header: \"%V: %V\"",
1135 &h->key, &h->value);
1137 if (u->buffer.pos < u->buffer.last) {
1138 continue;
1141 /* the end of the FastCGI record */
1143 break;
1146 if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1148 /* a whole header has been parsed successfully */
1150 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1151 "http fastcgi header done");
1153 if (u->headers_in.status) {
1154 status_line = &u->headers_in.status->value;
1156 status = ngx_atoi(status_line->data, 3);
1158 if (status == NGX_ERROR) {
1159 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1160 "upstream sent invalid status \"%V\"",
1161 status_line);
1162 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1165 u->headers_in.status_n = status;
1166 u->headers_in.status_line = *status_line;
1168 } else if (u->headers_in.location) {
1169 u->headers_in.status_n = 302;
1170 u->headers_in.status_line.len =
1171 sizeof("302 Moved Temporarily") - 1;
1172 u->headers_in.status_line.data =
1173 (u_char *) "302 Moved Temporarily";
1175 } else {
1176 u->headers_in.status_n = 200;
1177 u->headers_in.status_line.len = sizeof("200 OK") - 1;
1178 u->headers_in.status_line.data = (u_char *) "200 OK";
1181 u->state->status = u->headers_in.status_n;
1182 #if 0
1183 if (u->cacheable) {
1184 u->cacheable = ngx_http_upstream_is_cacheable(r);
1186 #endif
1188 break;
1191 /* there was error while a header line parsing */
1193 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1194 "upstream sent invalid header");
1196 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
1199 if (last) {
1200 u->buffer.last = last;
1203 f->length -= u->buffer.pos - start;
1205 if (f->length == 0) {
1206 if (f->padding) {
1207 f->state = ngx_http_fastcgi_st_padding;
1208 } else {
1209 f->state = ngx_http_fastcgi_st_version;
1213 if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
1214 return NGX_OK;
1217 if (rc == NGX_OK) {
1218 continue;
1221 /* rc == NGX_AGAIN */
1223 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1224 "upstream split a header line in FastCGI records");
1226 if (f->split_parts == NULL) {
1227 f->split_parts = ngx_array_create(r->pool, 1,
1228 sizeof(ngx_http_fastcgi_split_part_t));
1229 if (f->split_parts == NULL) {
1230 return NGX_ERROR;
1234 part = ngx_array_push(f->split_parts);
1236 part->start = part_start;
1237 part->end = u->buffer.last;
1239 return NGX_AGAIN;
1244 static ngx_int_t
1245 ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
1247 ngx_int_t rc;
1248 ngx_buf_t *b, **prev;
1249 ngx_str_t line;
1250 ngx_chain_t *cl;
1251 ngx_http_request_t *r;
1252 ngx_http_fastcgi_ctx_t *f;
1254 if (buf->pos == buf->last) {
1255 return NGX_OK;
1258 r = p->input_ctx;
1259 f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
1261 b = NULL;
1262 prev = &buf->shadow;
1264 f->pos = buf->pos;
1265 f->last = buf->last;
1267 for ( ;; ) {
1268 if (f->state < ngx_http_fastcgi_st_data) {
1270 rc = ngx_http_fastcgi_process_record(r, f);
1272 if (rc == NGX_AGAIN) {
1273 break;
1276 if (rc == NGX_ERROR) {
1277 return NGX_ERROR;
1280 if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
1281 f->state = ngx_http_fastcgi_st_version;
1282 p->upstream_done = 1;
1284 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1285 "http fastcgi closed stdout");
1287 continue;
1290 if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
1291 f->state = ngx_http_fastcgi_st_version;
1292 p->upstream_done = 1;
1294 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
1295 "http fastcgi sent end request");
1297 break;
1302 if (f->state == ngx_http_fastcgi_st_padding) {
1304 if (f->pos + f->padding < f->last) {
1305 f->state = ngx_http_fastcgi_st_version;
1306 f->pos += f->padding;
1308 continue;
1311 if (f->pos + f->padding == f->last) {
1312 f->state = ngx_http_fastcgi_st_version;
1314 break;
1317 f->padding -= f->last - f->pos;
1319 break;
1323 /* f->state == ngx_http_fastcgi_st_data */
1325 if (f->type == NGX_HTTP_FASTCGI_STDERR) {
1327 if (f->length) {
1329 if (f->pos == f->last) {
1330 break;
1333 line.data = f->pos;
1335 if (f->pos + f->length <= f->last) {
1336 line.len = f->length;
1337 f->pos += f->length;
1338 f->length = 0;
1339 f->state = ngx_http_fastcgi_st_padding;
1341 } else {
1342 line.len = f->last - f->pos;
1343 f->length -= f->last - f->pos;
1344 f->pos = f->last;
1347 while (line.data[line.len - 1] == LF
1348 || line.data[line.len - 1] == CR
1349 || line.data[line.len - 1] == '.'
1350 || line.data[line.len - 1] == ' ')
1352 line.len--;
1355 ngx_log_error(NGX_LOG_ERR, p->log, 0,
1356 "FastCGI sent in stderr: \"%V\"", &line);
1358 if (f->pos == f->last) {
1359 break;
1362 } else {
1363 f->state = ngx_http_fastcgi_st_version;
1366 continue;
1370 /* f->type == NGX_HTTP_FASTCGI_STDOUT */
1372 if (f->pos == f->last) {
1373 break;
1376 if (p->free) {
1377 b = p->free->buf;
1378 p->free = p->free->next;
1380 } else {
1381 b = ngx_alloc_buf(p->pool);
1382 if (b == NULL) {
1383 return NGX_ERROR;
1387 ngx_memzero(b, sizeof(ngx_buf_t));
1389 b->pos = f->pos;
1390 b->start = buf->start;
1391 b->end = buf->end;
1392 b->tag = p->tag;
1393 b->temporary = 1;
1394 b->recycled = 1;
1396 *prev = b;
1397 prev = &b->shadow;
1399 cl = ngx_alloc_chain_link(p->pool);
1400 if (cl == NULL) {
1401 return NGX_ERROR;
1404 cl->buf = b;
1405 cl->next = NULL;
1407 if (p->in) {
1408 *p->last_in = cl;
1409 } else {
1410 p->in = cl;
1412 p->last_in = &cl->next;
1415 /* STUB */ b->num = buf->num;
1417 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1418 "input buf #%d %p", b->num, b->pos);
1420 if (f->pos + f->length < f->last) {
1422 if (f->padding) {
1423 f->state = ngx_http_fastcgi_st_padding;
1424 } else {
1425 f->state = ngx_http_fastcgi_st_version;
1428 f->pos += f->length;
1429 b->last = f->pos;
1431 continue;
1434 if (f->pos + f->length == f->last) {
1436 if (f->padding) {
1437 f->state = ngx_http_fastcgi_st_padding;
1438 } else {
1439 f->state = ngx_http_fastcgi_st_version;
1442 b->last = f->last;
1444 break;
1447 f->length -= f->last - f->pos;
1449 b->last = f->last;
1451 break;
1455 if (b) {
1456 b->shadow = buf;
1457 b->last_shadow = 1;
1459 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
1460 "input buf %p %z", b->pos, b->last - b->pos);
1462 return NGX_OK;
1465 /* there is no data record in the buf, add it to free chain */
1467 if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
1468 return NGX_ERROR;
1471 return NGX_OK;
1475 static ngx_int_t
1476 ngx_http_fastcgi_process_record(ngx_http_request_t *r,
1477 ngx_http_fastcgi_ctx_t *f)
1479 u_char ch, *p;
1480 ngx_http_fastcgi_state_e state;
1482 state = f->state;
1484 for (p = f->pos; p < f->last; p++) {
1486 ch = *p;
1488 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1489 "http fastcgi record byte: %02Xd", ch);
1491 switch (state) {
1493 case ngx_http_fastcgi_st_version:
1494 if (ch != 1) {
1495 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1496 "upstream sent unsupported FastCGI "
1497 "protocol version: %d", ch);
1498 return NGX_ERROR;
1500 state = ngx_http_fastcgi_st_type;
1501 break;
1503 case ngx_http_fastcgi_st_type:
1504 switch (ch) {
1505 case NGX_HTTP_FASTCGI_STDOUT:
1506 case NGX_HTTP_FASTCGI_STDERR:
1507 case NGX_HTTP_FASTCGI_END_REQUEST:
1508 f->type = (ngx_uint_t) ch;
1509 break;
1510 default:
1511 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1512 "upstream sent invalid FastCGI "
1513 "record type: %d", ch);
1514 return NGX_ERROR;
1517 state = ngx_http_fastcgi_st_request_id_hi;
1518 break;
1520 /* we support the single request per connection */
1522 case ngx_http_fastcgi_st_request_id_hi:
1523 if (ch != 0) {
1524 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1525 "upstream sent unexpected FastCGI "
1526 "request id high byte: %d", ch);
1527 return NGX_ERROR;
1529 state = ngx_http_fastcgi_st_request_id_lo;
1530 break;
1532 case ngx_http_fastcgi_st_request_id_lo:
1533 if (ch != 1) {
1534 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1535 "upstream sent unexpected FastCGI "
1536 "request id low byte: %d", ch);
1537 return NGX_ERROR;
1539 state = ngx_http_fastcgi_st_content_length_hi;
1540 break;
1542 case ngx_http_fastcgi_st_content_length_hi:
1543 f->length = ch << 8;
1544 state = ngx_http_fastcgi_st_content_length_lo;
1545 break;
1547 case ngx_http_fastcgi_st_content_length_lo:
1548 f->length |= (size_t) ch;
1549 state = ngx_http_fastcgi_st_padding_length;
1550 break;
1552 case ngx_http_fastcgi_st_padding_length:
1553 f->padding = (size_t) ch;
1554 state = ngx_http_fastcgi_st_reserved;
1555 break;
1557 case ngx_http_fastcgi_st_reserved:
1558 state = ngx_http_fastcgi_st_data;
1560 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1561 "http fastcgi record length: %z", f->length);
1563 f->pos = p + 1;
1564 f->state = state;
1566 return NGX_OK;
1568 /* suppress warning */
1569 case ngx_http_fastcgi_st_data:
1570 case ngx_http_fastcgi_st_padding:
1571 break;
1575 f->state = state;
1577 return NGX_AGAIN;
1581 static void
1582 ngx_http_fastcgi_abort_request(ngx_http_request_t *r)
1584 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1585 "abort http fastcgi request");
1587 return;
1591 static void
1592 ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
1594 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1595 "finalize http fastcgi request");
1597 return;
1601 static ngx_int_t
1602 ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
1604 ngx_http_variable_t *var;
1606 var = ngx_http_add_variable(cf, &ngx_http_fastcgi_script_name,
1607 NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE);
1608 if (var == NULL) {
1609 return NGX_ERROR;
1612 var->get_handler = ngx_http_fastcgi_script_name_variable;
1614 return NGX_OK;
1618 static void *
1619 ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
1621 ngx_http_fastcgi_loc_conf_t *conf;
1623 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));
1624 if (conf == NULL) {
1625 return NGX_CONF_ERROR;
1629 * set by ngx_pcalloc():
1631 * conf->upstream.bufs.num = 0;
1632 * conf->upstream.next_upstream = 0;
1633 * conf->upstream.temp_path = NULL;
1634 * conf->upstream.hide_headers_hash = { NULL, 0 };
1635 * conf->upstream.schema = { 0, NULL };
1636 * conf->upstream.uri = { 0, NULL };
1637 * conf->upstream.location = NULL;
1638 * conf->upstream.store_lengths = NULL;
1639 * conf->upstream.store_values = NULL;
1641 * conf->index.len = 0;
1642 * conf->index.data = NULL;
1645 conf->upstream.store = NGX_CONF_UNSET;
1646 conf->upstream.store_access = NGX_CONF_UNSET_UINT;
1647 conf->upstream.buffering = NGX_CONF_UNSET;
1648 conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
1650 conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
1651 conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
1652 conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
1654 conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
1655 conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
1657 conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
1658 conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
1659 conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
1661 conf->upstream.pass_request_headers = NGX_CONF_UNSET;
1662 conf->upstream.pass_request_body = NGX_CONF_UNSET;
1664 conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
1665 conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
1667 conf->upstream.intercept_errors = NGX_CONF_UNSET;
1669 /* "fastcgi_cyclic_temp_file" is disabled */
1670 conf->upstream.cyclic_temp_file = 0;
1672 conf->catch_stderr = NGX_CONF_UNSET_PTR;
1674 return conf;
1678 static char *
1679 ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1681 ngx_http_fastcgi_loc_conf_t *prev = parent;
1682 ngx_http_fastcgi_loc_conf_t *conf = child;
1684 u_char *p;
1685 size_t size;
1686 uintptr_t *code;
1687 ngx_uint_t i;
1688 ngx_keyval_t *src;
1689 ngx_hash_init_t hash;
1690 ngx_http_script_compile_t sc;
1691 ngx_http_script_copy_code_t *copy;
1693 if (conf->upstream.store != 0) {
1694 ngx_conf_merge_value(conf->upstream.store,
1695 prev->upstream.store, 0);
1697 if (conf->upstream.store_lengths == NULL) {
1698 conf->upstream.store_lengths = prev->upstream.store_lengths;
1699 conf->upstream.store_values = prev->upstream.store_values;
1703 ngx_conf_merge_uint_value(conf->upstream.store_access,
1704 prev->upstream.store_access, 0600);
1706 ngx_conf_merge_value(conf->upstream.buffering,
1707 prev->upstream.buffering, 1);
1709 ngx_conf_merge_value(conf->upstream.ignore_client_abort,
1710 prev->upstream.ignore_client_abort, 0);
1712 ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
1713 prev->upstream.connect_timeout, 60000);
1715 ngx_conf_merge_msec_value(conf->upstream.send_timeout,
1716 prev->upstream.send_timeout, 60000);
1718 ngx_conf_merge_msec_value(conf->upstream.read_timeout,
1719 prev->upstream.read_timeout, 60000);
1721 ngx_conf_merge_size_value(conf->upstream.send_lowat,
1722 prev->upstream.send_lowat, 0);
1724 ngx_conf_merge_size_value(conf->upstream.buffer_size,
1725 prev->upstream.buffer_size,
1726 (size_t) ngx_pagesize);
1729 ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
1730 8, ngx_pagesize);
1732 if (conf->upstream.bufs.num < 2) {
1733 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1734 "there must be at least 2 \"fastcgi_buffers\"");
1735 return NGX_CONF_ERROR;
1739 size = conf->upstream.buffer_size;
1740 if (size < conf->upstream.bufs.size) {
1741 size = conf->upstream.bufs.size;
1745 ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
1746 prev->upstream.busy_buffers_size_conf,
1747 NGX_CONF_UNSET_SIZE);
1749 if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
1750 conf->upstream.busy_buffers_size = 2 * size;
1751 } else {
1752 conf->upstream.busy_buffers_size =
1753 conf->upstream.busy_buffers_size_conf;
1756 if (conf->upstream.busy_buffers_size < size) {
1757 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1758 "\"fastcgi_busy_buffers_size\" must be equal or bigger than "
1759 "maximum of the value of \"fastcgi_buffer_size\" and "
1760 "one of the \"fastcgi_buffers\"");
1762 return NGX_CONF_ERROR;
1765 if (conf->upstream.busy_buffers_size
1766 > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
1768 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1769 "\"fastcgi_busy_buffers_size\" must be less than "
1770 "the size of all \"fastcgi_buffers\" minus one buffer");
1772 return NGX_CONF_ERROR;
1776 ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
1777 prev->upstream.temp_file_write_size_conf,
1778 NGX_CONF_UNSET_SIZE);
1780 if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
1781 conf->upstream.temp_file_write_size = 2 * size;
1782 } else {
1783 conf->upstream.temp_file_write_size =
1784 conf->upstream.temp_file_write_size_conf;
1787 if (conf->upstream.temp_file_write_size < size) {
1788 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1789 "\"fastcgi_temp_file_write_size\" must be equal or bigger than "
1790 "maximum of the value of \"fastcgi_buffer_size\" and "
1791 "one of the \"fastcgi_buffers\"");
1793 return NGX_CONF_ERROR;
1797 ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
1798 prev->upstream.max_temp_file_size_conf,
1799 NGX_CONF_UNSET_SIZE);
1801 if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
1802 conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
1803 } else {
1804 conf->upstream.max_temp_file_size =
1805 conf->upstream.max_temp_file_size_conf;
1808 if (conf->upstream.max_temp_file_size != 0
1809 && conf->upstream.max_temp_file_size < size)
1811 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1812 "\"fastcgi_max_temp_file_size\" must be equal to zero to disable "
1813 "the temporary files usage or must be equal or bigger than "
1814 "maximum of the value of \"fastcgi_buffer_size\" and "
1815 "one of the \"fastcgi_buffers\"");
1817 return NGX_CONF_ERROR;
1821 ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
1822 prev->upstream.next_upstream,
1823 (NGX_CONF_BITMASK_SET
1824 |NGX_HTTP_UPSTREAM_FT_ERROR
1825 |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
1827 if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
1828 conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
1829 |NGX_HTTP_UPSTREAM_FT_OFF;
1832 ngx_conf_merge_path_value(conf->upstream.temp_path,
1833 prev->upstream.temp_path,
1834 NGX_HTTP_FASTCGI_TEMP_PATH, 1, 2, 0,
1835 ngx_garbage_collector_temp_handler, cf);
1837 ngx_conf_merge_value(conf->upstream.pass_request_headers,
1838 prev->upstream.pass_request_headers, 1);
1839 ngx_conf_merge_value(conf->upstream.pass_request_body,
1840 prev->upstream.pass_request_body, 1);
1842 ngx_conf_merge_value(conf->upstream.intercept_errors,
1843 prev->upstream.intercept_errors, 0);
1845 ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);
1848 ngx_conf_merge_str_value(conf->index, prev->index, "");
1850 hash.max_size = 512;
1851 hash.bucket_size = ngx_align(64, ngx_cacheline_size);
1852 hash.name = "fastcgi_hide_headers_hash";
1854 if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
1855 &prev->upstream,
1856 ngx_http_fastcgi_hide_headers,
1857 &hash)
1858 != NGX_OK)
1860 return NGX_CONF_ERROR;
1863 if (conf->upstream.upstream == NULL) {
1864 conf->upstream.upstream = prev->upstream.upstream;
1865 conf->upstream.schema = prev->upstream.schema;
1868 if (conf->params_source == NULL) {
1869 conf->flushes = prev->flushes;
1870 conf->params_len = prev->params_len;
1871 conf->params = prev->params;
1872 conf->params_source = prev->params_source;
1874 if (conf->params_source == NULL) {
1875 return NGX_CONF_OK;
1879 conf->params_len = ngx_array_create(cf->pool, 64, 1);
1880 if (conf->params_len == NULL) {
1881 return NGX_CONF_ERROR;
1884 conf->params = ngx_array_create(cf->pool, 512, 1);
1885 if (conf->params == NULL) {
1886 return NGX_CONF_ERROR;
1889 src = conf->params_source->elts;
1890 for (i = 0; i < conf->params_source->nelts; i++) {
1892 if (ngx_http_script_variables_count(&src[i].value) == 0) {
1893 copy = ngx_array_push_n(conf->params_len,
1894 sizeof(ngx_http_script_copy_code_t));
1895 if (copy == NULL) {
1896 return NGX_CONF_ERROR;
1899 copy->code = (ngx_http_script_code_pt)
1900 ngx_http_script_copy_len_code;
1901 copy->len = src[i].key.len;
1904 copy = ngx_array_push_n(conf->params_len,
1905 sizeof(ngx_http_script_copy_code_t));
1906 if (copy == NULL) {
1907 return NGX_CONF_ERROR;
1910 copy->code = (ngx_http_script_code_pt)
1911 ngx_http_script_copy_len_code;
1912 copy->len = src[i].value.len;
1915 size = (sizeof(ngx_http_script_copy_code_t)
1916 + src[i].key.len + src[i].value.len
1917 + sizeof(uintptr_t) - 1)
1918 & ~(sizeof(uintptr_t) - 1);
1920 copy = ngx_array_push_n(conf->params, size);
1921 if (copy == NULL) {
1922 return NGX_CONF_ERROR;
1925 copy->code = ngx_http_script_copy_code;
1926 copy->len = src[i].key.len + src[i].value.len;
1928 p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
1930 p = ngx_cpymem(p, src[i].key.data, src[i].key.len);
1931 ngx_memcpy(p, src[i].value.data, src[i].value.len);
1933 } else {
1934 copy = ngx_array_push_n(conf->params_len,
1935 sizeof(ngx_http_script_copy_code_t));
1936 if (copy == NULL) {
1937 return NGX_CONF_ERROR;
1940 copy->code = (ngx_http_script_code_pt)
1941 ngx_http_script_copy_len_code;
1942 copy->len = src[i].key.len;
1945 size = (sizeof(ngx_http_script_copy_code_t)
1946 + src[i].key.len + sizeof(uintptr_t) - 1)
1947 & ~(sizeof(uintptr_t) - 1);
1949 copy = ngx_array_push_n(conf->params, size);
1950 if (copy == NULL) {
1951 return NGX_CONF_ERROR;
1954 copy->code = ngx_http_script_copy_code;
1955 copy->len = src[i].key.len;
1957 p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
1958 ngx_memcpy(p, src[i].key.data, src[i].key.len);
1961 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1963 sc.cf = cf;
1964 sc.source = &src[i].value;
1965 sc.flushes = &conf->flushes;
1966 sc.lengths = &conf->params_len;
1967 sc.values = &conf->params;
1969 if (ngx_http_script_compile(&sc) != NGX_OK) {
1970 return NGX_CONF_ERROR;
1974 code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
1975 if (code == NULL) {
1976 return NGX_CONF_ERROR;
1979 *code = (uintptr_t) NULL;
1982 code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
1983 if (code == NULL) {
1984 return NGX_CONF_ERROR;
1987 *code = (uintptr_t) NULL;
1990 code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
1991 if (code == NULL) {
1992 return NGX_CONF_ERROR;
1995 *code = (uintptr_t) NULL;
1997 return NGX_CONF_OK;
2001 static ngx_int_t
2002 ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
2003 ngx_http_variable_value_t *v, uintptr_t data)
2005 u_char *p;
2006 ngx_http_fastcgi_loc_conf_t *flcf;
2008 if (r->uri.len) {
2009 v->valid = 1;
2010 v->no_cacheable = 0;
2011 v->not_found = 0;
2013 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
2015 if (r->uri.data[r->uri.len - 1] != '/') {
2016 v->len = r->uri.len;
2017 v->data = r->uri.data;
2018 return NGX_OK;
2021 v->len = r->uri.len + flcf->index.len;
2023 v->data = ngx_pnalloc(r->pool, v->len);
2024 if (v->data == NULL) {
2025 return NGX_ERROR;
2028 p = ngx_copy(v->data, r->uri.data, r->uri.len);
2029 ngx_memcpy(p, flcf->index.data, flcf->index.len);
2031 } else {
2032 v->len = 0;
2033 v->valid = 1;
2034 v->no_cacheable = 0;
2035 v->not_found = 0;
2036 v->data = NULL;
2038 return NGX_OK;
2041 return NGX_OK;
2045 static char *
2046 ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2048 ngx_http_fastcgi_loc_conf_t *lcf = conf;
2050 ngx_url_t u;
2051 ngx_str_t *value;
2052 ngx_http_core_loc_conf_t *clcf;
2054 if (lcf->upstream.schema.len) {
2055 return "is duplicate";
2058 value = cf->args->elts;
2060 ngx_memzero(&u, sizeof(ngx_url_t));
2062 u.url = value[1];
2063 u.no_resolve = 1;
2065 lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
2066 if (lcf->upstream.upstream == NULL) {
2067 return NGX_CONF_ERROR;
2070 lcf->upstream.schema.len = sizeof("fastcgi://") - 1;
2071 lcf->upstream.schema.data = (u_char *) "fastcgi://";
2073 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
2075 clcf->handler = ngx_http_fastcgi_handler;
2077 if (clcf->name.data[clcf->name.len - 1] == '/') {
2078 clcf->auto_redirect = 1;
2081 return NGX_CONF_OK;
2085 static char *
2086 ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2088 ngx_http_fastcgi_loc_conf_t *flcf = conf;
2090 ngx_str_t *value;
2091 ngx_http_script_compile_t sc;
2093 if (flcf->upstream.store != NGX_CONF_UNSET || flcf->upstream.store_lengths)
2095 return "is duplicate";
2098 value = cf->args->elts;
2100 if (ngx_strcmp(value[1].data, "on") == 0) {
2101 flcf->upstream.store = 1;
2102 return NGX_CONF_OK;
2105 if (ngx_strcmp(value[1].data, "off") == 0) {
2106 flcf->upstream.store = 0;
2107 return NGX_CONF_OK;
2110 /* include the terminating '\0' into script */
2111 value[1].len++;
2113 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2115 sc.cf = cf;
2116 sc.source = &value[1];
2117 sc.lengths = &flcf->upstream.store_lengths;
2118 sc.values = &flcf->upstream.store_values;
2119 sc.variables = ngx_http_script_variables_count(&value[1]);
2120 sc.complete_lengths = 1;
2121 sc.complete_values = 1;
2123 if (ngx_http_script_compile(&sc) != NGX_OK) {
2124 return NGX_CONF_ERROR;
2127 return NGX_CONF_OK;
2131 static char *
2132 ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
2134 #if (NGX_FREEBSD)
2135 ssize_t *np = data;
2137 if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
2138 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2139 "\"fastcgi_send_lowat\" must be less than %d "
2140 "(sysctl net.inet.tcp.sendspace)",
2141 ngx_freebsd_net_inet_tcp_sendspace);
2143 return NGX_CONF_ERROR;
2146 #elif !(NGX_HAVE_SO_SNDLOWAT)
2147 ssize_t *np = data;
2149 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
2150 "\"fastcgi_send_lowat\" is not supported, ignored");
2152 *np = 0;
2154 #endif
2156 return NGX_CONF_OK;
2160 static char *
2161 ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
2162 ngx_command_t *cmd, void *conf)
2164 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2165 "\"fastcgi_upstream_max_fails\" is not supported, "
2166 "use the \"max_fails\" parameter of the \"server\" directive ",
2167 "inside the \"upstream\" block");
2169 return NGX_CONF_ERROR;
2173 static char *
2174 ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
2175 ngx_command_t *cmd, void *conf)
2177 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2178 "\"fastcgi_upstream_fail_timeout\" is not supported, "
2179 "use the \"fail_timeout\" parameter of the \"server\" directive ",
2180 "inside the \"upstream\" block");
2182 return NGX_CONF_ERROR;