16
#include "http_chunk.h"
18
#include "connections.h"
24
#include "inet_ntop_cache.h"
25
#include "stat_cache.h"
30
#ifdef HAVE_SYS_FILIO_H
31
# include <sys/filio.h>
34
#include "sys-socket.h"
38
# define UNIX_PATH_MAX 108
44
#ifdef HAVE_SYS_WAIT_H
53
* - add timeout for a connect to a non-fastcgi process
54
* (use state_timestamp + state)
58
typedef struct fcgi_proc {
59
size_t id; /* id will be between 1 and max_procs */
60
buffer *socket; /* config.socket + "-" + id */
61
unsigned port; /* config.port + pno */
63
pid_t pid; /* PID of the spawned process (0 if not spawned locally) */
66
size_t load; /* number of requests waiting on this process */
68
time_t last_used; /* see idle_timeout */
69
size_t requests; /* see max_requests */
70
struct fcgi_proc *prev, *next; /* see first */
72
time_t disable_ts; /* replace by host->something */
76
enum { PROC_STATE_UNSET, /* init-phase */
77
PROC_STATE_RUNNING, /* alive */
78
PROC_STATE_DIED_WAIT_FOR_PID,
79
PROC_STATE_KILLED, /* was killed as we don't have the load anymore */
80
PROC_STATE_DIED, /* marked as dead, should be restarted */
81
PROC_STATE_DISABLED /* proc disabled as it resulted in an error */
86
/* list of processes handling this extension
87
* sorted by lowest load
89
* whenever a job is done move it up in the list
90
* until it is sorted, move it down as soon as the
94
fcgi_proc *unused_procs;
97
* spawn at least min_procs, at max_procs.
99
* as soon as the load of the first entry
100
* is max_load_per_proc we spawn a new one
101
* and add it to the first entry and give it
106
unsigned short min_procs;
107
unsigned short max_procs;
108
size_t num_procs; /* how many procs are started */
109
size_t active_procs; /* how many of them are really running */
111
unsigned short max_load_per_proc;
114
* kick the process from the list if it was not
115
* used for idle_timeout until min_procs is
116
* reached. this helps to get the processlist
117
* small again we had a small peak load.
121
unsigned short idle_timeout;
124
* time after a disabled remote connection is tried to be re-enabled
129
unsigned short disable_time;
132
* same fastcgi processes get a little bit larger
133
* than wanted. max_requests_per_proc kills a
134
* process after a number of handled requests.
137
size_t max_requests_per_proc;
145
* if host is one of the local IP adresses the
146
* whole connection is local
148
* if tcp/ip should be used host AND port have
158
* instead of TCP/IP we can use Unix Domain Sockets
159
* - more secure (you have fileperms to play with)
160
* - more control (on locally)
161
* - more speed (no extra overhead)
165
/* if socket is local we can start the fastcgi
168
* bin-path is the path to the binary
170
* check min_procs and max_procs for the number
171
* of process to start-up
175
/* bin-path is set bin-environment is taken to
176
* create the environement before starting the
185
* docroot-translation between URL->phys and the
189
* - different dir-layout if remote
204
* check_local tell you if the phys file is stat()ed
205
* or not. FastCGI doesn't care if the service is
206
* remote. If the web-server side doesn't contain
207
* the fastcgi-files we should not stat() for them
208
* and say '404 not found'.
210
unsigned short check_local;
213
* append PATH_INFO to SCRIPT_FILENAME
215
* php needs this if cgi.fix_pathinfo is provied
219
unsigned short break_scriptfilename_for_php;
222
* If the backend includes X-LIGHTTPD-send-file in the response
223
* we use the value as filename and ignore the content.
226
unsigned short allow_xsendfile;
228
ssize_t load; /* replace by host->load */
230
size_t max_id; /* corresponds most of the time to
233
only if a process is killed max_id waits for the process itself
234
to die and decrements its afterwards */
236
buffer *strip_request_uri;
237
} fcgi_extension_host;
240
* one extension can have multiple hosts assigned
241
* one host can spawn additional processes on the same
242
* socket (if we control it)
244
* ext -> host -> procs
247
* if the fastcgi process is remote that whole goes down
250
* ext -> host -> procs
253
* in case of PHP and FCGI_CHILDREN we have again a procs
254
* but we don't control it directly.
259
buffer *key; /* like .php */
261
fcgi_extension_host **hosts;
268
fcgi_extension **exts;
294
/* generic plugin data, shared between all connections */
297
buffer_uint fcgi_request_id;
302
buffer *parse_response;
304
plugin_config **config_storage;
306
plugin_config conf; /* this is only used as long as no handler_ctx is setup */
309
/* connection specific data */
310
typedef enum { FCGI_STATE_INIT, FCGI_STATE_CONNECT, FCGI_STATE_PREPARE_WRITE,
311
FCGI_STATE_WRITE, FCGI_STATE_READ
312
} fcgi_connection_state_t;
316
fcgi_extension_host *host;
318
fcgi_connection_state_t state;
319
time_t state_timestamp;
321
int reconnects; /* number of reconnect attempts */
323
chunkqueue *rb; /* read queue */
324
chunkqueue *wb; /* write queue */
326
buffer *response_header;
328
int delayed; /* flag to mark that the connect() is delayed */
331
int fd; /* fd to the fastcgi process */
332
int fde_ndx; /* index into the fd-event buffer */
337
int send_content_body;
341
connection *remote_conn; /* dumb pointer */
342
plugin_data *plugin_data; /* dumb pointer */
346
/* ok, we need a prototype */
347
static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents);
349
int fcgi_proclist_sort_down(server *srv, fcgi_extension_host *host, fcgi_proc *proc);
353
static handler_ctx * handler_ctx_init() {
356
hctx = calloc(1, sizeof(*hctx));
361
hctx->response_header = buffer_init();
363
hctx->request_id = 0;
364
hctx->state = FCGI_STATE_INIT;
371
hctx->reconnects = 0;
372
hctx->send_content_body = 1;
374
hctx->rb = chunkqueue_init();
375
hctx->wb = chunkqueue_init();
380
static void handler_ctx_free(handler_ctx *hctx) {
381
buffer_free(hctx->response_header);
383
chunkqueue_free(hctx->rb);
384
chunkqueue_free(hctx->wb);
389
fcgi_proc *fastcgi_process_init() {
392
f = calloc(1, sizeof(*f));
393
f->socket = buffer_init();
401
void fastcgi_process_free(fcgi_proc *f) {
404
fastcgi_process_free(f->next);
406
buffer_free(f->socket);
411
fcgi_extension_host *fastcgi_host_init() {
412
fcgi_extension_host *f;
414
f = calloc(1, sizeof(*f));
416
f->host = buffer_init();
417
f->unixsocket = buffer_init();
418
f->docroot = buffer_init();
419
f->bin_path = buffer_init();
420
f->bin_env = array_init();
421
f->bin_env_copy = array_init();
422
f->strip_request_uri = buffer_init();
427
void fastcgi_host_free(fcgi_extension_host *h) {
430
buffer_free(h->host);
431
buffer_free(h->unixsocket);
432
buffer_free(h->docroot);
433
buffer_free(h->bin_path);
434
buffer_free(h->strip_request_uri);
435
array_free(h->bin_env);
436
array_free(h->bin_env_copy);
438
fastcgi_process_free(h->first);
439
fastcgi_process_free(h->unused_procs);
445
fcgi_exts *fastcgi_extensions_init() {
448
f = calloc(1, sizeof(*f));
453
void fastcgi_extensions_free(fcgi_exts *f) {
458
for (i = 0; i < f->used; i++) {
464
for (j = 0; j < fe->used; j++) {
465
fcgi_extension_host *h;
469
fastcgi_host_free(h);
472
buffer_free(fe->key);
483
int fastcgi_extension_insert(fcgi_exts *ext, buffer *key, fcgi_extension_host *fh) {
487
/* there is something */
489
for (i = 0; i < ext->used; i++) {
490
if (buffer_is_equal(key, ext->exts[i]->key)) {
495
if (i == ext->used) {
496
/* filextension is new */
497
fe = calloc(1, sizeof(*fe));
499
fe->key = buffer_init();
500
buffer_copy_string_buffer(fe->key, key);
504
if (ext->size == 0) {
506
ext->exts = malloc(ext->size * sizeof(*(ext->exts)));
508
} else if (ext->used == ext->size) {
510
ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
513
ext->exts[ext->used++] = fe;
520
fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
522
} else if (fe->size == fe->used) {
524
fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
528
fe->hosts[fe->used++] = fh;
534
INIT_FUNC(mod_fastcgi_init) {
537
p = calloc(1, sizeof(*p));
539
p->fcgi_env = buffer_init();
541
p->path = buffer_init();
542
p->parse_response = buffer_init();
548
FREE_FUNC(mod_fastcgi_free) {
549
plugin_data *p = p_d;
550
buffer_uint *r = &(p->fcgi_request_id);
554
if (r->ptr) free(r->ptr);
556
buffer_free(p->fcgi_env);
557
buffer_free(p->path);
558
buffer_free(p->parse_response);
560
if (p->config_storage) {
562
for (i = 0; i < srv->config_context->used; i++) {
563
plugin_config *s = p->config_storage[i];
570
for (j = 0; j < exts->used; j++) {
575
for (n = 0; n < ex->used; n++) {
577
fcgi_extension_host *host;
581
for (proc = host->first; proc; proc = proc->next) {
582
if (proc->pid != 0) kill(proc->pid, SIGTERM);
584
if (proc->is_local &&
585
!buffer_is_empty(proc->socket)) {
586
unlink(proc->socket->ptr);
590
for (proc = host->unused_procs; proc; proc = proc->next) {
591
if (proc->pid != 0) kill(proc->pid, SIGTERM);
593
if (proc->is_local &&
594
!buffer_is_empty(proc->socket)) {
595
unlink(proc->socket->ptr);
601
fastcgi_extensions_free(s->exts);
605
free(p->config_storage);
610
return HANDLER_GO_ON;
613
static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
616
if (!key || !val) return -1;
618
dst = malloc(key_len + val_len + 3);
619
memcpy(dst, key, key_len);
621
/* add the \0 from the value */
622
memcpy(dst + key_len + 1, val, val_len + 1);
624
if (env->size == 0) {
626
env->ptr = malloc(env->size * sizeof(*env->ptr));
627
} else if (env->size == env->used + 1) {
629
env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
632
env->ptr[env->used++] = dst;
637
static int parse_binpath(char_array *env, buffer *b) {
640
/* search for spaces */
643
for (i = 0; i < b->used - 1; i++) {
647
/* a WS, stop here and copy the argument */
649
if (env->size == 0) {
651
env->ptr = malloc(env->size * sizeof(*env->ptr));
652
} else if (env->size == env->used) {
654
env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
659
env->ptr[env->used++] = start;
661
start = b->ptr + i + 1;
668
if (env->size == 0) {
670
env->ptr = malloc(env->size * sizeof(*env->ptr));
671
} else if (env->size == env->used) { /* we need one extra for the terminating NULL */
673
env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
677
env->ptr[env->used++] = start;
679
if (env->size == 0) {
681
env->ptr = malloc(env->size * sizeof(*env->ptr));
682
} else if (env->size == env->used) { /* we need one extra for the terminating NULL */
684
env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
688
env->ptr[env->used++] = NULL;
693
static int fcgi_spawn_connection(server *srv,
695
fcgi_extension_host *host,
698
int socket_type, status;
699
struct timeval tv = { 0, 100 * 1000 };
701
struct sockaddr_un fcgi_addr_un;
703
struct sockaddr_in fcgi_addr_in;
704
struct sockaddr *fcgi_addr;
713
log_error_write(srv, __FILE__, __LINE__, "sdb",
714
"new proc, socket:", proc->port, proc->socket);
717
if (!buffer_is_empty(proc->socket)) {
718
memset(&fcgi_addr, 0, sizeof(fcgi_addr));
721
fcgi_addr_un.sun_family = AF_UNIX;
722
strcpy(fcgi_addr_un.sun_path, proc->socket->ptr);
725
servlen = SUN_LEN(&fcgi_addr_un);
728
servlen = proc->socket->used - 1 + sizeof(fcgi_addr_un.sun_family);
730
socket_type = AF_UNIX;
731
fcgi_addr = (struct sockaddr *) &fcgi_addr_un;
733
log_error_write(srv, __FILE__, __LINE__, "s",
734
"ERROR: Unix Domain sockets are not supported.");
738
fcgi_addr_in.sin_family = AF_INET;
740
if (buffer_is_empty(host->host)) {
741
fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
745
/* set a usefull default */
746
fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
749
if (NULL == (he = gethostbyname(host->host->ptr))) {
750
log_error_write(srv, __FILE__, __LINE__,
751
"sdb", "gethostbyname failed: ",
752
h_errno, host->host);
756
if (he->h_addrtype != AF_INET) {
757
log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype);
761
if (he->h_length != sizeof(struct in_addr)) {
762
log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length);
766
memcpy(&(fcgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length);
769
fcgi_addr_in.sin_port = htons(proc->port);
770
servlen = sizeof(fcgi_addr_in);
772
socket_type = AF_INET;
773
fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
776
if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
777
log_error_write(srv, __FILE__, __LINE__, "ss",
778
"failed:", strerror(errno));
782
if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
783
/* server is not up, spawn in */
787
if (!buffer_is_empty(proc->socket)) {
788
unlink(proc->socket->ptr);
794
if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
795
log_error_write(srv, __FILE__, __LINE__, "ss",
796
"socket failed:", strerror(errno));
801
if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
802
log_error_write(srv, __FILE__, __LINE__, "ss",
803
"socketsockopt failed:", strerror(errno));
808
if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) {
809
log_error_write(srv, __FILE__, __LINE__, "sbds",
817
if (-1 == listen(fcgi_fd, 1024)) {
818
log_error_write(srv, __FILE__, __LINE__, "ss",
819
"listen failed:", strerror(errno));
824
switch ((child = fork())) {
830
/* create environment */
839
if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
840
close(FCGI_LISTENSOCK_FILENO);
841
dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
845
/* we don't need the client socket */
846
for (i = 3; i < 256; i++) {
850
/* build clean environment */
851
if (host->bin_env_copy->used) {
852
for (i = 0; i < host->bin_env_copy->used; i++) {
853
data_string *ds = (data_string *)host->bin_env_copy->data[i];
856
if (NULL != (ge = getenv(ds->value->ptr))) {
857
env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
861
for (i = 0; environ[i]; i++) {
864
if (NULL != (eq = strchr(environ[i], '='))) {
865
env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1));
870
/* create environment */
871
for (i = 0; i < host->bin_env->used; i++) {
872
data_string *ds = (data_string *)host->bin_env->data[i];
874
env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
877
for (i = 0; i < env.used; i++) {
878
/* search for PHP_FCGI_CHILDREN */
879
if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
882
/* not found, add a default */
884
env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
887
env.ptr[env.used] = NULL;
889
parse_binpath(&arg, host->bin_path);
892
execve(arg.ptr[0], arg.ptr, env.ptr);
894
log_error_write(srv, __FILE__, __LINE__, "sbs",
895
"execve failed for:", host->bin_path, strerror(errno));
908
select(0, NULL, NULL, NULL, &tv);
910
switch (waitpid(child, &status, WNOHANG)) {
912
/* child still running after timeout, good */
915
/* no PID found ? should never happen */
916
log_error_write(srv, __FILE__, __LINE__, "ss",
917
"pid not found:", strerror(errno));
920
log_error_write(srv, __FILE__, __LINE__, "sbs",
921
"the fastcgi-backend", host->bin_path, "failed to start:");
922
/* the child should not terminate at all */
923
if (WIFEXITED(status)) {
924
log_error_write(srv, __FILE__, __LINE__, "sdb",
925
"child exited with status",
926
WEXITSTATUS(status), host->bin_path);
927
log_error_write(srv, __FILE__, __LINE__, "s",
928
"if you try do run PHP as FastCGI backend make sure you use the FastCGI enabled version.\n"
929
"You can find out if it is the right one by executing 'php -v' and it should display '(cgi-fcgi)' "
930
"in the output, NOT (cgi) NOR (cli)\n"
931
"For more information check http://www.lighttpd.net/documentation/fastcgi.html#preparing-php-as-a-fastcgi-program");
932
log_error_write(srv, __FILE__, __LINE__, "s",
933
"If this is PHP on Gentoo add fastcgi to the USE flags");
934
} else if (WIFSIGNALED(status)) {
935
log_error_write(srv, __FILE__, __LINE__, "sd",
936
"terminated by signal:",
939
if (WTERMSIG(status) == 11) {
940
log_error_write(srv, __FILE__, __LINE__, "ss",
941
"to be exact: it seg-fault, crashed, died, ... you get the idea." );
942
log_error_write(srv, __FILE__, __LINE__, "s",
943
"If this is PHP try to remove the byte-code caches for now and try again.");
946
log_error_write(srv, __FILE__, __LINE__, "sd",
947
"child died somehow:",
953
/* register process */
955
proc->last_used = srv->cur_ts;
966
log_error_write(srv, __FILE__, __LINE__, "sb",
967
"(debug) socket is already used, won't spawn:",
972
proc->state = PROC_STATE_RUNNING;
973
host->active_procs++;
981
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
982
plugin_data *p = p_d;
985
buffer *fcgi_mode = buffer_init();
987
config_values_t cv[] = {
988
{ "fastcgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
989
{ "fastcgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
990
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
993
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
995
for (i = 0; i < srv->config_context->used; i++) {
999
s = malloc(sizeof(plugin_config));
1000
s->exts = fastcgi_extensions_init();
1003
cv[0].destination = s->exts;
1004
cv[1].destination = &(s->debug);
1006
p->config_storage[i] = s;
1007
ca = ((data_config *)srv->config_context->data[i])->value;
1009
if (0 != config_insert_values_global(srv, ca, cv)) {
1010
return HANDLER_ERROR;
1017
if (NULL != (du = array_get_element(ca, "fastcgi.server"))) {
1019
data_array *da = (data_array *)du;
1021
if (du->type != TYPE_ARRAY) {
1022
log_error_write(srv, __FILE__, __LINE__, "sss",
1023
"unexpected type for key: ", "fastcgi.server", "array of strings");
1025
return HANDLER_ERROR;
1030
* fastcgi.server = ( "<ext>" => ( ... ),
1031
* "<ext>" => ( ... ) )
1034
for (j = 0; j < da->value->used; j++) {
1036
data_array *da_ext = (data_array *)da->value->data[j];
1038
if (da->value->data[j]->type != TYPE_ARRAY) {
1039
log_error_write(srv, __FILE__, __LINE__, "sssbs",
1040
"unexpected type for key: ", "fastcgi.server",
1041
"[", da->value->data[j]->key, "](string)");
1043
return HANDLER_ERROR;
1047
* da_ext->key == name of the extension
1051
* fastcgi.server = ( "<ext>" =>
1052
* ( "<host>" => ( ... ),
1053
* "<host>" => ( ... )
1058
for (n = 0; n < da_ext->value->used; n++) {
1059
data_array *da_host = (data_array *)da_ext->value->data[n];
1061
fcgi_extension_host *df;
1063
config_values_t fcv[] = {
1064
{ "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1065
{ "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1066
{ "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1067
{ "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
1068
{ "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
1070
{ "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
1071
{ "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
1072
{ "min-procs-not-working", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 this is broken for now */
1073
{ "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
1074
{ "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
1075
{ "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
1076
{ "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
1078
{ "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
1079
{ "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
1081
{ "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
1082
{ "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
1083
{ "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
1085
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1088
if (da_host->type != TYPE_ARRAY) {
1089
log_error_write(srv, __FILE__, __LINE__, "ssSBS",
1090
"unexpected type for key:",
1092
"[", da_host->key, "](string)");
1094
return HANDLER_ERROR;
1097
df = fastcgi_host_init();
1099
df->check_local = 1;
1102
df->max_load_per_proc = 1;
1103
df->idle_timeout = 60;
1104
df->mode = FCGI_RESPONDER;
1105
df->disable_time = 60;
1106
df->break_scriptfilename_for_php = 0;
1107
df->allow_xsendfile = 0; /* handle X-LIGHTTPD-send-file */
1109
fcv[0].destination = df->host;
1110
fcv[1].destination = df->docroot;
1111
fcv[2].destination = fcgi_mode;
1112
fcv[3].destination = df->unixsocket;
1113
fcv[4].destination = df->bin_path;
1115
fcv[5].destination = &(df->check_local);
1116
fcv[6].destination = &(df->port);
1117
fcv[7].destination = &(df->min_procs);
1118
fcv[8].destination = &(df->max_procs);
1119
fcv[9].destination = &(df->max_load_per_proc);
1120
fcv[10].destination = &(df->idle_timeout);
1121
fcv[11].destination = &(df->disable_time);
1123
fcv[12].destination = df->bin_env;
1124
fcv[13].destination = df->bin_env_copy;
1125
fcv[14].destination = &(df->break_scriptfilename_for_php);
1126
fcv[15].destination = &(df->allow_xsendfile);
1127
fcv[16].destination = df->strip_request_uri;
1129
if (0 != config_insert_values_internal(srv, da_host->value, fcv)) {
1130
return HANDLER_ERROR;
1133
if ((!buffer_is_empty(df->host) || df->port) &&
1134
!buffer_is_empty(df->unixsocket)) {
1135
log_error_write(srv, __FILE__, __LINE__, "s",
1136
"either host+port or socket");
1138
return HANDLER_ERROR;
1141
if (!buffer_is_empty(df->unixsocket)) {
1142
/* unix domain socket */
1144
if (df->unixsocket->used > UNIX_PATH_MAX - 2) {
1145
log_error_write(srv, __FILE__, __LINE__, "s",
1146
"path of the unixdomain socket is too large");
1147
return HANDLER_ERROR;
1152
if (buffer_is_empty(df->host) &&
1153
buffer_is_empty(df->bin_path)) {
1154
log_error_write(srv, __FILE__, __LINE__, "sbbbs",
1155
"missing key (string):",
1161
return HANDLER_ERROR;
1162
} else if (df->port == 0) {
1163
log_error_write(srv, __FILE__, __LINE__, "sbbbs",
1164
"missing key (short):",
1169
return HANDLER_ERROR;
1173
if (!buffer_is_empty(df->bin_path)) {
1174
/* a local socket + self spawning */
1177
/* HACK: just to make sure the adaptive spawing is disabled */
1178
df->min_procs = df->max_procs;
1180
if (df->min_procs > df->max_procs) df->max_procs = df->min_procs;
1181
if (df->max_load_per_proc < 1) df->max_load_per_proc = 0;
1184
log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",
1185
"--- fastcgi spawning local",
1186
"\n\tproc:", df->bin_path,
1187
"\n\tport:", df->port,
1188
"\n\tsocket", df->unixsocket,
1189
"\n\tmin-procs:", df->min_procs,
1190
"\n\tmax-procs:", df->max_procs);
1193
for (pno = 0; pno < df->min_procs; pno++) {
1196
proc = fastcgi_process_init();
1197
proc->id = df->num_procs++;
1200
if (buffer_is_empty(df->unixsocket)) {
1201
proc->port = df->port + pno;
1203
buffer_copy_string_buffer(proc->socket, df->unixsocket);
1204
buffer_append_string(proc->socket, "-");
1205
buffer_append_long(proc->socket, pno);
1209
log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1210
"--- fastcgi spawning",
1211
"\n\tport:", df->port,
1212
"\n\tsocket", df->unixsocket,
1213
"\n\tcurrent:", pno, "/", df->min_procs);
1216
if (fcgi_spawn_connection(srv, p, df, proc)) {
1217
log_error_write(srv, __FILE__, __LINE__, "s",
1218
"[ERROR]: spawning fcgi failed.");
1219
return HANDLER_ERROR;
1222
proc->next = df->first;
1223
if (df->first) df->first->prev = proc;
1230
fp = fastcgi_process_init();
1231
fp->id = df->num_procs++;
1234
fp->state = PROC_STATE_RUNNING;
1236
if (buffer_is_empty(df->unixsocket)) {
1237
fp->port = df->port;
1239
buffer_copy_string_buffer(fp->socket, df->unixsocket);
1248
if (!buffer_is_empty(fcgi_mode)) {
1249
if (strcmp(fcgi_mode->ptr, "responder") == 0) {
1250
df->mode = FCGI_RESPONDER;
1251
} else if (strcmp(fcgi_mode->ptr, "authorizer") == 0) {
1252
df->mode = FCGI_AUTHORIZER;
1253
if (buffer_is_empty(df->docroot)) {
1254
log_error_write(srv, __FILE__, __LINE__, "s",
1255
"ERROR: docroot is required for authorizer mode.");
1256
return HANDLER_ERROR;
1259
log_error_write(srv, __FILE__, __LINE__, "sbs",
1260
"WARNING: unknown fastcgi mode:",
1261
fcgi_mode, "(ignored, mode set to responder)");
1265
/* if extension already exists, take it */
1266
fastcgi_extension_insert(s->exts, da_ext->key, df);
1272
buffer_free(fcgi_mode);
1274
return HANDLER_GO_ON;
1277
static int fcgi_set_state(server *srv, handler_ctx *hctx, fcgi_connection_state_t state) {
1278
hctx->state = state;
1279
hctx->state_timestamp = srv->cur_ts;
1285
static size_t fcgi_requestid_new(server *srv, plugin_data *p) {
1288
buffer_uint *r = &(p->fcgi_request_id);
1292
for (i = 0; i < r->used; i++) {
1293
if (r->ptr[i] > m) m = r->ptr[i];
1298
r->ptr = malloc(sizeof(*r->ptr) * r->size);
1299
} else if (r->used == r->size) {
1301
r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
1304
r->ptr[r->used++] = ++m;
1309
static int fcgi_requestid_del(server *srv, plugin_data *p, size_t request_id) {
1311
buffer_uint *r = &(p->fcgi_request_id);
1315
for (i = 0; i < r->used; i++) {
1316
if (r->ptr[i] == request_id) break;
1322
if (i != r->used - 1) {
1323
r->ptr[i] = r->ptr[r->used - 1];
1331
void fcgi_connection_close(server *srv, handler_ctx *hctx) {
1335
if (NULL == hctx) return;
1337
p = hctx->plugin_data;
1338
con = hctx->remote_conn;
1340
if (con->mode != p->id) {
1345
if (hctx->fd != -1) {
1346
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1347
fdevent_unregister(srv->ev, hctx->fd);
1352
if (hctx->request_id != 0) {
1353
fcgi_requestid_del(srv, p, hctx->request_id);
1356
if (hctx->host && hctx->proc) {
1359
if (hctx->got_proc) {
1360
/* after the connect the process gets a load */
1363
if (p->conf.debug) {
1364
log_error_write(srv, __FILE__, __LINE__, "sddb",
1367
hctx->proc->pid, hctx->proc->socket);
1371
fcgi_proclist_sort_down(srv, hctx->host, hctx->proc);
1375
handler_ctx_free(hctx);
1376
con->plugin_ctx[p->id] = NULL;
1379
static int fcgi_reconnect(server *srv, handler_ctx *hctx) {
1380
plugin_data *p = hctx->plugin_data;
1386
* connect was ok, connection was accepted
1387
* but the php accept loop checks after the accept if it should die or not.
1389
* if yes we can only detect it at a write()
1391
* next step is resetting this attemp and setup a connection again
1393
* if we have more then 5 reconnects for the same request, die
1397
* we have a connection but the child died by some other reason
1401
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1402
fdevent_unregister(srv->ev, hctx->fd);
1406
fcgi_requestid_del(srv, p, hctx->request_id);
1408
fcgi_set_state(srv, hctx, FCGI_STATE_INIT);
1410
hctx->request_id = 0;
1413
if (p->conf.debug) {
1414
log_error_write(srv, __FILE__, __LINE__, "sddb",
1417
hctx->proc->pid, hctx->proc->socket);
1421
fcgi_proclist_sort_down(srv, hctx->host, hctx->proc);
1427
static handler_t fcgi_connection_reset(server *srv, connection *con, void *p_d) {
1428
plugin_data *p = p_d;
1430
fcgi_connection_close(srv, con->plugin_ctx[p->id]);
1432
return HANDLER_GO_ON;
1436
static int fcgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) {
1439
if (!key || !val) return -1;
1441
len = key_len + val_len;
1443
len += key_len > 127 ? 4 : 1;
1444
len += val_len > 127 ? 4 : 1;
1446
buffer_prepare_append(env, len);
1448
if (key_len > 127) {
1449
env->ptr[env->used++] = ((key_len >> 24) & 0xff) | 0x80;
1450
env->ptr[env->used++] = (key_len >> 16) & 0xff;
1451
env->ptr[env->used++] = (key_len >> 8) & 0xff;
1452
env->ptr[env->used++] = (key_len >> 0) & 0xff;
1454
env->ptr[env->used++] = (key_len >> 0) & 0xff;
1457
if (val_len > 127) {
1458
env->ptr[env->used++] = ((val_len >> 24) & 0xff) | 0x80;
1459
env->ptr[env->used++] = (val_len >> 16) & 0xff;
1460
env->ptr[env->used++] = (val_len >> 8) & 0xff;
1461
env->ptr[env->used++] = (val_len >> 0) & 0xff;
1463
env->ptr[env->used++] = (val_len >> 0) & 0xff;
1466
memcpy(env->ptr + env->used, key, key_len);
1467
env->used += key_len;
1468
memcpy(env->ptr + env->used, val, val_len);
1469
env->used += val_len;
1474
static int fcgi_header(FCGI_Header * header, unsigned char type, size_t request_id, int contentLength, unsigned char paddingLength) {
1475
header->version = FCGI_VERSION_1;
1476
header->type = type;
1477
header->requestIdB0 = request_id & 0xff;
1478
header->requestIdB1 = (request_id >> 8) & 0xff;
1479
header->contentLengthB0 = contentLength & 0xff;
1480
header->contentLengthB1 = (contentLength >> 8) & 0xff;
1481
header->paddingLength = paddingLength;
1482
header->reserved = 0;
1491
* 1 not connected yet
1494
static int fcgi_establish_connection(server *srv, handler_ctx *hctx) {
1495
struct sockaddr *fcgi_addr;
1496
struct sockaddr_in fcgi_addr_in;
1497
#ifdef HAVE_SYS_UN_H
1498
struct sockaddr_un fcgi_addr_un;
1502
fcgi_extension_host *host = hctx->host;
1503
fcgi_proc *proc = hctx->proc;
1504
int fcgi_fd = hctx->fd;
1506
memset(&fcgi_addr, 0, sizeof(fcgi_addr));
1508
if (!buffer_is_empty(proc->socket)) {
1509
#ifdef HAVE_SYS_UN_H
1510
/* use the unix domain socket */
1511
fcgi_addr_un.sun_family = AF_UNIX;
1512
strcpy(fcgi_addr_un.sun_path, proc->socket->ptr);
1514
servlen = SUN_LEN(&fcgi_addr_un);
1517
servlen = proc->socket->used - 1 + sizeof(fcgi_addr_un.sun_family);
1519
fcgi_addr = (struct sockaddr *) &fcgi_addr_un;
1524
fcgi_addr_in.sin_family = AF_INET;
1525
if (0 == inet_aton(host->host->ptr, &(fcgi_addr_in.sin_addr))) {
1526
log_error_write(srv, __FILE__, __LINE__, "sbs",
1527
"converting IP-adress failed for", host->host,
1528
"\nBe sure to specify an IP address here");
1532
fcgi_addr_in.sin_port = htons(proc->port);
1533
servlen = sizeof(fcgi_addr_in);
1535
fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
1538
if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
1539
if (errno == EINPROGRESS ||
1540
errno == EALREADY ||
1542
if (hctx->conf.debug) {
1543
log_error_write(srv, __FILE__, __LINE__, "sd",
1544
"connect delayed, will continue later:", fcgi_fd);
1548
} else if (errno == EAGAIN) {
1550
if(hctx->delayed == 0) {
1551
log_error_write(srv, __FILE__, __LINE__, "sdsdsdb",
1552
"need reconnect, will continue later:", fcgi_fd,
1553
"reconnects:", hctx->reconnects,
1554
"load:", host->load,
1561
log_error_write(srv, __FILE__, __LINE__, "sdsddb",
1562
"connect failed:", fcgi_fd,
1563
strerror(errno), errno,
1564
proc->port, proc->socket);
1567
if (errno == EAGAIN) {
1568
log_error_write(srv, __FILE__, __LINE__, "sd",
1569
"This means that the you have more incoming requests than your fastcgi-backend can handle in parallel. "
1570
"Perhaps it helps to spawn more fastcgi backend or php-children, if not decrease server.max-connections."
1571
"The load for this fastcgi backend is:", proc->load);
1579
if(hctx->delayed == 1) {
1580
log_error_write(srv, __FILE__, __LINE__, "sdsdsdb",
1581
"reconnected:", fcgi_fd,
1582
"reconnects:", hctx->reconnects,
1583
"load:", host->load,
1587
hctx->reconnects = 0;
1588
if (hctx->conf.debug > 1) {
1589
log_error_write(srv, __FILE__, __LINE__, "sd",
1590
"connect succeeded: ", fcgi_fd);
1596
static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_data *p) {
1599
for (i = 0; i < con->request.headers->used; i++) {
1602
ds = (data_string *)con->request.headers->data[i];
1604
if (ds->value->used && ds->key->used) {
1606
buffer_reset(srv->tmp_buf);
1608
if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
1609
BUFFER_COPY_STRING_CONST(srv->tmp_buf, "HTTP_");
1610
srv->tmp_buf->used--;
1613
buffer_prepare_append(srv->tmp_buf, ds->key->used + 2);
1614
for (j = 0; j < ds->key->used - 1; j++) {
1616
if (light_isalpha(ds->key->ptr[j])) {
1618
c = ds->key->ptr[j] & ~32;
1619
} else if (light_isdigit(ds->key->ptr[j])) {
1621
c = ds->key->ptr[j];
1623
srv->tmp_buf->ptr[srv->tmp_buf->used++] = c;
1625
srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0';
1627
fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value));
1631
for (i = 0; i < con->environment->used; i++) {
1634
ds = (data_string *)con->environment->data[i];
1636
if (ds->value->used && ds->key->used) {
1638
buffer_reset(srv->tmp_buf);
1640
buffer_prepare_append(srv->tmp_buf, ds->key->used + 2);
1641
for (j = 0; j < ds->key->used - 1; j++) {
1642
srv->tmp_buf->ptr[srv->tmp_buf->used++] =
1643
isalpha((unsigned char)ds->key->ptr[j]) ?
1644
toupper((unsigned char)ds->key->ptr[j]) : '_';
1646
srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0';
1648
fcgi_env_add(p->fcgi_env, CONST_BUF_LEN(srv->tmp_buf), CONST_BUF_LEN(ds->value));
1656
static int fcgi_create_env(server *srv, handler_ctx *hctx, size_t request_id) {
1657
FCGI_BeginRequestRecord beginRecord;
1664
char b2[INET6_ADDRSTRLEN + 1];
1667
plugin_data *p = hctx->plugin_data;
1668
fcgi_extension_host *host= hctx->host;
1670
connection *con = hctx->remote_conn;
1671
server_socket *srv_sock = con->srv_socket;
1674
socklen_t our_addr_len;
1676
/* send FCGI_BEGIN_REQUEST */
1678
fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0);
1679
beginRecord.body.roleB0 = host->mode;
1680
beginRecord.body.roleB1 = 0;
1681
beginRecord.body.flags = 0;
1682
memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved));
1684
b = chunkqueue_get_append_buffer(hctx->wb);
1686
buffer_copy_memory(b, (const char *)&beginRecord, sizeof(beginRecord));
1688
/* send FCGI_PARAMS */
1689
buffer_prepare_copy(p->fcgi_env, 1024);
1692
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION));
1694
if (con->server_name->used) {
1695
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), CONST_BUF_LEN(con->server_name));
1698
s = inet_ntop(srv_sock->addr.plain.sa_family,
1699
srv_sock->addr.plain.sa_family == AF_INET6 ?
1700
(const void *) &(srv_sock->addr.ipv6.sin6_addr) :
1701
(const void *) &(srv_sock->addr.ipv4.sin_addr),
1704
s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
1706
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
1709
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
1713
ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
1715
ntohs(srv_sock->addr.ipv4.sin_port)
1719
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
1721
/* get the server-side of the connection to the client */
1722
our_addr_len = sizeof(our_addr);
1724
if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) {
1725
s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr));
1727
s = inet_ntop_cache_get_ip(srv, &(our_addr));
1729
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
1733
ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
1735
ntohs(con->dst_addr.ipv4.sin_port)
1739
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
1741
s = inet_ntop_cache_get_ip(srv, &(con->dst_addr));
1742
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
1744
if (!buffer_is_empty(con->authed_user)) {
1745
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_USER"),
1746
CONST_BUF_LEN(con->authed_user));
1749
if (con->request.content_length > 0 && host->mode != FCGI_AUTHORIZER) {
1750
/* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */
1752
/* request.content_length < SSIZE_MAX, see request.c */
1753
ltostr(buf, con->request.content_length);
1754
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
1757
if (host->mode != FCGI_AUTHORIZER) {
1759
* SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to
1760
* http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html
1761
* (6.1.14, 6.1.6, 6.1.7)
1762
* For AUTHORIZER mode these headers should be omitted.
1765
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
1767
if (!buffer_is_empty(con->request.pathinfo)) {
1768
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
1770
/* PATH_TRANSLATED is only defined if PATH_INFO is set */
1772
if (!buffer_is_empty(host->docroot)) {
1773
buffer_copy_string_buffer(p->path, host->docroot);
1775
buffer_copy_string_buffer(p->path, con->physical.doc_root);
1777
buffer_append_string_buffer(p->path, con->request.pathinfo);
1778
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->path));
1780
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN(""));
1785
* SCRIPT_FILENAME and DOCUMENT_ROOT for php. The PHP manual
1786
* http://www.php.net/manual/en/reserved.variables.php
1787
* treatment of PATH_TRANSLATED is different from the one of CGI specs.
1788
* TODO: this code should be checked against cgi.fix_pathinfo php
1792
if (!buffer_is_empty(host->docroot)) {
1794
* rewrite SCRIPT_FILENAME
1798
buffer_copy_string_buffer(p->path, host->docroot);
1799
buffer_append_string_buffer(p->path, con->uri.path);
1801
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path));
1802
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(host->docroot));
1804
buffer_copy_string_buffer(p->path, con->physical.path);
1806
/* cgi.fix_pathinfo need a broken SCRIPT_FILENAME to find out what PATH_INFO is itself
1808
* see src/sapi/cgi_main.c, init_request_info()
1810
if (host->break_scriptfilename_for_php) {
1811
buffer_append_string_buffer(p->path, con->request.pathinfo);
1814
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(p->path));
1815
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));
1818
if (host->strip_request_uri->used > 1) {
1819
/* we need at least one char to strip off */
1823
* stripping /app1 or /app1/ should lead to
1828
if ('/' != host->strip_request_uri->ptr[host->strip_request_uri->used - 2]) {
1829
/* fix the user-input to have / as last char */
1830
buffer_append_string(host->strip_request_uri, "/");
1833
if (con->request.orig_uri->used >= host->strip_request_uri->used &&
1834
0 == strncmp(con->request.orig_uri->ptr, host->strip_request_uri->ptr, host->strip_request_uri->used - 1)) {
1835
/* the left is the same */
1837
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"),
1838
con->request.orig_uri->ptr + (host->strip_request_uri->used - 2),
1839
con->request.orig_uri->used - (host->strip_request_uri->used - 2));
1841
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
1844
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
1846
if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) {
1847
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri));
1849
if (!buffer_is_empty(con->uri.query)) {
1850
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
1852
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
1855
s = get_http_method_name(con->request.http_method);
1856
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
1857
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */
1858
s = get_http_version_name(con->request.http_version);
1859
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
1862
if (srv_sock->is_ssl) {
1863
fcgi_env_add(p->fcgi_env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
1868
fcgi_env_add_request_headers(srv, con, p);
1870
fcgi_header(&(header), FCGI_PARAMS, request_id, p->fcgi_env->used, 0);
1871
buffer_append_memory(b, (const char *)&header, sizeof(header));
1872
buffer_append_memory(b, (const char *)p->fcgi_env->ptr, p->fcgi_env->used);
1874
fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0);
1875
buffer_append_memory(b, (const char *)&header, sizeof(header));
1877
b->used++; /* add virtual \0 */
1878
hctx->wb->bytes_in += b->used - 1;
1880
if (con->request.content_length) {
1881
chunkqueue *req_cq = con->request_content_queue;
1885
/* something to send ? */
1886
for (offset = 0, req_c = req_cq->first; offset != req_cq->bytes_in; ) {
1887
off_t weWant = req_cq->bytes_in - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : req_cq->bytes_in - offset;
1891
/* we announce toWrite octects
1892
* now take all the request_content chunk that we need to fill this request
1895
b = chunkqueue_get_append_buffer(hctx->wb);
1896
fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0);
1897
buffer_copy_memory(b, (const char *)&header, sizeof(header));
1898
hctx->wb->bytes_in += sizeof(header);
1900
if (p->conf.debug > 10) {
1901
fprintf(stderr, "%s.%d: tosend: %lld / %lld\n", __FILE__, __LINE__, offset, req_cq->bytes_in);
1904
for (written = 0; written != weWant; ) {
1905
if (p->conf.debug > 10) {
1906
fprintf(stderr, "%s.%d: chunk: %lld / %lld\n", __FILE__, __LINE__, written, weWant);
1909
switch (req_c->type) {
1911
weHave = req_c->file.length - req_c->offset;
1913
if (weHave > weWant - written) weHave = weWant - written;
1915
if (p->conf.debug > 10) {
1916
fprintf(stderr, "%s.%d: sending %lld bytes from (%lld / %lld) %s\n",
1921
req_c->file.name->ptr);
1924
assert(weHave != 0);
1926
chunkqueue_append_file(hctx->wb, req_c->file.name, req_c->offset, weHave);
1928
req_c->offset += weHave;
1929
req_cq->bytes_out += weHave;
1932
hctx->wb->bytes_in += weHave;
1934
/* steal the tempfile
1937
* - we reference the tempfile from the request-content-queue several times
1938
* if the req_c is larger than FCGI_MAX_LENGTH
1939
* - we can't simply cleanup the request-content-queue as soon as possible
1940
* as it would remove the tempfiles
1941
* - the idea is to 'steal' the tempfiles and attach the is_temp flag to the last
1942
* referencing chunk of the fastcgi-write-queue
1946
if (req_c->offset == req_c->file.length) {
1949
if (p->conf.debug > 10) {
1950
fprintf(stderr, "%s.%d: next chunk\n", __FILE__, __LINE__);
1954
assert(c->type == FILE_CHUNK);
1955
assert(req_c->file.is_temp == 1);
1957
c->file.is_temp = 1;
1958
req_c->file.is_temp = 0;
1960
chunkqueue_remove_finished_chunks(req_cq);
1962
req_c = req_cq->first;
1967
/* append to the buffer */
1968
weHave = req_c->mem->used - 1 - req_c->offset;
1970
if (weHave > weWant - written) weHave = weWant - written;
1972
buffer_append_memory(b, req_c->mem->ptr + req_c->offset, weHave);
1974
req_c->offset += weHave;
1975
req_cq->bytes_out += weHave;
1978
hctx->wb->bytes_in += weHave;
1980
if (req_c->offset == req_c->mem->used - 1) {
1981
chunkqueue_remove_finished_chunks(req_cq);
1983
req_c = req_cq->first;
1992
b->used++; /* add virtual \0 */
1997
b = chunkqueue_get_append_buffer(hctx->wb);
1998
/* terminate STDIN */
1999
fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0);
2000
buffer_copy_memory(b, (const char *)&header, sizeof(header));
2001
b->used++; /* add virtual \0 */
2003
hctx->wb->bytes_in += sizeof(header);
2006
for (i = 0; i < hctx->write_buffer->used; i++) {
2007
fprintf(stderr, "%02x ", hctx->write_buffer->ptr[i]);
2008
if ((i+1) % 16 == 0) {
2010
for (j = i-15; j <= i; j++) {
2011
fprintf(stderr, "%c",
2012
isprint((unsigned char)hctx->write_buffer->ptr[j]) ? hctx->write_buffer->ptr[j] : '.');
2014
fprintf(stderr, "\n");
2022
static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
2025
handler_ctx *hctx = con->plugin_ctx[p->id];
2026
fcgi_extension_host *host= hctx->host;
2030
buffer_copy_string_buffer(p->parse_response, in);
2033
for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) {
2038
/* a good day. Someone has read the specs and is sending a \r\n to us */
2040
if (ns > p->parse_response->ptr &&
2048
if (NULL == (value = strchr(s, ':'))) {
2049
/* we expect: "<key>: <value>\n" */
2053
key_len = value - key;
2057
while (*value == ' ' || *value == '\t') value++;
2059
if (host->mode != FCGI_AUTHORIZER ||
2060
!(con->http_status == 0 ||
2061
con->http_status == 200)) {
2062
/* authorizers shouldn't affect the response headers sent back to the client */
2064
/* don't forward Status: */
2065
if (0 != strncasecmp(key, "Status", key_len)) {
2066
if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
2067
ds = data_response_init();
2069
buffer_copy_string_len(ds->key, key, key_len);
2070
buffer_copy_string(ds->value, value);
2072
array_insert_unique(con->response.headers, (data_unset *)ds);
2078
if (0 == strncasecmp(key, "Date", key_len)) {
2079
con->parsed_response |= HTTP_DATE;
2083
if (0 == strncasecmp(key, "Status", key_len)) {
2084
con->http_status = strtol(value, NULL, 10);
2085
con->parsed_response |= HTTP_STATUS;
2089
if (0 == strncasecmp(key, "Location", key_len)) {
2090
con->parsed_response |= HTTP_LOCATION;
2094
if (0 == strncasecmp(key, "Connection", key_len)) {
2095
con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
2096
con->parsed_response |= HTTP_CONNECTION;
2100
if (0 == strncasecmp(key, "Content-Length", key_len)) {
2101
con->response.content_length = strtol(value, NULL, 10);
2102
con->parsed_response |= HTTP_CONTENT_LENGTH;
2104
if (con->response.content_length < 0) con->response.content_length = 0;
2112
/* CGI/1.1 rev 03 - 7.2.1.2 */
2113
if ((con->parsed_response & HTTP_LOCATION) &&
2114
!(con->parsed_response & HTTP_STATUS)) {
2115
con->http_status = 302;
2127
} fastcgi_response_packet;
2129
static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) {
2133
FCGI_Header *header;
2135
if (!hctx->rb->first) return -1;
2137
packet->b = buffer_init();
2140
packet->padding = 0;
2141
packet->request_id = 0;
2143
/* get at least the FastCGI header */
2144
for (c = hctx->rb->first; c; c = c->next) {
2145
if (packet->b->used == 0) {
2146
buffer_copy_string_len(packet->b, c->mem->ptr + c->offset, c->mem->used - c->offset - 1);
2148
buffer_append_string_len(packet->b, c->mem->ptr + c->offset, c->mem->used - c->offset - 1);
2151
if (packet->b->used >= sizeof(*header) + 1) break;
2154
if ((packet->b->used == 0) ||
2155
(packet->b->used - 1 < sizeof(FCGI_Header))) {
2157
buffer_free(packet->b);
2159
log_error_write(srv, __FILE__, __LINE__, "s", "FastCGI: header to small");
2163
/* we have at least a header, now check how much me have to fetch */
2164
header = (FCGI_Header *)(packet->b->ptr);
2166
packet->len = (header->contentLengthB0 | (header->contentLengthB1 << 8)) + header->paddingLength;
2167
packet->request_id = (header->requestIdB0 | (header->requestIdB1 << 8));
2168
packet->type = header->type;
2169
packet->padding = header->paddingLength;
2171
/* the first bytes in packet->b are the header */
2172
offset = sizeof(*header);
2174
/* ->b should only be the content */
2175
buffer_copy_string(packet->b, ""); /* used == 1 */
2178
/* copy the content */
2179
for (; c && (packet->b->used < packet->len + 1); c = c->next) {
2180
size_t weWant = packet->len - (packet->b->used - 1);
2181
size_t weHave = c->mem->used - c->offset - offset - 1;
2183
if (weHave > weWant) weHave = weWant;
2185
buffer_append_string_len(packet->b, c->mem->ptr + c->offset + offset, weHave);
2187
/* we only skipped the first 8 bytes as they are the fcgi header */
2191
if (packet->b->used < packet->len + 1) {
2192
/* we didn't got the full packet */
2194
buffer_free(packet->b);
2198
packet->b->used -= packet->padding;
2199
packet->b->ptr[packet->b->used - 1] = '\0';
2202
/* tag the chunks as read */
2203
toread = packet->len + sizeof(FCGI_Header);
2204
for (c = hctx->rb->first; c && toread; c = c->next) {
2205
if (c->mem->used - c->offset - 1 <= toread) {
2206
/* we read this whole buffer, move it to unused */
2207
toread -= c->mem->used - c->offset - 1;
2208
c->offset = c->mem->used - 1; /* everthing has been written */
2210
c->offset += toread;
2215
chunkqueue_remove_finished_chunks(hctx->rb);
2220
static int fcgi_demux_response(server *srv, handler_ctx *hctx) {
2225
plugin_data *p = hctx->plugin_data;
2226
connection *con = hctx->remote_conn;
2227
int fcgi_fd = hctx->fd;
2228
fcgi_extension_host *host= hctx->host;
2229
fcgi_proc *proc = hctx->proc;
2232
* check how much we have to read
2234
if (ioctl(hctx->fd, FIONREAD, &toread)) {
2235
log_error_write(srv, __FILE__, __LINE__, "sd",
2236
"unexpected end-of-file (perhaps the fastcgi process died):",
2241
/* init read-buffer */
2246
b = chunkqueue_get_append_buffer(hctx->rb);
2247
buffer_prepare_copy(b, toread + 1);
2249
/* append to read-buffer */
2250
if (-1 == (r = read(hctx->fd, b->ptr, toread))) {
2251
log_error_write(srv, __FILE__, __LINE__, "sds",
2252
"unexpected end-of-file (perhaps the fastcgi process died):",
2253
fcgi_fd, strerror(errno));
2257
/* this should be catched by the b > 0 above */
2260
b->used = r + 1; /* one extra for the fake \0 */
2261
b->ptr[b->used - 1] = '\0';
2263
log_error_write(srv, __FILE__, __LINE__, "ssdsdsd",
2264
"unexpected end-of-file (perhaps the fastcgi process died):",
2266
"fcgi-fd:", fcgi_fd,
2267
"remote-fd:", con->fd);
2273
* parse the fastcgi packets and forward the content to the write-queue
2277
fastcgi_response_packet packet;
2279
/* check if we have at least one packet */
2280
if (0 != fastcgi_get_packet(srv, hctx, &packet)) {
2281
/* no full packet */
2288
switch(packet.type) {
2290
if (packet.len == 0) break;
2292
/* is the header already finished */
2293
if (0 == con->file_started) {
2298
/* search for header terminator
2300
* if we start with \r\n check if last packet terminated with \r\n
2301
* if we start with \n check if last packet terminated with \n
2302
* search for \r\n\r\n
2306
if (hctx->response_header->used == 0) {
2307
buffer_copy_string_buffer(hctx->response_header, packet.b);
2309
buffer_append_string_buffer(hctx->response_header, packet.b);
2312
if (NULL != (c = buffer_search_string_len(hctx->response_header, CONST_STR_LEN("\r\n\r\n")))) {
2313
blen = hctx->response_header->used - (c - hctx->response_header->ptr) - 4;
2314
hctx->response_header->used = (c - hctx->response_header->ptr) + 3;
2315
c += 4; /* point the the start of the response */
2316
} else if (NULL != (c = buffer_search_string_len(hctx->response_header, CONST_STR_LEN("\n\n")))) {
2317
blen = hctx->response_header->used - (c - hctx->response_header->ptr) - 2;
2318
hctx->response_header->used = c - hctx->response_header->ptr + 2;
2319
c += 2; /* point the the start of the response */
2321
/* no luck, no header found */
2325
/* parse the response header */
2326
fcgi_response_parse(srv, con, p, hctx->response_header);
2328
con->file_started = 1;
2330
if (host->mode == FCGI_AUTHORIZER &&
2331
(con->http_status == 0 ||
2332
con->http_status == 200)) {
2333
/* a authorizer with approved the static request, ignore the content here */
2334
hctx->send_content_body = 0;
2337
if (host->allow_xsendfile &&
2338
NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-LIGHTTPD-send-file"))) {
2339
stat_cache_entry *sce;
2341
if (HANDLER_ERROR != stat_cache_get_entry(srv, con, ds->value, &sce)) {
2344
http_chunk_append_file(srv, con, ds->value, 0, sce->st.st_size);
2345
hctx->send_content_body = 0; /* ignore the content */
2346
joblist_append(srv, con);
2351
if (hctx->send_content_body && blen > 1) {
2352
/* enable chunked-transfer-encoding */
2353
if (con->request.http_version == HTTP_VERSION_1_1 &&
2354
!(con->parsed_response & HTTP_CONTENT_LENGTH)) {
2355
con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
2358
http_chunk_append_mem(srv, con, c, blen);
2359
joblist_append(srv, con);
2361
} else if (hctx->send_content_body && packet.b->used > 1) {
2362
if (con->request.http_version == HTTP_VERSION_1_1 &&
2363
!(con->parsed_response & HTTP_CONTENT_LENGTH)) {
2364
/* enable chunked-transfer-encoding */
2365
con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
2368
http_chunk_append_mem(srv, con, packet.b->ptr, packet.b->used);
2369
joblist_append(srv, con);
2373
log_error_write(srv, __FILE__, __LINE__, "sb",
2374
"FastCGI-stderr:", packet.b);
2377
case FCGI_END_REQUEST:
2378
con->file_finished = 1;
2380
if (host->mode != FCGI_AUTHORIZER ||
2381
!(con->http_status == 0 ||
2382
con->http_status == 200)) {
2383
/* send chunk-end if nesseary */
2384
http_chunk_append_mem(srv, con, NULL, 0);
2385
joblist_append(srv, con);
2391
log_error_write(srv, __FILE__, __LINE__, "sd",
2392
"FastCGI: header.type not handled: ", packet.type);
2395
buffer_free(packet.b);
2401
int fcgi_proclist_sort_up(server *srv, fcgi_extension_host *host, fcgi_proc *proc) {
2406
/* we have been the smallest of the current list
2407
* and we want to insert the node sorted as soon
2417
/* nothing to sort, only one element */
2418
if (host->first == proc && proc->next == NULL) return 0;
2420
for (p = proc; p->next && p->next->load < proc->load; p = p->next);
2422
/* no need to move something
2430
if (p == proc) return 0;
2432
if (host->first == proc) {
2433
/* we have been the first elememt */
2435
host->first = proc->next;
2436
host->first->prev = NULL;
2439
/* disconnect proc */
2441
if (proc->prev) proc->prev->next = proc->next;
2442
if (proc->next) proc->next->prev = proc->prev;
2444
/* proc should be right of p */
2446
proc->next = p->next;
2448
if (p->next) p->next->prev = proc;
2451
for(p = host->first; p; p = p->next) {
2452
log_error_write(srv, __FILE__, __LINE__, "dd",
2462
int fcgi_proclist_sort_down(server *srv, fcgi_extension_host *host, fcgi_proc *proc) {
2467
/* we have been the smallest of the current list
2468
* and we want to insert the node sorted as soon
2477
* the basic is idea is:
2478
* - the last active fastcgi process should be still
2479
* in ram and is not swapped out yet
2480
* - processes that are not reused will be killed
2481
* after some time by the trigger-handler
2483
* everything > 0 is hot
2484
* all unused procs are colder the more right they are
2485
* ice-cold processes are propably unused since more
2486
* than 'unused-timeout', are swaped out and won't be
2487
* reused in the next seconds anyway.
2491
/* nothing to sort, only one element */
2492
if (host->first == proc && proc->next == NULL) return 0;
2494
for (p = host->first; p != proc && p->load < proc->load; p = p->next);
2497
/* no need to move something
2505
if (p == proc) return 0;
2507
/* we have to move left. If we are already the first element
2509
if (host->first == proc) return 0;
2512
if (proc->prev) proc->prev->next = proc->next;
2513
if (proc->next) proc->next->prev = proc->prev;
2515
/* proc should be left of p */
2517
proc->prev = p->prev;
2518
if (p->prev) p->prev->next = proc;
2521
if (proc->prev == NULL) host->first = proc;
2523
for(p = host->first; p; p = p->next) {
2524
log_error_write(srv, __FILE__, __LINE__, "dd",
2534
static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_host *host) {
2537
for (proc = host->first; proc; proc = proc->next) {
2538
if (p->conf.debug) {
2539
log_error_write(srv, __FILE__, __LINE__, "sbdbdddd",
2541
host->host, proc->port,
2549
if (0 == proc->is_local) {
2551
* external servers might get disabled
2553
* enable the server again, perhaps it is back again
2556
if ((proc->state == PROC_STATE_DISABLED) &&
2557
(srv->cur_ts - proc->disable_ts > host->disable_time)) {
2558
proc->state = PROC_STATE_RUNNING;
2559
host->active_procs++;
2561
log_error_write(srv, __FILE__, __LINE__, "sbdb",
2562
"fcgi-server re-enabled:",
2563
host->host, host->port,
2567
/* the child should not terminate at all */
2570
if (proc->state == PROC_STATE_DIED_WAIT_FOR_PID) {
2571
switch(waitpid(proc->pid, &status, WNOHANG)) {
2573
/* child is still alive */
2578
if (WIFEXITED(status)) {
2580
log_error_write(srv, __FILE__, __LINE__, "sdsd",
2581
"child exited, pid:", proc->pid,
2582
"status:", WEXITSTATUS(status));
2584
} else if (WIFSIGNALED(status)) {
2585
log_error_write(srv, __FILE__, __LINE__, "sd",
2589
log_error_write(srv, __FILE__, __LINE__, "sd",
2590
"child died somehow:",
2594
proc->state = PROC_STATE_DIED;
2600
* local servers might died, but we restart them
2603
if (proc->state == PROC_STATE_DIED &&
2605
/* restart the child */
2607
if (p->conf.debug) {
2608
log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
2609
"--- fastcgi spawning",
2610
"\n\tport:", host->port,
2611
"\n\tsocket", host->unixsocket,
2612
"\n\tcurrent:", 1, "/", host->min_procs);
2615
if (fcgi_spawn_connection(srv, p, host, proc)) {
2616
log_error_write(srv, __FILE__, __LINE__, "s",
2617
"ERROR: spawning fcgi failed.");
2618
return HANDLER_ERROR;
2621
fcgi_proclist_sort_down(srv, host, proc);
2630
static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {
2631
plugin_data *p = hctx->plugin_data;
2632
fcgi_extension_host *host= hctx->host;
2633
connection *con = hctx->remote_conn;
2639
((!host->host->used || !host->port) && !host->unixsocket->used)) {
2640
log_error_write(srv, __FILE__, __LINE__, "sxddd",
2645
host->unixsocket->used);
2646
return HANDLER_ERROR;
2650
switch(hctx->state) {
2651
case FCGI_STATE_INIT:
2652
ret = host->unixsocket->used ? AF_UNIX : AF_INET;
2654
if (-1 == (hctx->fd = socket(ret, SOCK_STREAM, 0))) {
2655
if (errno == EMFILE ||
2657
log_error_write(srv, __FILE__, __LINE__, "sd",
2658
"wait for fd at connection:", con->fd);
2660
return HANDLER_WAIT_FOR_FD;
2663
log_error_write(srv, __FILE__, __LINE__, "ssdd",
2664
"socket failed:", strerror(errno), srv->cur_fds, srv->max_fds);
2665
return HANDLER_ERROR;
2671
fdevent_register(srv->ev, hctx->fd, fcgi_handle_fdevent, hctx);
2673
if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
2674
log_error_write(srv, __FILE__, __LINE__, "ss",
2675
"fcntl failed: ", strerror(errno));
2677
return HANDLER_ERROR;
2681
case FCGI_STATE_CONNECT:
2682
if (hctx->state == FCGI_STATE_INIT || hctx->delayed == 1) {
2683
for (hctx->proc = hctx->host->first;
2684
hctx->proc && hctx->proc->state != PROC_STATE_RUNNING;
2685
hctx->proc = hctx->proc->next);
2687
/* all childs are dead */
2688
if (hctx->proc == NULL) {
2691
return HANDLER_ERROR;
2694
if (hctx->proc->is_local) {
2695
hctx->pid = hctx->proc->pid;
2698
switch (fcgi_establish_connection(srv, hctx)) {
2700
fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT);
2702
/* connection is in progress, wait for an event and call getsockopt() below */
2704
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2707
return HANDLER_WAIT_FOR_EVENT;
2709
/* if ECONNREFUSED/EAGAIN re-try connect() */
2711
fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT);
2713
return HANDLER_WAIT_FOR_EVENT;
2715
/* everything is ok, go on */
2721
socklen_t socket_error_len = sizeof(socket_error);
2723
/* try to finish the connect() */
2724
if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) {
2725
log_error_write(srv, __FILE__, __LINE__, "ss",
2726
"getsockopt failed:", strerror(errno));
2728
return HANDLER_ERROR;
2730
if (socket_error != 0) {
2731
if (!hctx->proc->is_local || p->conf.debug) {
2732
/* local procs get restarted */
2734
log_error_write(srv, __FILE__, __LINE__, "ss",
2735
"establishing connection failed:", strerror(socket_error),
2736
"port:", hctx->proc->port);
2739
return HANDLER_ERROR;
2743
/* ok, we have the connection */
2746
hctx->proc->last_used = srv->cur_ts;
2749
if (p->conf.debug) {
2750
log_error_write(srv, __FILE__, __LINE__, "sddbdd",
2759
/* move the proc-list entry down the list */
2760
fcgi_proclist_sort_up(srv, hctx->host, hctx->proc);
2762
if (hctx->request_id == 0) {
2763
hctx->request_id = fcgi_requestid_new(srv, p);
2765
log_error_write(srv, __FILE__, __LINE__, "sd",
2766
"fcgi-request is already in use:", hctx->request_id);
2769
fcgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE);
2771
case FCGI_STATE_PREPARE_WRITE:
2772
fcgi_create_env(srv, hctx, hctx->request_id);
2774
fcgi_set_state(srv, hctx, FCGI_STATE_WRITE);
2777
case FCGI_STATE_WRITE:
2778
ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb);
2780
chunkqueue_remove_finished_chunks(hctx->wb);
2785
/* the connection got dropped after accept()
2787
* this is most of the time a PHP which dies
2788
* after PHP_FCGI_MAX_REQUESTS
2791
if (hctx->wb->bytes_out == 0 &&
2792
hctx->reconnects < 5) {
2793
usleep(10000); /* take away the load of the webserver
2794
* to let the php a chance to restart
2797
fcgi_reconnect(srv, hctx);
2799
return HANDLER_WAIT_FOR_FD;
2802
/* not reconnected ... why
2804
* far@#lighttpd report this for FreeBSD
2808
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
2809
"[REPORT ME] connection was dropped after accept(). reconnect() denied:",
2810
"write-offset:", hctx->wb->bytes_out,
2811
"reconnect attempts:", hctx->reconnects);
2813
return HANDLER_ERROR;
2816
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2818
return HANDLER_WAIT_FOR_EVENT;
2820
log_error_write(srv, __FILE__, __LINE__, "ssd",
2821
"write failed:", strerror(errno), errno);
2823
return HANDLER_ERROR;
2827
if (hctx->wb->bytes_out == hctx->wb->bytes_in) {
2828
/* we don't need the out event anymore */
2829
fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
2830
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
2831
fcgi_set_state(srv, hctx, FCGI_STATE_READ);
2833
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
2835
return HANDLER_WAIT_FOR_EVENT;
2839
case FCGI_STATE_READ:
2840
/* waiting for a response */
2843
log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
2844
return HANDLER_ERROR;
2847
return HANDLER_WAIT_FOR_EVENT;
2850
SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) {
2851
plugin_data *p = p_d;
2853
handler_ctx *hctx = con->plugin_ctx[p->id];
2855
fcgi_extension_host *host;
2857
if (NULL == hctx) return HANDLER_GO_ON;
2860
if (con->mode != p->id) return HANDLER_GO_ON;
2862
/* ok, create the request */
2863
switch(fcgi_write_request(srv, hctx)) {
2870
0 == proc->is_local &&
2871
proc->state != PROC_STATE_DISABLED) {
2872
/* only disable remote servers as we don't manage them*/
2874
log_error_write(srv, __FILE__, __LINE__, "sbdb", "fcgi-server disabled:",
2879
/* disable this server */
2880
proc->disable_ts = srv->cur_ts;
2881
proc->state = PROC_STATE_DISABLED;
2882
host->active_procs--;
2886
if (hctx->state == FCGI_STATE_INIT ||
2887
hctx->state == FCGI_STATE_CONNECT) {
2888
/* connect() or getsockopt() failed,
2889
* restart the request-handling
2891
if (proc && proc->is_local) {
2893
if (p->conf.debug) {
2894
log_error_write(srv, __FILE__, __LINE__, "sbdb", "connect() to fastcgi failed, restarting the request-handling:",
2901
* several hctx might reference the same proc
2903
* Only one of them should mark the proc as dead all the other
2904
* ones should just take a new one.
2906
* If a new proc was started with the old struct this might lead
2907
* the mark a perfect proc as dead otherwise
2910
if (proc->state == PROC_STATE_RUNNING &&
2911
hctx->pid == proc->pid) {
2912
proc->state = PROC_STATE_DIED_WAIT_FOR_PID;
2915
fcgi_restart_dead_procs(srv, p, host);
2917
fcgi_connection_close(srv, hctx);
2919
buffer_reset(con->physical.path);
2921
joblist_append(srv, con); /* really ? */
2923
/* mis-using HANDLER_WAIT_FOR_FD to break out of the loop
2924
* and hope that the childs will be restarted
2928
/* we might get into a LOOP here
2930
* but how to handle this ?
2932
* if we enter a endless loop, we will burn the CPU
2934
* let this handle by the loop-detection
2937
return HANDLER_WAIT_FOR_FD;
2939
fcgi_connection_close(srv, hctx);
2941
buffer_reset(con->physical.path);
2943
con->http_status = 503;
2944
joblist_append(srv, con); /* really ? */
2946
return HANDLER_FINISHED;
2948
case HANDLER_WAIT_FOR_EVENT:
2949
if (con->file_started == 1) {
2950
return HANDLER_FINISHED;
2952
return HANDLER_WAIT_FOR_EVENT;
2954
case HANDLER_WAIT_FOR_FD:
2955
return HANDLER_WAIT_FOR_FD;
2957
log_error_write(srv, __FILE__, __LINE__, "s", "subrequest write-req default");
2958
return HANDLER_ERROR;
2962
static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) {
2963
server *srv = (server *)s;
2964
handler_ctx *hctx = ctx;
2965
connection *con = hctx->remote_conn;
2966
plugin_data *p = hctx->plugin_data;
2968
fcgi_proc *proc = hctx->proc;
2969
fcgi_extension_host *host= hctx->host;
2971
if ((revents & FDEVENT_IN) &&
2972
hctx->state == FCGI_STATE_READ) {
2973
switch (fcgi_demux_response(srv, hctx)) {
2978
if (host->mode == FCGI_AUTHORIZER &&
2979
(con->http_status == 200 ||
2980
con->http_status == 0)) {
2982
* If we are here in AUTHORIZER mode then a request for autorizer
2983
* was proceeded already, and status 200 has been returned. We need
2984
* now to handle autorized request.
2987
buffer_copy_string_buffer(con->physical.doc_root, host->docroot);
2989
buffer_copy_string_buffer(con->physical.path, host->docroot);
2990
buffer_append_string_buffer(con->physical.path, con->uri.path);
2991
fcgi_connection_close(srv, hctx);
2994
con->file_started = 1; /* fcgi_extension won't touch the request afterwards */
2997
fcgi_connection_close(srv, hctx);
3000
joblist_append(srv, con);
3001
return HANDLER_FINISHED;
3003
if (proc->pid && proc->state != PROC_STATE_DIED) {
3006
/* only fetch the zombie if it is not already done */
3008
switch(waitpid(proc->pid, &status, WNOHANG)) {
3010
/* child is still alive */
3015
/* the child should not terminate at all */
3016
if (WIFEXITED(status)) {
3017
log_error_write(srv, __FILE__, __LINE__, "sdsd",
3018
"child exited, pid:", proc->pid,
3019
"status:", WEXITSTATUS(status));
3020
} else if (WIFSIGNALED(status)) {
3021
log_error_write(srv, __FILE__, __LINE__, "sd",
3025
log_error_write(srv, __FILE__, __LINE__, "sd",
3026
"child died somehow:",
3030
if (p->conf.debug) {
3031
log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
3032
"--- fastcgi spawning",
3033
"\n\tport:", host->port,
3034
"\n\tsocket", host->unixsocket,
3035
"\n\tcurrent:", 1, "/", host->min_procs);
3038
if (fcgi_spawn_connection(srv, p, host, proc)) {
3040
proc->state = PROC_STATE_DIED;
3042
fcgi_proclist_sort_down(srv, host, proc);
3049
if (con->file_started == 0) {
3050
/* nothing has been send out yet, try to use another child */
3052
if (hctx->wb->bytes_out == 0 &&
3053
hctx->reconnects < 5) {
3054
fcgi_reconnect(srv, hctx);
3056
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
3057
"response not sent, request not sent, reconnection.",
3058
"connection-fd:", con->fd,
3059
"fcgi-fd:", hctx->fd);
3061
return HANDLER_WAIT_FOR_FD;
3064
log_error_write(srv, __FILE__, __LINE__, "sosdsd",
3065
"response not sent, request sent:", hctx->wb->bytes_out,
3066
"connection-fd:", con->fd,
3067
"fcgi-fd:", hctx->fd);
3069
fcgi_connection_close(srv, hctx);
3071
connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
3072
buffer_reset(con->physical.path);
3073
con->http_status = 500;
3076
/* response might have been already started, kill the connection */
3077
fcgi_connection_close(srv, hctx);
3079
log_error_write(srv, __FILE__, __LINE__, "ssdsd",
3080
"response already sent out, termination connection",
3081
"connection-fd:", con->fd,
3082
"fcgi-fd:", hctx->fd);
3084
connection_set_state(srv, con, CON_STATE_ERROR);
3090
joblist_append(srv, con);
3091
return HANDLER_FINISHED;
3095
if (revents & FDEVENT_OUT) {
3096
if (hctx->state == FCGI_STATE_CONNECT ||
3097
hctx->state == FCGI_STATE_WRITE) {
3098
/* we are allowed to send something out
3100
* 1. in a unfinished connect() call
3101
* 2. in a unfinished write() call (long POST request)
3103
return mod_fastcgi_handle_subrequest(srv, con, p);
3105
log_error_write(srv, __FILE__, __LINE__, "sd",
3106
"got a FDEVENT_OUT and didn't know why:",
3111
/* perhaps this issue is already handled */
3112
if (revents & FDEVENT_HUP) {
3113
if (hctx->state == FCGI_STATE_CONNECT) {
3114
/* getoptsock will catch this one (right ?)
3116
* if we are in connect we might get a EINPROGRESS
3117
* in the first call and a FDEVENT_HUP in the
3120
* FIXME: as it is a bit ugly.
3123
return mod_fastcgi_handle_subrequest(srv, con, p);
3124
} else if (hctx->state == FCGI_STATE_READ &&
3125
hctx->proc->port == 0) {
3128
* ioctl says 8192 bytes to read from PHP and we receive directly a HUP for the socket
3129
* even if the FCGI_FIN packet is not received yet
3132
log_error_write(srv, __FILE__, __LINE__, "sbSBSDSd",
3133
"error: unexpected close of fastcgi connection for",
3135
"(no fastcgi process on host: ",
3142
connection_set_state(srv, con, CON_STATE_ERROR);
3143
fcgi_connection_close(srv, hctx);
3144
joblist_append(srv, con);
3146
} else if (revents & FDEVENT_ERR) {
3147
log_error_write(srv, __FILE__, __LINE__, "s",
3148
"fcgi: got a FDEVENT_ERR. Don't know why.");
3149
/* kill all connections to the fastcgi process */
3152
connection_set_state(srv, con, CON_STATE_ERROR);
3153
fcgi_connection_close(srv, hctx);
3154
joblist_append(srv, con);
3157
return HANDLER_FINISHED;
3161
static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) {
3163
plugin_config *s = p->config_storage[0];
3168
/* skip the first, the global context */
3169
for (i = 1; i < srv->config_context->used; i++) {
3170
data_config *dc = (data_config *)srv->config_context->data[i];
3171
s = p->config_storage[i];
3173
/* condition didn't match */
3174
if (!config_check_cond(srv, con, dc)) continue;
3177
for (j = 0; j < dc->value->used; j++) {
3178
data_unset *du = dc->value->data[j];
3180
if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.server"))) {
3182
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.debug"))) {
3193
static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) {
3194
plugin_data *p = p_d;
3200
fcgi_extension *extension = NULL;
3202
/* Possibly, we processed already this request */
3203
if (con->file_started == 1) return HANDLER_GO_ON;
3207
if (fn->used == 0) {
3208
return HANDLER_ERROR;
3211
s_len = fn->used - 1;
3213
fcgi_patch_connection(srv, con, p);
3215
/* check if extension matches */
3216
for (k = 0; k < p->conf.exts->used; k++) {
3217
size_t ct_len; /* length of the config entry */
3219
extension = p->conf.exts->exts[k];
3221
if (extension->key->used == 0) continue;
3223
ct_len = extension->key->used - 1;
3225
if (s_len < ct_len) continue;
3227
/* check extension in the form "/fcgi_pattern" */
3228
if (*(extension->key->ptr) == '/' && strncmp(fn->ptr, extension->key->ptr, ct_len) == 0) {
3230
} else if (0 == strncmp(fn->ptr + s_len - ct_len, extension->key->ptr, ct_len)) {
3231
/* check extension in the form ".fcg" */
3236
/* extension doesn't match */
3237
if (k == p->conf.exts->used) {
3238
return HANDLER_GO_ON;
3241
/* get best server */
3242
for (k = 0, ndx = -1; k < extension->used; k++) {
3243
fcgi_extension_host *host = extension->hosts[k];
3245
/* we should have at least one proc that can do somthing */
3246
if (host->active_procs == 0) continue;
3248
if (used == -1 || host->load < used) {
3255
/* found a server */
3257
fcgi_extension_host *host = extension->hosts[ndx];
3260
* if check-local is disabled, use the uri.path handler
3264
/* init handler-context */
3265
if (uri_path_handler) {
3266
if (host->check_local == 0) {
3270
hctx = handler_ctx_init();
3272
hctx->remote_conn = con;
3273
hctx->plugin_data = p;
3277
hctx->conf.exts = p->conf.exts;
3278
hctx->conf.debug = p->conf.debug;
3280
con->plugin_ctx[p->id] = hctx;
3286
if (con->conf.log_request_handling) {
3287
log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi");
3290
/* the prefix is the SCRIPT_NAME,
3291
* everthing from start to the next slash
3292
* this is important for check-local = "disable"
3294
* if prefix = /admin.fcgi
3296
* /admin.fcgi/foo/bar
3298
* SCRIPT_NAME = /admin.fcgi
3299
* PATH_INFO = /foo/bar
3301
* if prefix = /fcgi-bin/
3305
* SCRIPT_NAME = /fcgi-bin/foo
3310
/* the rewrite is only done for /prefix/? matches */
3311
if (extension->key->ptr[0] == '/' &&
3312
con->uri.path->used > extension->key->used &&
3313
NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) {
3314
/* rewrite uri.path and pathinfo */
3316
buffer_copy_string(con->request.pathinfo, pathinfo);
3318
con->uri.path->used -= con->request.pathinfo->used - 1;
3319
con->uri.path->ptr[con->uri.path->used - 1] = '\0';
3322
return HANDLER_GO_ON;
3325
hctx = handler_ctx_init();
3327
hctx->remote_conn = con;
3328
hctx->plugin_data = p;
3332
hctx->conf.exts = p->conf.exts;
3333
hctx->conf.debug = p->conf.debug;
3335
con->plugin_ctx[p->id] = hctx;
3341
if (con->conf.log_request_handling) {
3342
log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi");
3345
return HANDLER_GO_ON;
3348
/* no handler found */
3349
buffer_reset(con->physical.path);
3350
con->http_status = 500;
3352
log_error_write(srv, __FILE__, __LINE__, "sb",
3353
"no fcgi-handler found for:",
3356
return HANDLER_FINISHED;
3358
return HANDLER_GO_ON;
3361
/* uri-path handler */
3362
static handler_t fcgi_check_extension_1(server *srv, connection *con, void *p_d) {
3363
return fcgi_check_extension(srv, con, p_d, 1);
3366
/* start request handler */
3367
static handler_t fcgi_check_extension_2(server *srv, connection *con, void *p_d) {
3368
return fcgi_check_extension(srv, con, p_d, 0);
3371
JOBLIST_FUNC(mod_fastcgi_handle_joblist) {
3372
plugin_data *p = p_d;
3373
handler_ctx *hctx = con->plugin_ctx[p->id];
3375
if (hctx == NULL) return HANDLER_GO_ON;
3377
if (hctx->fd != -1) {
3378
switch (hctx->state) {
3379
case FCGI_STATE_READ:
3380
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
3383
case FCGI_STATE_CONNECT:
3384
case FCGI_STATE_WRITE:
3385
fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
3388
case FCGI_STATE_INIT:
3392
log_error_write(srv, __FILE__, __LINE__, "sd", "unhandled fcgi.state", hctx->state);
3397
return HANDLER_GO_ON;
3401
static handler_t fcgi_connection_close_callback(server *srv, connection *con, void *p_d) {
3402
plugin_data *p = p_d;
3404
fcgi_connection_close(srv, con->plugin_ctx[p->id]);
3406
return HANDLER_GO_ON;
3409
TRIGGER_FUNC(mod_fastcgi_handle_trigger) {
3410
plugin_data *p = p_d;
3414
/* perhaps we should kill a connect attempt after 10-15 seconds
3416
* currently we wait for the TCP timeout which is on Linux 180 seconds
3422
/* check all childs if they are still up */
3424
for (i = 0; i < srv->config_context->used; i++) {
3425
plugin_config *conf;
3428
conf = p->config_storage[i];
3432
for (j = 0; j < exts->used; j++) {
3437
for (n = 0; n < ex->used; n++) {
3440
unsigned long sum_load = 0;
3441
fcgi_extension_host *host;
3443
host = ex->hosts[n];
3445
fcgi_restart_dead_procs(srv, p, host);
3447
for (proc = host->first; proc; proc = proc->next) {
3448
sum_load += proc->load;
3451
if (host->num_procs &&
3452
host->num_procs < host->max_procs &&
3453
(sum_load / host->num_procs) > host->max_load_per_proc) {
3454
/* overload, spawn new child */
3455
fcgi_proc *fp = NULL;
3457
if (p->conf.debug) {
3458
log_error_write(srv, __FILE__, __LINE__, "s",
3459
"overload detected, spawning a new child");
3462
for (fp = host->unused_procs; fp && fp->pid != 0; fp = fp->next);
3465
if (fp == host->unused_procs) host->unused_procs = fp->next;
3467
if (fp->next) fp->next->prev = NULL;
3471
fp = fastcgi_process_init();
3472
fp->id = host->max_id++;
3477
if (buffer_is_empty(host->unixsocket)) {
3478
fp->port = host->port + fp->id;
3480
buffer_copy_string_buffer(fp->socket, host->unixsocket);
3481
buffer_append_string(fp->socket, "-");
3482
buffer_append_long(fp->socket, fp->id);
3485
if (fcgi_spawn_connection(srv, p, host, fp)) {
3486
log_error_write(srv, __FILE__, __LINE__, "s",
3487
"ERROR: spawning fcgi failed.");
3488
return HANDLER_ERROR;
3492
fp->next = host->first;
3494
host->first->prev = fp;
3499
for (proc = host->first; proc; proc = proc->next) {
3500
if (proc->load != 0) break;
3501
if (host->num_procs <= host->min_procs) break;
3502
if (proc->pid == 0) continue;
3504
if (srv->cur_ts - proc->last_used > host->idle_timeout) {
3505
/* a proc is idling for a long time now,
3508
if (p->conf.debug) {
3509
log_error_write(srv, __FILE__, __LINE__, "ssbsd",
3510
"idle-timeout reached, terminating child:",
3511
"socket:", proc->socket,
3516
if (proc->next) proc->next->prev = proc->prev;
3517
if (proc->prev) proc->prev->next = proc->next;
3519
if (proc->prev == NULL) host->first = proc->next;
3522
proc->next = host->unused_procs;
3524
if (host->unused_procs) host->unused_procs->prev = proc;
3525
host->unused_procs = proc;
3527
kill(proc->pid, SIGTERM);
3529
proc->state = PROC_STATE_KILLED;
3531
log_error_write(srv, __FILE__, __LINE__, "ssbsd",
3533
"socket:", proc->socket,
3538
/* proc is now in unused, let the next second handle the next process */
3543
for (proc = host->unused_procs; proc; proc = proc->next) {
3546
if (proc->pid == 0) continue;
3548
switch (waitpid(proc->pid, &status, WNOHANG)) {
3550
/* child still running after timeout, good */
3553
if (errno != EINTR) {
3554
/* no PID found ? should never happen */
3555
log_error_write(srv, __FILE__, __LINE__, "sddss",
3556
"pid ", proc->pid, proc->state,
3557
"not found:", strerror(errno));
3560
if (errno == ECHILD) {
3561
/* someone else has cleaned up for us */
3563
proc->state = PROC_STATE_UNSET;
3569
/* the child should not terminate at all */
3570
if (WIFEXITED(status)) {
3571
if (proc->state != PROC_STATE_KILLED) {
3572
log_error_write(srv, __FILE__, __LINE__, "sdb",
3574
WEXITSTATUS(status), proc->socket);
3576
} else if (WIFSIGNALED(status)) {
3577
if (WTERMSIG(status) != SIGTERM) {
3578
log_error_write(srv, __FILE__, __LINE__, "sd",
3583
log_error_write(srv, __FILE__, __LINE__, "sd",
3584
"child died somehow:",
3588
proc->state = PROC_STATE_UNSET;
3596
return HANDLER_GO_ON;
3600
int mod_fastcgi_plugin_init(plugin *p) {
3601
p->version = LIGHTTPD_VERSION_ID;
3602
p->name = buffer_init_string("fastcgi");
3604
p->init = mod_fastcgi_init;
3605
p->cleanup = mod_fastcgi_free;
3606
p->set_defaults = mod_fastcgi_set_defaults;
3607
p->connection_reset = fcgi_connection_reset;
3608
p->handle_connection_close = fcgi_connection_close_callback;
3609
p->handle_uri_clean = fcgi_check_extension_1;
3610
p->handle_subrequest_start = fcgi_check_extension_2;
3611
p->handle_subrequest = mod_fastcgi_handle_subrequest;
3612
p->handle_joblist = mod_fastcgi_handle_joblist;
3613
p->handle_trigger = mod_fastcgi_handle_trigger;