[mod_openssl] remove erroneous SSL_set_shutdown()
[lighttpd.git] / src / gw_backend.c
blob0cf7b57b355f2cb9e0cf39d36c7e68eaefe1a983
1 #include "first.h"
3 #include "gw_backend.h"
5 #include <sys/types.h>
6 #include "sys-socket.h"
7 #ifdef HAVE_SYS_UIO_H
8 #include <sys/uio.h>
9 #endif
10 #ifdef HAVE_SYS_WAIT_H
11 #include <sys/wait.h>
12 #endif
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <stdlib.h>
18 #include <signal.h>
19 #include <string.h>
20 #include <unistd.h>
22 #include "array.h"
23 #include "buffer.h"
24 #include "crc32.h"
25 #include "fdevent.h"
26 #include "inet_ntop_cache.h"
27 #include "log.h"
32 #include "status_counter.h"
34 static data_integer * gw_status_get_di(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) {
35 buffer *b = srv->tmp_buf;
36 buffer_copy_string_len(b, CONST_STR_LEN("gw.backend."));
37 buffer_append_string_buffer(b, host->id);
38 if (proc) {
39 buffer_append_string_len(b, CONST_STR_LEN("."));
40 buffer_append_int(b, proc->id);
42 buffer_append_string_len(b, tag, len);
43 return status_counter_get_counter(srv, CONST_BUF_LEN(b));
46 static void gw_proc_tag_inc(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) {
47 data_integer *di = gw_status_get_di(srv, host, proc, tag, len);
48 ++di->value;
51 static void gw_proc_load_inc(server *srv, gw_host *host, gw_proc *proc) {
52 data_integer *di = gw_status_get_di(srv,host,proc,CONST_STR_LEN(".load"));
53 di->value = ++proc->load;
55 status_counter_inc(srv, CONST_STR_LEN("gw.active-requests"));
58 static void gw_proc_load_dec(server *srv, gw_host *host, gw_proc *proc) {
59 data_integer *di = gw_status_get_di(srv,host,proc,CONST_STR_LEN(".load"));
60 di->value = --proc->load;
62 status_counter_dec(srv, CONST_STR_LEN("gw.active-requests"));
65 static void gw_host_assign(server *srv, gw_host *host) {
66 data_integer *di = gw_status_get_di(srv,host,NULL,CONST_STR_LEN(".load"));
67 di->value = ++host->load;
70 static void gw_host_reset(server *srv, gw_host *host) {
71 data_integer *di = gw_status_get_di(srv,host,NULL,CONST_STR_LEN(".load"));
72 di->value = --host->load;
75 static int gw_status_init(server *srv, gw_host *host, gw_proc *proc) {
76 gw_status_get_di(srv, host, proc, CONST_STR_LEN(".disabled"))->value = 0;
77 gw_status_get_di(srv, host, proc, CONST_STR_LEN(".died"))->value = 0;
78 gw_status_get_di(srv, host, proc, CONST_STR_LEN(".overloaded"))->value = 0;
79 gw_status_get_di(srv, host, proc, CONST_STR_LEN(".connected"))->value = 0;
80 gw_status_get_di(srv, host, proc, CONST_STR_LEN(".load"))->value = 0;
82 gw_status_get_di(srv, host, NULL, CONST_STR_LEN(".load"))->value = 0;
84 return 0;
90 static void gw_proc_set_state(gw_host *host, gw_proc *proc, int state) {
91 if ((int)proc->state == state) return;
92 if (proc->state == PROC_STATE_RUNNING) {
93 --host->active_procs;
94 } else if (state == PROC_STATE_RUNNING) {
95 ++host->active_procs;
97 proc->state = state;
101 static gw_proc *gw_proc_init(void) {
102 gw_proc *f = calloc(1, sizeof(*f));
103 force_assert(f);
105 f->unixsocket = buffer_init();
106 f->connection_name = buffer_init();
108 f->prev = NULL;
109 f->next = NULL;
110 f->state = PROC_STATE_DIED;
112 return f;
115 static void gw_proc_free(gw_proc *f) {
116 if (!f) return;
118 gw_proc_free(f->next);
120 buffer_free(f->unixsocket);
121 buffer_free(f->connection_name);
122 free(f->saddr);
124 free(f);
127 static gw_host *gw_host_init(void) {
128 gw_host *f = calloc(1, sizeof(*f));
129 force_assert(f);
131 f->id = buffer_init();
132 f->host = buffer_init();
133 f->unixsocket = buffer_init();
134 f->docroot = buffer_init();
135 f->bin_path = buffer_init();
136 f->bin_env = array_init();
137 f->bin_env_copy = array_init();
138 f->strip_request_uri = buffer_init();
139 f->xsendfile_docroot = array_init();
141 return f;
144 static void gw_host_free(gw_host *h) {
145 if (!h) return;
146 if (h->refcount) {
147 --h->refcount;
148 return;
151 buffer_free(h->id);
152 buffer_free(h->host);
153 buffer_free(h->unixsocket);
154 buffer_free(h->docroot);
155 buffer_free(h->bin_path);
156 buffer_free(h->strip_request_uri);
157 array_free(h->bin_env);
158 array_free(h->bin_env_copy);
159 array_free(h->xsendfile_docroot);
161 gw_proc_free(h->first);
162 gw_proc_free(h->unused_procs);
164 for (size_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]);
165 free(h->args.ptr);
166 free(h);
169 static gw_exts *gw_extensions_init(void) {
170 gw_exts *f = calloc(1, sizeof(*f));
171 force_assert(f);
172 return f;
175 static void gw_extensions_free(gw_exts *f) {
176 if (!f) return;
177 for (size_t i = 0; i < f->used; ++i) {
178 gw_extension *fe = f->exts[i];
179 for (size_t j = 0; j < fe->used; ++j) {
180 gw_host_free(fe->hosts[j]);
182 buffer_free(fe->key);
183 free(fe->hosts);
184 free(fe);
186 free(f->exts);
187 free(f);
190 static int gw_extension_insert(gw_exts *ext, buffer *key, gw_host *fh) {
191 gw_extension *fe = NULL;
192 for (size_t i = 0; i < ext->used; ++i) {
193 if (buffer_is_equal(key, ext->exts[i]->key)) {
194 fe = ext->exts[i];
195 break;
199 if (NULL == fe) {
200 fe = calloc(1, sizeof(*fe));
201 force_assert(fe);
202 fe->key = buffer_init();
203 fe->last_used_ndx = -1;
204 buffer_copy_buffer(fe->key, key);
206 if (ext->size == 0) {
207 ext->size = 8;
208 ext->exts = malloc(ext->size * sizeof(*(ext->exts)));
209 force_assert(ext->exts);
210 } else if (ext->used == ext->size) {
211 ext->size += 8;
212 ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
213 force_assert(ext->exts);
215 ext->exts[ext->used++] = fe;
216 fe->size = 4;
217 fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
218 force_assert(fe->hosts);
219 } else if (fe->size == fe->used) {
220 fe->size += 4;
221 fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
222 force_assert(fe->hosts);
225 fe->hosts[fe->used++] = fh;
226 return 0;
229 static void gw_proc_connect_success(server *srv, gw_host *host, gw_proc *proc, int debug) {
230 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".connected"));
231 proc->last_used = srv->cur_ts;
233 if (debug) {
234 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
235 "got proc:",
236 "pid:", proc->pid,
237 "socket:", proc->connection_name,
238 "load:", proc->load);
242 static void gw_proc_connect_error(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) {
243 log_error_write(srv, __FILE__, __LINE__, "sssb",
244 "establishing connection failed:", strerror(errnum),
245 "socket:", proc->connection_name);
247 if (!proc->is_local) {
248 proc->disabled_until = srv->cur_ts + host->disable_time;
249 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
251 else if (proc->pid == pid && proc->state == PROC_STATE_RUNNING) {
252 /* several requests from lighttpd might reference the same proc
254 * Only one of them should mark the proc
255 * and all other ones should just take a new one.
257 * If a new proc was started with the old struct, this might
258 * otherwise lead to marking a perfectly good proc as dead
260 log_error_write(srv, __FILE__, __LINE__, "sdssd",
261 "backend error; we'll disable for", host->disable_time,
262 "secs and send the request to another backend instead:",
263 "load:", host->load);
264 if (EAGAIN == errnum) {
265 /* - EAGAIN: cool down the backend; it is overloaded */
266 #ifdef __linux__
267 log_error_write(srv, __FILE__, __LINE__, "s",
268 "If this happened on Linux: You have run out of local ports. "
269 "Check the manual, section Performance how to handle this.");
270 #endif
271 if (debug) {
272 log_error_write(srv, __FILE__, __LINE__, "sbsd",
273 "This means that you have more incoming requests than your "
274 "FastCGI backend can handle in parallel. It might help to "
275 "spawn more FastCGI backends or PHP children; if not, "
276 "decrease server.max-connections. The load for this FastCGI "
277 "backend", proc->connection_name, "is", proc->load);
279 proc->disabled_until = srv->cur_ts + host->disable_time;
280 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
282 else {
283 /* we got a hard error from the backend like
284 * - ECONNREFUSED for tcp-ip sockets
285 * - ENOENT for unix-domain-sockets
287 #if 0
288 gw_proc_set_state(host, proc, PROC_STATE_DIED_WAIT_FOR_PID);
289 #else /* treat as overloaded (future: unless we send kill() signal)*/
290 proc->disabled_until = srv->cur_ts + host->disable_time;
291 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
292 #endif
296 if (EAGAIN == errnum) {
297 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".overloaded"));
299 else {
300 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".died"));
304 static void gw_proc_release(server *srv, gw_host *host, gw_proc *proc, int debug) {
305 gw_proc_load_dec(srv, host, proc);
307 if (debug) {
308 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
309 "released proc:",
310 "pid:", proc->pid,
311 "socket:", proc->connection_name,
312 "load:", proc->load);
316 static void gw_proc_check_enable(server *srv, gw_host *host, gw_proc *proc) {
317 if (srv->cur_ts <= proc->disabled_until) return;
318 if (proc->state != PROC_STATE_OVERLOADED) return;
320 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
322 log_error_write(srv, __FILE__, __LINE__, "sbbdb",
323 "gw-server re-enabled:", proc->connection_name,
324 host->host, host->port, host->unixsocket);
327 static void gw_proc_waitpid_log(server *srv, gw_host *host, gw_proc *proc, int status) {
328 UNUSED(host);
329 if (WIFEXITED(status)) {
330 if (proc->state != PROC_STATE_KILLED) {
331 log_error_write(srv, __FILE__, __LINE__, "sdb",
332 "child exited:",
333 WEXITSTATUS(status), proc->connection_name);
335 } else if (WIFSIGNALED(status)) {
336 if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT) {
337 log_error_write(srv, __FILE__, __LINE__, "sd",
338 "child signalled:", WTERMSIG(status));
340 } else {
341 log_error_write(srv, __FILE__, __LINE__, "sd",
342 "child died somehow:", status);
346 static int gw_proc_waitpid(server *srv, gw_host *host, gw_proc *proc) {
347 int rc, status;
349 if (!proc->is_local) return 0;
350 if (proc->pid <= 0) return 0;
352 do {
353 rc = waitpid(proc->pid, &status, WNOHANG);
354 } while (-1 == rc && errno == EINTR);
355 if (0 == rc) return 0; /* child still running */
357 /* child terminated */
358 if (-1 == rc) {
359 /* EINVAL or ECHILD no child processes */
360 /* should not happen; someone else has cleaned up for us */
361 log_error_write(srv, __FILE__, __LINE__, "sddss",
362 "pid ", proc->pid, proc->state,
363 "not found:", strerror(errno));
365 else {
366 gw_proc_waitpid_log(srv, host, proc, status);
369 proc->pid = 0;
370 if (proc->state != PROC_STATE_KILLED)
371 proc->disabled_until = srv->cur_ts;
372 gw_proc_set_state(host, proc, PROC_STATE_DIED);
373 return 1;
376 static int gw_proc_sockaddr_init(server *srv, gw_host *host, gw_proc *proc) {
377 sock_addr addr;
378 socklen_t addrlen;
380 if (!buffer_string_is_empty(proc->unixsocket)) {
381 if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen,
382 proc->unixsocket->ptr, AF_UNIX, 0)) {
383 errno = EINVAL;
384 return -1;
386 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
387 buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
389 else {
390 /*(note: name resolution here is *blocking* if IP string not supplied)*/
391 if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen,
392 host->host->ptr, 0, proc->port)) {
393 errno = EINVAL;
394 return -1;
396 else {
397 /* overwrite host->host buffer with IP addr string so that
398 * any further use of gw_host does not block on DNS lookup */
399 sock_addr_inet_ntop_copy_buffer(host->host, &addr);
400 host->family = addr.plain.sa_family;
402 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
403 buffer_append_string_buffer(proc->connection_name, host->host);
404 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
405 buffer_append_int(proc->connection_name, proc->port);
408 if (NULL != proc->saddr && proc->saddrlen < addrlen) {
409 free(proc->saddr);
410 proc->saddr = NULL;
412 if (NULL == proc->saddr) {
413 proc->saddr = (struct sockaddr *)malloc(addrlen);
414 force_assert(proc->saddr);
416 proc->saddrlen = addrlen;
417 memcpy(proc->saddr, &addr, addrlen);
418 return 0;
421 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
422 char *dst;
424 if (!key || !val) return -1;
426 dst = malloc(key_len + val_len + 3);
427 force_assert(dst);
428 memcpy(dst, key, key_len);
429 dst[key_len] = '=';
430 memcpy(dst + key_len + 1, val, val_len + 1); /* add the \0 from the value */
432 for (size_t i = 0; i < env->used; ++i) {
433 if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
434 free(env->ptr[i]);
435 env->ptr[i] = dst;
436 return 0;
440 if (env->size == 0) {
441 env->size = 16;
442 env->ptr = malloc(env->size * sizeof(*env->ptr));
443 force_assert(env->ptr);
444 } else if (env->size == env->used + 1) {
445 env->size += 16;
446 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
447 force_assert(env->ptr);
450 env->ptr[env->used++] = dst;
452 return 0;
455 static int gw_spawn_connection(server *srv, gw_host *host, gw_proc *proc, int debug) {
456 int gw_fd;
457 int status;
458 struct timeval tv = { 0, 10 * 1000 };
460 if (debug) {
461 log_error_write(srv, __FILE__, __LINE__, "sdb",
462 "new proc, socket:", proc->port, proc->unixsocket);
465 gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
466 if (-1 == gw_fd) {
467 log_error_write(srv, __FILE__, __LINE__, "ss",
468 "failed:", strerror(errno));
469 return -1;
472 do {
473 status = connect(gw_fd, proc->saddr, proc->saddrlen);
474 } while (-1 == status && errno == EINTR);
476 if (-1 == status && errno != ENOENT
477 && !buffer_string_is_empty(proc->unixsocket)) {
478 log_error_write(srv, __FILE__, __LINE__, "sbss",
479 "unlink", proc->unixsocket,
480 "after connect failed:", strerror(errno));
481 unlink(proc->unixsocket->ptr);
484 close(gw_fd);
486 if (-1 == status) {
487 /* server is not up, spawn it */
488 char_array env;
489 size_t i;
490 int dfd = -1;
492 /* reopen socket */
493 gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
494 if (-1 == gw_fd) {
495 log_error_write(srv, __FILE__, __LINE__, "ss",
496 "socket failed:", strerror(errno));
497 return -1;
500 if (fdevent_set_so_reuseaddr(gw_fd, 1) < 0) {
501 log_error_write(srv, __FILE__, __LINE__, "ss",
502 "socketsockopt failed:", strerror(errno));
503 close(gw_fd);
504 return -1;
507 /* create socket */
508 if (-1 == bind(gw_fd, proc->saddr, proc->saddrlen)) {
509 log_error_write(srv, __FILE__, __LINE__, "sbs",
510 "bind failed for:",
511 proc->connection_name,
512 strerror(errno));
513 close(gw_fd);
514 return -1;
517 if (-1 == listen(gw_fd, host->listen_backlog)) {
518 log_error_write(srv, __FILE__, __LINE__, "ss",
519 "listen failed:", strerror(errno));
520 close(gw_fd);
521 return -1;
525 /* create environment */
526 env.ptr = NULL;
527 env.size = 0;
528 env.used = 0;
530 /* build clean environment */
531 if (host->bin_env_copy->used) {
532 for (i = 0; i < host->bin_env_copy->used; ++i) {
533 data_string *ds=(data_string *)host->bin_env_copy->data[i];
534 char *ge;
536 if (NULL != (ge = getenv(ds->value->ptr))) {
537 env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
540 } else {
541 char ** const e = environ;
542 for (i = 0; e[i]; ++i) {
543 char *eq;
545 if (NULL != (eq = strchr(e[i], '='))) {
546 env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1));
551 /* create environment */
552 for (i = 0; i < host->bin_env->used; ++i) {
553 data_string *ds = (data_string *)host->bin_env->data[i];
555 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
558 for (i = 0; i < env.used; ++i) {
559 /* search for PHP_FCGI_CHILDREN */
560 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=",
561 sizeof("PHP_FCGI_CHILDREN=")-1)) {
562 break;
566 /* not found, add a default */
567 if (i == env.used) {
568 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"),
569 CONST_STR_LEN("1"));
572 env.ptr[env.used] = NULL;
575 dfd = fdevent_open_dirname(host->args.ptr[0]);
576 if (-1 == dfd) {
577 log_error_write(srv, __FILE__, __LINE__, "sss",
578 "open dirname failed:", strerror(errno),
579 host->args.ptr[0]);
582 /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/
583 proc->pid = (dfd >= 0)
584 ? fdevent_fork_execve(host->args.ptr[0], host->args.ptr,
585 env.ptr, gw_fd, -1, -1, dfd)
586 : -1;
588 for (i = 0; i < env.used; ++i) free(env.ptr[i]);
589 free(env.ptr);
590 if (-1 != dfd) close(dfd);
591 close(gw_fd);
593 if (-1 == proc->pid) {
594 log_error_write(srv, __FILE__, __LINE__, "sb",
595 "gw-backend failed to start:", host->bin_path);
596 proc->pid = 0;
597 proc->disabled_until = srv->cur_ts;
598 return -1;
601 /* register process */
602 proc->last_used = srv->cur_ts;
603 proc->is_local = 1;
605 /* wait */
606 select(0, NULL, NULL, NULL, &tv);
608 if (0 != gw_proc_waitpid(srv, host, proc)) {
609 log_error_write(srv, __FILE__, __LINE__, "sb",
610 "gw-backend failed to start:", host->bin_path);
611 log_error_write(srv, __FILE__, __LINE__, "s",
612 "If you're trying to run your app as a FastCGI backend, make "
613 "sure you're using the FastCGI-enabled version. If this is PHP "
614 "on Gentoo, add 'fastcgi' to the USE flags. If this is PHP, try "
615 "removing the bytecode caches for now and try again.");
616 return -1;
618 } else {
619 proc->is_local = 0;
620 proc->pid = 0;
622 if (debug) {
623 log_error_write(srv, __FILE__, __LINE__, "sb",
624 "(debug) socket is already used; won't spawn:",
625 proc->connection_name);
629 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
630 return 0;
633 static void gw_proc_spawn(server *srv, gw_host *host, int debug) {
634 gw_proc *proc;
635 for (proc = host->unused_procs; proc; proc = proc->next) {
636 /* (proc->pid <= 0 indicates PROC_STATE_DIED, not PROC_STATE_KILLED) */
637 if (proc->pid > 0) continue;
638 /* (do not attempt to spawn another proc if a proc just exited) */
639 if (proc->disabled_until >= srv->cur_ts) return;
640 break;
642 if (proc) {
643 if (proc == host->unused_procs)
644 host->unused_procs = proc->next;
645 else
646 proc->prev->next = proc->next;
648 if (proc->next) {
649 proc->next->prev = proc->prev;
650 proc->next = NULL;
653 proc->prev = NULL;
654 } else {
655 proc = gw_proc_init();
656 proc->id = host->max_id++;
659 ++host->num_procs;
661 if (buffer_string_is_empty(host->unixsocket)) {
662 proc->port = host->port + proc->id;
663 } else {
664 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
665 buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
666 buffer_append_int(proc->unixsocket, proc->id);
669 if (0 != gw_proc_sockaddr_init(srv, host, proc)) {
670 /*(should not happen if host->host validated at startup,
671 * and translated from name to IP address at startup)*/
672 log_error_write(srv, __FILE__, __LINE__, "s",
673 "ERROR: spawning backend failed.");
674 --host->num_procs;
675 if (proc->id == host->max_id-1) --host->max_id;
676 gw_proc_free(proc);
677 } else if (gw_spawn_connection(srv, host, proc, debug)) {
678 log_error_write(srv, __FILE__, __LINE__, "s",
679 "ERROR: spawning backend failed.");
680 proc->next = host->unused_procs;
681 if (host->unused_procs)
682 host->unused_procs->prev = proc;
683 host->unused_procs = proc;
684 } else {
685 proc->next = host->first;
686 if (host->first)
687 host->first->prev = proc;
688 host->first = proc;
692 static void gw_proc_kill(server *srv, gw_host *host, gw_proc *proc) {
693 UNUSED(srv);
694 if (proc->next) proc->next->prev = proc->prev;
695 if (proc->prev) proc->prev->next = proc->next;
697 if (proc->prev == NULL) host->first = proc->next;
699 proc->prev = NULL;
700 proc->next = host->unused_procs;
701 proc->disabled_until = 0;
703 if (host->unused_procs)
704 host->unused_procs->prev = proc;
705 host->unused_procs = proc;
707 kill(proc->pid, SIGTERM);
709 gw_proc_set_state(host, proc, PROC_STATE_KILLED);
711 --host->num_procs;
714 static gw_host * unixsocket_is_dup(gw_plugin_data *p, size_t used, buffer *unixsocket) {
715 for (size_t i = 0; i < used; ++i) {
716 gw_exts *exts = p->config_storage[i]->exts;
717 if (NULL == exts) continue;
718 for (size_t j = 0; j < exts->used; ++j) {
719 gw_extension *ex = exts->exts[j];
720 for (size_t n = 0; n < ex->used; ++n) {
721 gw_host *host = ex->hosts[n];
722 if (!buffer_string_is_empty(host->unixsocket)
723 && buffer_is_equal(host->unixsocket, unixsocket)
724 && !buffer_string_is_empty(host->bin_path))
725 return host;
730 return NULL;
733 static int parse_binpath(char_array *env, buffer *b) {
734 char *start = b->ptr;
735 char c;
736 /* search for spaces */
737 for (size_t i = 0; i < buffer_string_length(b); ++i) {
738 switch(b->ptr[i]) {
739 case ' ':
740 case '\t':
741 /* a WS, stop here and copy the argument */
743 if (env->size == 0) {
744 env->size = 16;
745 env->ptr = malloc(env->size * sizeof(*env->ptr));
746 } else if (env->size == env->used) {
747 env->size += 16;
748 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
751 c = b->ptr[i];
752 b->ptr[i] = '\0';
753 env->ptr[env->used++] = strdup(start);
754 b->ptr[i] = c;
756 start = b->ptr + i + 1;
757 break;
758 default:
759 break;
763 if (env->size == 0) {
764 env->size = 16;
765 env->ptr = malloc(env->size * sizeof(*env->ptr));
766 } else if (env->size == env->used) { /*need one extra for terminating NULL*/
767 env->size += 16;
768 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
771 /* the rest */
772 env->ptr[env->used++] = strdup(start);
774 if (env->size == 0) {
775 env->size = 16;
776 env->ptr = malloc(env->size * sizeof(*env->ptr));
777 } else if (env->size == env->used) { /*need one extra for terminating NULL*/
778 env->size += 16;
779 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
782 /* terminate */
783 env->ptr[env->used++] = NULL;
785 return 0;
788 enum {
789 GW_BALANCE_LEAST_CONNECTION,
790 GW_BALANCE_RR,
791 GW_BALANCE_HASH,
792 GW_BALANCE_STICKY
795 static gw_host * gw_host_get(server *srv, connection *con, gw_extension *extension, int balance, int debug) {
796 gw_host *host;
797 unsigned long last_max = ULONG_MAX;
798 int max_usage = INT_MAX;
799 int ndx = -1;
800 size_t k;
802 if (extension->used <= 1) {
803 if (1 == extension->used && extension->hosts[0]->active_procs > 0) {
804 ndx = 0;
806 } else switch(balance) {
807 case GW_BALANCE_HASH:
808 /* hash balancing */
810 if (debug) {
811 log_error_write(srv, __FILE__, __LINE__, "sd",
812 "proxy - used hash balancing, hosts:",
813 extension->used);
816 for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
817 unsigned long cur_max;
818 host = extension->hosts[k];
819 if (0 == host->active_procs) continue;
821 cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path))
822 + generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */
823 + generate_crc32c(CONST_BUF_LEN(con->uri.authority));
825 if (debug) {
826 log_error_write(srv, __FILE__, __LINE__, "sbbbd",
827 "proxy - election:", con->uri.path,
828 host->host, con->uri.authority, cur_max);
831 if (last_max < cur_max || last_max == ULONG_MAX) {
832 last_max = cur_max;
833 ndx = k;
837 break;
838 case GW_BALANCE_LEAST_CONNECTION:
839 /* fair balancing */
840 if (debug) {
841 log_error_write(srv, __FILE__, __LINE__, "s",
842 "proxy - used least connection");
845 for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->used; ++k) {
846 host = extension->hosts[k];
847 if (0 == host->active_procs) continue;
849 if (host->load < max_usage) {
850 max_usage = host->load;
851 ndx = k;
855 break;
856 case GW_BALANCE_RR:
857 /* round robin */
858 if (debug) {
859 log_error_write(srv, __FILE__, __LINE__, "s",
860 "proxy - used round-robin balancing");
863 /* just to be sure */
864 force_assert(extension->used < INT_MAX);
866 host = extension->hosts[0];
868 /* Use last_used_ndx from first host in list */
869 k = extension->last_used_ndx;
870 ndx = k + 1; /* use next host after the last one */
871 if (ndx < 0) ndx = 0;
873 /* Search first active host after last_used_ndx */
874 while (ndx < (int) extension->used
875 && 0 == (host = extension->hosts[ndx])->active_procs) ++ndx;
877 if (ndx >= (int) extension->used) {
878 /* didn't find a higher id, wrap to the start */
879 for (ndx = 0; ndx <= (int) k; ++ndx) {
880 host = extension->hosts[ndx];
881 if (0 != host->active_procs) break;
884 /* No active host found */
885 if (0 == host->active_procs) ndx = -1;
888 /* Save new index for next round */
889 extension->last_used_ndx = ndx;
891 break;
892 case GW_BALANCE_STICKY:
893 /* source sticky balancing */
895 if (debug) {
896 log_error_write(srv, __FILE__, __LINE__, "sd",
897 "proxy - used sticky balancing, hosts:",
898 extension->used);
901 for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
902 unsigned long cur_max;
903 host = extension->hosts[k];
905 if (0 == host->active_procs) continue;
907 cur_max = generate_crc32c(CONST_BUF_LEN(con->dst_addr_buf))
908 + generate_crc32c(CONST_BUF_LEN(host->host))
909 + host->port;
911 if (debug) {
912 log_error_write(srv, __FILE__, __LINE__, "sbbdd",
913 "proxy - election:", con->dst_addr_buf,
914 host->host, host->port, cur_max);
917 if (last_max < cur_max || last_max == ULONG_MAX) {
918 last_max = cur_max;
919 ndx = k;
923 break;
924 default:
925 break;
928 if (-1 != ndx) {
929 /* found a server */
930 host = extension->hosts[ndx];
932 if (debug) {
933 log_error_write(srv, __FILE__, __LINE__, "sbd",
934 "gw - found a host", host->host, host->port);
937 return host;
938 } else if (0 == srv->srvconf.max_worker) {
939 /* special-case adaptive spawning and 0 == host->min_procs */
940 for (k = 0; k < extension->used; ++k) {
941 host = extension->hosts[k];
942 if (0 == host->min_procs && 0 == host->num_procs
943 && !buffer_string_is_empty(host->bin_path)) {
944 gw_proc_spawn(srv, host, debug);
945 if (host->num_procs) return host;
950 /* all hosts are down */
951 /* sorry, we don't have a server alive for this ext */
952 con->http_status = 503; /* Service Unavailable */
953 con->mode = DIRECT;
955 /* only send the 'no handler' once */
956 if (!extension->note_is_sent) {
957 extension->note_is_sent = 1;
958 log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
959 "all handlers for", con->uri.path, "?",
960 con->uri.query, "on", extension->key, "are down.");
963 return NULL;
966 static int gw_establish_connection(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) {
967 if (-1 == connect(gw_fd, proc->saddr, proc->saddrlen)) {
968 if (errno == EINPROGRESS ||
969 errno == EALREADY ||
970 errno == EINTR) {
971 if (debug > 2) {
972 log_error_write(srv, __FILE__, __LINE__, "sb",
973 "connect delayed; will continue later:",
974 proc->connection_name);
977 return 1;
978 } else {
979 gw_proc_connect_error(srv, host, proc, pid, errno, debug);
980 return -1;
984 if (debug > 1) {
985 log_error_write(srv, __FILE__, __LINE__, "sd",
986 "connect succeeded: ", gw_fd);
989 return 0;
992 static void gw_restart_dead_procs(server *srv, gw_host *host, int debug) {
993 for (gw_proc *proc = host->first; proc; proc = proc->next) {
994 if (debug > 2) {
995 log_error_write(srv, __FILE__, __LINE__, "sbdddd",
996 "proc:", proc->connection_name, proc->state,
997 proc->is_local, proc->load, proc->pid);
1000 switch (proc->state) {
1001 case PROC_STATE_RUNNING:
1002 break;
1003 case PROC_STATE_OVERLOADED:
1004 gw_proc_check_enable(srv, host, proc);
1005 break;
1006 case PROC_STATE_KILLED:
1007 break;
1008 case PROC_STATE_DIED_WAIT_FOR_PID:
1009 /*(state should not happen in workers if server.max-worker > 0)*/
1010 /*(if PROC_STATE_DIED_WAIT_FOR_PID is used in future, might want
1011 * to save proc->disabled_until before gw_proc_waitpid() since
1012 * gw_proc_waitpid will set proc->disabled_until to srv->cur_ts,
1013 * and so process will not be restarted below until one sec later)*/
1014 if (0 == gw_proc_waitpid(srv, host, proc)) {
1015 gw_proc_check_enable(srv, host, proc);
1018 if (proc->state != PROC_STATE_DIED) break;
1019 /* fall through *//*(we have a dead proc now)*/
1021 case PROC_STATE_DIED:
1022 /* local procs get restarted by us,
1023 * remote ones hopefully by the admin */
1025 if (!buffer_string_is_empty(host->bin_path)) {
1026 /* we still have connections bound to this proc,
1027 * let them terminate first */
1028 if (proc->load != 0) break;
1030 /* avoid spinning if child exits too quickly */
1031 if (proc->disabled_until >= srv->cur_ts) break;
1033 /* restart the child */
1035 if (debug) {
1036 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
1037 "--- gw spawning",
1038 "\n\tsocket", proc->connection_name,
1039 "\n\tcurrent:", 1, "/", host->max_procs);
1042 if (gw_spawn_connection(srv, host, proc, debug)) {
1043 log_error_write(srv, __FILE__, __LINE__, "s",
1044 "ERROR: spawning gw failed.");
1046 } else {
1047 gw_proc_check_enable(srv, host, proc);
1049 break;
1057 #include "base.h"
1058 #include "connections.h"
1059 #include "joblist.h"
1060 #include "keyvalue.h"
1061 #include "plugin.h"
1062 #include "response.h"
1065 /* ok, we need a prototype */
1066 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents);
1069 static gw_handler_ctx * handler_ctx_init(size_t sz) {
1070 gw_handler_ctx *hctx = calloc(1, 0 == sz ? sizeof(*hctx) : sz);
1071 force_assert(hctx);
1073 hctx->fde_ndx = -1;
1075 /*hctx->response = buffer_init();*//*(allocated when needed)*/
1077 hctx->request_id = 0;
1078 hctx->gw_mode = GW_RESPONDER;
1079 hctx->state = GW_STATE_INIT;
1080 hctx->proc = NULL;
1082 hctx->fd = -1;
1084 hctx->reconnects = 0;
1085 hctx->send_content_body = 1;
1087 /*hctx->rb = chunkqueue_init();*//*(allocated when needed)*/
1088 hctx->wb = chunkqueue_init();
1089 hctx->wb_reqlen = 0;
1091 return hctx;
1094 static void handler_ctx_free(gw_handler_ctx *hctx) {
1095 /* caller MUST have called gw_backend_close(srv, hctx) if necessary */
1096 if (hctx->handler_ctx_free) hctx->handler_ctx_free(hctx);
1097 buffer_free(hctx->response);
1099 chunkqueue_free(hctx->rb);
1100 chunkqueue_free(hctx->wb);
1102 free(hctx);
1105 static void handler_ctx_clear(gw_handler_ctx *hctx) {
1106 /* caller MUST have called gw_backend_close(srv, hctx) if necessary */
1108 hctx->proc = NULL;
1109 hctx->host = NULL;
1110 hctx->ext = NULL;
1111 /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/
1113 hctx->gw_mode = GW_RESPONDER;
1114 hctx->state = GW_STATE_INIT;
1115 /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/
1117 if (hctx->rb) chunkqueue_reset(hctx->rb);
1118 if (hctx->wb) chunkqueue_reset(hctx->wb);
1119 hctx->wb_reqlen = 0;
1121 buffer_reset(hctx->response);
1123 hctx->fd = -1;
1124 hctx->fde_ndx = -1;
1125 hctx->reconnects = 0;
1126 hctx->request_id = 0;
1127 hctx->send_content_body = 1;
1129 /*plugin_config conf;*//*(no need to reset for same request)*/
1131 /*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/
1132 /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/
1136 void * gw_init(void) {
1137 return calloc(1, sizeof(gw_plugin_data));
1141 void gw_plugin_config_free(gw_plugin_config *s) {
1142 gw_exts *exts = s->exts;
1143 if (exts) {
1144 for (size_t j = 0; j < exts->used; ++j) {
1145 gw_extension *ex = exts->exts[j];
1146 for (size_t n = 0; n < ex->used; ++n) {
1147 gw_proc *proc;
1148 gw_host *host = ex->hosts[n];
1150 for (proc = host->first; proc; proc = proc->next) {
1151 if (proc->pid > 0) {
1152 kill(proc->pid, host->kill_signal);
1155 if (proc->is_local &&
1156 !buffer_string_is_empty(proc->unixsocket)) {
1157 unlink(proc->unixsocket->ptr);
1161 for (proc = host->unused_procs; proc; proc = proc->next) {
1162 if (proc->pid > 0) {
1163 kill(proc->pid, host->kill_signal);
1165 if (proc->is_local &&
1166 !buffer_string_is_empty(proc->unixsocket)) {
1167 unlink(proc->unixsocket->ptr);
1173 gw_extensions_free(s->exts);
1174 gw_extensions_free(s->exts_auth);
1175 gw_extensions_free(s->exts_resp);
1177 array_free(s->ext_mapping);
1178 free(s);
1181 handler_t gw_free(server *srv, void *p_d) {
1182 gw_plugin_data *p = p_d;
1183 if (p->config_storage) {
1184 for (size_t i = 0; i < srv->config_context->used; ++i) {
1185 gw_plugin_config *s = p->config_storage[i];
1186 if (NULL == s) continue;
1187 gw_plugin_config_free(s);
1189 free(p->config_storage);
1191 free(p);
1192 return HANDLER_GO_ON;
1195 int gw_set_defaults_backend(server *srv, gw_plugin_data *p, data_unset *du, size_t i, int sh_exec) {
1196 /* per-module plugin_config MUST have common "base class" gw_plugin_config*/
1197 /* per-module plugin_data MUST have pointer-compatible common "base class"
1198 * with gw_plugin_data (stemming from gw_plugin_config compatibility) */
1200 data_array *da = (data_array *)du;
1201 gw_plugin_config *s = p->config_storage[i];
1202 buffer *gw_mode;
1203 gw_host *host = NULL;
1205 if (NULL == da) return 1;
1207 if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) {
1208 log_error_write(srv, __FILE__, __LINE__, "s",
1209 "unexpected value for xxxxx.server; expected "
1210 "( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1211 return 0;
1214 p->srv_pid = srv->pid;
1216 gw_mode = buffer_init();
1218 s->exts = gw_extensions_init();
1219 s->exts_auth = gw_extensions_init();
1220 s->exts_resp = gw_extensions_init();
1221 /*s->balance = GW_BALANCE_LEAST_CONNECTION;*//*(default)*/
1224 * gw.server = ( "<ext>" => ( ... ),
1225 * "<ext>" => ( ... ) )
1228 for (size_t j = 0; j < da->value->used; ++j) {
1229 data_array *da_ext = (data_array *)da->value->data[j];
1232 * da_ext->key == name of the extension
1236 * gw.server = ( "<ext>" =>
1237 * ( "<host>" => ( ... ),
1238 * "<host>" => ( ... )
1239 * ),
1240 * "<ext>" => ... )
1243 for (size_t n = 0; n < da_ext->value->used; ++n) {
1244 data_array *da_host = (data_array *)da_ext->value->data[n];
1246 config_values_t fcv[] = {
1247 { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1248 { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1249 { "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1250 { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
1251 { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
1253 { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
1254 { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
1255 { "min-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
1256 { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
1257 { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
1258 { "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
1259 { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
1261 { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
1262 { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
1264 { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
1265 { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
1266 { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
1267 { "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */
1268 { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
1269 { "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */
1270 { "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 20 */
1271 { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 21 */
1273 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1275 unsigned short host_mode = GW_RESPONDER;
1277 if (da_host->type != TYPE_ARRAY || !array_is_kvany(da_host->value)){
1278 log_error_write(srv, __FILE__, __LINE__, "SBS",
1279 "unexpected value for gw.server near [",
1280 da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1282 goto error;
1285 host = gw_host_init();
1286 buffer_reset(gw_mode);
1288 buffer_copy_buffer(host->id, da_host->key);
1290 host->check_local = 1;
1291 host->min_procs = 4;
1292 host->max_procs = 4;
1293 host->max_load_per_proc = 1;
1294 host->idle_timeout = 60;
1295 host->disable_time = 1;
1296 host->break_scriptfilename_for_php = 0;
1297 host->kill_signal = SIGTERM;
1298 host->fix_root_path_name = 0;
1299 host->listen_backlog = 1024;
1300 host->xsendfile_allow = 0;
1301 host->refcount = 0;
1303 fcv[0].destination = host->host;
1304 fcv[1].destination = host->docroot;
1305 fcv[2].destination = gw_mode;
1306 fcv[3].destination = host->unixsocket;
1307 fcv[4].destination = host->bin_path;
1309 fcv[5].destination = &(host->check_local);
1310 fcv[6].destination = &(host->port);
1311 fcv[7].destination = &(host->min_procs);
1312 fcv[8].destination = &(host->max_procs);
1313 fcv[9].destination = &(host->max_load_per_proc);
1314 fcv[10].destination = &(host->idle_timeout);
1315 fcv[11].destination = &(host->disable_time);
1317 fcv[12].destination = host->bin_env;
1318 fcv[13].destination = host->bin_env_copy;
1319 fcv[14].destination = &(host->break_scriptfilename_for_php);
1320 fcv[15].destination = &(host->xsendfile_allow);
1321 fcv[16].destination = host->strip_request_uri;
1322 fcv[17].destination = &(host->kill_signal);
1323 fcv[18].destination = &(host->fix_root_path_name);
1324 fcv[19].destination = &(host->listen_backlog);
1325 fcv[20].destination = &(host->xsendfile_allow);
1326 fcv[21].destination = host->xsendfile_docroot;
1328 if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) {
1329 goto error;
1332 for (size_t m = 0; m < da_host->value->used; ++m) {
1333 if (NULL != strchr(da_host->value->data[m]->key->ptr, '_')) {
1334 log_error_write(srv, __FILE__, __LINE__, "sb",
1335 "incorrect directive contains underscore ('_') instead of dash ('-'):",
1336 da_host->value->data[m]->key);
1340 if ((!buffer_string_is_empty(host->host) || host->port)
1341 && !buffer_string_is_empty(host->unixsocket)) {
1342 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1343 "either host/port or socket have to be set in:",
1344 da->key, "= (",
1345 da_ext->key, " => (",
1346 da_host->key, " ( ...");
1348 goto error;
1351 if (!buffer_string_is_empty(host->host) && *host->host->ptr == '/'
1352 && buffer_string_is_empty(host->unixsocket)) {
1353 buffer_copy_buffer(host->unixsocket, host->host);
1356 if (!buffer_string_is_empty(host->unixsocket)) {
1357 /* unix domain socket */
1358 struct sockaddr_un un;
1360 if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1361 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1362 "unixsocket is too long in:",
1363 da->key, "= (",
1364 da_ext->key, " => (",
1365 da_host->key, " ( ...");
1367 goto error;
1370 if (!buffer_string_is_empty(host->bin_path)) {
1371 gw_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket);
1372 if (NULL != duplicate) {
1373 if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) {
1374 log_error_write(srv, __FILE__, __LINE__, "sb",
1375 "duplicate unixsocket path:",
1376 host->unixsocket);
1377 goto error;
1379 gw_host_free(host);
1380 host = duplicate;
1381 ++host->refcount;
1385 host->family = AF_UNIX;
1386 } else {
1387 /* tcp/ip */
1389 if (buffer_string_is_empty(host->host) &&
1390 buffer_string_is_empty(host->bin_path)) {
1391 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1392 "host or binpath have to be set in:",
1393 da->key, "= (",
1394 da_ext->key, " => (",
1395 da_host->key, " ( ...");
1397 goto error;
1398 } else if (0 == host->port) {
1399 host->port = 80;
1402 host->family = (!buffer_string_is_empty(host->host)
1403 && NULL != strchr(host->host->ptr, ':'))
1404 ? AF_INET6
1405 : AF_INET;
1408 if (host->refcount) {
1409 /* already init'd; skip spawning */
1410 } else if (!buffer_string_is_empty(host->bin_path)) {
1411 /* a local socket + self spawning */
1412 struct stat st;
1413 parse_binpath(&host->args, host->bin_path);
1414 if (0 != stat(host->args.ptr[0], &st) || !S_ISREG(st.st_mode)
1415 || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
1416 log_error_write(srv, __FILE__, __LINE__, "SSs",
1417 "invalid \"bin-path\" => \"", host->bin_path->ptr,
1418 "\" (check that file exists, is regular file, "
1419 "and is executable by lighttpd)");
1422 if (sh_exec) {
1423 /*(preserve prior behavior for SCGI exec of command)*/
1424 /*(admin should really prefer to put
1425 * any complex command into a script)*/
1426 for (size_t m = 0; m < host->args.used; ++m)
1427 free(host->args.ptr[m]);
1428 free(host->args.ptr);
1430 host->args.ptr = calloc(4, sizeof(char *));
1431 force_assert(host->args.ptr);
1432 host->args.used = 3;
1433 host->args.size = 4;
1434 host->args.ptr[0] = malloc(sizeof("/bin/sh"));
1435 force_assert(host->args.ptr[0]);
1436 memcpy(host->args.ptr[0], "/bin/sh", sizeof("/bin/sh"));
1437 host->args.ptr[1] = malloc(sizeof("-c"));
1438 force_assert(host->args.ptr[1]);
1439 memcpy(host->args.ptr[1], "-c", sizeof("-c"));
1440 host->args.ptr[2] =
1441 malloc(sizeof("exec ")-1
1442 + buffer_string_length(host->bin_path) + 1);
1443 force_assert(host->args.ptr[2]);
1444 memcpy(host->args.ptr[2], "exec ", sizeof("exec ")-1);
1445 memcpy(host->args.ptr[2]+sizeof("exec ")-1,
1446 host->bin_path->ptr,
1447 buffer_string_length(host->bin_path)+1);
1448 host->args.ptr[3] = NULL;
1451 if (host->min_procs > host->max_procs)
1452 host->min_procs = host->max_procs;
1453 if (host->min_procs!= host->max_procs
1454 && 0 != srv->srvconf.max_worker) {
1455 host->min_procs = host->max_procs;
1456 log_error_write(srv, __FILE__, __LINE__, "s",
1457 "adaptive backend spawning disabled "
1458 "(server.max_worker is non-zero)");
1460 if (host->max_load_per_proc < 1)
1461 host->max_load_per_proc = 0;
1463 if (s->debug) {
1464 log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",
1465 "--- gw spawning local",
1466 "\n\tproc:", host->bin_path,
1467 "\n\tport:", host->port,
1468 "\n\tsocket", host->unixsocket,
1469 "\n\tmin-procs:", host->min_procs,
1470 "\n\tmax-procs:", host->max_procs);
1473 for (size_t pno = 0; pno < host->min_procs; ++pno) {
1474 gw_proc *proc = gw_proc_init();
1475 proc->id = host->num_procs++;
1476 host->max_id++;
1478 if (buffer_string_is_empty(host->unixsocket)) {
1479 proc->port = host->port + pno;
1480 } else {
1481 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1482 buffer_append_string_len(proc->unixsocket,
1483 CONST_STR_LEN("-"));
1484 buffer_append_int(proc->unixsocket, pno);
1487 if (s->debug) {
1488 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1489 "--- gw spawning",
1490 "\n\tport:", host->port,
1491 "\n\tsocket", host->unixsocket,
1492 "\n\tcurrent:", pno, "/", host->max_procs);
1495 if (0 != gw_proc_sockaddr_init(srv, host, proc)) {
1496 gw_proc_free(proc);
1497 goto error;
1500 if (!srv->srvconf.preflight_check
1501 && gw_spawn_connection(srv, host, proc, s->debug)) {
1502 log_error_write(srv, __FILE__, __LINE__, "s",
1503 "[ERROR]: spawning gw failed.");
1504 gw_proc_free(proc);
1505 goto error;
1508 gw_status_init(srv, host, proc);
1510 proc->next = host->first;
1511 if (host->first) host->first->prev = proc;
1513 host->first = proc;
1515 } else {
1516 gw_proc *proc;
1518 proc = gw_proc_init();
1519 proc->id = host->num_procs++;
1520 host->max_id++;
1521 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
1523 if (buffer_string_is_empty(host->unixsocket)) {
1524 proc->port = host->port;
1525 } else {
1526 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1529 gw_status_init(srv, host, proc);
1531 host->first = proc;
1533 host->min_procs = 1;
1534 host->max_procs = 1;
1536 if (0 != gw_proc_sockaddr_init(srv, host, proc)) goto error;
1539 if (!buffer_string_is_empty(gw_mode)) {
1540 if (strcmp(gw_mode->ptr, "responder") == 0) {
1541 host_mode = GW_RESPONDER;
1542 } else if (strcmp(gw_mode->ptr, "authorizer") == 0) {
1543 host_mode = GW_AUTHORIZER;
1544 } else {
1545 log_error_write(srv, __FILE__, __LINE__, "sbs",
1546 "WARNING: unknown gw mode:",
1547 gw_mode,"(ignored, mode set to responder)");
1551 if (host->xsendfile_docroot->used) {
1552 size_t k;
1553 for (k = 0; k < host->xsendfile_docroot->used; ++k) {
1554 data_string *ds = (data_string *)host->xsendfile_docroot->data[k];
1555 if (ds->type != TYPE_STRING) {
1556 log_error_write(srv, __FILE__, __LINE__, "s",
1557 "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )");
1558 goto error;
1560 if (ds->value->ptr[0] != '/') {
1561 log_error_write(srv, __FILE__, __LINE__, "SBs",
1562 "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
1563 goto error;
1565 buffer_path_simplify(ds->value, ds->value);
1566 buffer_append_slash(ds->value);
1570 /* s->exts is list of exts -> hosts
1571 * s->exts now used as combined list
1572 * of authorizer and responder hosts (for backend maintenance)
1573 * s->exts_auth is list of exts -> authorizer hosts
1574 * s->exts_resp is list of exts -> responder hosts
1575 * For each path/extension:
1576 * there may be an independent GW_AUTHORIZER and GW_RESPONDER
1577 * (The GW_AUTHORIZER and GW_RESPONDER could be handled by the same
1578 * host, and an admin might want to do that for large uploads,
1579 * since GW_AUTHORIZER runs prior to receiving (potentially large)
1580 * request body from client and can authorizer or deny request
1581 * prior to receiving the full upload)
1583 gw_extension_insert(s->exts, da_ext->key, host);
1585 if (host_mode == GW_AUTHORIZER) {
1586 ++host->refcount;
1587 gw_extension_insert(s->exts_auth, da_ext->key, host);
1588 } else if (host_mode == GW_RESPONDER) {
1589 ++host->refcount;
1590 gw_extension_insert(s->exts_resp, da_ext->key, host);
1591 } /*(else should have been rejected above)*/
1593 host = NULL;
1597 buffer_free(gw_mode);
1598 return 1;
1600 error:
1601 if (NULL != host) gw_host_free(host);
1602 buffer_free(gw_mode);
1603 return 0;
1606 int gw_set_defaults_balance(server *srv, gw_plugin_config *s, data_unset *du) {
1607 buffer *b;
1608 if (NULL == du) {
1609 b = NULL;
1610 } else if (du->type == TYPE_STRING) {
1611 b = ((data_string *)du)->value;
1612 } else {
1613 log_error_write(srv, __FILE__, __LINE__, "s",
1614 "unexpected type for xxxxx.balance; expected string");
1615 return 0;
1617 if (buffer_string_is_empty(b)) {
1618 s->balance = GW_BALANCE_LEAST_CONNECTION;
1619 } else if (buffer_is_equal_string(b, CONST_STR_LEN("fair"))) {
1620 s->balance = GW_BALANCE_LEAST_CONNECTION;
1621 } else if (buffer_is_equal_string(b, CONST_STR_LEN("least-connection"))) {
1622 s->balance = GW_BALANCE_LEAST_CONNECTION;
1623 } else if (buffer_is_equal_string(b, CONST_STR_LEN("round-robin"))) {
1624 s->balance = GW_BALANCE_RR;
1625 } else if (buffer_is_equal_string(b, CONST_STR_LEN("hash"))) {
1626 s->balance = GW_BALANCE_HASH;
1627 } else if (buffer_is_equal_string(b, CONST_STR_LEN("sticky"))) {
1628 s->balance = GW_BALANCE_STICKY;
1629 } else {
1630 log_error_write(srv, __FILE__, __LINE__, "sb",
1631 "xxxxx.balance has to be one of: "
1632 "least-connection, round-robin, hash, sticky, but not:",
1634 return 0;
1636 return 1;
1639 static void gw_set_state(server *srv, gw_handler_ctx *hctx, gw_connection_state_t state) {
1640 hctx->state = state;
1641 hctx->state_timestamp = srv->cur_ts;
1645 void gw_set_transparent(server *srv, gw_handler_ctx *hctx) {
1646 if (AF_UNIX != hctx->host->family) {
1647 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1648 /*(error, but not critical)*/
1651 hctx->wb_reqlen = -1;
1652 gw_set_state(srv, hctx, GW_STATE_WRITE);
1656 static void gw_backend_close(server *srv, gw_handler_ctx *hctx) {
1657 if (hctx->fd >= 0) {
1658 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1659 fdevent_unregister(srv->ev, hctx->fd);
1660 fdevent_sched_close(srv->ev, hctx->fd, 1);
1661 hctx->fd = -1;
1662 hctx->fde_ndx = -1;
1665 if (hctx->host) {
1666 if (hctx->proc) {
1667 gw_proc_release(srv, hctx->host, hctx->proc, hctx->conf.debug);
1668 hctx->proc = NULL;
1671 gw_host_reset(srv, hctx->host);
1672 hctx->host = NULL;
1676 static void gw_connection_close(server *srv, gw_handler_ctx *hctx) {
1677 gw_plugin_data *p = hctx->plugin_data;
1678 connection *con = hctx->remote_conn;
1680 gw_backend_close(srv, hctx);
1681 handler_ctx_free(hctx);
1682 con->plugin_ctx[p->id] = NULL;
1684 if (con->mode == p->id) {
1685 http_response_backend_done(srv, con);
1689 static handler_t gw_reconnect(server *srv, gw_handler_ctx *hctx) {
1690 gw_backend_close(srv, hctx);
1692 hctx->host = gw_host_get(srv, hctx->remote_conn, hctx->ext,
1693 hctx->conf.balance, hctx->conf.debug);
1694 if (NULL == hctx->host) return HANDLER_FINISHED;
1696 gw_host_assign(srv, hctx->host);
1697 hctx->request_id = 0;
1698 hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow;
1699 hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot;
1700 gw_set_state(srv, hctx, GW_STATE_INIT);
1701 return HANDLER_COMEBACK;
1705 handler_t gw_connection_reset(server *srv, connection *con, void *p_d) {
1706 gw_plugin_data *p = p_d;
1707 gw_handler_ctx *hctx = con->plugin_ctx[p->id];
1708 if (hctx) gw_connection_close(srv, hctx);
1710 return HANDLER_GO_ON;
1714 static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) {
1715 switch(hctx->state) {
1716 case GW_STATE_INIT:
1717 /* do we have a running process for this host (max-procs) ? */
1718 hctx->proc = NULL;
1720 for (gw_proc *proc = hctx->host->first; proc; proc = proc->next) {
1721 if (proc->state == PROC_STATE_RUNNING) {
1722 hctx->proc = proc;
1723 break;
1727 /* all children are dead */
1728 if (hctx->proc == NULL) {
1729 return HANDLER_ERROR;
1732 /* check the other procs if they have a lower load */
1733 for (gw_proc *proc = hctx->proc->next; proc; proc = proc->next) {
1734 if (proc->state != PROC_STATE_RUNNING) continue;
1735 if (proc->load < hctx->proc->load) hctx->proc = proc;
1738 gw_proc_load_inc(srv, hctx->host, hctx->proc);
1740 hctx->fd = fdevent_socket_nb_cloexec(hctx->host->family,SOCK_STREAM,0);
1741 if (-1 == hctx->fd) {
1742 if (errno == EMFILE || errno == EINTR) {
1743 log_error_write(srv, __FILE__, __LINE__, "sd",
1744 "wait for fd at connection:",
1745 hctx->remote_conn->fd);
1746 return HANDLER_WAIT_FOR_FD;
1749 log_error_write(srv, __FILE__, __LINE__, "ssdd",
1750 "socket failed:", strerror(errno),
1751 srv->cur_fds, srv->max_fds);
1752 return HANDLER_ERROR;
1755 srv->cur_fds++;
1757 fdevent_register(srv->ev, hctx->fd, gw_handle_fdevent, hctx);
1759 if (hctx->proc->is_local) {
1760 hctx->pid = hctx->proc->pid;
1763 switch (gw_establish_connection(srv, hctx->host, hctx->proc, hctx->pid,
1764 hctx->fd, hctx->conf.debug)) {
1765 case 1: /* connection is in progress */
1766 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
1767 gw_set_state(srv, hctx, GW_STATE_CONNECT_DELAYED);
1768 return HANDLER_WAIT_FOR_EVENT;
1769 case -1:/* connection error */
1770 return HANDLER_ERROR;
1771 case 0: /* everything is ok, go on */
1772 hctx->reconnects = 0;
1773 break;
1775 /* fall through */
1776 case GW_STATE_CONNECT_DELAYED:
1777 if (hctx->state == GW_STATE_CONNECT_DELAYED) { /*(not GW_STATE_INIT)*/
1778 int socket_error = fdevent_connect_status(hctx->fd);
1779 if (socket_error != 0) {
1780 gw_proc_connect_error(srv, hctx->host, hctx->proc, hctx->pid,
1781 socket_error, hctx->conf.debug);
1782 return HANDLER_ERROR;
1784 /* go on with preparing the request */
1787 gw_proc_connect_success(srv, hctx->host, hctx->proc, hctx->conf.debug);
1789 gw_set_state(srv, hctx, GW_STATE_PREPARE_WRITE);
1790 /* fall through */
1791 case GW_STATE_PREPARE_WRITE:
1792 /* ok, we have the connection */
1795 handler_t rc = hctx->create_env(srv, hctx);
1796 if (HANDLER_GO_ON != rc) {
1797 if (HANDLER_FINISHED != rc && HANDLER_ERROR != rc)
1798 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd,
1799 FDEVENT_OUT);
1800 return rc;
1804 /*(disable Nagle algorithm if streaming and content-length unknown)*/
1805 if (AF_UNIX != hctx->host->family) {
1806 connection *con = hctx->remote_conn;
1807 if (-1 == con->request.content_length) {
1808 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1809 /*(error, but not critical)*/
1814 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1815 gw_set_state(srv, hctx, GW_STATE_WRITE);
1816 /* fall through */
1817 case GW_STATE_WRITE:
1818 if (!chunkqueue_is_empty(hctx->wb)) {
1819 connection *con = hctx->remote_conn;
1820 int ret;
1821 #if 0
1822 if (hctx->conf.debug > 1) {
1823 log_error_write(srv, __FILE__, __LINE__, "sdsx",
1824 "send data to backend ( fd =", hctx->fd,
1825 "), size =", chunkqueue_length(hctx->wb));
1827 #endif
1828 ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb,
1829 MAX_WRITE_LIMIT);
1831 chunkqueue_remove_finished_chunks(hctx->wb);
1833 if (ret < 0) {
1834 switch(errno) {
1835 case EPIPE:
1836 case ENOTCONN:
1837 case ECONNRESET:
1838 /* the connection got dropped after accept()
1839 * we don't care about that --
1840 * if you accept() it, you have to handle it.
1842 log_error_write(srv, __FILE__, __LINE__, "ssosb",
1843 "connection was dropped after accept() "
1844 "(perhaps the gw process died),",
1845 "write-offset:", hctx->wb->bytes_out,
1846 "socket:", hctx->proc->connection_name);
1847 return HANDLER_ERROR;
1848 default:
1849 log_error_write(srv, __FILE__, __LINE__, "ssd",
1850 "write failed:", strerror(errno), errno);
1851 return HANDLER_ERROR;
1856 if (hctx->wb->bytes_out == hctx->wb_reqlen) {
1857 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT);
1858 gw_set_state(srv, hctx, GW_STATE_READ);
1859 } else {
1860 off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out;
1861 if ((hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0)
1862 && wblen < 65536 - 16384) {
1863 connection *con = hctx->remote_conn;
1864 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
1865 if (!(con->conf.stream_request_body
1866 & FDEVENT_STREAM_REQUEST_POLLIN)) {
1867 con->conf.stream_request_body |=
1868 FDEVENT_STREAM_REQUEST_POLLIN;
1869 con->is_readable = 1;/*trigger optimistic read from client*/
1872 if (0 == wblen) {
1873 fdevent_event_clr(srv->ev,&hctx->fde_ndx,hctx->fd,FDEVENT_OUT);
1874 } else {
1875 fdevent_event_add(srv->ev,&hctx->fde_ndx,hctx->fd,FDEVENT_OUT);
1879 return HANDLER_WAIT_FOR_EVENT;
1880 case GW_STATE_READ:
1881 /* waiting for a response */
1882 return HANDLER_WAIT_FOR_EVENT;
1883 default:
1884 log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
1885 return HANDLER_ERROR;
1889 static handler_t gw_write_error(server *srv, gw_handler_ctx *hctx) {
1890 connection *con = hctx->remote_conn;
1891 int status = con->http_status;
1893 if (hctx->state == GW_STATE_INIT ||
1894 hctx->state == GW_STATE_CONNECT_DELAYED) {
1896 /* (optimization to detect backend process exit while processing a
1897 * large number of ready events; (this block could be removed)) */
1898 if (0 == srv->srvconf.max_worker)
1899 gw_restart_dead_procs(srv, hctx->host, hctx->conf.debug);
1901 /* cleanup this request and let request handler start request again */
1902 if (hctx->reconnects++ < 5) return gw_reconnect(srv, hctx);
1905 if (hctx->backend_error) hctx->backend_error(hctx);
1906 gw_connection_close(srv, hctx);
1907 con->http_status = (status == 400) ? 400 : 503;
1908 return HANDLER_FINISHED;
1911 static handler_t gw_send_request(server *srv, gw_handler_ctx *hctx) {
1912 handler_t rc = gw_write_request(srv, hctx);
1913 return (HANDLER_ERROR != rc) ? rc : gw_write_error(srv, hctx);
1917 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx);
1920 handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d) {
1921 gw_plugin_data *p = p_d;
1922 gw_handler_ctx *hctx = con->plugin_ctx[p->id];
1923 if (NULL == hctx) return HANDLER_GO_ON;
1924 if (con->mode != p->id) return HANDLER_GO_ON; /* not my job */
1926 if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
1927 && con->file_started) {
1928 if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
1929 fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1931 else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)){
1932 /* optimistic read from backend */
1933 handler_t rc;
1934 rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/
1935 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
1936 fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1940 /* (do not receive request body before GW_AUTHORIZER has run or else
1941 * the request body is discarded with handler_ctx_clear() after running
1942 * the FastCGI Authorizer) */
1944 if (hctx->gw_mode != GW_AUTHORIZER
1945 && (0 == hctx->wb->bytes_in
1946 ? (con->state == CON_STATE_READ_POST || -1 == hctx->wb_reqlen)
1947 : (hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0))) {
1948 /* leave excess data in con->request_content_queue, which is
1949 * buffered to disk if too large and backend can not keep up */
1950 /*(64k - 4k to attempt to avoid temporary files
1951 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
1952 if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096) {
1953 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) {
1954 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1956 if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT;
1958 else {
1959 handler_t r = connection_handle_read_post_state(srv, con);
1960 chunkqueue *req_cq = con->request_content_queue;
1961 #if 0 /*(not reached since we send 411 Length Required below)*/
1962 if (hctx->wb_reqlen < -1 && con->request.content_length >= 0) {
1963 /* (completed receiving Transfer-Encoding: chunked) */
1964 hctx->wb_reqlen= -hctx->wb_reqlen + con->request.content_length;
1965 if (hctx->stdin_append) {
1966 handler_t rc = hctx->stdin_append(srv, hctx);
1967 if (HANDLER_GO_ON != rc) return rc;
1970 #endif
1971 if ((0 != hctx->wb->bytes_in || -1 == hctx->wb_reqlen)
1972 && !chunkqueue_is_empty(req_cq)) {
1973 if (hctx->stdin_append) {
1974 handler_t rc = hctx->stdin_append(srv, hctx);
1975 if (HANDLER_GO_ON != rc) return rc;
1977 else
1978 chunkqueue_append_chunkqueue(hctx->wb, req_cq);
1979 if (fdevent_event_get_interest(srv->ev,hctx->fd) & FDEVENT_OUT){
1980 return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
1983 if (r != HANDLER_GO_ON) return r;
1986 /* XXX: create configurable flag */
1987 /* CGI environment requires that Content-Length be set.
1988 * Send 411 Length Required if Content-Length missing.
1989 * (occurs here if client sends Transfer-Encoding: chunked
1990 * and module is flagged to stream request body to backend) */
1991 /* proxy currently sends HTTP/1.0 request and ideally should send
1992 * Content-Length with request if request body is present, so
1993 * send 411 Length Required if Content-Length missing. */
1994 if (-1 == con->request.content_length) {
1995 return connection_handle_read_post_error(srv, con, 411);
2000 return ((0 == hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb))
2001 && hctx->state != GW_STATE_CONNECT_DELAYED)
2002 ? gw_send_request(srv, hctx)
2003 : HANDLER_WAIT_FOR_EVENT;
2007 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx) {
2008 connection *con = hctx->remote_conn;
2009 gw_proc *proc = hctx->proc;
2010 gw_host *host = hctx->host;
2011 /*(XXX: make this a configurable flag for other protocols)*/
2012 buffer *b = hctx->opts.backend == BACKEND_FASTCGI
2013 ? buffer_init()
2014 : hctx->response;
2016 switch (http_response_read(srv, hctx->remote_conn, &hctx->opts,
2017 b, hctx->fd, &hctx->fde_ndx)) {
2018 default:
2019 break;
2020 case HANDLER_FINISHED:
2021 if (b != hctx->response) buffer_free(b);
2022 if (hctx->gw_mode == GW_AUTHORIZER
2023 && (200 == con->http_status || 0 == con->http_status)) {
2025 * If we are here in AUTHORIZER mode then a request for authorizer
2026 * was processed already, and status 200 has been returned. We need
2027 * now to handle authorized request.
2029 buffer *physpath = NULL;
2031 if (!buffer_string_is_empty(host->docroot)) {
2032 buffer_copy_buffer(con->physical.doc_root, host->docroot);
2033 buffer_copy_buffer(con->physical.basedir, host->docroot);
2035 buffer_copy_buffer(con->physical.path, host->docroot);
2036 buffer_append_string_buffer(con->physical.path, con->uri.path);
2037 physpath = con->physical.path;
2040 proc->last_used = srv->cur_ts;
2041 gw_backend_close(srv, hctx);
2042 handler_ctx_clear(hctx);
2044 /* don't do more than 6 loops here; normally shouldn't happen */
2045 if (++con->loops_per_request > 5) {
2046 log_error_write(srv, __FILE__, __LINE__, "sb",
2047 "too many loops while processing request:",
2048 con->request.orig_uri);
2049 con->http_status = 500; /* Internal Server Error */
2050 con->mode = DIRECT;
2051 return HANDLER_FINISHED;
2054 /* restart the request so other handlers can process it */
2056 if (physpath) con->physical.path = NULL;
2057 connection_response_reset(srv,con);/*(includes con->http_status=0)*/
2058 /* preserve con->physical.path with modified docroot */
2059 if (physpath) con->physical.path = physpath;
2061 /*(FYI: if multiple FastCGI authorizers were to be supported,
2062 * next one could be started here instead of restarting request)*/
2064 con->mode = DIRECT;
2065 return HANDLER_COMEBACK;
2066 } else {
2067 /* we are done */
2068 gw_connection_close(srv, hctx);
2071 return HANDLER_FINISHED;
2072 case HANDLER_COMEBACK: /*(not expected; treat as error)*/
2073 case HANDLER_ERROR:
2074 if (b != hctx->response) buffer_free(b);
2075 /* (optimization to detect backend process exit while processing a
2076 * large number of ready events; (this block could be removed)) */
2077 if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid
2078 && proc->state != PROC_STATE_DIED && 0 == srv->srvconf.max_worker) {
2079 /* intentionally check proc->disabed_until before gw_proc_waitpid */
2080 if (proc->disabled_until < srv->cur_ts
2081 && 0 != gw_proc_waitpid(srv, host, proc)) {
2082 if (hctx->conf.debug) {
2083 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2084 "--- gw spawning",
2085 "\n\tsocket", proc->connection_name,
2086 "\n\tcurrent:", 1, "/", host->num_procs);
2089 if (gw_spawn_connection(srv, host, proc, hctx->conf.debug)) {
2090 log_error_write(srv, __FILE__, __LINE__, "s",
2091 "respawning failed, will retry later");
2096 if (con->file_started == 0) {
2097 /* nothing has been sent out yet, try to use another child */
2099 if (hctx->wb->bytes_out == 0 &&
2100 hctx->reconnects++ < 5) {
2102 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2103 "response not received, request not sent",
2104 "on socket:", proc->connection_name,
2105 "for", con->uri.path, "?", con->uri.query, ", reconnecting");
2107 return gw_reconnect(srv, hctx);
2110 log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
2111 "response not received, request sent:", hctx->wb->bytes_out,
2112 "on socket:", proc->connection_name, "for",
2113 con->uri.path, "?", con->uri.query, ", closing connection");
2114 } else {
2115 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2116 "response already sent out, but backend returned error",
2117 "on socket:", proc->connection_name, "for",
2118 con->uri.path, "?", con->uri.query, ", terminating connection");
2121 if (hctx->backend_error) hctx->backend_error(hctx);
2122 http_response_backend_error(srv, con);
2123 gw_connection_close(srv, hctx);
2124 return HANDLER_FINISHED;
2127 if (b != hctx->response) buffer_free(b);
2128 return HANDLER_GO_ON;
2132 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents) {
2133 gw_handler_ctx *hctx = ctx;
2134 connection *con = hctx->remote_conn;
2136 joblist_append(srv, con);
2138 if (revents & FDEVENT_IN) {
2139 handler_t rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/
2140 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
2143 if (revents & FDEVENT_OUT) {
2144 return gw_send_request(srv, hctx); /*(might invalidate hctx)*/
2147 /* perhaps this issue is already handled */
2148 if (revents & FDEVENT_HUP) {
2149 if (hctx->state == GW_STATE_CONNECT_DELAYED) {
2150 /* getoptsock will catch this one (right ?)
2152 * if we are in connect we might get an EINPROGRESS
2153 * in the first call and an FDEVENT_HUP in the
2154 * second round
2156 * FIXME: as it is a bit ugly.
2159 gw_send_request(srv, hctx);
2160 } else if (con->file_started) {
2161 /* drain any remaining data from kernel pipe buffers
2162 * even if (con->conf.stream_response_body
2163 * & FDEVENT_STREAM_RESPONSE_BUFMIN)
2164 * since event loop will spin on fd FDEVENT_HUP event
2165 * until unregistered. */
2166 handler_t rc;
2167 do {
2168 rc = gw_recv_response(srv,hctx); /*(might invalidate hctx)*/
2169 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
2170 return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
2171 } else {
2172 gw_proc *proc = hctx->proc;
2173 log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd",
2174 "error: unexpected close of gw connection for",
2175 con->uri.path, "?", con->uri.query,
2176 "(no gw process on socket:", proc->connection_name, "?)",
2177 hctx->state);
2179 gw_connection_close(srv, hctx);
2181 } else if (revents & FDEVENT_ERR) {
2182 log_error_write(srv, __FILE__, __LINE__, "s",
2183 "gw: got a FDEVENT_ERR. Don't know why.");
2185 if (hctx->backend_error) hctx->backend_error(hctx);
2186 http_response_backend_error(srv, con);
2187 gw_connection_close(srv, hctx);
2190 return HANDLER_FINISHED;
2193 handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz) {
2194 #if 0 /*(caller must handle)*/
2195 if (con->mode != DIRECT) return HANDLER_GO_ON;
2196 gw_patch_connection(srv, con, p);
2197 if (NULL == p->conf.exts) return HANDLER_GO_ON;
2198 #endif
2200 buffer *fn = uri_path_handler ? con->uri.path : con->physical.path;
2201 size_t s_len = buffer_string_length(fn);
2202 gw_extension *extension = NULL;
2203 gw_host *host = NULL;
2204 gw_handler_ctx *hctx;
2205 unsigned short gw_mode;
2207 if (0 == s_len) return HANDLER_GO_ON; /*(not expected)*/
2209 /* check p->conf.exts_auth list and then p->conf.ext_resp list
2210 * (skip p->conf.exts_auth if array is empty
2211 * or if GW_AUTHORIZER already ran in this request) */
2212 hctx = con->plugin_ctx[p->id];
2213 /*(hctx not NULL if GW_AUTHORIZER ran; hctx->ext_auth check is redundant)*/
2214 gw_mode = (NULL == hctx || NULL == hctx->ext_auth)
2215 ? 0 /*GW_AUTHORIZER p->conf.exts_auth will be searched next*/
2216 : GW_AUTHORIZER; /*GW_RESPONDER p->conf.exts_resp will be searched next*/
2218 do {
2220 gw_exts *exts;
2221 if (0 == gw_mode) {
2222 gw_mode = GW_AUTHORIZER;
2223 exts = p->conf.exts_auth;
2224 } else {
2225 gw_mode = GW_RESPONDER;
2226 exts = p->conf.exts_resp;
2229 if (0 == exts->used) continue;
2231 /* gw.map-extensions maps extensions to existing gw.server entries
2233 * gw.map-extensions = ( ".php3" => ".php" )
2235 * gw.server = ( ".php" => ... )
2237 * */
2239 /* check if extension-mapping matches */
2240 if (p->conf.ext_mapping) {
2241 for (size_t k = 0; k < p->conf.ext_mapping->used; ++k) {
2242 data_string *ds = (data_string *)p->conf.ext_mapping->data[k];
2243 size_t ct_len = buffer_string_length(ds->key);
2244 if (s_len < ct_len) continue;
2246 /* found a mapping */
2247 if (0 == memcmp(fn->ptr+s_len-ct_len, ds->key->ptr, ct_len)) {
2248 /* check if we know the extension */
2250 /* we can reuse k here */
2251 for (k = 0; k < exts->used; ++k) {
2252 extension = exts->exts[k];
2254 if (buffer_is_equal(ds->value, extension->key)) {
2255 break;
2259 if (k == exts->used) {
2260 /* found nothing */
2261 extension = NULL;
2263 break;
2268 if (extension == NULL) {
2269 size_t uri_path_len = buffer_string_length(con->uri.path);
2271 /* check if extension matches */
2272 for (size_t k = 0; k < exts->used; ++k) {
2273 gw_extension *ext = exts->exts[k];
2274 size_t ct_len = buffer_string_length(ext->key);
2276 /* check _url_ in the form "/gw_pattern" */
2277 if (ext->key->ptr[0] == '/') {
2278 if (ct_len <= uri_path_len
2279 && 0==memcmp(con->uri.path->ptr,ext->key->ptr,ct_len)){
2280 extension = ext;
2281 break;
2283 } else if (ct_len <= s_len
2284 && 0 == memcmp(fn->ptr + s_len - ct_len,
2285 ext->key->ptr, ct_len)) {
2286 /* check extension in the form ".fcg" */
2287 extension = ext;
2288 break;
2293 } while (NULL == extension && gw_mode != GW_RESPONDER);
2295 /* extension doesn't match */
2296 if (NULL == extension) {
2297 return HANDLER_GO_ON;
2300 /* check if we have at least one server for this extension up and running */
2301 host = gw_host_get(srv, con, extension, p->conf.balance, p->conf.debug);
2302 if (NULL == host) {
2303 return HANDLER_FINISHED;
2306 /* a note about no handler is not sent yet */
2307 extension->note_is_sent = 0;
2310 * if check-local is disabled, use the uri.path handler
2314 /* init handler-context */
2315 if (uri_path_handler) {
2316 if (host->check_local != 0) {
2317 return HANDLER_GO_ON;
2318 } else {
2319 /* do not split path info for authorizer */
2320 if (gw_mode != GW_AUTHORIZER) {
2321 /* the prefix is the SCRIPT_NAME,
2322 * everything from start to the next slash
2323 * this is important for check-local = "disable"
2325 * if prefix = /admin.gw
2327 * /admin.gw/foo/bar
2329 * SCRIPT_NAME = /admin.gw
2330 * PATH_INFO = /foo/bar
2332 * if prefix = /cgi-bin/
2334 * /cgi-bin/foo/bar
2336 * SCRIPT_NAME = /cgi-bin/foo
2337 * PATH_INFO = /bar
2339 * if prefix = /, and fix-root-path-name is enable
2341 * /cgi-bin/foo/bar
2343 * SCRIPT_NAME = /cgi-bin/foo
2344 * PATH_INFO = /bar
2347 char *pathinfo;
2349 /* the rewrite is only done for /prefix/? matches */
2350 if (host->fix_root_path_name && extension->key->ptr[0] == '/'
2351 && extension->key->ptr[1] == '\0'){
2352 buffer_copy_buffer(con->request.pathinfo, con->uri.path);
2353 buffer_string_set_length(con->uri.path, 0);
2354 } else if (extension->key->ptr[0] == '/'
2355 && buffer_string_length(con->uri.path)
2356 > buffer_string_length(extension->key)
2357 && (pathinfo =
2358 strchr(con->uri.path->ptr
2359 + buffer_string_length(extension->key),
2360 '/')) != NULL) {
2361 /* rewrite uri.path and pathinfo */
2363 buffer_copy_string(con->request.pathinfo, pathinfo);
2364 buffer_string_set_length(
2365 con->uri.path,
2366 buffer_string_length(con->uri.path)
2367 - buffer_string_length(con->request.pathinfo));
2373 if (!hctx) hctx = handler_ctx_init(hctx_sz);
2375 hctx->remote_conn = con;
2376 hctx->plugin_data = p;
2377 hctx->host = host;
2378 hctx->proc = NULL;
2379 hctx->ext = extension;
2380 gw_host_assign(srv, host);
2382 hctx->gw_mode = gw_mode;
2383 if (gw_mode == GW_AUTHORIZER) {
2384 hctx->ext_auth = hctx->ext;
2387 /*hctx->conf.exts = p->conf.exts;*/
2388 /*hctx->conf.exts_auth = p->conf.exts_auth;*/
2389 /*hctx->conf.exts_resp = p->conf.exts_resp;*/
2390 /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/
2391 hctx->conf.balance = p->conf.balance;
2392 hctx->conf.proto = p->conf.proto;
2393 hctx->conf.debug = p->conf.debug;
2395 hctx->opts.fdfmt = S_IFSOCK;
2396 hctx->opts.authorizer = (gw_mode == GW_AUTHORIZER);
2397 hctx->opts.local_redir = 0;
2398 hctx->opts.xsendfile_allow = host->xsendfile_allow;
2399 hctx->opts.xsendfile_docroot = host->xsendfile_docroot;
2401 con->plugin_ctx[p->id] = hctx;
2403 con->mode = p->id;
2405 if (con->conf.log_request_handling) {
2406 log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_gw");
2409 return HANDLER_GO_ON;
2412 static void gw_handle_trigger_host(server *srv, gw_host *host, int debug) {
2414 * TODO:
2416 * - add timeout for a connect to a non-gw process
2417 * (use state_timestamp + state)
2419 * perhaps we should kill a connect attempt after 10-15 seconds
2421 * currently we wait for the TCP timeout which is 180 seconds on Linux
2424 /* check each child proc to detect if proc exited */
2426 gw_proc *proc;
2427 time_t idle_timestamp;
2428 int overload = 1;
2430 for (proc = host->first; proc; proc = proc->next) {
2431 gw_proc_waitpid(srv, host, proc);
2434 gw_restart_dead_procs(srv, host, debug);
2436 /* check if adaptive spawning enabled */
2437 if (host->min_procs == host->max_procs) return;
2438 if (buffer_string_is_empty(host->bin_path)) return;
2440 for (proc = host->first; proc; proc = proc->next) {
2441 if (proc->load <= host->max_load_per_proc) {
2442 overload = 0;
2443 break;
2447 if (overload && host->num_procs && host->num_procs < host->max_procs) {
2448 /* overload, spawn new child */
2449 if (debug) {
2450 log_error_write(srv, __FILE__, __LINE__, "s",
2451 "overload detected, spawning a new child");
2454 gw_proc_spawn(srv, host, debug);
2457 idle_timestamp = srv->cur_ts - host->idle_timeout;
2458 for (proc = host->first; proc; proc = proc->next) {
2459 if (host->num_procs <= host->min_procs) break;
2460 if (0 != proc->load) continue;
2461 if (proc->pid <= 0) continue;
2462 if (proc->last_used >= idle_timestamp) continue;
2464 /* terminate proc that has been idling for a long time */
2465 if (debug) {
2466 log_error_write(srv, __FILE__, __LINE__, "ssbsd",
2467 "idle-timeout reached, terminating child:",
2468 "socket:", proc->unixsocket, "pid", proc->pid);
2471 gw_proc_kill(srv, host, proc);
2473 /* proc is now in unused, let next second handle next process */
2474 break;
2477 for (proc = host->unused_procs; proc; proc = proc->next) {
2478 gw_proc_waitpid(srv, host, proc);
2482 static void gw_handle_trigger_exts(server *srv, gw_exts *exts, int debug) {
2483 for (size_t j = 0; j < exts->used; ++j) {
2484 gw_extension *ex = exts->exts[j];
2485 for (size_t n = 0; n < ex->used; ++n) {
2486 gw_handle_trigger_host(srv, ex->hosts[n], debug);
2491 handler_t gw_handle_trigger(server *srv, void *p_d) {
2492 gw_plugin_data *p = p_d;
2493 if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid)
2494 return HANDLER_GO_ON;
2496 for (size_t i = 0; i < srv->config_context->used; i++) {
2497 gw_plugin_config *conf = p->config_storage[i];
2498 gw_exts *exts = conf->exts;
2499 int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
2500 if (NULL == exts) continue;
2501 gw_handle_trigger_exts(srv, exts, debug);
2504 return HANDLER_GO_ON;
2507 handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
2508 gw_plugin_data *p = p_d;
2509 if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid)
2510 return HANDLER_GO_ON;
2512 for (size_t i = 0; i < srv->config_context->used; ++i) {
2513 gw_plugin_config *conf = p->config_storage[i];
2514 gw_exts *exts = conf->exts;
2515 int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
2516 if (NULL == exts) continue;
2517 for (size_t j = 0; j < exts->used; ++j) {
2518 gw_extension *ex = exts->exts[j];
2519 for (size_t n = 0; n < ex->used; ++n) {
2520 gw_host *host = ex->hosts[n];
2521 gw_proc *proc;
2522 for (proc = host->first; proc; proc = proc->next) {
2523 if (!proc->is_local || proc->pid != pid) continue;
2525 gw_proc_waitpid_log(srv, host, proc, status);
2526 gw_proc_set_state(host, proc, PROC_STATE_DIED);
2527 proc->pid = 0;
2529 /* restart, but avoid spinning if child exits too quickly */
2530 if (proc->disabled_until < srv->cur_ts) {
2531 if (proc->state != PROC_STATE_KILLED)
2532 proc->disabled_until = srv->cur_ts;
2533 if (gw_spawn_connection(srv, host, proc, debug)) {
2534 log_error_write(srv, __FILE__, __LINE__, "s",
2535 "ERROR: spawning gw failed.");
2539 return HANDLER_FINISHED;
2541 for (proc = host->unused_procs; proc; proc = proc->next) {
2542 if (!proc->is_local || proc->pid != pid) continue;
2544 gw_proc_waitpid_log(srv, host, proc, status);
2545 if (proc->state != PROC_STATE_KILLED)
2546 proc->disabled_until = srv->cur_ts;
2547 gw_proc_set_state(host, proc, PROC_STATE_DIED);
2548 proc->pid = 0;
2549 return HANDLER_FINISHED;
2555 return HANDLER_GO_ON;