8 #include "gw_backend.h"
9 typedef gw_plugin_config plugin_config
;
10 typedef gw_plugin_data plugin_data
;
11 typedef gw_handler_ctx handler_ctx
;
16 #include "http_chunk.h"
18 #include "status_counter.h"
20 #ifdef HAVE_FASTCGI_FASTCGI_H
21 # include <fastcgi/fastcgi.h>
23 # ifdef HAVE_FASTCGI_H
28 #endif /* HAVE_FASTCGI_FASTCGI_H */
30 #if GW_RESPONDER != FCGI_RESPONDER
31 #error "mismatched defines: (GW_RESPONDER != FCGI_RESPONDER)"
33 #if GW_AUTHORIZER != FCGI_AUTHORIZER
34 #error "mismatched defines: (GW_AUTHORIZER != FCGI_AUTHORIZER)"
36 #if GW_FILTER != FCGI_FILTER
37 #error "mismatched defines: (GW_FILTER != FCGI_FILTER)"
40 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults
) {
45 config_values_t cv
[] = {
46 { "fastcgi.server", NULL
, T_CONFIG_LOCAL
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
47 { "fastcgi.debug", NULL
, T_CONFIG_INT
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
48 { "fastcgi.map-extensions", NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 2 */
49 { "fastcgi.balance", NULL
, T_CONFIG_LOCAL
, T_CONFIG_SCOPE_CONNECTION
}, /* 3 */
50 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
53 p
->config_storage
= calloc(srv
->config_context
->used
, sizeof(plugin_config
*));
54 force_assert(p
->config_storage
);
56 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
57 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
60 s
= calloc(1, sizeof(plugin_config
));
66 s
->ext_mapping
= array_init();
68 cv
[0].destination
= s
->exts
; /* not used; T_CONFIG_LOCAL */
69 cv
[1].destination
= &(s
->debug
);
70 cv
[2].destination
= s
->ext_mapping
;
71 cv
[3].destination
= NULL
; /* not used; T_CONFIG_LOCAL */
73 p
->config_storage
[i
] = s
;
75 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
79 du
= array_get_element(config
->value
, "fastcgi.server");
80 if (!gw_set_defaults_backend(srv
, p
, du
, i
, 0)) {
84 du
= array_get_element(config
->value
, "fastcgi.balance");
85 if (!gw_set_defaults_balance(srv
, s
, du
)) {
93 static int fcgi_env_add(void *venv
, const char *key
, size_t key_len
, const char *val
, size_t val_len
) {
97 size_t len_enc_len
= 0;
100 if (!key
|| !val
) return -1;
102 len
= key_len
+ val_len
;
104 len
+= key_len
> 127 ? 4 : 1;
105 len
+= val_len
> 127 ? 4 : 1;
107 if (buffer_string_length(env
) + len
>= FCGI_MAX_LENGTH
+ sizeof(FCGI_BeginRequestRecord
) + sizeof(FCGI_Header
)) {
109 * we can't append more headers, ignore it
115 * field length can be 31bit max
117 * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit
119 force_assert(key_len
< 0x7fffffffu
);
120 force_assert(val_len
< 0x7fffffffu
);
122 if (buffer_string_space(env
) < len
) {
123 size_t extend
= env
->size
* 2 - buffer_string_length(env
);
124 extend
= extend
> len
? extend
: len
+ 4095;
125 buffer_string_prepare_append(env
, extend
);
129 len_enc
[len_enc_len
++] = ((key_len
>> 24) & 0xff) | 0x80;
130 len_enc
[len_enc_len
++] = (key_len
>> 16) & 0xff;
131 len_enc
[len_enc_len
++] = (key_len
>> 8) & 0xff;
132 len_enc
[len_enc_len
++] = (key_len
>> 0) & 0xff;
134 len_enc
[len_enc_len
++] = (key_len
>> 0) & 0xff;
138 len_enc
[len_enc_len
++] = ((val_len
>> 24) & 0xff) | 0x80;
139 len_enc
[len_enc_len
++] = (val_len
>> 16) & 0xff;
140 len_enc
[len_enc_len
++] = (val_len
>> 8) & 0xff;
141 len_enc
[len_enc_len
++] = (val_len
>> 0) & 0xff;
143 len_enc
[len_enc_len
++] = (val_len
>> 0) & 0xff;
146 dst
= buffer_string_prepare_append(env
, len
);
147 memcpy(dst
, len_enc
, len_enc_len
);
148 memcpy(dst
+ len_enc_len
, key
, key_len
);
149 memcpy(dst
+ len_enc_len
+ key_len
, val
, val_len
);
150 buffer_commit(env
, len
);
155 static void fcgi_header(FCGI_Header
* header
, unsigned char type
, int request_id
, int contentLength
, unsigned char paddingLength
) {
156 force_assert(contentLength
<= FCGI_MAX_LENGTH
);
158 header
->version
= FCGI_VERSION_1
;
160 header
->requestIdB0
= request_id
& 0xff;
161 header
->requestIdB1
= (request_id
>> 8) & 0xff;
162 header
->contentLengthB0
= contentLength
& 0xff;
163 header
->contentLengthB1
= (contentLength
>> 8) & 0xff;
164 header
->paddingLength
= paddingLength
;
165 header
->reserved
= 0;
168 static handler_t
fcgi_stdin_append(server
*srv
, handler_ctx
*hctx
) {
170 connection
*con
= hctx
->remote_conn
;
171 chunkqueue
*req_cq
= con
->request_content_queue
;
172 off_t offset
, weWant
;
173 const off_t req_cqlen
= req_cq
->bytes_in
- req_cq
->bytes_out
;
174 int request_id
= hctx
->request_id
;
177 /* something to send ? */
178 for (offset
= 0; offset
!= req_cqlen
; offset
+= weWant
) {
179 weWant
= req_cqlen
- offset
> FCGI_MAX_LENGTH
? FCGI_MAX_LENGTH
: req_cqlen
- offset
;
181 if (-1 != hctx
->wb_reqlen
) {
182 if (hctx
->wb_reqlen
>= 0) {
183 hctx
->wb_reqlen
+= sizeof(header
);
185 hctx
->wb_reqlen
-= sizeof(header
);
189 fcgi_header(&(header
), FCGI_STDIN
, request_id
, weWant
, 0);
190 (chunkqueue_is_empty(hctx
->wb
) || hctx
->wb
->first
->type
== MEM_CHUNK
) /* else FILE_CHUNK for temp file */
191 ? chunkqueue_append_mem(hctx
->wb
, (const char *)&header
, sizeof(header
))
192 : chunkqueue_append_mem_min(hctx
->wb
, (const char *)&header
, sizeof(header
));
193 chunkqueue_steal(hctx
->wb
, req_cq
, weWant
);
194 /*(hctx->wb_reqlen already includes content_length)*/
197 if (hctx
->wb
->bytes_in
== hctx
->wb_reqlen
) {
198 /* terminate STDIN */
199 /* (future: must defer ending FCGI_STDIN
200 * if might later upgrade protocols
201 * and then have more data to send) */
202 fcgi_header(&(header
), FCGI_STDIN
, request_id
, 0, 0);
203 chunkqueue_append_mem(hctx
->wb
, (const char *)&header
, sizeof(header
));
204 hctx
->wb_reqlen
+= (int)sizeof(header
);
207 return HANDLER_GO_ON
;
210 static handler_t
fcgi_create_env(server
*srv
, handler_ctx
*hctx
) {
211 FCGI_BeginRequestRecord beginRecord
;
215 gw_host
*host
= hctx
->host
;
216 connection
*con
= hctx
->remote_conn
;
218 http_cgi_opts opts
= {
219 (hctx
->gw_mode
== FCGI_AUTHORIZER
),
220 host
->break_scriptfilename_for_php
,
222 host
->strip_request_uri
225 size_t rsz
= (size_t)(con
->read_queue
->bytes_out
- hctx
->wb
->bytes_in
);
226 buffer
* const b
= chunkqueue_prepend_buffer_open_sz(hctx
->wb
, rsz
< 65536 ? rsz
: con
->header_len
);
228 /* send FCGI_BEGIN_REQUEST */
230 if (hctx
->request_id
== 0) {
231 hctx
->request_id
= 1; /* always use id 1 as we don't use multiplexing */
233 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
234 "fcgi-request is already in use:", hctx
->request_id
);
236 request_id
= hctx
->request_id
;
238 fcgi_header(&(beginRecord
.header
), FCGI_BEGIN_REQUEST
, request_id
, sizeof(beginRecord
.body
), 0);
239 beginRecord
.body
.roleB0
= hctx
->gw_mode
;
240 beginRecord
.body
.roleB1
= 0;
241 beginRecord
.body
.flags
= 0;
242 memset(beginRecord
.body
.reserved
, 0, sizeof(beginRecord
.body
.reserved
));
244 buffer_copy_string_len(b
, (const char *)&beginRecord
, sizeof(beginRecord
));
245 fcgi_header(&header
, FCGI_PARAMS
, request_id
, 0, 0); /*(set aside space to fill in later)*/
246 buffer_append_string_len(b
, (const char *)&header
, sizeof(header
));
248 /* send FCGI_PARAMS */
250 if (0 != http_cgi_headers(srv
, con
, &opts
, fcgi_env_add
, b
)) {
251 con
->http_status
= 400;
254 chunkqueue_remove_finished_chunks(hctx
->wb
);
255 return HANDLER_FINISHED
;
257 fcgi_header(&(header
), FCGI_PARAMS
, request_id
,
258 buffer_string_length(b
) - sizeof(FCGI_BeginRequestRecord
) - sizeof(FCGI_Header
), 0);
259 memcpy(b
->ptr
+sizeof(FCGI_BeginRequestRecord
), (const char *)&header
, sizeof(header
));
261 fcgi_header(&(header
), FCGI_PARAMS
, request_id
, 0, 0);
262 buffer_append_string_len(b
, (const char *)&header
, sizeof(header
));
264 hctx
->wb_reqlen
= buffer_string_length(b
);
265 chunkqueue_prepend_buffer_commit(hctx
->wb
);
268 if (con
->request
.content_length
) {
269 /*chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);*/
270 if (con
->request
.content_length
> 0)
271 hctx
->wb_reqlen
+= con
->request
.content_length
;/* (eventual) (minimal) total request size, not necessarily including all fcgi_headers around content length yet */
272 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/
273 hctx
->wb_reqlen
= -hctx
->wb_reqlen
;
275 fcgi_stdin_append(srv
, hctx
);
277 status_counter_inc(srv
, CONST_STR_LEN("fastcgi.requests"));
278 return HANDLER_GO_ON
;
286 } fastcgi_response_packet
;
288 static int fastcgi_get_packet(server
*srv
, handler_ctx
*hctx
, fastcgi_response_packet
*packet
) {
290 size_t toread
= sizeof(FCGI_Header
), flen
= 0;
291 off_t rblen
= chunkqueue_length(hctx
->rb
);
292 if (rblen
< (off_t
)sizeof(FCGI_Header
)) {
294 if (hctx
->conf
.debug
&& 0 != rblen
) {
295 log_error_write(srv
, __FILE__
, __LINE__
, "sosds", "FastCGI: header too small:", rblen
, "bytes <", sizeof(FCGI_Header
), "bytes, waiting for more data");
300 /* get at least the FastCGI header */
301 for (chunk
*c
= hctx
->rb
->first
; c
; c
= c
->next
) {
302 size_t weHave
= buffer_string_length(c
->mem
) - c
->offset
;
303 if (weHave
>= toread
) {
304 memcpy((char *)&header
+ flen
, c
->mem
->ptr
+ c
->offset
, toread
);
308 memcpy((char *)&header
+ flen
, c
->mem
->ptr
+ c
->offset
, weHave
);
313 /* we have at least a header, now check how much we have to fetch */
314 packet
->len
= (header
.contentLengthB0
| (header
.contentLengthB1
<< 8)) + header
.paddingLength
;
315 packet
->request_id
= (header
.requestIdB0
| (header
.requestIdB1
<< 8));
316 packet
->type
= header
.type
;
317 packet
->padding
= header
.paddingLength
;
319 if (packet
->len
> (unsigned int)rblen
-sizeof(FCGI_Header
)) {
320 return -1; /* we didn't get the full packet */
323 chunkqueue_mark_written(hctx
->rb
, sizeof(FCGI_Header
));
327 static void fastcgi_get_packet_body(buffer
*b
, handler_ctx
*hctx
, fastcgi_response_packet
*packet
) {
328 /* copy content; hctx->rb must contain at least packet->len content */
329 size_t toread
= packet
->len
- packet
->padding
;
330 buffer_string_prepare_append(b
, toread
);
331 for (chunk
*c
= hctx
->rb
->first
; c
; c
= c
->next
) {
332 size_t weHave
= buffer_string_length(c
->mem
) - c
->offset
;
333 if (weHave
>= toread
) {
334 buffer_append_string_len(b
, c
->mem
->ptr
+ c
->offset
, toread
);
338 buffer_append_string_len(b
, c
->mem
->ptr
+ c
->offset
, weHave
);
341 chunkqueue_mark_written(hctx
->rb
, packet
->len
);
344 static handler_t
fcgi_recv_parse(server
*srv
, connection
*con
, struct http_response_opts_t
*opts
, buffer
*b
, size_t n
) {
345 handler_ctx
*hctx
= (handler_ctx
*)opts
->pdata
;
349 if (-1 == hctx
->request_id
) return HANDLER_FINISHED
; /*(flag request ended)*/
350 if (!(fdevent_fdnode_interest(hctx
->fdn
) & FDEVENT_IN
)
351 && !(con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE_POLLRDHUP
))
352 return HANDLER_GO_ON
;
353 log_error_write(srv
, __FILE__
, __LINE__
, "ssdsb",
354 "unexpected end-of-file (perhaps the fastcgi process died):",
355 "pid:", hctx
->proc
->pid
,
356 "socket:", hctx
->proc
->connection_name
);
358 return HANDLER_ERROR
;
361 chunkqueue_append_buffer(hctx
->rb
, b
);
364 * parse the fastcgi packets and forward the content to the write-queue
368 fastcgi_response_packet packet
;
370 /* check if we have at least one packet */
371 if (0 != fastcgi_get_packet(srv
, hctx
, &packet
)) {
376 switch(packet
.type
) {
378 if (packet
.len
== 0) break;
380 /* is the header already finished */
381 if (0 == con
->file_started
) {
382 /* split header from body */
383 buffer
*hdrs
= hctx
->response
;
386 buffer_clear(srv
->tmp_buf
);
388 fastcgi_get_packet_body(hdrs
, hctx
, &packet
);
389 if (HANDLER_GO_ON
!= http_response_parse_headers(srv
, con
, &hctx
->opts
, hdrs
)) {
390 hctx
->send_content_body
= 0;
394 if (0 == con
->file_started
) {
395 if (!hctx
->response
) {
396 hctx
->response
= chunk_buffer_acquire();
397 buffer_copy_buffer(hctx
->response
, hdrs
);
400 else if (hctx
->gw_mode
== GW_AUTHORIZER
&&
401 (con
->http_status
== 0 || con
->http_status
== 200)) {
402 /* authorizer approved request; ignore the content here */
403 hctx
->send_content_body
= 0;
405 } else if (hctx
->send_content_body
) {
406 if (0 != http_chunk_transfer_cqlen(srv
, con
, hctx
->rb
, packet
.len
- packet
.padding
)) {
407 /* error writing to tempfile;
408 * truncate response or send 500 if nothing sent yet */
411 if (packet
.padding
) chunkqueue_mark_written(hctx
->rb
, packet
.padding
);
413 chunkqueue_mark_written(hctx
->rb
, packet
.len
);
417 if (packet
.len
== 0) break;
419 buffer_clear(srv
->tmp_buf
);
420 fastcgi_get_packet_body(srv
->tmp_buf
, hctx
, &packet
);
421 log_error_write_multiline_buffer(srv
, __FILE__
, __LINE__
, srv
->tmp_buf
, "s",
425 case FCGI_END_REQUEST
:
426 hctx
->request_id
= -1; /*(flag request ended)*/
430 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
431 "FastCGI: header.type not handled: ", packet
.type
);
432 chunkqueue_mark_written(hctx
->rb
, packet
.len
);
437 return 0 == fin
? HANDLER_GO_ON
: HANDLER_FINISHED
;
442 static int fcgi_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
444 plugin_config
*s
= p
->config_storage
[0];
453 /* skip the first, the global context */
454 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
455 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
456 s
= p
->config_storage
[i
];
458 /* condition didn't match */
459 if (!config_check_cond(srv
, con
, dc
)) continue;
462 for (j
= 0; j
< dc
->value
->used
; j
++) {
463 data_unset
*du
= dc
->value
->data
[j
];
465 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("fastcgi.server"))) {
469 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("fastcgi.debug"))) {
471 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("fastcgi.balance"))) {
473 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("fastcgi.map-extensions"))) {
483 static handler_t
fcgi_check_extension(server
*srv
, connection
*con
, void *p_d
, int uri_path_handler
) {
484 plugin_data
*p
= p_d
;
487 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
489 fcgi_patch_connection(srv
, con
, p
);
490 if (NULL
== p
->conf
.exts
) return HANDLER_GO_ON
;
492 rc
= gw_check_extension(srv
, con
, p
, uri_path_handler
, 0);
493 if (HANDLER_GO_ON
!= rc
) return rc
;
495 if (con
->mode
== p
->id
) {
496 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
497 hctx
->opts
.backend
= BACKEND_FASTCGI
;
498 hctx
->opts
.parse
= fcgi_recv_parse
;
499 hctx
->opts
.pdata
= hctx
;
500 hctx
->stdin_append
= fcgi_stdin_append
;
501 hctx
->create_env
= fcgi_create_env
;
503 hctx
->rb
= chunkqueue_init();
506 chunkqueue_reset(hctx
->rb
);
510 return HANDLER_GO_ON
;
513 /* uri-path handler */
514 static handler_t
fcgi_check_extension_1(server
*srv
, connection
*con
, void *p_d
) {
515 return fcgi_check_extension(srv
, con
, p_d
, 1);
518 /* start request handler */
519 static handler_t
fcgi_check_extension_2(server
*srv
, connection
*con
, void *p_d
) {
520 return fcgi_check_extension(srv
, con
, p_d
, 0);
524 int mod_fastcgi_plugin_init(plugin
*p
);
525 int mod_fastcgi_plugin_init(plugin
*p
) {
526 p
->version
= LIGHTTPD_VERSION_ID
;
527 p
->name
= buffer_init_string("fastcgi");
530 p
->cleanup
= gw_free
;
531 p
->set_defaults
= mod_fastcgi_set_defaults
;
532 p
->connection_reset
= gw_connection_reset
;
533 p
->handle_uri_clean
= fcgi_check_extension_1
;
534 p
->handle_subrequest_start
= fcgi_check_extension_2
;
535 p
->handle_subrequest
= gw_handle_subrequest
;
536 p
->handle_trigger
= gw_handle_trigger
;
537 p
->handle_waitpid
= gw_handle_waitpid_cb
;