7 #include "gw_backend.h"
8 typedef gw_plugin_config plugin_config
;
9 typedef gw_plugin_data plugin_data
;
10 typedef gw_handler_ctx handler_ctx
;
15 #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(1, 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;
99 if (!key
|| !val
) return -1;
101 len
= key_len
+ val_len
;
103 len
+= key_len
> 127 ? 4 : 1;
104 len
+= val_len
> 127 ? 4 : 1;
106 if (buffer_string_length(env
) + len
>= FCGI_MAX_LENGTH
) {
108 * we can't append more headers, ignore it
114 * field length can be 31bit max
116 * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit
118 force_assert(key_len
< 0x7fffffffu
);
119 force_assert(val_len
< 0x7fffffffu
);
121 buffer_string_prepare_append(env
, len
);
124 len_enc
[len_enc_len
++] = ((key_len
>> 24) & 0xff) | 0x80;
125 len_enc
[len_enc_len
++] = (key_len
>> 16) & 0xff;
126 len_enc
[len_enc_len
++] = (key_len
>> 8) & 0xff;
127 len_enc
[len_enc_len
++] = (key_len
>> 0) & 0xff;
129 len_enc
[len_enc_len
++] = (key_len
>> 0) & 0xff;
133 len_enc
[len_enc_len
++] = ((val_len
>> 24) & 0xff) | 0x80;
134 len_enc
[len_enc_len
++] = (val_len
>> 16) & 0xff;
135 len_enc
[len_enc_len
++] = (val_len
>> 8) & 0xff;
136 len_enc
[len_enc_len
++] = (val_len
>> 0) & 0xff;
138 len_enc
[len_enc_len
++] = (val_len
>> 0) & 0xff;
141 buffer_append_string_len(env
, len_enc
, len_enc_len
);
142 buffer_append_string_len(env
, key
, key_len
);
143 buffer_append_string_len(env
, val
, val_len
);
148 static void fcgi_header(FCGI_Header
* header
, unsigned char type
, int request_id
, int contentLength
, unsigned char paddingLength
) {
149 force_assert(contentLength
<= FCGI_MAX_LENGTH
);
151 header
->version
= FCGI_VERSION_1
;
153 header
->requestIdB0
= request_id
& 0xff;
154 header
->requestIdB1
= (request_id
>> 8) & 0xff;
155 header
->contentLengthB0
= contentLength
& 0xff;
156 header
->contentLengthB1
= (contentLength
>> 8) & 0xff;
157 header
->paddingLength
= paddingLength
;
158 header
->reserved
= 0;
161 static handler_t
fcgi_stdin_append(server
*srv
, handler_ctx
*hctx
) {
163 connection
*con
= hctx
->remote_conn
;
164 chunkqueue
*req_cq
= con
->request_content_queue
;
165 off_t offset
, weWant
;
166 const off_t req_cqlen
= req_cq
->bytes_in
- req_cq
->bytes_out
;
167 int request_id
= hctx
->request_id
;
169 /* something to send ? */
170 for (offset
= 0; offset
!= req_cqlen
; offset
+= weWant
) {
171 weWant
= req_cqlen
- offset
> FCGI_MAX_LENGTH
? FCGI_MAX_LENGTH
: req_cqlen
- offset
;
173 /* we announce toWrite octets
174 * now take all request_content chunks available
177 fcgi_header(&(header
), FCGI_STDIN
, request_id
, weWant
, 0);
178 chunkqueue_append_mem(hctx
->wb
, (const char *)&header
, sizeof(header
));
179 if (-1 != hctx
->wb_reqlen
) {
180 if (hctx
->wb_reqlen
>= 0) {
181 hctx
->wb_reqlen
+= sizeof(header
);
183 hctx
->wb_reqlen
-= sizeof(header
);
187 if (hctx
->conf
.debug
> 10) {
188 log_error_write(srv
, __FILE__
, __LINE__
, "soso", "tosend:", offset
, "/", req_cqlen
);
191 chunkqueue_steal(hctx
->wb
, req_cq
, weWant
);
192 /*(hctx->wb_reqlen already includes content_length)*/
195 if (hctx
->wb
->bytes_in
== hctx
->wb_reqlen
) {
196 /* terminate STDIN */
197 /* (future: must defer ending FCGI_STDIN
198 * if might later upgrade protocols
199 * and then have more data to send) */
200 fcgi_header(&(header
), FCGI_STDIN
, request_id
, 0, 0);
201 chunkqueue_append_mem(hctx
->wb
, (const char *)&header
, sizeof(header
));
202 hctx
->wb_reqlen
+= (int)sizeof(header
);
205 return HANDLER_GO_ON
;
208 static handler_t
fcgi_create_env(server
*srv
, handler_ctx
*hctx
) {
209 FCGI_BeginRequestRecord beginRecord
;
213 buffer
*fcgi_env
= buffer_init();
214 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 /* send FCGI_BEGIN_REQUEST */
227 if (hctx
->request_id
== 0) {
228 hctx
->request_id
= 1; /* always use id 1 as we don't use multiplexing */
230 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
231 "fcgi-request is already in use:", hctx
->request_id
);
233 request_id
= hctx
->request_id
;
235 fcgi_header(&(beginRecord
.header
), FCGI_BEGIN_REQUEST
, request_id
, sizeof(beginRecord
.body
), 0);
236 beginRecord
.body
.roleB0
= hctx
->gw_mode
;
237 beginRecord
.body
.roleB1
= 0;
238 beginRecord
.body
.flags
= 0;
239 memset(beginRecord
.body
.reserved
, 0, sizeof(beginRecord
.body
.reserved
));
241 /* send FCGI_PARAMS */
242 buffer_string_prepare_copy(fcgi_env
, 1023);
244 if (0 != http_cgi_headers(srv
, con
, &opts
, fcgi_env_add
, fcgi_env
)) {
245 con
->http_status
= 400;
246 buffer_free(fcgi_env
);
247 return HANDLER_FINISHED
;
249 buffer
*b
= buffer_init();
251 buffer_copy_string_len(b
, (const char *)&beginRecord
, sizeof(beginRecord
));
253 fcgi_header(&(header
), FCGI_PARAMS
, request_id
, buffer_string_length(fcgi_env
), 0);
254 buffer_append_string_len(b
, (const char *)&header
, sizeof(header
));
255 buffer_append_string_buffer(b
, fcgi_env
);
256 buffer_free(fcgi_env
);
258 fcgi_header(&(header
), FCGI_PARAMS
, request_id
, 0, 0);
259 buffer_append_string_len(b
, (const char *)&header
, sizeof(header
));
261 hctx
->wb_reqlen
= buffer_string_length(b
);
262 chunkqueue_append_buffer(hctx
->wb
, b
);
266 if (con
->request
.content_length
) {
267 /*chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);*/
268 if (con
->request
.content_length
> 0)
269 hctx
->wb_reqlen
+= con
->request
.content_length
;/* (eventual) (minimal) total request size, not necessarily including all fcgi_headers around content length yet */
270 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/
271 hctx
->wb_reqlen
= -hctx
->wb_reqlen
;
273 fcgi_stdin_append(srv
, hctx
);
275 status_counter_inc(srv
, CONST_STR_LEN("fastcgi.requests"));
276 return HANDLER_GO_ON
;
285 } fastcgi_response_packet
;
287 static int fastcgi_get_packet(server
*srv
, handler_ctx
*hctx
, fastcgi_response_packet
*packet
) {
293 if (!hctx
->rb
->first
) return -1;
295 packet
->b
= buffer_init();
299 packet
->request_id
= 0;
301 offset
= 0; toread
= 8;
302 /* get at least the FastCGI header */
303 for (c
= hctx
->rb
->first
; c
; c
= c
->next
) {
304 size_t weHave
= buffer_string_length(c
->mem
) - c
->offset
;
306 if (weHave
> toread
) weHave
= toread
;
308 buffer_append_string_len(packet
->b
, c
->mem
->ptr
+ c
->offset
, weHave
);
310 offset
= weHave
; /* skip offset bytes in chunk for "real" data */
312 if (0 == toread
) break;
315 if (buffer_string_length(packet
->b
) < sizeof(FCGI_Header
)) {
317 if (hctx
->conf
.debug
) {
318 log_error_write(srv
, __FILE__
, __LINE__
, "sdsds", "FastCGI: header too small:", buffer_string_length(packet
->b
), "bytes <", sizeof(FCGI_Header
), "bytes, waiting for more data");
321 buffer_free(packet
->b
);
326 /* we have at least a header, now check how much me have to fetch */
327 header
= (FCGI_Header
*)(packet
->b
->ptr
);
329 packet
->len
= (header
->contentLengthB0
| (header
->contentLengthB1
<< 8)) + header
->paddingLength
;
330 packet
->request_id
= (header
->requestIdB0
| (header
->requestIdB1
<< 8));
331 packet
->type
= header
->type
;
332 packet
->padding
= header
->paddingLength
;
334 /* ->b should only be the content */
335 buffer_string_set_length(packet
->b
, 0);
338 /* copy the content */
339 for (; c
&& (buffer_string_length(packet
->b
) < packet
->len
); c
= c
->next
) {
340 size_t weWant
= packet
->len
- buffer_string_length(packet
->b
);
341 size_t weHave
= buffer_string_length(c
->mem
) - c
->offset
- offset
;
343 if (weHave
> weWant
) weHave
= weWant
;
345 buffer_append_string_len(packet
->b
, c
->mem
->ptr
+ c
->offset
+ offset
, weHave
);
347 /* we only skipped the first bytes as they belonged to the fcgi header */
351 if (buffer_string_length(packet
->b
) < packet
->len
) {
352 /* we didn't get the full packet */
354 buffer_free(packet
->b
);
358 buffer_string_set_length(packet
->b
, buffer_string_length(packet
->b
) - packet
->padding
);
361 chunkqueue_mark_written(hctx
->rb
, packet
->len
+ sizeof(FCGI_Header
));
366 static handler_t
fcgi_recv_parse(server
*srv
, connection
*con
, struct http_response_opts_t
*opts
, buffer
*b
, size_t n
) {
367 handler_ctx
*hctx
= (handler_ctx
*)opts
->pdata
;
371 if (!(fdevent_event_get_interest(srv
->ev
, hctx
->fd
) & FDEVENT_IN
)) return HANDLER_GO_ON
;
372 log_error_write(srv
, __FILE__
, __LINE__
, "ssdsb",
373 "unexpected end-of-file (perhaps the fastcgi process died):",
374 "pid:", hctx
->proc
->pid
,
375 "socket:", hctx
->proc
->connection_name
);
377 return HANDLER_ERROR
;
380 chunkqueue_append_buffer(hctx
->rb
, b
);
383 * parse the fastcgi packets and forward the content to the write-queue
387 fastcgi_response_packet packet
;
389 /* check if we have at least one packet */
390 if (0 != fastcgi_get_packet(srv
, hctx
, &packet
)) {
395 switch(packet
.type
) {
397 if (packet
.len
== 0) break;
399 /* is the header already finished */
400 if (0 == con
->file_started
) {
401 /* split header from body */
402 buffer
*hdrs
= (!hctx
->response
)
404 : (buffer_append_string_buffer(hctx
->response
, packet
.b
), hctx
->response
);
405 handler_t rc
= http_response_parse_headers(srv
, con
, &hctx
->opts
, hdrs
);
406 if (rc
!= HANDLER_GO_ON
) {
407 hctx
->send_content_body
= 0;
411 if (0 == con
->file_started
) {
412 if (!hctx
->response
) {
413 hctx
->response
= packet
.b
;
417 else if (hctx
->gw_mode
== GW_AUTHORIZER
&&
418 (con
->http_status
== 0 || con
->http_status
== 200)) {
419 /* authorizer approved request; ignore the content here */
420 hctx
->send_content_body
= 0;
422 } else if (hctx
->send_content_body
&& !buffer_string_is_empty(packet
.b
)) {
423 if (0 != http_chunk_append_buffer(srv
, con
, packet
.b
)) {
424 /* error writing to tempfile;
425 * truncate response or send 500 if nothing sent yet */
432 if (packet
.len
== 0) break;
434 log_error_write_multiline_buffer(srv
, __FILE__
, __LINE__
, packet
.b
, "s",
438 case FCGI_END_REQUEST
:
442 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
443 "FastCGI: header.type not handled: ", packet
.type
);
446 buffer_free(packet
.b
);
449 return 0 == fin
? HANDLER_GO_ON
: HANDLER_FINISHED
;
454 static int fcgi_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
456 plugin_config
*s
= p
->config_storage
[0];
464 /* skip the first, the global context */
465 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
466 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
467 s
= p
->config_storage
[i
];
469 /* condition didn't match */
470 if (!config_check_cond(srv
, con
, dc
)) continue;
473 for (j
= 0; j
< dc
->value
->used
; j
++) {
474 data_unset
*du
= dc
->value
->data
[j
];
476 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("fastcgi.server"))) {
480 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("fastcgi.debug"))) {
482 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("fastcgi.map-extensions"))) {
492 static handler_t
fcgi_check_extension(server
*srv
, connection
*con
, void *p_d
, int uri_path_handler
) {
493 plugin_data
*p
= p_d
;
496 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
498 fcgi_patch_connection(srv
, con
, p
);
499 if (NULL
== p
->conf
.exts
) return HANDLER_GO_ON
;
501 rc
= gw_check_extension(srv
, con
, p
, uri_path_handler
, 0);
502 if (HANDLER_GO_ON
!= rc
) return rc
;
504 if (con
->mode
== p
->id
) {
505 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
506 hctx
->opts
.backend
= BACKEND_FASTCGI
;
507 hctx
->opts
.parse
= fcgi_recv_parse
;
508 hctx
->opts
.pdata
= hctx
;
509 hctx
->stdin_append
= fcgi_stdin_append
;
510 hctx
->create_env
= fcgi_create_env
;
511 hctx
->rb
= chunkqueue_init();
514 return HANDLER_GO_ON
;
517 /* uri-path handler */
518 static handler_t
fcgi_check_extension_1(server
*srv
, connection
*con
, void *p_d
) {
519 return fcgi_check_extension(srv
, con
, p_d
, 1);
522 /* start request handler */
523 static handler_t
fcgi_check_extension_2(server
*srv
, connection
*con
, void *p_d
) {
524 return fcgi_check_extension(srv
, con
, p_d
, 0);
528 int mod_fastcgi_plugin_init(plugin
*p
);
529 int mod_fastcgi_plugin_init(plugin
*p
) {
530 p
->version
= LIGHTTPD_VERSION_ID
;
531 p
->name
= buffer_init_string("fastcgi");
534 p
->cleanup
= gw_free
;
535 p
->set_defaults
= mod_fastcgi_set_defaults
;
536 p
->connection_reset
= gw_connection_reset
;
537 p
->handle_uri_clean
= fcgi_check_extension_1
;
538 p
->handle_subrequest_start
= fcgi_check_extension_2
;
539 p
->handle_subrequest
= gw_handle_subrequest
;
540 p
->handle_trigger
= gw_handle_trigger
;
541 p
->handle_waitpid
= gw_handle_waitpid_cb
;