[mod_proxy] move data_fastcgi into mod_proxy.c
[lighttpd.git] / src / mod_fastcgi.c
blob728ae4116e28f2d0ebf604bc7d8f8c2591a96267
1 #include "first.h"
3 #include "buffer.h"
4 #include "server.h"
5 #include "keyvalue.h"
6 #include "log.h"
8 #include "http_chunk.h"
9 #include "fdevent.h"
10 #include "connections.h"
11 #include "response.h"
12 #include "joblist.h"
13 #include "inet_ntop_cache.h"
15 #include "plugin.h"
17 #include "status_counter.h"
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <signal.h>
27 #ifdef HAVE_FASTCGI_FASTCGI_H
28 # include <fastcgi/fastcgi.h>
29 #else
30 # ifdef HAVE_FASTCGI_H
31 # include <fastcgi.h>
32 # else
33 # include "fastcgi.h"
34 # endif
35 #endif /* HAVE_FASTCGI_FASTCGI_H */
37 #include "sys-socket.h"
39 #ifdef HAVE_SYS_UIO_H
40 #include <sys/uio.h>
41 #endif
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
48 * TODO:
50 * - add timeout for a connect to a non-fastcgi process
51 * (use state_timestamp + state)
55 typedef struct fcgi_proc {
56 size_t id; /* id will be between 1 and max_procs */
57 buffer *unixsocket; /* config.socket + "-" + id */
58 unsigned port; /* config.port + pno */
60 buffer *connection_name; /* either tcp:<host>:<port> or unix:<socket> for debugging purposes */
62 pid_t pid; /* PID of the spawned process (0 if not spawned locally) */
65 size_t load; /* number of requests waiting on this process */
67 size_t requests; /* see max_requests */
68 struct fcgi_proc *prev, *next; /* see first */
70 time_t disabled_until; /* this proc is disabled until, use something else until then */
72 int is_local;
74 enum {
75 PROC_STATE_RUNNING, /* alive */
76 PROC_STATE_OVERLOADED, /* listen-queue is full,
77 don't send anything to this proc for the next 2 seconds */
78 PROC_STATE_DIED_WAIT_FOR_PID, /* */
79 PROC_STATE_DIED, /* marked as dead, should be restarted */
80 PROC_STATE_KILLED /* was killed as we don't have the load anymore */
81 } state;
82 } fcgi_proc;
84 typedef struct {
85 /* the key that is used to reference this value */
86 buffer *id;
88 /* list of processes handling this extension
89 * sorted by lowest load
91 * whenever a job is done move it up in the list
92 * until it is sorted, move it down as soon as the
93 * job is started
95 fcgi_proc *first;
96 fcgi_proc *unused_procs;
99 * spawn at least min_procs, at max_procs.
101 * as soon as the load of the first entry
102 * is max_load_per_proc we spawn a new one
103 * and add it to the first entry and give it
104 * the load
108 unsigned short max_procs;
109 size_t num_procs; /* how many procs are started */
110 size_t active_procs; /* how many of them are really running, i.e. state = PROC_STATE_RUNNING */
113 * time after a disabled remote connection is tried to be re-enabled
118 unsigned short disable_time;
121 * some fastcgi processes get a little bit larger
122 * than wanted. max_requests_per_proc kills a
123 * process after a number of handled requests.
126 size_t max_requests_per_proc;
129 /* config */
132 * host:port
134 * if host is one of the local IP adresses the
135 * whole connection is local
137 * if port is not 0, and host is not specified,
138 * "localhost" (INADDR_LOOPBACK) is assumed.
141 buffer *host;
142 unsigned short port;
143 sa_family_t family;
146 * Unix Domain Socket
148 * instead of TCP/IP we can use Unix Domain Sockets
149 * - more secure (you have fileperms to play with)
150 * - more control (on locally)
151 * - more speed (no extra overhead)
153 buffer *unixsocket;
155 /* if socket is local we can start the fastcgi
156 * process ourself
158 * bin-path is the path to the binary
160 * check min_procs and max_procs for the number
161 * of process to start up
163 buffer *bin_path;
165 /* bin-path is set bin-environment is taken to
166 * create the environement before starting the
167 * FastCGI process
170 array *bin_env;
172 array *bin_env_copy;
175 * docroot-translation between URL->phys and the
176 * remote host
178 * reasons:
179 * - different dir-layout if remote
180 * - chroot if local
183 buffer *docroot;
186 * check_local tells you if the phys file is stat()ed
187 * or not. FastCGI doesn't care if the service is
188 * remote. If the web-server side doesn't contain
189 * the fastcgi-files we should not stat() for them
190 * and say '404 not found'.
192 unsigned short check_local;
195 * append PATH_INFO to SCRIPT_FILENAME
197 * php needs this if cgi.fix_pathinfo is provided
201 unsigned short break_scriptfilename_for_php;
204 * workaround for program when prefix="/"
206 * rule to build PATH_INFO is hardcoded for when check_local is disabled
207 * enable this option to use the workaround
211 unsigned short fix_root_path_name;
214 * If the backend includes X-Sendfile in the response
215 * we use the value as filename and ignore the content.
218 unsigned short xsendfile_allow;
219 array *xsendfile_docroot;
221 ssize_t load; /* replace by host->load */
223 size_t max_id; /* corresponds most of the time to num_procs */
225 buffer *strip_request_uri;
227 unsigned short kill_signal; /* we need a setting for this as libfcgi
228 applications prefer SIGUSR1 while the
229 rest of the world would use SIGTERM
230 *sigh* */
232 int listen_backlog;
233 int refcount;
234 } fcgi_extension_host;
237 * one extension can have multiple hosts assigned
238 * one host can spawn additional processes on the same
239 * socket (if we control it)
241 * ext -> host -> procs
242 * 1:n 1:n
244 * if the fastcgi process is remote that whole goes down
245 * to
247 * ext -> host -> procs
248 * 1:n 1:1
250 * in case of PHP and FCGI_CHILDREN we have again a procs
251 * but we don't control it directly.
255 typedef struct {
256 buffer *key; /* like .php */
258 int note_is_sent;
259 int last_used_ndx;
261 fcgi_extension_host **hosts;
263 size_t used;
264 size_t size;
265 } fcgi_extension;
267 typedef struct {
268 fcgi_extension **exts;
270 size_t used;
271 size_t size;
272 } fcgi_exts;
275 typedef struct {
276 fcgi_exts *exts;
277 fcgi_exts *exts_auth;
278 fcgi_exts *exts_resp;
280 array *ext_mapping;
282 unsigned int debug;
283 } plugin_config;
285 typedef struct {
286 char **ptr;
288 size_t size;
289 size_t used;
290 } char_array;
292 /* generic plugin data, shared between all connections */
293 typedef struct {
294 PLUGIN_DATA;
296 buffer *fcgi_env;
298 buffer *statuskey;
300 plugin_config **config_storage;
302 plugin_config conf; /* this is only used as long as no handler_ctx is setup */
303 } plugin_data;
305 /* connection specific data */
306 typedef enum {
307 FCGI_STATE_INIT,
308 FCGI_STATE_CONNECT_DELAYED,
309 FCGI_STATE_PREPARE_WRITE,
310 FCGI_STATE_WRITE,
311 FCGI_STATE_READ
312 } fcgi_connection_state_t;
314 typedef struct {
315 fcgi_proc *proc;
316 fcgi_extension_host *host;
317 fcgi_extension *ext;
318 fcgi_extension *ext_auth; /* (might be used in future to allow multiple authorizers)*/
319 unsigned short fcgi_mode; /* FastCGI mode: FCGI_AUTHORIZER or FCGI_RESPONDER */
321 fcgi_connection_state_t state;
322 time_t state_timestamp;
324 chunkqueue *rb; /* read queue */
325 chunkqueue *wb; /* write queue */
326 off_t wb_reqlen;
328 buffer *response_header;
330 int fd; /* fd to the fastcgi process */
331 int fde_ndx; /* index into the fd-event buffer */
333 pid_t pid;
334 int got_proc;
335 int reconnects; /* number of reconnect attempts */
337 int request_id;
338 int send_content_body;
340 http_response_opts opts;
341 plugin_config conf;
343 connection *remote_conn; /* dumb pointer */
344 plugin_data *plugin_data; /* dumb pointer */
345 } handler_ctx;
348 /* ok, we need a prototype */
349 static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents);
351 static void fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) {
352 buffer_copy_string_len(b, CONST_STR_LEN("fastcgi.backend."));
353 buffer_append_string_buffer(b, host->id);
354 if (proc) {
355 buffer_append_string_len(b, CONST_STR_LEN("."));
356 buffer_append_int(b, proc->id);
360 static void fcgi_proc_load_inc(server *srv, handler_ctx *hctx) {
361 plugin_data *p = hctx->plugin_data;
362 hctx->proc->load++;
364 status_counter_inc(srv, CONST_STR_LEN("fastcgi.active-requests"));
366 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
367 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
369 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load);
372 static void fcgi_proc_load_dec(server *srv, handler_ctx *hctx) {
373 plugin_data *p = hctx->plugin_data;
374 hctx->proc->load--;
376 status_counter_dec(srv, CONST_STR_LEN("fastcgi.active-requests"));
378 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
379 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
381 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load);
384 static void fcgi_host_assign(server *srv, handler_ctx *hctx, fcgi_extension_host *host) {
385 plugin_data *p = hctx->plugin_data;
386 hctx->host = host;
387 hctx->host->load++;
389 fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL);
390 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
392 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load);
395 static void fcgi_host_reset(server *srv, handler_ctx *hctx) {
396 plugin_data *p = hctx->plugin_data;
397 hctx->host->load--;
399 fastcgi_status_copy_procname(p->statuskey, hctx->host, NULL);
400 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".load"));
402 status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->host->load);
404 hctx->host = NULL;
407 static int fastcgi_status_init(server *srv, buffer *b, fcgi_extension_host *host, fcgi_proc *proc) {
408 #define CLEAN(x) \
409 fastcgi_status_copy_procname(b, host, proc); \
410 buffer_append_string_len(b, CONST_STR_LEN(x)); \
411 status_counter_set(srv, CONST_BUF_LEN(b), 0);
413 CLEAN(".disabled");
414 CLEAN(".died");
415 CLEAN(".overloaded");
416 CLEAN(".connected");
417 CLEAN(".load");
419 #undef CLEAN
421 #define CLEAN(x) \
422 fastcgi_status_copy_procname(b, host, NULL); \
423 buffer_append_string_len(b, CONST_STR_LEN(x)); \
424 status_counter_set(srv, CONST_BUF_LEN(b), 0);
426 CLEAN(".load");
428 #undef CLEAN
430 return 0;
433 static handler_ctx * handler_ctx_init(void) {
434 handler_ctx * hctx;
436 hctx = calloc(1, sizeof(*hctx));
437 force_assert(hctx);
439 hctx->fde_ndx = -1;
441 /*hctx->response_header = buffer_init();*//*(allocated when needed)*/
443 hctx->request_id = 0;
444 hctx->fcgi_mode = FCGI_RESPONDER;
445 hctx->state = FCGI_STATE_INIT;
446 hctx->proc = NULL;
448 hctx->fd = -1;
450 hctx->reconnects = 0;
451 hctx->send_content_body = 1;
453 hctx->rb = chunkqueue_init();
454 hctx->wb = chunkqueue_init();
455 hctx->wb_reqlen = 0;
457 return hctx;
460 static void handler_ctx_free(handler_ctx *hctx) {
461 /* caller MUST have called fcgi_backend_close(srv, hctx) if necessary */
462 buffer_free(hctx->response_header);
464 chunkqueue_free(hctx->rb);
465 chunkqueue_free(hctx->wb);
467 free(hctx);
470 static void handler_ctx_clear(handler_ctx *hctx) {
471 /* caller MUST have called fcgi_backend_close(srv, hctx) if necessary */
473 hctx->proc = NULL;
474 hctx->host = NULL;
475 hctx->ext = NULL;
476 /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/
478 hctx->fcgi_mode = FCGI_RESPONDER;
479 hctx->state = FCGI_STATE_INIT;
480 /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/
482 chunkqueue_reset(hctx->rb);
483 chunkqueue_reset(hctx->wb);
484 hctx->wb_reqlen = 0;
486 buffer_reset(hctx->response_header);
488 hctx->fd = -1;
489 hctx->fde_ndx = -1;
490 hctx->got_proc = 0;
491 hctx->reconnects = 0;
492 hctx->request_id = 0;
493 hctx->send_content_body = 1;
495 /*plugin_config conf;*//*(no need to reset for same request)*/
497 /*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/
498 /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/
501 static fcgi_proc *fastcgi_process_init(void) {
502 fcgi_proc *f;
504 f = calloc(1, sizeof(*f));
505 f->unixsocket = buffer_init();
506 f->connection_name = buffer_init();
508 f->prev = NULL;
509 f->next = NULL;
510 f->state = PROC_STATE_DIED;
512 return f;
515 static void fastcgi_process_free(fcgi_proc *f) {
516 if (!f) return;
518 fastcgi_process_free(f->next);
520 buffer_free(f->unixsocket);
521 buffer_free(f->connection_name);
523 free(f);
526 static fcgi_extension_host *fastcgi_host_init(void) {
527 fcgi_extension_host *f;
529 f = calloc(1, sizeof(*f));
531 f->id = buffer_init();
532 f->host = buffer_init();
533 f->unixsocket = buffer_init();
534 f->docroot = buffer_init();
535 f->bin_path = buffer_init();
536 f->bin_env = array_init();
537 f->bin_env_copy = array_init();
538 f->strip_request_uri = buffer_init();
539 f->xsendfile_docroot = array_init();
541 return f;
544 static void fastcgi_host_free(fcgi_extension_host *h) {
545 if (!h) return;
546 if (h->refcount) {
547 --h->refcount;
548 return;
551 buffer_free(h->id);
552 buffer_free(h->host);
553 buffer_free(h->unixsocket);
554 buffer_free(h->docroot);
555 buffer_free(h->bin_path);
556 buffer_free(h->strip_request_uri);
557 array_free(h->bin_env);
558 array_free(h->bin_env_copy);
559 array_free(h->xsendfile_docroot);
561 fastcgi_process_free(h->first);
562 fastcgi_process_free(h->unused_procs);
564 free(h);
568 static fcgi_exts *fastcgi_extensions_init(void) {
569 fcgi_exts *f;
571 f = calloc(1, sizeof(*f));
573 return f;
576 static void fastcgi_extensions_free(fcgi_exts *f) {
577 size_t i;
579 if (!f) return;
581 for (i = 0; i < f->used; i++) {
582 fcgi_extension *fe;
583 size_t j;
585 fe = f->exts[i];
587 for (j = 0; j < fe->used; j++) {
588 fcgi_extension_host *h;
590 h = fe->hosts[j];
592 fastcgi_host_free(h);
595 buffer_free(fe->key);
596 free(fe->hosts);
598 free(fe);
601 free(f->exts);
603 free(f);
606 static int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *fh) {
607 fcgi_extension *fe;
608 size_t i;
610 /* there is something */
612 for (i = 0; i < ext->used; i++) {
613 if (buffer_is_equal(key, ext->exts[i]->key)) {
614 break;
618 if (i == ext->used) {
619 /* filextension is new */
620 fe = calloc(1, sizeof(*fe));
621 force_assert(fe);
622 fe->key = buffer_init();
623 fe->last_used_ndx = -1;
624 buffer_copy_buffer(fe->key, key);
626 /* */
628 if (ext->size == 0) {
629 ext->size = 8;
630 ext->exts = malloc(ext->size * sizeof(*(ext->exts)));
631 force_assert(ext->exts);
632 } else if (ext->used == ext->size) {
633 ext->size += 8;
634 ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
635 force_assert(ext->exts);
637 ext->exts[ext->used++] = fe;
638 } else {
639 fe = ext->exts[i];
642 if (fe->size == 0) {
643 fe->size = 4;
644 fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
645 force_assert(fe->hosts);
646 } else if (fe->size == fe->used) {
647 fe->size += 4;
648 fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
649 force_assert(fe->hosts);
652 fe->hosts[fe->used++] = fh;
654 return 0;
658 static void fcgi_proc_set_state(fcgi_extension_host *host, fcgi_proc *proc, int state) {
659 if ((int)proc->state == state) return;
660 if (proc->state == PROC_STATE_RUNNING) {
661 --host->active_procs;
662 } else if (state == PROC_STATE_RUNNING) {
663 ++host->active_procs;
665 proc->state = state;
668 static void fcgi_proc_disable(server *srv, fcgi_extension_host *host, fcgi_proc *proc, handler_ctx *hctx) {
669 if (host->disable_time || (proc->is_local && proc->pid == hctx->pid)) {
670 proc->disabled_until = srv->cur_ts + host->disable_time;
671 fcgi_proc_set_state(host, proc, proc->is_local ? PROC_STATE_DIED_WAIT_FOR_PID : PROC_STATE_DIED);
673 if (hctx->conf.debug) {
674 log_error_write(srv, __FILE__, __LINE__, "sds",
675 "backend disabled for", host->disable_time, "seconds");
680 static void fcgi_proc_check_enable(server *srv, fcgi_extension_host *host, fcgi_proc *proc) {
681 if (srv->cur_ts <= proc->disabled_until) return;
682 if (proc->state == PROC_STATE_RUNNING) return;
684 fcgi_proc_set_state(host, proc, PROC_STATE_RUNNING);
686 log_error_write(srv, __FILE__, __LINE__, "sbbdb",
687 "fcgi-server re-enabled:", proc->connection_name,
688 host->host, host->port, host->unixsocket);
691 static int fcgi_proc_waitpid(server *srv, fcgi_extension_host *host, fcgi_proc *proc) {
692 int rc, status;
694 if (!proc->is_local) return 0;
695 if (proc->pid <= 0) return 0;
697 do {
698 rc = waitpid(proc->pid, &status, WNOHANG);
699 } while (-1 == rc && errno == EINTR);
700 if (0 == rc) return 0; /* child still running */
702 /* child terminated */
703 if (-1 == rc) {
704 /* EINVAL or ECHILD no child processes */
705 /* should not happen; someone else has cleaned up for us */
706 log_error_write(srv, __FILE__, __LINE__, "sddss",
707 "pid ", proc->pid, proc->state,
708 "not found:", strerror(errno));
709 } else if (WIFEXITED(status)) {
710 if (proc->state != PROC_STATE_KILLED) {
711 log_error_write(srv, __FILE__, __LINE__, "sdb",
712 "child exited:",
713 WEXITSTATUS(status), proc->connection_name);
715 } else if (WIFSIGNALED(status)) {
716 if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT) {
717 log_error_write(srv, __FILE__, __LINE__, "sd",
718 "child signalled:", WTERMSIG(status));
720 } else {
721 log_error_write(srv, __FILE__, __LINE__, "sd",
722 "child died somehow:", status);
725 proc->pid = 0;
726 fcgi_proc_set_state(host, proc, PROC_STATE_DIED);
727 return 1;
730 INIT_FUNC(mod_fastcgi_init) {
731 plugin_data *p;
733 p = calloc(1, sizeof(*p));
735 p->fcgi_env = buffer_init();
737 p->statuskey = buffer_init();
739 return p;
743 FREE_FUNC(mod_fastcgi_free) {
744 plugin_data *p = p_d;
746 UNUSED(srv);
748 buffer_free(p->fcgi_env);
749 buffer_free(p->statuskey);
751 if (p->config_storage) {
752 size_t i, j, n;
753 for (i = 0; i < srv->config_context->used; i++) {
754 plugin_config *s = p->config_storage[i];
755 fcgi_exts *exts;
757 if (NULL == s) continue;
759 exts = s->exts;
761 if (exts) {
762 for (j = 0; j < exts->used; j++) {
763 fcgi_extension *ex;
765 ex = exts->exts[j];
767 for (n = 0; n < ex->used; n++) {
768 fcgi_proc *proc;
769 fcgi_extension_host *host;
771 host = ex->hosts[n];
773 for (proc = host->first; proc; proc = proc->next) {
774 if (proc->pid > 0) {
775 kill(proc->pid, host->kill_signal);
778 if (proc->is_local &&
779 !buffer_string_is_empty(proc->unixsocket)) {
780 unlink(proc->unixsocket->ptr);
784 for (proc = host->unused_procs; proc; proc = proc->next) {
785 if (proc->pid > 0) {
786 kill(proc->pid, host->kill_signal);
788 if (proc->is_local &&
789 !buffer_string_is_empty(proc->unixsocket)) {
790 unlink(proc->unixsocket->ptr);
796 fastcgi_extensions_free(s->exts);
797 fastcgi_extensions_free(s->exts_auth);
798 fastcgi_extensions_free(s->exts_resp);
800 array_free(s->ext_mapping);
802 free(s);
804 free(p->config_storage);
807 free(p);
809 return HANDLER_GO_ON;
812 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
813 char *dst;
814 size_t i;
816 if (!key || !val) return -1;
818 dst = malloc(key_len + val_len + 3);
819 memcpy(dst, key, key_len);
820 dst[key_len] = '=';
821 memcpy(dst + key_len + 1, val, val_len);
822 dst[key_len + 1 + val_len] = '\0';
824 for (i = 0; i < env->used; i++) {
825 if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
826 free(env->ptr[i]);
827 env->ptr[i] = dst;
828 return 0;
832 if (env->size == 0) {
833 env->size = 16;
834 env->ptr = malloc(env->size * sizeof(*env->ptr));
835 } else if (env->size == env->used + 1) {
836 env->size += 16;
837 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
840 env->ptr[env->used++] = dst;
842 return 0;
845 static int parse_binpath(char_array *env, buffer *b) {
846 char *start;
847 size_t i;
848 /* search for spaces */
850 start = b->ptr;
851 for (i = 0; i < buffer_string_length(b); i++) {
852 switch(b->ptr[i]) {
853 case ' ':
854 case '\t':
855 /* a WS, stop here and copy the argument */
857 if (env->size == 0) {
858 env->size = 16;
859 env->ptr = malloc(env->size * sizeof(*env->ptr));
860 } else if (env->size == env->used) {
861 env->size += 16;
862 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
865 b->ptr[i] = '\0';
867 env->ptr[env->used++] = start;
869 start = b->ptr + i + 1;
870 break;
871 default:
872 break;
876 if (env->size == 0) {
877 env->size = 16;
878 env->ptr = malloc(env->size * sizeof(*env->ptr));
879 } else if (env->size == env->used) { /* we need one extra for the terminating NULL */
880 env->size += 16;
881 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
884 /* the rest */
885 env->ptr[env->used++] = start;
887 if (env->size == 0) {
888 env->size = 16;
889 env->ptr = malloc(env->size * sizeof(*env->ptr));
890 } else if (env->size == env->used) { /* we need one extra for the terminating NULL */
891 env->size += 16;
892 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
895 /* terminate */
896 env->ptr[env->used++] = NULL;
898 return 0;
901 static int fcgi_spawn_connection(server *srv,
902 plugin_data *p,
903 fcgi_extension_host *host,
904 fcgi_proc *proc) {
905 int fcgi_fd;
906 int status;
907 struct timeval tv = { 0, 10 * 1000 };
908 sock_addr addr;
909 struct sockaddr *fcgi_addr = (struct sockaddr *)&addr;
910 socklen_t servlen;
912 if (p->conf.debug) {
913 log_error_write(srv, __FILE__, __LINE__, "sdb",
914 "new proc, socket:", proc->port, proc->unixsocket);
917 if (!buffer_string_is_empty(proc->unixsocket)) {
918 if (1 != sock_addr_from_str_hints(srv, &addr, &servlen, proc->unixsocket->ptr, AF_UNIX, 0)) {
919 return -1;
921 } else {
922 if (1 != sock_addr_from_buffer_hints_numeric(srv, &addr, &servlen, host->host, host->family, proc->port)) {
923 return -1;
927 if (!buffer_string_is_empty(proc->unixsocket)) {
928 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
929 buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
930 } else {
931 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
932 if (!buffer_string_is_empty(host->host)) {
933 buffer_append_string_buffer(proc->connection_name, host->host);
934 } else {
935 buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost"));
937 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
938 buffer_append_int(proc->connection_name, proc->port);
941 if (-1 == (fcgi_fd = fdevent_socket_cloexec(fcgi_addr->sa_family, SOCK_STREAM, 0))) {
942 log_error_write(srv, __FILE__, __LINE__, "ss",
943 "failed:", strerror(errno));
944 return -1;
947 do {
948 status = connect(fcgi_fd, fcgi_addr, servlen);
949 } while (-1 == status && errno == EINTR);
951 if (-1 == status && errno != ENOENT
952 && !buffer_string_is_empty(proc->unixsocket)) {
953 log_error_write(srv, __FILE__, __LINE__, "sbss",
954 "unlink", proc->unixsocket,
955 "after connect failed:", strerror(errno));
956 unlink(proc->unixsocket->ptr);
959 close(fcgi_fd);
961 if (-1 == status) {
962 /* server is not up, spawn it */
963 char_array env;
964 char_array arg;
965 buffer *bin_path = NULL;
966 size_t i;
967 int val;
968 int dfd = -1;
970 /* reopen socket */
971 if (-1 == (fcgi_fd = fdevent_socket_cloexec(fcgi_addr->sa_family, SOCK_STREAM, 0))) {
972 log_error_write(srv, __FILE__, __LINE__, "ss",
973 "socket failed:", strerror(errno));
974 return -1;
977 val = 1;
978 if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
979 log_error_write(srv, __FILE__, __LINE__, "ss",
980 "socketsockopt failed:", strerror(errno));
981 close(fcgi_fd);
982 return -1;
985 /* create socket */
986 if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) {
987 log_error_write(srv, __FILE__, __LINE__, "sbs",
988 "bind failed for:",
989 proc->connection_name,
990 strerror(errno));
991 close(fcgi_fd);
992 return -1;
995 if (-1 == listen(fcgi_fd, host->listen_backlog)) {
996 log_error_write(srv, __FILE__, __LINE__, "ss",
997 "listen failed:", strerror(errno));
998 close(fcgi_fd);
999 return -1;
1003 /* create environment */
1004 env.ptr = NULL;
1005 env.size = 0;
1006 env.used = 0;
1008 arg.ptr = NULL;
1009 arg.size = 0;
1010 arg.used = 0;
1012 /* build clean environment */
1013 if (host->bin_env_copy->used) {
1014 for (i = 0; i < host->bin_env_copy->used; i++) {
1015 data_string *ds = (data_string *)host->bin_env_copy->data[i];
1016 char *ge;
1018 if (NULL != (ge = getenv(ds->value->ptr))) {
1019 env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
1022 } else {
1023 char ** const e = environ;
1024 for (i = 0; e[i]; ++i) {
1025 char *eq;
1027 if (NULL != (eq = strchr(e[i], '='))) {
1028 env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1));
1033 /* create environment */
1034 for (i = 0; i < host->bin_env->used; i++) {
1035 data_string *ds = (data_string *)host->bin_env->data[i];
1037 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
1040 for (i = 0; i < env.used; i++) {
1041 /* search for PHP_FCGI_CHILDREN */
1042 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
1045 /* not found, add a default */
1046 if (i == env.used) {
1047 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
1050 env.ptr[env.used] = NULL;
1052 bin_path = buffer_init_buffer(host->bin_path);
1053 parse_binpath(&arg, bin_path);
1056 dfd = fdevent_open_dirname(arg.ptr[0]);
1057 if (-1 == dfd) {
1058 log_error_write(srv, __FILE__, __LINE__, "sss", "open dirname failed:", strerror(errno), arg.ptr[0]);
1061 /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/
1062 proc->pid = (dfd >= 0) ? fdevent_fork_execve(arg.ptr[0], arg.ptr, env.ptr, fcgi_fd, -1, -1, dfd) : -1;
1064 for (i = 0; i < env.used; ++i) free(env.ptr[i]);
1065 free(env.ptr);
1066 /*(arg[] contains string references into bin_path)*/
1067 /*for (i = 0; i < arg.used; ++i) free(arg.ptr[i]);*/
1068 free(arg.ptr);
1069 buffer_free(bin_path);
1070 if (-1 != dfd) close(dfd);
1071 close(fcgi_fd);
1073 if (-1 == proc->pid) {
1074 log_error_write(srv, __FILE__, __LINE__, "sb",
1075 "fastcgi-backend failed to start:", host->bin_path);
1076 return -1;
1079 /* register process */
1080 proc->is_local = 1;
1082 /* wait */
1083 select(0, NULL, NULL, NULL, &tv);
1085 if (0 != fcgi_proc_waitpid(srv, host, proc)) {
1086 log_error_write(srv, __FILE__, __LINE__, "sb",
1087 "fastcgi-backend failed to start:", host->bin_path);
1088 log_error_write(srv, __FILE__, __LINE__, "s",
1089 "If you're trying to run your app as a FastCGI backend, make sure you're using the FastCGI-enabled version. "
1090 "If this is PHP on Gentoo, add 'fastcgi' to the USE flags. "
1091 "If this is PHP, try removing the bytecode caches for now and try again.");
1092 return -1;
1094 } else {
1095 proc->is_local = 0;
1096 proc->pid = 0;
1098 if (p->conf.debug) {
1099 log_error_write(srv, __FILE__, __LINE__, "sb",
1100 "(debug) socket is already used; won't spawn:",
1101 proc->connection_name);
1105 fcgi_proc_set_state(host, proc, PROC_STATE_RUNNING);
1106 return 0;
1109 static fcgi_extension_host * unixsocket_is_dup(plugin_data *p, size_t used, buffer *unixsocket) {
1110 size_t i, j, n;
1111 for (i = 0; i < used; ++i) {
1112 fcgi_exts *exts = p->config_storage[i]->exts;
1113 if (NULL == exts) continue;
1114 for (j = 0; j < exts->used; ++j) {
1115 fcgi_extension *ex = exts->exts[j];
1116 for (n = 0; n < ex->used; ++n) {
1117 fcgi_extension_host *host = ex->hosts[n];
1118 if (!buffer_string_is_empty(host->unixsocket)
1119 && buffer_is_equal(host->unixsocket, unixsocket)
1120 && !buffer_string_is_empty(host->bin_path))
1121 return host;
1126 return NULL;
1129 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
1130 plugin_data *p = p_d;
1131 data_unset *du;
1132 size_t i = 0;
1133 buffer *fcgi_mode = buffer_init();
1134 fcgi_extension_host *host = NULL;
1136 config_values_t cv[] = {
1137 { "fastcgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1138 { "fastcgi.debug", NULL, T_CONFIG_INT , T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1139 { "fastcgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1140 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1143 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1145 for (i = 0; i < srv->config_context->used; i++) {
1146 data_config const* config = (data_config const*)srv->config_context->data[i];
1147 plugin_config *s;
1149 s = calloc(1, sizeof(plugin_config));
1150 s->exts = NULL;
1151 s->exts_auth = NULL;
1152 s->exts_resp = NULL;
1153 s->debug = 0;
1154 s->ext_mapping = array_init();
1156 cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */
1157 cv[1].destination = &(s->debug);
1158 cv[2].destination = s->ext_mapping;
1160 p->config_storage[i] = s;
1162 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1163 goto error;
1167 * <key> = ( ... )
1170 if (NULL != (du = array_get_element(config->value, "fastcgi.server"))) {
1171 size_t j;
1172 data_array *da = (data_array *)du;
1174 if (du->type != TYPE_ARRAY || !array_is_kvarray(da->value)) {
1175 log_error_write(srv, __FILE__, __LINE__, "s",
1176 "unexpected value for fastcgi.server; expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1178 goto error;
1181 s->exts = fastcgi_extensions_init();
1182 s->exts_auth = fastcgi_extensions_init();
1183 s->exts_resp = fastcgi_extensions_init();
1186 * fastcgi.server = ( "<ext>" => ( ... ),
1187 * "<ext>" => ( ... ) )
1190 for (j = 0; j < da->value->used; j++) {
1191 size_t n;
1192 data_array *da_ext = (data_array *)da->value->data[j];
1195 * da_ext->key == name of the extension
1199 * fastcgi.server = ( "<ext>" =>
1200 * ( "<host>" => ( ... ),
1201 * "<host>" => ( ... )
1202 * ),
1203 * "<ext>" => ... )
1206 for (n = 0; n < da_ext->value->used; n++) {
1207 data_array *da_host = (data_array *)da_ext->value->data[n];
1209 config_values_t fcv[] = {
1210 { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1211 { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1212 { "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1213 { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
1214 { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
1216 { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
1217 { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
1218 { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
1219 { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
1221 { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
1222 { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
1224 { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
1225 { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
1226 { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
1227 { "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
1228 { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
1229 { "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
1230 { "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 17 */
1231 { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
1233 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1235 unsigned short host_mode = FCGI_RESPONDER;
1237 if (da_host->type != TYPE_ARRAY || !array_is_kvany(da_host->value)) {
1238 log_error_write(srv, __FILE__, __LINE__, "SBS",
1239 "unexpected value for fastcgi.server near [",
1240 da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1242 goto error;
1245 host = fastcgi_host_init();
1246 buffer_reset(fcgi_mode);
1248 buffer_copy_buffer(host->id, da_host->key);
1250 host->check_local = 1;
1251 host->max_procs = 4;
1252 host->disable_time = 1;
1253 host->break_scriptfilename_for_php = 0;
1254 host->xsendfile_allow = 0;
1255 host->kill_signal = SIGTERM;
1256 host->fix_root_path_name = 0;
1257 host->listen_backlog = 1024;
1258 host->refcount = 0;
1260 fcv[0].destination = host->host;
1261 fcv[1].destination = host->docroot;
1262 fcv[2].destination = fcgi_mode;
1263 fcv[3].destination = host->unixsocket;
1264 fcv[4].destination = host->bin_path;
1266 fcv[5].destination = &(host->check_local);
1267 fcv[6].destination = &(host->port);
1268 fcv[7].destination = &(host->max_procs);
1269 fcv[8].destination = &(host->disable_time);
1271 fcv[9].destination = host->bin_env;
1272 fcv[10].destination = host->bin_env_copy;
1273 fcv[11].destination = &(host->break_scriptfilename_for_php);
1274 fcv[12].destination = &(host->xsendfile_allow);
1275 fcv[13].destination = host->strip_request_uri;
1276 fcv[14].destination = &(host->kill_signal);
1277 fcv[15].destination = &(host->fix_root_path_name);
1278 fcv[16].destination = &(host->listen_backlog);
1279 fcv[17].destination = &(host->xsendfile_allow);
1280 fcv[18].destination = host->xsendfile_docroot;
1282 if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) {
1283 goto error;
1286 if ((!buffer_string_is_empty(host->host) || host->port) &&
1287 !buffer_string_is_empty(host->unixsocket)) {
1288 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1289 "either host/port or socket have to be set in:",
1290 da->key, "= (",
1291 da_ext->key, " => (",
1292 da_host->key, " ( ...");
1294 goto error;
1297 if (!buffer_string_is_empty(host->unixsocket)) {
1298 /* unix domain socket */
1299 struct sockaddr_un un;
1301 if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1302 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1303 "unixsocket is too long in:",
1304 da->key, "= (",
1305 da_ext->key, " => (",
1306 da_host->key, " ( ...");
1308 goto error;
1311 if (!buffer_string_is_empty(host->bin_path)) {
1312 fcgi_extension_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket);
1313 if (NULL != duplicate) {
1314 if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) {
1315 log_error_write(srv, __FILE__, __LINE__, "sb",
1316 "duplicate unixsocket path:",
1317 host->unixsocket);
1318 goto error;
1320 fastcgi_host_free(host);
1321 host = duplicate;
1322 ++host->refcount;
1326 host->family = AF_UNIX;
1327 } else {
1328 /* tcp/ip */
1330 if (buffer_string_is_empty(host->host) &&
1331 buffer_string_is_empty(host->bin_path)) {
1332 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1333 "host or binpath have to be set in:",
1334 da->key, "= (",
1335 da_ext->key, " => (",
1336 da_host->key, " ( ...");
1338 goto error;
1339 } else if (host->port == 0) {
1340 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1341 "port has to be set in:",
1342 da->key, "= (",
1343 da_ext->key, " => (",
1344 da_host->key, " ( ...");
1346 goto error;
1349 host->family = (!buffer_string_is_empty(host->host) && NULL != strchr(host->host->ptr, ':')) ? AF_INET6 : AF_INET;
1352 if (host->refcount) {
1353 /* already init'd; skip spawning */
1354 } else if (!buffer_string_is_empty(host->bin_path)) {
1355 /* a local socket + self spawning */
1356 size_t pno;
1358 struct stat st;
1359 size_t nchars = strcspn(host->bin_path->ptr, " \t");
1360 char c = host->bin_path->ptr[nchars];
1361 host->bin_path->ptr[nchars] = '\0';
1362 if (0 == nchars || 0 != stat(host->bin_path->ptr, &st) || !S_ISREG(st.st_mode) || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
1363 host->bin_path->ptr[nchars] = c;
1364 log_error_write(srv, __FILE__, __LINE__, "SSs",
1365 "invalid \"bin-path\" => \"", host->bin_path->ptr,
1366 "\" (check that file exists, is regular file, and is executable by lighttpd)");
1368 host->bin_path->ptr[nchars] = c;
1370 if (s->debug) {
1371 log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsd",
1372 "--- fastcgi spawning local",
1373 "\n\tproc:", host->bin_path,
1374 "\n\tport:", host->port,
1375 "\n\tsocket", host->unixsocket,
1376 "\n\tmax-procs:", host->max_procs);
1379 for (pno = 0; pno < host->max_procs; pno++) {
1380 fcgi_proc *proc;
1382 proc = fastcgi_process_init();
1383 proc->id = host->num_procs++;
1384 host->max_id++;
1386 if (buffer_string_is_empty(host->unixsocket)) {
1387 proc->port = host->port + pno;
1388 } else {
1389 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1390 buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
1391 buffer_append_int(proc->unixsocket, pno);
1394 if (s->debug) {
1395 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1396 "--- fastcgi spawning",
1397 "\n\tport:", host->port,
1398 "\n\tsocket", host->unixsocket,
1399 "\n\tcurrent:", pno, "/", host->max_procs);
1402 if (!srv->srvconf.preflight_check
1403 && fcgi_spawn_connection(srv, p, host, proc)) {
1404 log_error_write(srv, __FILE__, __LINE__, "s",
1405 "[ERROR]: spawning fcgi failed.");
1406 fastcgi_process_free(proc);
1407 goto error;
1410 fastcgi_status_init(srv, p->statuskey, host, proc);
1412 proc->next = host->first;
1413 if (host->first) host->first->prev = proc;
1415 host->first = proc;
1417 } else {
1418 fcgi_proc *proc;
1420 proc = fastcgi_process_init();
1421 proc->id = host->num_procs++;
1422 host->max_id++;
1423 fcgi_proc_set_state(host, proc, PROC_STATE_RUNNING);
1425 if (buffer_string_is_empty(host->unixsocket)) {
1426 proc->port = host->port;
1427 } else {
1428 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1431 fastcgi_status_init(srv, p->statuskey, host, proc);
1433 host->first = proc;
1435 host->max_procs = 1;
1438 if (!buffer_string_is_empty(fcgi_mode)) {
1439 if (strcmp(fcgi_mode->ptr, "responder") == 0) {
1440 host_mode = FCGI_RESPONDER;
1441 } else if (strcmp(fcgi_mode->ptr, "authorizer") == 0) {
1442 host_mode = FCGI_AUTHORIZER;
1443 } else {
1444 log_error_write(srv, __FILE__, __LINE__, "sbs",
1445 "WARNING: unknown fastcgi mode:",
1446 fcgi_mode, "(ignored, mode set to responder)");
1450 if (host->xsendfile_docroot->used) {
1451 size_t k;
1452 for (k = 0; k < host->xsendfile_docroot->used; ++k) {
1453 data_string *ds = (data_string *)host->xsendfile_docroot->data[k];
1454 if (ds->type != TYPE_STRING) {
1455 log_error_write(srv, __FILE__, __LINE__, "s",
1456 "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )");
1457 goto error;
1459 if (ds->value->ptr[0] != '/') {
1460 log_error_write(srv, __FILE__, __LINE__, "SBs",
1461 "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
1462 goto error;
1464 buffer_path_simplify(ds->value, ds->value);
1465 buffer_append_slash(ds->value);
1469 /* s->exts is list of exts -> hosts
1470 * s->exts now used as combined list of authorizer and responder hosts (for backend maintenance)
1471 * s->exts_auth is list of exts -> authorizer hosts
1472 * s->exts_resp is list of exts -> responder hosts
1473 * For each path/extension, there may be an independent FCGI_AUTHORIZER and FCGI_RESPONDER
1474 * (The FCGI_AUTHORIZER and FCGI_RESPONDER could be handled by the same host,
1475 * and an admin might want to do that for large uploads, since FCGI_AUTHORIZER
1476 * runs prior to receiving (potentially large) request body from client and can
1477 * authorizer or deny request prior to receiving the full upload)
1479 fastcgi_extension_insert(s->exts, da_ext->key, host);
1481 if (host_mode == FCGI_AUTHORIZER) {
1482 ++host->refcount;
1483 fastcgi_extension_insert(s->exts_auth, da_ext->key, host);
1484 } else if (host_mode == FCGI_RESPONDER) {
1485 ++host->refcount;
1486 fastcgi_extension_insert(s->exts_resp, da_ext->key, host);
1487 } /*(else should have been rejected above)*/
1489 host = NULL;
1495 buffer_free(fcgi_mode);
1496 return HANDLER_GO_ON;
1498 error:
1499 if (NULL != host) fastcgi_host_free(host);
1500 buffer_free(fcgi_mode);
1501 return HANDLER_ERROR;
1504 static int fcgi_set_state(server *srv, handler_ctx *hctx, fcgi_connection_state_t state) {
1505 hctx->state = state;
1506 hctx->state_timestamp = srv->cur_ts;
1508 return 0;
1512 static void fcgi_backend_close(server *srv, handler_ctx *hctx) {
1513 if (hctx->fd != -1) {
1514 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1515 fdevent_unregister(srv->ev, hctx->fd);
1516 fdevent_sched_close(srv->ev, hctx->fd, 1);
1517 hctx->fd = -1;
1518 hctx->fde_ndx = -1;
1521 if (hctx->host) {
1522 if (hctx->proc && hctx->got_proc) {
1523 /* after the connect the process gets a load */
1524 fcgi_proc_load_dec(srv, hctx);
1526 if (hctx->conf.debug) {
1527 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
1528 "released proc:",
1529 "pid:", hctx->proc->pid,
1530 "socket:", hctx->proc->connection_name,
1531 "load:", hctx->proc->load);
1535 fcgi_host_reset(srv, hctx);
1539 static fcgi_extension_host * fcgi_extension_host_get(server *srv, connection *con, plugin_data *p, fcgi_extension *extension) {
1540 fcgi_extension_host *host;
1541 int ndx = extension->last_used_ndx + 1;
1542 if (ndx >= (int) extension->used || ndx < 0) ndx = 0;
1543 UNUSED(p);
1545 /* check if the next server has no load */
1546 host = extension->hosts[ndx];
1547 if (host->load > 0 || host->active_procs == 0) {
1548 /* get backend with the least load */
1549 size_t k;
1550 int used = -1;
1551 for (k = 0, ndx = -1; k < extension->used; k++) {
1552 host = extension->hosts[k];
1554 /* we should have at least one proc that can do something */
1555 if (host->active_procs == 0) continue;
1557 if (used == -1 || host->load < used) {
1558 used = host->load;
1559 ndx = k;
1564 if (ndx == -1) {
1565 /* all hosts are down */
1566 /* sorry, we don't have a server alive for this ext */
1567 con->http_status = 503; /* Service Unavailable */
1568 con->mode = DIRECT;
1570 /* only send the 'no handler' once */
1571 if (!extension->note_is_sent) {
1572 extension->note_is_sent = 1;
1574 log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
1575 "all handlers for", con->uri.path, "?", con->uri.query,
1576 "on", extension->key,
1577 "are down.");
1580 return NULL;
1583 /* found a server */
1584 extension->last_used_ndx = ndx;
1585 return extension->hosts[ndx];
1588 static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
1589 plugin_data *p;
1590 connection *con;
1592 p = hctx->plugin_data;
1593 con = hctx->remote_conn;
1595 fcgi_backend_close(srv, hctx);
1596 handler_ctx_free(hctx);
1597 con->plugin_ctx[p->id] = NULL;
1599 /* finish response (if not already con->file_started, con->file_finished) */
1600 if (con->mode == p->id) {
1601 http_response_backend_done(srv, con);
1605 static handler_t fcgi_reconnect(server *srv, handler_ctx *hctx) {
1606 fcgi_backend_close(srv, hctx);
1608 hctx->host = fcgi_extension_host_get(srv, hctx->remote_conn, hctx->plugin_data, hctx->ext);
1609 if (NULL == hctx->host) return HANDLER_FINISHED;
1611 fcgi_host_assign(srv, hctx, hctx->host);
1612 hctx->request_id = 0;
1613 hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow;
1614 hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot;
1615 fcgi_set_state(srv, hctx, FCGI_STATE_INIT);
1616 return HANDLER_COMEBACK;
1620 static handler_t fcgi_connection_reset(server *srv, connection *con, void *p_d) {
1621 plugin_data *p = p_d;
1622 handler_ctx *hctx = con->plugin_ctx[p->id];
1623 if (hctx) fcgi_connection_close(srv, hctx);
1625 return HANDLER_GO_ON;
1629 static int fcgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) {
1630 buffer *env = venv;
1631 size_t len;
1632 char len_enc[8];
1633 size_t len_enc_len = 0;
1635 if (!key || !val) return -1;
1637 len = key_len + val_len;
1639 len += key_len > 127 ? 4 : 1;
1640 len += val_len > 127 ? 4 : 1;
1642 if (buffer_string_length(env) + len >= FCGI_MAX_LENGTH) {
1644 * we can't append more headers, ignore it
1646 return -1;
1650 * field length can be 31bit max
1652 * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit
1654 force_assert(key_len < 0x7fffffffu);
1655 force_assert(val_len < 0x7fffffffu);
1657 buffer_string_prepare_append(env, len);
1659 if (key_len > 127) {
1660 len_enc[len_enc_len++] = ((key_len >> 24) & 0xff) | 0x80;
1661 len_enc[len_enc_len++] = (key_len >> 16) & 0xff;
1662 len_enc[len_enc_len++] = (key_len >> 8) & 0xff;
1663 len_enc[len_enc_len++] = (key_len >> 0) & 0xff;
1664 } else {
1665 len_enc[len_enc_len++] = (key_len >> 0) & 0xff;
1668 if (val_len > 127) {
1669 len_enc[len_enc_len++] = ((val_len >> 24) & 0xff) | 0x80;
1670 len_enc[len_enc_len++] = (val_len >> 16) & 0xff;
1671 len_enc[len_enc_len++] = (val_len >> 8) & 0xff;
1672 len_enc[len_enc_len++] = (val_len >> 0) & 0xff;
1673 } else {
1674 len_enc[len_enc_len++] = (val_len >> 0) & 0xff;
1677 buffer_append_string_len(env, len_enc, len_enc_len);
1678 buffer_append_string_len(env, key, key_len);
1679 buffer_append_string_len(env, val, val_len);
1681 return 0;
1684 static int fcgi_header(FCGI_Header * header, unsigned char type, int request_id, int contentLength, unsigned char paddingLength) {
1685 force_assert(contentLength <= FCGI_MAX_LENGTH);
1687 header->version = FCGI_VERSION_1;
1688 header->type = type;
1689 header->requestIdB0 = request_id & 0xff;
1690 header->requestIdB1 = (request_id >> 8) & 0xff;
1691 header->contentLengthB0 = contentLength & 0xff;
1692 header->contentLengthB1 = (contentLength >> 8) & 0xff;
1693 header->paddingLength = paddingLength;
1694 header->reserved = 0;
1696 return 0;
1699 typedef enum {
1700 CONNECTION_OK,
1701 CONNECTION_DELAYED, /* retry after event, take same host */
1702 CONNECTION_OVERLOADED, /* disable for 1 second, take another backend */
1703 CONNECTION_DEAD /* disable for 60 seconds, take another backend */
1704 } connection_result_t;
1706 static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *hctx) {
1707 sock_addr addr;
1708 struct sockaddr *fcgi_addr = (struct sockaddr *)&addr;
1709 socklen_t servlen;
1711 fcgi_extension_host *host = hctx->host;
1712 fcgi_proc *proc = hctx->proc;
1713 int fcgi_fd = hctx->fd;
1715 if (!buffer_string_is_empty(proc->unixsocket)) {
1716 if (1 != sock_addr_from_str_hints(srv, &addr, &servlen, proc->unixsocket->ptr, AF_UNIX, 0)) {
1717 return CONNECTION_DEAD;
1719 } else {
1720 if (1 != sock_addr_from_buffer_hints_numeric(srv, &addr, &servlen, host->host, host->family, proc->port)) {
1721 return CONNECTION_DEAD;
1725 if (!buffer_string_is_empty(proc->unixsocket)) {
1726 if (buffer_string_is_empty(proc->connection_name)) {
1727 /* on remote spawing we have to set the connection-name now */
1728 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
1729 buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
1731 } else {
1732 if (buffer_string_is_empty(proc->connection_name)) {
1733 /* on remote spawing we have to set the connection-name now */
1734 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
1735 if (!buffer_string_is_empty(host->host)) {
1736 buffer_append_string_buffer(proc->connection_name, host->host);
1737 } else {
1738 buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost"));
1740 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
1741 buffer_append_int(proc->connection_name, proc->port);
1745 if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
1746 if (errno == EINPROGRESS ||
1747 errno == EALREADY ||
1748 errno == EINTR) {
1749 if (hctx->conf.debug > 2) {
1750 log_error_write(srv, __FILE__, __LINE__, "sb",
1751 "connect delayed; will continue later:", proc->connection_name);
1754 return CONNECTION_DELAYED;
1755 } else if (errno == EAGAIN) {
1756 if (hctx->conf.debug) {
1757 log_error_write(srv, __FILE__, __LINE__, "sbsd",
1758 "This means that you have more incoming requests than your FastCGI backend can handle in parallel."
1759 "It might help to spawn more FastCGI backends or PHP children; if not, decrease server.max-connections."
1760 "The load for this FastCGI backend", proc->connection_name, "is", proc->load);
1763 return CONNECTION_OVERLOADED;
1764 } else {
1765 log_error_write(srv, __FILE__, __LINE__, "sssb",
1766 "connect failed:",
1767 strerror(errno), "on",
1768 proc->connection_name);
1770 return CONNECTION_DEAD;
1774 hctx->reconnects = 0;
1775 if (hctx->conf.debug > 1) {
1776 log_error_write(srv, __FILE__, __LINE__, "sd",
1777 "connect succeeded: ", fcgi_fd);
1780 return CONNECTION_OK;
1783 static void fcgi_stdin_append(server *srv, connection *con, handler_ctx *hctx, int request_id) {
1784 FCGI_Header header;
1785 chunkqueue *req_cq = con->request_content_queue;
1786 off_t offset, weWant;
1787 const off_t req_cqlen = req_cq->bytes_in - req_cq->bytes_out;
1789 /* something to send ? */
1790 for (offset = 0; offset != req_cqlen; offset += weWant) {
1791 weWant = req_cqlen - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : req_cqlen - offset;
1793 /* we announce toWrite octets
1794 * now take all request_content chunks available
1795 * */
1797 fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0);
1798 chunkqueue_append_mem(hctx->wb, (const char *)&header, sizeof(header));
1799 if (-1 != hctx->wb_reqlen) {
1800 if (hctx->wb_reqlen >= 0) {
1801 hctx->wb_reqlen += sizeof(header);
1802 } else {
1803 hctx->wb_reqlen -= sizeof(header);
1807 if (hctx->conf.debug > 10) {
1808 log_error_write(srv, __FILE__, __LINE__, "soso", "tosend:", offset, "/", req_cqlen);
1811 chunkqueue_steal(hctx->wb, req_cq, weWant);
1812 /*(hctx->wb_reqlen already includes content_length)*/
1815 if (hctx->wb->bytes_in == hctx->wb_reqlen) {
1816 /* terminate STDIN */
1817 /* (future: must defer ending FCGI_STDIN
1818 * if might later upgrade protocols
1819 * and then have more data to send) */
1820 fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0);
1821 chunkqueue_append_mem(hctx->wb, (const char *)&header, sizeof(header));
1822 hctx->wb_reqlen += (int)sizeof(header);
1826 static int fcgi_create_env(server *srv, handler_ctx *hctx, int request_id) {
1827 FCGI_BeginRequestRecord beginRecord;
1828 FCGI_Header header;
1830 plugin_data *p = hctx->plugin_data;
1831 fcgi_extension_host *host= hctx->host;
1833 connection *con = hctx->remote_conn;
1835 http_cgi_opts opts = {
1836 (hctx->fcgi_mode == FCGI_AUTHORIZER),
1837 host->break_scriptfilename_for_php,
1838 host->docroot,
1839 host->strip_request_uri
1842 /* send FCGI_BEGIN_REQUEST */
1844 fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0);
1845 beginRecord.body.roleB0 = hctx->fcgi_mode;
1846 beginRecord.body.roleB1 = 0;
1847 beginRecord.body.flags = 0;
1848 memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved));
1850 /* send FCGI_PARAMS */
1851 buffer_string_prepare_copy(p->fcgi_env, 1023);
1853 if (0 != http_cgi_headers(srv, con, &opts, fcgi_env_add, p->fcgi_env)) {
1854 con->http_status = 400;
1855 return -1;
1856 } else {
1857 buffer *b = buffer_init();
1859 buffer_copy_string_len(b, (const char *)&beginRecord, sizeof(beginRecord));
1861 fcgi_header(&(header), FCGI_PARAMS, request_id, buffer_string_length(p->fcgi_env), 0);
1862 buffer_append_string_len(b, (const char *)&header, sizeof(header));
1863 buffer_append_string_buffer(b, p->fcgi_env);
1865 fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0);
1866 buffer_append_string_len(b, (const char *)&header, sizeof(header));
1868 hctx->wb_reqlen = buffer_string_length(b);
1869 chunkqueue_append_buffer(hctx->wb, b);
1870 buffer_free(b);
1873 if (con->request.content_length) {
1874 /*chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);*/
1875 if (con->request.content_length > 0)
1876 hctx->wb_reqlen += con->request.content_length;/* (eventual) (minimal) total request size, not necessarily including all fcgi_headers around content length yet */
1877 else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/
1878 hctx->wb_reqlen = -hctx->wb_reqlen;
1880 fcgi_stdin_append(srv, con, hctx, request_id);
1882 return 0;
1885 typedef struct {
1886 buffer *b;
1887 unsigned int len;
1888 int type;
1889 int padding;
1890 int request_id;
1891 } fastcgi_response_packet;
1893 static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) {
1894 chunk *c;
1895 size_t offset;
1896 size_t toread;
1897 FCGI_Header *header;
1899 if (!hctx->rb->first) return -1;
1901 packet->b = buffer_init();
1902 packet->len = 0;
1903 packet->type = 0;
1904 packet->padding = 0;
1905 packet->request_id = 0;
1907 offset = 0; toread = 8;
1908 /* get at least the FastCGI header */
1909 for (c = hctx->rb->first; c; c = c->next) {
1910 size_t weHave = buffer_string_length(c->mem) - c->offset;
1912 if (weHave > toread) weHave = toread;
1914 buffer_append_string_len(packet->b, c->mem->ptr + c->offset, weHave);
1915 toread -= weHave;
1916 offset = weHave; /* skip offset bytes in chunk for "real" data */
1918 if (0 == toread) break;
1921 if (buffer_string_length(packet->b) < sizeof(FCGI_Header)) {
1922 /* no header */
1923 if (hctx->conf.debug) {
1924 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");
1927 buffer_free(packet->b);
1929 return -1;
1932 /* we have at least a header, now check how much me have to fetch */
1933 header = (FCGI_Header *)(packet->b->ptr);
1935 packet->len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength;
1936 packet->request_id = (header->requestIdB0 | (header->requestIdB1 << 8));
1937 packet->type = header->type;
1938 packet->padding = header->paddingLength;
1940 /* ->b should only be the content */
1941 buffer_string_set_length(packet->b, 0);
1943 if (packet->len) {
1944 /* copy the content */
1945 for (; c && (buffer_string_length(packet->b) < packet->len); c = c->next) {
1946 size_t weWant = packet->len - buffer_string_length(packet->b);
1947 size_t weHave = buffer_string_length(c->mem) - c->offset - offset;
1949 if (weHave > weWant) weHave = weWant;
1951 buffer_append_string_len(packet->b, c->mem->ptr + c->offset + offset, weHave);
1953 /* we only skipped the first bytes as they belonged to the fcgi header */
1954 offset = 0;
1957 if (buffer_string_length(packet->b) < packet->len) {
1958 /* we didn't get the full packet */
1960 buffer_free(packet->b);
1961 return -1;
1964 buffer_string_set_length(packet->b, buffer_string_length(packet->b) - packet->padding);
1967 chunkqueue_mark_written(hctx->rb, packet->len + sizeof(FCGI_Header));
1969 return 0;
1972 static handler_t fcgi_recv_parse(server *srv, connection *con, struct http_response_opts_t *opts, buffer *b, size_t n) {
1973 handler_ctx *hctx = (handler_ctx *)opts->pdata;
1974 int fin = 0;
1976 if (0 == n) {
1977 if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) return 0;
1978 log_error_write(srv, __FILE__, __LINE__, "ssdsb",
1979 "unexpected end-of-file (perhaps the fastcgi process died):",
1980 "pid:", hctx->proc->pid,
1981 "socket:", hctx->proc->connection_name);
1983 return HANDLER_ERROR;
1986 chunkqueue_append_buffer(hctx->rb, b);
1989 * parse the fastcgi packets and forward the content to the write-queue
1992 while (fin == 0) {
1993 fastcgi_response_packet packet;
1995 /* check if we have at least one packet */
1996 if (0 != fastcgi_get_packet(srv, hctx, &packet)) {
1997 /* no full packet */
1998 break;
2001 switch(packet.type) {
2002 case FCGI_STDOUT:
2003 if (packet.len == 0) break;
2005 /* is the header already finished */
2006 if (0 == con->file_started) {
2007 /* split header from body */
2008 buffer *hdrs = (!hctx->response_header)
2009 ? packet.b
2010 : (buffer_append_string_buffer(hctx->response_header, packet.b), hctx->response_header);
2011 handler_t rc = http_response_parse_headers(srv, con, &hctx->opts, hdrs);
2012 if (rc != HANDLER_GO_ON) {
2013 hctx->send_content_body = 0;
2014 fin = 1;
2015 break;
2017 if (0 == con->file_started) {
2018 if (!hctx->response_header) {
2019 hctx->response_header = packet.b;
2020 packet.b = NULL;
2023 else if (hctx->fcgi_mode == FCGI_AUTHORIZER &&
2024 (con->http_status == 0 || con->http_status == 200)) {
2025 /* authorizer approved request; ignore the content here */
2026 hctx->send_content_body = 0;
2028 } else if (hctx->send_content_body && !buffer_string_is_empty(packet.b)) {
2029 if (0 != http_chunk_append_buffer(srv, con, packet.b)) {
2030 /* error writing to tempfile;
2031 * truncate response or send 500 if nothing sent yet */
2032 fin = 1;
2033 break;
2036 break;
2037 case FCGI_STDERR:
2038 if (packet.len == 0) break;
2040 log_error_write_multiline_buffer(srv, __FILE__, __LINE__, packet.b, "s",
2041 "FastCGI-stderr:");
2043 break;
2044 case FCGI_END_REQUEST:
2045 fin = 1;
2046 break;
2047 default:
2048 log_error_write(srv, __FILE__, __LINE__, "sd",
2049 "FastCGI: header.type not handled: ", packet.type);
2050 break;
2052 buffer_free(packet.b);
2055 return 0 == fin ? HANDLER_GO_ON : HANDLER_FINISHED;
2058 static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_host *host) {
2059 fcgi_proc *proc;
2061 for (proc = host->first; proc; proc = proc->next) {
2062 if (p->conf.debug > 2) {
2063 log_error_write(srv, __FILE__, __LINE__, "sbdddd",
2064 "proc:",
2065 proc->connection_name,
2066 proc->state,
2067 proc->is_local,
2068 proc->load,
2069 proc->pid);
2073 * if the remote side is overloaded, we check back after <n> seconds
2076 switch (proc->state) {
2077 case PROC_STATE_KILLED:
2078 /* this should never happen as long as adaptive spawing is disabled */
2079 force_assert(0);
2081 break;
2082 case PROC_STATE_RUNNING:
2083 break;
2084 case PROC_STATE_OVERLOADED:
2085 case PROC_STATE_DIED_WAIT_FOR_PID:
2086 if (0 == fcgi_proc_waitpid(srv, host, proc)) {
2087 fcgi_proc_check_enable(srv, host, proc);
2090 /* fall through if we have a dead proc now */
2091 if (proc->state != PROC_STATE_DIED) break;
2093 case PROC_STATE_DIED:
2094 /* local procs get restarted by us,
2095 * remote ones hopefully by the admin */
2097 if (!buffer_string_is_empty(host->bin_path)) {
2098 /* we still have connections bound to this proc,
2099 * let them terminate first */
2100 if (proc->load != 0) break;
2102 /* restart the child */
2104 if (p->conf.debug) {
2105 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2106 "--- fastcgi spawning",
2107 "\n\tsocket", proc->connection_name,
2108 "\n\tcurrent:", 1, "/", host->max_procs);
2111 if (fcgi_spawn_connection(srv, p, host, proc)) {
2112 log_error_write(srv, __FILE__, __LINE__, "s",
2113 "ERROR: spawning fcgi failed.");
2114 return HANDLER_ERROR;
2116 } else {
2117 fcgi_proc_check_enable(srv, host, proc);
2119 break;
2123 return 0;
2126 static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
2127 plugin_data *p = hctx->plugin_data;
2128 fcgi_extension_host *host= hctx->host;
2129 connection *con = hctx->remote_conn;
2130 fcgi_proc *proc;
2132 int ret;
2134 /* we can't handle this in the switch as we have to fall through in it */
2135 if (hctx->state == FCGI_STATE_CONNECT_DELAYED) {
2136 int socket_error = fdevent_connect_status(hctx->fd);
2137 if (socket_error != 0) {
2138 if (!hctx->proc->is_local || hctx->conf.debug) {
2139 /* local procs get restarted */
2141 log_error_write(srv, __FILE__, __LINE__, "sssb",
2142 "establishing connection failed:", strerror(socket_error),
2143 "socket:", hctx->proc->connection_name);
2146 fcgi_proc_disable(srv, hctx->host, hctx->proc, hctx);
2147 log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2148 "backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2149 "reconnects:", hctx->reconnects,
2150 "load:", host->load);
2152 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2153 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
2155 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2157 return HANDLER_ERROR;
2159 /* go on with preparing the request */
2160 hctx->state = FCGI_STATE_PREPARE_WRITE;
2164 switch(hctx->state) {
2165 case FCGI_STATE_CONNECT_DELAYED:
2166 /* should never happen */
2167 return HANDLER_WAIT_FOR_EVENT;
2168 case FCGI_STATE_INIT:
2169 /* do we have a running process for this host (max-procs) ? */
2170 hctx->proc = NULL;
2172 for (proc = hctx->host->first;
2173 proc && proc->state != PROC_STATE_RUNNING;
2174 proc = proc->next);
2176 /* all children are dead */
2177 if (proc == NULL) {
2178 return HANDLER_ERROR;
2181 hctx->proc = proc;
2183 /* check the other procs if they have a lower load */
2184 for (proc = proc->next; proc; proc = proc->next) {
2185 if (proc->state != PROC_STATE_RUNNING) continue;
2186 if (proc->load < hctx->proc->load) hctx->proc = proc;
2189 if (-1 == (hctx->fd = fdevent_socket_nb_cloexec(host->family, SOCK_STREAM, 0))) {
2190 if (errno == EMFILE ||
2191 errno == EINTR) {
2192 log_error_write(srv, __FILE__, __LINE__, "sd",
2193 "wait for fd at connection:", con->fd);
2195 return HANDLER_WAIT_FOR_FD;
2198 log_error_write(srv, __FILE__, __LINE__, "ssdd",
2199 "socket failed:", strerror(errno), srv->cur_fds, srv->max_fds);
2200 return HANDLER_ERROR;
2203 srv->cur_fds++;
2205 fdevent_register(srv->ev, hctx->fd, fcgi_handle_fdevent, hctx);
2207 if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
2208 log_error_write(srv, __FILE__, __LINE__, "ss",
2209 "fcntl failed:", strerror(errno));
2211 return HANDLER_ERROR;
2214 if (hctx->proc->is_local) {
2215 hctx->pid = hctx->proc->pid;
2218 switch (fcgi_establish_connection(srv, hctx)) {
2219 case CONNECTION_DELAYED:
2220 /* connection is in progress, wait for an event and call getsockopt() below */
2222 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2224 fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT_DELAYED);
2225 return HANDLER_WAIT_FOR_EVENT;
2226 case CONNECTION_OVERLOADED:
2227 /* cool down the backend, it is overloaded
2228 * -> EAGAIN */
2230 if (hctx->host->disable_time) {
2231 log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2232 "backend is overloaded; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2233 "reconnects:", hctx->reconnects,
2234 "load:", host->load);
2236 hctx->proc->disabled_until = srv->cur_ts + hctx->host->disable_time;
2237 fcgi_proc_set_state(hctx->host, hctx->proc, PROC_STATE_OVERLOADED);
2240 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2241 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".overloaded"));
2243 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2245 return HANDLER_ERROR;
2246 case CONNECTION_DEAD:
2247 /* we got a hard error from the backend like
2248 * - ECONNREFUSED for tcp-ip sockets
2249 * - ENOENT for unix-domain-sockets
2251 * for check if the host is back in hctx->host->disable_time seconds
2252 * */
2254 fcgi_proc_disable(srv, hctx->host, hctx->proc, hctx);
2256 log_error_write(srv, __FILE__, __LINE__, "sdssdsd",
2257 "backend died; we'll disable it for", hctx->host->disable_time, "seconds and send the request to another backend instead:",
2258 "reconnects:", hctx->reconnects,
2259 "load:", host->load);
2261 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2262 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".died"));
2264 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2266 return HANDLER_ERROR;
2267 case CONNECTION_OK:
2268 /* everything is ok, go on */
2270 fcgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE);
2272 break;
2274 /* fallthrough */
2275 case FCGI_STATE_PREPARE_WRITE:
2276 /* ok, we have the connection */
2278 fcgi_proc_load_inc(srv, hctx);
2279 hctx->got_proc = 1;
2281 status_counter_inc(srv, CONST_STR_LEN("fastcgi.requests"));
2283 fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc);
2284 buffer_append_string_len(p->statuskey, CONST_STR_LEN(".connected"));
2286 status_counter_inc(srv, CONST_BUF_LEN(p->statuskey));
2288 if (hctx->conf.debug) {
2289 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
2290 "got proc:",
2291 "pid:", hctx->proc->pid,
2292 "socket:", hctx->proc->connection_name,
2293 "load:", hctx->proc->load);
2296 /* move the proc-list entry down the list */
2297 if (hctx->request_id == 0) {
2298 hctx->request_id = 1; /* always use id 1 as we don't use multiplexing */
2299 } else {
2300 log_error_write(srv, __FILE__, __LINE__, "sd",
2301 "fcgi-request is already in use:", hctx->request_id);
2304 if (-1 == fcgi_create_env(srv, hctx, hctx->request_id)) return HANDLER_ERROR;
2306 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2307 fcgi_set_state(srv, hctx, FCGI_STATE_WRITE);
2308 /* fall through */
2309 case FCGI_STATE_WRITE:
2310 ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, MAX_WRITE_LIMIT);
2312 chunkqueue_remove_finished_chunks(hctx->wb);
2314 if (ret < 0) {
2315 switch(errno) {
2316 case EPIPE:
2317 case ENOTCONN:
2318 case ECONNRESET:
2319 /* the connection got dropped after accept()
2320 * we don't care about that - if you accept() it, you have to handle it.
2323 log_error_write(srv, __FILE__, __LINE__, "ssosb",
2324 "connection was dropped after accept() (perhaps the fastcgi process died),",
2325 "write-offset:", hctx->wb->bytes_out,
2326 "socket:", hctx->proc->connection_name);
2328 return HANDLER_ERROR;
2329 default:
2330 log_error_write(srv, __FILE__, __LINE__, "ssd",
2331 "write failed:", strerror(errno), errno);
2333 return HANDLER_ERROR;
2337 if (hctx->wb->bytes_out == hctx->wb_reqlen) {
2338 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2339 fcgi_set_state(srv, hctx, FCGI_STATE_READ);
2340 } else {
2341 off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out;
2342 if ((hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0) && wblen < 65536 - 16384) {
2343 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
2344 if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
2345 con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
2346 con->is_readable = 1; /* trigger optimistic read from client */
2349 if (0 == wblen) {
2350 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2351 } else {
2352 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2356 return HANDLER_WAIT_FOR_EVENT;
2357 case FCGI_STATE_READ:
2358 /* waiting for a response */
2359 return HANDLER_WAIT_FOR_EVENT;
2360 default:
2361 log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
2362 return HANDLER_ERROR;
2367 /* might be called on fdevent after a connect() is delay too
2368 * */
2369 static handler_t fcgi_send_request(server *srv, handler_ctx *hctx) {
2370 /* ok, create the request */
2371 fcgi_extension_host *host = hctx->host;
2372 handler_t rc = fcgi_write_request(srv, hctx);
2373 if (HANDLER_ERROR != rc) {
2374 return rc;
2375 } else {
2376 plugin_data *p = hctx->plugin_data;
2377 connection *con = hctx->remote_conn;
2379 if (hctx->state == FCGI_STATE_INIT ||
2380 hctx->state == FCGI_STATE_CONNECT_DELAYED) {
2381 fcgi_restart_dead_procs(srv, p, host);
2383 /* cleanup this request and let the request handler start this request again */
2384 if (hctx->reconnects++ < 5) {
2385 return fcgi_reconnect(srv, hctx);
2386 } else {
2387 fcgi_connection_close(srv, hctx);
2388 con->http_status = 503;
2390 return HANDLER_FINISHED;
2392 } else {
2393 int status = con->http_status;
2394 fcgi_connection_close(srv, hctx);
2395 con->http_status = (status == 400) ? 400 : 503;
2397 return HANDLER_FINISHED;
2403 static handler_t fcgi_recv_response(server *srv, handler_ctx *hctx);
2406 SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
2407 plugin_data *p = p_d;
2409 handler_ctx *hctx = con->plugin_ctx[p->id];
2411 if (NULL == hctx) return HANDLER_GO_ON;
2413 /* not my job */
2414 if (con->mode != p->id) return HANDLER_GO_ON;
2416 if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
2417 && con->file_started) {
2418 if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
2419 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2420 } else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) {
2421 /* optimistic read from backend */
2422 handler_t rc = fcgi_recv_response(srv, hctx); /*(might invalidate hctx)*/
2423 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
2424 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2428 /* (do not receive request body before FCGI_AUTHORIZER has run or else
2429 * the request body is discarded with handler_ctx_clear() after running
2430 * the FastCGI Authorizer) */
2432 if (hctx->fcgi_mode != FCGI_AUTHORIZER
2433 && (0 == hctx->wb->bytes_in
2434 ? con->state == CON_STATE_READ_POST
2435 : (hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0))) {
2436 /* leave excess data in con->request_content_queue, which is
2437 * buffered to disk if too large and backend can not keep up */
2438 /*(64k - 4k to attempt to avoid temporary files
2439 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
2440 if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096) {
2441 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) {
2442 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
2444 if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT;
2445 } else {
2446 handler_t r = connection_handle_read_post_state(srv, con);
2447 chunkqueue *req_cq = con->request_content_queue;
2448 #if 0 /*(not reached since we send 411 Length Required below)*/
2449 if (hctx->wb_reqlen < -1 && con->request.content_length >= 0) {
2450 /* (completed receiving Transfer-Encoding: chunked) */
2451 hctx->wb_reqlen = -hctx->wb_reqlen + con->request.content_length;
2452 fcgi_stdin_append(srv, con, hctx, hctx->request_id);
2454 #endif
2455 if (0 != hctx->wb->bytes_in && !chunkqueue_is_empty(req_cq)) {
2456 fcgi_stdin_append(srv, con, hctx, hctx->request_id);
2457 if (fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_OUT) {
2458 return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
2461 if (r != HANDLER_GO_ON) return r;
2463 /* CGI environment requires that Content-Length be set.
2464 * Send 411 Length Required if Content-Length missing.
2465 * (occurs here if client sends Transfer-Encoding: chunked
2466 * and module is flagged to stream request body to backend) */
2467 if (-1 == con->request.content_length) {
2468 return connection_handle_read_post_error(srv, con, 411);
2473 return ((0 == hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb))
2474 && hctx->state != FCGI_STATE_CONNECT_DELAYED)
2475 ? fcgi_send_request(srv, hctx)
2476 : HANDLER_WAIT_FOR_EVENT;
2480 static handler_t fcgi_recv_response(server *srv, handler_ctx *hctx) {
2481 connection *con = hctx->remote_conn;
2482 plugin_data *p = hctx->plugin_data;
2484 fcgi_proc *proc = hctx->proc;
2485 fcgi_extension_host *host= hctx->host;
2486 buffer *b = buffer_init();
2488 switch (http_response_read(srv, hctx->remote_conn, &hctx->opts,
2489 b, hctx->fd, &hctx->fde_ndx)) {
2490 default:
2491 break;
2492 case HANDLER_FINISHED:
2493 buffer_free(b);
2494 if (hctx->fcgi_mode == FCGI_AUTHORIZER &&
2495 (con->http_status == 200 ||
2496 con->http_status == 0)) {
2498 * If we are here in AUTHORIZER mode then a request for authorizer
2499 * was processed already, and status 200 has been returned. We need
2500 * now to handle authorized request.
2502 buffer *physpath = NULL;
2504 if (!buffer_string_is_empty(host->docroot)) {
2505 buffer_copy_buffer(con->physical.doc_root, host->docroot);
2506 buffer_copy_buffer(con->physical.basedir, host->docroot);
2508 buffer_copy_buffer(con->physical.path, host->docroot);
2509 buffer_append_string_buffer(con->physical.path, con->uri.path);
2510 physpath = con->physical.path;
2513 fcgi_backend_close(srv, hctx);
2514 handler_ctx_clear(hctx);
2516 /* don't do more than 6 loops here, that normally shouldn't happen */
2517 if (++con->loops_per_request > 5) {
2518 log_error_write(srv, __FILE__, __LINE__, "sb", "too many loops while processing request:", con->request.orig_uri);
2519 con->http_status = 500; /* Internal Server Error */
2520 con->mode = DIRECT;
2521 return HANDLER_FINISHED;
2524 /* restart the request so other handlers can process it */
2526 if (physpath) con->physical.path = NULL;
2527 connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
2528 if (physpath) con->physical.path = physpath; /* preserve con->physical.path with modified docroot */
2530 /*(FYI: if multiple FastCGI authorizers were to be supported,
2531 * next one could be started here instead of restarting request)*/
2533 con->mode = DIRECT;
2534 return HANDLER_COMEBACK;
2535 } else {
2536 /* we are done */
2537 fcgi_connection_close(srv, hctx);
2540 return HANDLER_FINISHED;
2541 case HANDLER_COMEBACK: /*(not expected; treat as error)*/
2542 case HANDLER_ERROR:
2543 buffer_free(b);
2544 if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid && proc->state != PROC_STATE_DIED) {
2545 if (0 != fcgi_proc_waitpid(srv, host, proc)) {
2546 if (hctx->conf.debug) {
2547 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2548 "--- fastcgi spawning",
2549 "\n\tsocket", proc->connection_name,
2550 "\n\tcurrent:", 1, "/", host->max_procs);
2553 if (fcgi_spawn_connection(srv, p, host, proc)) {
2554 log_error_write(srv, __FILE__, __LINE__, "s",
2555 "respawning failed, will retry later");
2560 if (con->file_started == 0) {
2561 /* nothing has been sent out yet, try to use another child */
2563 if (hctx->wb->bytes_out == 0 &&
2564 hctx->reconnects++ < 5) {
2566 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2567 "response not received, request not sent",
2568 "on socket:", proc->connection_name,
2569 "for", con->uri.path, "?", con->uri.query, ", reconnecting");
2571 return fcgi_reconnect(srv, hctx);
2574 log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
2575 "response not received, request sent:", hctx->wb->bytes_out,
2576 "on socket:", proc->connection_name,
2577 "for", con->uri.path, "?", con->uri.query, ", closing connection");
2578 } else {
2579 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2580 "response already sent out, but backend returned error",
2581 "on socket:", proc->connection_name,
2582 "for", con->uri.path, "?", con->uri.query, ", terminating connection");
2585 http_response_backend_error(srv, con);
2586 fcgi_connection_close(srv, hctx);
2587 return HANDLER_FINISHED;
2590 buffer_free(b);
2591 return HANDLER_GO_ON;
2595 static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {
2596 handler_ctx *hctx = ctx;
2597 connection *con = hctx->remote_conn;
2599 joblist_append(srv, con);
2601 if (revents & FDEVENT_IN) {
2602 handler_t rc = fcgi_recv_response(srv, hctx);/*(might invalidate hctx)*/
2603 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
2606 if (revents & FDEVENT_OUT) {
2607 return fcgi_send_request(srv, hctx); /*(might invalidate hctx)*/
2610 /* perhaps this issue is already handled */
2611 if (revents & FDEVENT_HUP) {
2612 if (hctx->state == FCGI_STATE_CONNECT_DELAYED) {
2613 /* getoptsock will catch this one (right ?)
2615 * if we are in connect we might get an EINPROGRESS
2616 * in the first call and an FDEVENT_HUP in the
2617 * second round
2619 * FIXME: as it is a bit ugly.
2622 fcgi_send_request(srv, hctx);
2623 } else if (con->file_started) {
2624 /* drain any remaining data from kernel pipe buffers
2625 * even if (con->conf.stream_response_body
2626 * & FDEVENT_STREAM_RESPONSE_BUFMIN)
2627 * since event loop will spin on fd FDEVENT_HUP event
2628 * until unregistered. */
2629 handler_t rc;
2630 do {
2631 rc = fcgi_recv_response(srv,hctx);/*(might invalidate hctx)*/
2632 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
2633 return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
2634 } else {
2635 fcgi_proc *proc = hctx->proc;
2636 log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd",
2637 "error: unexpected close of fastcgi connection for",
2638 con->uri.path, "?", con->uri.query,
2639 "(no fastcgi process on socket:", proc->connection_name, "?)",
2640 hctx->state);
2642 fcgi_connection_close(srv, hctx);
2644 } else if (revents & FDEVENT_ERR) {
2645 log_error_write(srv, __FILE__, __LINE__, "s",
2646 "fcgi: got a FDEVENT_ERR. Don't know why.");
2648 http_response_backend_error(srv, con);
2649 fcgi_connection_close(srv, hctx);
2652 return HANDLER_FINISHED;
2655 #define PATCH(x) \
2656 p->conf.x = s->x;
2657 static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) {
2658 size_t i, j;
2659 plugin_config *s = p->config_storage[0];
2661 PATCH(exts);
2662 PATCH(exts_auth);
2663 PATCH(exts_resp);
2664 PATCH(debug);
2665 PATCH(ext_mapping);
2667 /* skip the first, the global context */
2668 for (i = 1; i < srv->config_context->used; i++) {
2669 data_config *dc = (data_config *)srv->config_context->data[i];
2670 s = p->config_storage[i];
2672 /* condition didn't match */
2673 if (!config_check_cond(srv, con, dc)) continue;
2675 /* merge config */
2676 for (j = 0; j < dc->value->used; j++) {
2677 data_unset *du = dc->value->data[j];
2679 if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.server"))) {
2680 PATCH(exts);
2681 PATCH(exts_auth);
2682 PATCH(exts_resp);
2683 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.debug"))) {
2684 PATCH(debug);
2685 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.map-extensions"))) {
2686 PATCH(ext_mapping);
2691 return 0;
2693 #undef PATCH
2696 static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
2697 plugin_data *p = p_d;
2698 size_t s_len;
2699 size_t k;
2700 buffer *fn;
2701 fcgi_extension *extension = NULL;
2702 fcgi_extension_host *host = NULL;
2703 handler_ctx *hctx;
2704 unsigned short fcgi_mode;
2706 if (con->mode != DIRECT) return HANDLER_GO_ON;
2708 fn = uri_path_handler ? con->uri.path : con->physical.path;
2710 if (buffer_string_is_empty(fn)) return HANDLER_GO_ON;
2712 s_len = buffer_string_length(fn);
2714 fcgi_patch_connection(srv, con, p);
2715 if (NULL == p->conf.exts) return HANDLER_GO_ON;
2717 /* check p->conf.exts_auth list and then p->conf.ext_resp list
2718 * (skip p->conf.exts_auth if array is empty or if FCGI_AUTHORIZER already ran in this request */
2719 hctx = con->plugin_ctx[p->id]; /*(not NULL if FCGI_AUTHORIZER ran; hctx->ext-auth check is redundant)*/
2720 fcgi_mode = (NULL == hctx || NULL == hctx->ext_auth)
2721 ? 0 /* FCGI_AUTHORIZER p->conf.exts_auth will be searched next */
2722 : FCGI_AUTHORIZER; /* FCGI_RESPONDER p->conf.exts_resp will be searched next */
2724 do {
2726 fcgi_exts *exts;
2727 if (0 == fcgi_mode) {
2728 fcgi_mode = FCGI_AUTHORIZER;
2729 exts = p->conf.exts_auth;
2730 } else {
2731 fcgi_mode = FCGI_RESPONDER;
2732 exts = p->conf.exts_resp;
2735 if (0 == exts->used) continue;
2737 /* fastcgi.map-extensions maps extensions to existing fastcgi.server entries
2739 * fastcgi.map-extensions = ( ".php3" => ".php" )
2741 * fastcgi.server = ( ".php" => ... )
2743 * */
2745 /* check if extension-mapping matches */
2746 for (k = 0; k < p->conf.ext_mapping->used; k++) {
2747 data_string *ds = (data_string *)p->conf.ext_mapping->data[k];
2748 size_t ct_len; /* length of the config entry */
2750 if (buffer_is_empty(ds->key)) continue;
2752 ct_len = buffer_string_length(ds->key);
2754 if (s_len < ct_len) continue;
2756 /* found a mapping */
2757 if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
2758 /* check if we know the extension */
2760 /* we can reuse k here */
2761 for (k = 0; k < exts->used; k++) {
2762 extension = exts->exts[k];
2764 if (buffer_is_equal(ds->value, extension->key)) {
2765 break;
2769 if (k == exts->used) {
2770 /* found nothing */
2771 extension = NULL;
2773 break;
2777 if (extension == NULL) {
2778 size_t uri_path_len = buffer_string_length(con->uri.path);
2780 /* check if extension matches */
2781 for (k = 0; k < exts->used; k++) {
2782 size_t ct_len; /* length of the config entry */
2783 fcgi_extension *ext = exts->exts[k];
2785 if (buffer_is_empty(ext->key)) continue;
2787 ct_len = buffer_string_length(ext->key);
2789 /* check _url_ in the form "/fcgi_pattern" */
2790 if (ext->key->ptr[0] == '/') {
2791 if ((ct_len <= uri_path_len) &&
2792 (strncmp(con->uri.path->ptr, ext->key->ptr, ct_len) == 0)) {
2793 extension = ext;
2794 break;
2796 } else if ((ct_len <= s_len) && (0 == strncmp(fn->ptr + s_len - ct_len, ext->key->ptr, ct_len))) {
2797 /* check extension in the form ".fcg" */
2798 extension = ext;
2799 break;
2804 } while (NULL == extension && fcgi_mode != FCGI_RESPONDER);
2806 /* extension doesn't match */
2807 if (NULL == extension) {
2808 return HANDLER_GO_ON;
2811 /* check if we have at least one server for this extension up and running */
2812 host = fcgi_extension_host_get(srv, con, p, extension);
2813 if (NULL == host) {
2814 return HANDLER_FINISHED;
2817 /* a note about no handler is not sent yet */
2818 extension->note_is_sent = 0;
2821 * if check-local is disabled, use the uri.path handler
2825 /* init handler-context */
2826 if (uri_path_handler) {
2827 if (host->check_local != 0) {
2828 return HANDLER_GO_ON;
2829 } else {
2830 /* do not split path info for authorizer */
2831 if (fcgi_mode != FCGI_AUTHORIZER) {
2832 /* the prefix is the SCRIPT_NAME,
2833 * everything from start to the next slash
2834 * this is important for check-local = "disable"
2836 * if prefix = /admin.fcgi
2838 * /admin.fcgi/foo/bar
2840 * SCRIPT_NAME = /admin.fcgi
2841 * PATH_INFO = /foo/bar
2843 * if prefix = /fcgi-bin/
2845 * /fcgi-bin/foo/bar
2847 * SCRIPT_NAME = /fcgi-bin/foo
2848 * PATH_INFO = /bar
2850 * if prefix = /, and fix-root-path-name is enable
2852 * /fcgi-bin/foo/bar
2854 * SCRIPT_NAME = /fcgi-bin/foo
2855 * PATH_INFO = /bar
2858 char *pathinfo;
2860 /* the rewrite is only done for /prefix/? matches */
2861 if (host->fix_root_path_name && extension->key->ptr[0] == '/' && extension->key->ptr[1] == '\0') {
2862 buffer_copy_buffer(con->request.pathinfo, con->uri.path);
2863 buffer_string_set_length(con->uri.path, 0);
2864 } else if (extension->key->ptr[0] == '/' &&
2865 buffer_string_length(con->uri.path) > buffer_string_length(extension->key) &&
2866 NULL != (pathinfo = strchr(con->uri.path->ptr + buffer_string_length(extension->key), '/'))) {
2867 /* rewrite uri.path and pathinfo */
2869 buffer_copy_string(con->request.pathinfo, pathinfo);
2870 buffer_string_set_length(con->uri.path, buffer_string_length(con->uri.path) - buffer_string_length(con->request.pathinfo));
2876 if (!hctx) hctx = handler_ctx_init();
2878 hctx->remote_conn = con;
2879 hctx->plugin_data = p;
2880 hctx->proc = NULL;
2881 hctx->ext = extension;
2882 fcgi_host_assign(srv, hctx, host);
2884 hctx->fcgi_mode = fcgi_mode;
2885 if (fcgi_mode == FCGI_AUTHORIZER) {
2886 hctx->ext_auth = hctx->ext;
2889 /*hctx->conf.exts = p->conf.exts;*/
2890 /*hctx->conf.exts_auth = p->conf.exts_auth;*/
2891 /*hctx->conf.exts_resp = p->conf.exts_resp;*/
2892 /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/
2893 hctx->conf.debug = p->conf.debug;
2895 hctx->opts.fdfmt = S_IFSOCK;
2896 hctx->opts.backend = BACKEND_FASTCGI;
2897 hctx->opts.authorizer = (fcgi_mode == FCGI_AUTHORIZER);
2898 hctx->opts.local_redir = 0;
2899 hctx->opts.xsendfile_allow = host->xsendfile_allow;
2900 hctx->opts.xsendfile_docroot = host->xsendfile_docroot;
2901 hctx->opts.parse = fcgi_recv_parse;
2902 hctx->opts.pdata = hctx;
2904 con->plugin_ctx[p->id] = hctx;
2906 con->mode = p->id;
2908 if (con->conf.log_request_handling) {
2909 log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi");
2912 return HANDLER_GO_ON;
2915 /* uri-path handler */
2916 static handler_t fcgi_check_extension_1(server *srv, connection *con, void *p_d) {
2917 return fcgi_check_extension(srv, con, p_d, 1);
2920 /* start request handler */
2921 static handler_t fcgi_check_extension_2(server *srv, connection *con, void *p_d) {
2922 return fcgi_check_extension(srv, con, p_d, 0);
2926 TRIGGER_FUNC(mod_fastcgi_handle_trigger) {
2927 plugin_data *p = p_d;
2928 size_t i, j, n;
2931 /* perhaps we should kill a connect attempt after 10-15 seconds
2933 * currently we wait for the TCP timeout which is 180 seconds on Linux
2939 /* check all children if they are still up */
2941 for (i = 0; i < srv->config_context->used; i++) {
2942 plugin_config *conf;
2943 fcgi_exts *exts;
2945 conf = p->config_storage[i];
2947 exts = conf->exts;
2948 if (NULL == exts) continue;
2950 for (j = 0; j < exts->used; j++) {
2951 fcgi_extension *ex;
2953 ex = exts->exts[j];
2955 for (n = 0; n < ex->used; n++) {
2957 fcgi_proc *proc;
2958 fcgi_extension_host *host;
2960 host = ex->hosts[n];
2962 for (proc = host->first; proc; proc = proc->next) {
2963 fcgi_proc_waitpid(srv, host, proc);
2966 fcgi_restart_dead_procs(srv, p, host);
2968 for (proc = host->unused_procs; proc; proc = proc->next) {
2969 fcgi_proc_waitpid(srv, host, proc);
2975 return HANDLER_GO_ON;
2979 int mod_fastcgi_plugin_init(plugin *p);
2980 int mod_fastcgi_plugin_init(plugin *p) {
2981 p->version = LIGHTTPD_VERSION_ID;
2982 p->name = buffer_init_string("fastcgi");
2984 p->init = mod_fastcgi_init;
2985 p->cleanup = mod_fastcgi_free;
2986 p->set_defaults = mod_fastcgi_set_defaults;
2987 p->connection_reset = fcgi_connection_reset;
2988 p->handle_uri_clean = fcgi_check_extension_1;
2989 p->handle_subrequest_start = fcgi_check_extension_2;
2990 p->handle_subrequest = mod_fastcgi_handle_subrequest;
2991 p->handle_trigger = mod_fastcgi_handle_trigger;
2993 p->data = NULL;
2995 return 0;