[core] chunkqueue perf: code reuse
[lighttpd.git] / src / gw_backend.c
blob2020111de1fa3b3d20692a81d652a6a3f52ade46
1 #include "first.h"
3 #include "gw_backend.h"
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include "sys-socket.h"
8 #ifdef HAVE_SYS_UIO_H
9 #include <sys/uio.h>
10 #endif
11 #ifdef HAVE_SYS_WAIT_H
12 #include <sys/wait.h>
13 #endif
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <signal.h>
20 #include <string.h>
21 #include <unistd.h>
23 #include "base.h"
24 #include "array.h"
25 #include "buffer.h"
26 #include "crc32.h"
27 #include "fdevent.h"
28 #include "log.h"
29 #include "sock_addr.h"
34 #include "status_counter.h"
36 static int * gw_status_get_counter(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) {
37 buffer *b = srv->tmp_buf;
38 buffer_copy_string_len(b, CONST_STR_LEN("gw.backend."));
39 buffer_append_string_buffer(b, host->id);
40 if (proc) {
41 buffer_append_string_len(b, CONST_STR_LEN("."));
42 buffer_append_int(b, proc->id);
44 buffer_append_string_len(b, tag, len);
45 return status_counter_get_counter(srv, CONST_BUF_LEN(b));
48 static void gw_proc_tag_inc(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) {
49 ++(*gw_status_get_counter(srv, host, proc, tag, len));
52 static void gw_proc_load_inc(server *srv, gw_host *host, gw_proc *proc) {
53 *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = ++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 *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = --proc->load;
61 status_counter_dec(srv, CONST_STR_LEN("gw.active-requests"));
64 static void gw_host_assign(server *srv, gw_host *host) {
65 *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = ++host->load;
68 static void gw_host_reset(server *srv, gw_host *host) {
69 *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = --host->load;
72 static int gw_status_init(server *srv, gw_host *host, gw_proc *proc) {
73 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".disabled")) = 0;
74 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".died")) = 0;
75 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".overloaded")) = 0;
76 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".connected")) = 0;
77 *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".load")) = 0;
79 *gw_status_get_counter(srv, host, NULL, CONST_STR_LEN(".load")) = 0;
81 return 0;
87 static void gw_proc_set_state(gw_host *host, gw_proc *proc, int state) {
88 if ((int)proc->state == state) return;
89 if (proc->state == PROC_STATE_RUNNING) {
90 --host->active_procs;
91 } else if (state == PROC_STATE_RUNNING) {
92 ++host->active_procs;
94 proc->state = state;
98 static gw_proc *gw_proc_init(void) {
99 gw_proc *f = calloc(1, sizeof(*f));
100 force_assert(f);
102 f->unixsocket = buffer_init();
103 f->connection_name = buffer_init();
105 f->prev = NULL;
106 f->next = NULL;
107 f->state = PROC_STATE_DIED;
109 return f;
112 static void gw_proc_free(gw_proc *f) {
113 if (!f) return;
115 gw_proc_free(f->next);
117 buffer_free(f->unixsocket);
118 buffer_free(f->connection_name);
119 free(f->saddr);
121 free(f);
124 static gw_host *gw_host_init(void) {
125 gw_host *f = calloc(1, sizeof(*f));
126 force_assert(f);
128 f->id = buffer_init();
129 f->host = buffer_init();
130 f->unixsocket = buffer_init();
131 f->docroot = buffer_init();
132 f->bin_path = buffer_init();
133 f->bin_env = array_init();
134 f->bin_env_copy = array_init();
135 f->strip_request_uri = buffer_init();
136 f->xsendfile_docroot = array_init();
138 return f;
141 static void gw_host_free(gw_host *h) {
142 if (!h) return;
143 if (h->refcount) {
144 --h->refcount;
145 return;
148 buffer_free(h->id);
149 buffer_free(h->host);
150 buffer_free(h->unixsocket);
151 buffer_free(h->docroot);
152 buffer_free(h->bin_path);
153 buffer_free(h->strip_request_uri);
154 array_free(h->bin_env);
155 array_free(h->bin_env_copy);
156 array_free(h->xsendfile_docroot);
158 gw_proc_free(h->first);
159 gw_proc_free(h->unused_procs);
161 for (size_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]);
162 free(h->args.ptr);
163 free(h);
166 static gw_exts *gw_extensions_init(void) {
167 gw_exts *f = calloc(1, sizeof(*f));
168 force_assert(f);
169 return f;
172 static void gw_extensions_free(gw_exts *f) {
173 if (!f) return;
174 for (size_t i = 0; i < f->used; ++i) {
175 gw_extension *fe = f->exts[i];
176 for (size_t j = 0; j < fe->used; ++j) {
177 gw_host_free(fe->hosts[j]);
179 buffer_free(fe->key);
180 free(fe->hosts);
181 free(fe);
183 free(f->exts);
184 free(f);
187 static int gw_extension_insert(gw_exts *ext, buffer *key, gw_host *fh) {
188 gw_extension *fe = NULL;
189 for (size_t i = 0; i < ext->used; ++i) {
190 if (buffer_is_equal(key, ext->exts[i]->key)) {
191 fe = ext->exts[i];
192 break;
196 if (NULL == fe) {
197 fe = calloc(1, sizeof(*fe));
198 force_assert(fe);
199 fe->key = buffer_init();
200 fe->last_used_ndx = -1;
201 buffer_copy_buffer(fe->key, key);
203 if (ext->used == ext->size) {
204 ext->size += 8;
205 ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
206 force_assert(ext->exts);
208 ext->exts[ext->used++] = fe;
209 fe->size = 4;
210 fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
211 force_assert(fe->hosts);
212 } else if (fe->size == fe->used) {
213 fe->size += 4;
214 fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
215 force_assert(fe->hosts);
218 fe->hosts[fe->used++] = fh;
219 return 0;
222 static void gw_proc_connect_success(server *srv, gw_host *host, gw_proc *proc, int debug) {
223 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".connected"));
224 proc->last_used = srv->cur_ts;
226 if (debug) {
227 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
228 "got proc:",
229 "pid:", proc->pid,
230 "socket:", proc->connection_name,
231 "load:", proc->load);
235 static void gw_proc_connect_error(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) {
236 log_error_write(srv, __FILE__, __LINE__, "sssb",
237 "establishing connection failed:", strerror(errnum),
238 "socket:", proc->connection_name);
240 if (!proc->is_local) {
241 proc->disabled_until = srv->cur_ts + host->disable_time;
242 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
244 else if (proc->pid == pid && proc->state == PROC_STATE_RUNNING) {
245 /* several requests from lighttpd might reference the same proc
247 * Only one of them should mark the proc
248 * and all other ones should just take a new one.
250 * If a new proc was started with the old struct, this might
251 * otherwise lead to marking a perfectly good proc as dead
253 log_error_write(srv, __FILE__, __LINE__, "sdssd",
254 "backend error; we'll disable for", host->disable_time,
255 "secs and send the request to another backend instead:",
256 "load:", host->load);
257 if (EAGAIN == errnum) {
258 /* - EAGAIN: cool down the backend; it is overloaded */
259 #ifdef __linux__
260 log_error_write(srv, __FILE__, __LINE__, "s",
261 "If this happened on Linux: You have run out of local ports. "
262 "Check the manual, section Performance how to handle this.");
263 #endif
264 if (debug) {
265 log_error_write(srv, __FILE__, __LINE__, "sbsd",
266 "This means that you have more incoming requests than your "
267 "FastCGI backend can handle in parallel. It might help to "
268 "spawn more FastCGI backends or PHP children; if not, "
269 "decrease server.max-connections. The load for this FastCGI "
270 "backend", proc->connection_name, "is", proc->load);
272 proc->disabled_until = srv->cur_ts + host->disable_time;
273 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
275 else {
276 /* we got a hard error from the backend like
277 * - ECONNREFUSED for tcp-ip sockets
278 * - ENOENT for unix-domain-sockets
280 #if 0
281 gw_proc_set_state(host, proc, PROC_STATE_DIED_WAIT_FOR_PID);
282 #else /* treat as overloaded (future: unless we send kill() signal)*/
283 proc->disabled_until = srv->cur_ts + host->disable_time;
284 gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
285 #endif
289 if (EAGAIN == errnum) {
290 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".overloaded"));
292 else {
293 gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".died"));
297 static void gw_proc_release(server *srv, gw_host *host, gw_proc *proc, int debug) {
298 gw_proc_load_dec(srv, host, proc);
300 if (debug) {
301 log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
302 "released proc:",
303 "pid:", proc->pid,
304 "socket:", proc->connection_name,
305 "load:", proc->load);
309 static void gw_proc_check_enable(server *srv, gw_host *host, gw_proc *proc) {
310 if (srv->cur_ts <= proc->disabled_until) return;
311 if (proc->state != PROC_STATE_OVERLOADED) return;
313 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
315 log_error_write(srv, __FILE__, __LINE__, "sbbdb",
316 "gw-server re-enabled:", proc->connection_name,
317 host->host, host->port, host->unixsocket);
320 static void gw_proc_waitpid_log(server *srv, gw_host *host, gw_proc *proc, int status) {
321 UNUSED(host);
322 if (WIFEXITED(status)) {
323 if (proc->state != PROC_STATE_KILLED) {
324 log_error_write(srv, __FILE__, __LINE__, "sdb",
325 "child exited:",
326 WEXITSTATUS(status), proc->connection_name);
328 } else if (WIFSIGNALED(status)) {
329 if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT
330 && WTERMSIG(status) != host->kill_signal) {
331 log_error_write(srv, __FILE__, __LINE__, "sd",
332 "child signalled:", WTERMSIG(status));
334 } else {
335 log_error_write(srv, __FILE__, __LINE__, "sd",
336 "child died somehow:", status);
340 static int gw_proc_waitpid(server *srv, gw_host *host, gw_proc *proc) {
341 int rc, status;
343 if (!proc->is_local) return 0;
344 if (proc->pid <= 0) return 0;
346 do {
347 rc = waitpid(proc->pid, &status, WNOHANG);
348 } while (-1 == rc && errno == EINTR);
349 if (0 == rc) return 0; /* child still running */
351 /* child terminated */
352 if (-1 == rc) {
353 /* EINVAL or ECHILD no child processes */
354 /* should not happen; someone else has cleaned up for us */
355 log_error_write(srv, __FILE__, __LINE__, "sddss",
356 "pid ", proc->pid, proc->state,
357 "not found:", strerror(errno));
359 else {
360 gw_proc_waitpid_log(srv, host, proc, status);
363 proc->pid = 0;
364 if (proc->state != PROC_STATE_KILLED)
365 proc->disabled_until = srv->cur_ts;
366 gw_proc_set_state(host, proc, PROC_STATE_DIED);
367 return 1;
370 static int gw_proc_sockaddr_init(server *srv, gw_host *host, gw_proc *proc) {
371 sock_addr addr;
372 socklen_t addrlen;
374 if (!buffer_string_is_empty(proc->unixsocket)) {
375 if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen,
376 proc->unixsocket->ptr, AF_UNIX, 0)) {
377 errno = EINVAL;
378 return -1;
380 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
381 buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
383 else {
384 /*(note: name resolution here is *blocking* if IP string not supplied)*/
385 if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen,
386 host->host->ptr, 0, proc->port)) {
387 errno = EINVAL;
388 return -1;
390 else {
391 /* overwrite host->host buffer with IP addr string so that
392 * any further use of gw_host does not block on DNS lookup */
393 sock_addr_inet_ntop_copy_buffer(host->host, &addr);
394 host->family = sock_addr_get_family(&addr);
396 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
397 buffer_append_string_buffer(proc->connection_name, host->host);
398 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
399 buffer_append_int(proc->connection_name, proc->port);
402 if (NULL != proc->saddr && proc->saddrlen < addrlen) {
403 free(proc->saddr);
404 proc->saddr = NULL;
406 if (NULL == proc->saddr) {
407 proc->saddr = (struct sockaddr *)malloc(addrlen);
408 force_assert(proc->saddr);
410 proc->saddrlen = addrlen;
411 memcpy(proc->saddr, &addr, addrlen);
412 return 0;
415 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
416 char *dst;
418 if (!key || !val) return -1;
420 dst = malloc(key_len + val_len + 3);
421 force_assert(dst);
422 memcpy(dst, key, key_len);
423 dst[key_len] = '=';
424 memcpy(dst + key_len + 1, val, val_len + 1); /* add the \0 from the value */
426 for (size_t i = 0; i < env->used; ++i) {
427 if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
428 free(env->ptr[i]);
429 env->ptr[i] = dst;
430 return 0;
434 if (env->size <= env->used + 1) {
435 env->size += 16;
436 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
437 force_assert(env->ptr);
440 env->ptr[env->used++] = dst;
442 return 0;
445 static int gw_spawn_connection(server *srv, gw_host *host, gw_proc *proc, int debug) {
446 int gw_fd;
447 int status;
448 struct timeval tv = { 0, 10 * 1000 };
450 if (debug) {
451 log_error_write(srv, __FILE__, __LINE__, "sdb",
452 "new proc, socket:", proc->port, proc->unixsocket);
455 gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
456 if (-1 == gw_fd) {
457 log_error_write(srv, __FILE__, __LINE__, "ss",
458 "failed:", strerror(errno));
459 return -1;
462 do {
463 status = connect(gw_fd, proc->saddr, proc->saddrlen);
464 } while (-1 == status && errno == EINTR);
466 if (-1 == status && errno != ENOENT
467 && !buffer_string_is_empty(proc->unixsocket)) {
468 log_error_write(srv, __FILE__, __LINE__, "sbss",
469 "unlink", proc->unixsocket,
470 "after connect failed:", strerror(errno));
471 unlink(proc->unixsocket->ptr);
474 close(gw_fd);
476 if (-1 == status) {
477 /* server is not up, spawn it */
478 char_array env;
479 size_t i;
480 int dfd = -1;
482 /* reopen socket */
483 gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
484 if (-1 == gw_fd) {
485 log_error_write(srv, __FILE__, __LINE__, "ss",
486 "socket failed:", strerror(errno));
487 return -1;
490 if (fdevent_set_so_reuseaddr(gw_fd, 1) < 0) {
491 log_error_write(srv, __FILE__, __LINE__, "ss",
492 "socketsockopt failed:", strerror(errno));
493 close(gw_fd);
494 return -1;
497 /* create socket */
498 if (-1 == bind(gw_fd, proc->saddr, proc->saddrlen)) {
499 log_error_write(srv, __FILE__, __LINE__, "sbs",
500 "bind failed for:",
501 proc->connection_name,
502 strerror(errno));
503 close(gw_fd);
504 return -1;
507 if (-1 == listen(gw_fd, host->listen_backlog)) {
508 log_error_write(srv, __FILE__, __LINE__, "ss",
509 "listen failed:", strerror(errno));
510 close(gw_fd);
511 return -1;
515 /* create environment */
516 env.ptr = NULL;
517 env.size = 0;
518 env.used = 0;
520 /* build clean environment */
521 if (host->bin_env_copy->used) {
522 for (i = 0; i < host->bin_env_copy->used; ++i) {
523 data_string *ds=(data_string *)host->bin_env_copy->data[i];
524 char *ge;
526 if (NULL != (ge = getenv(ds->value->ptr))) {
527 env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
530 } else {
531 char ** const e = fdevent_environ();
532 for (i = 0; e[i]; ++i) {
533 char *eq;
535 if (NULL != (eq = strchr(e[i], '='))) {
536 env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1));
541 /* create environment */
542 for (i = 0; i < host->bin_env->used; ++i) {
543 data_string *ds = (data_string *)host->bin_env->data[i];
545 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
548 for (i = 0; i < env.used; ++i) {
549 /* search for PHP_FCGI_CHILDREN */
550 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=",
551 sizeof("PHP_FCGI_CHILDREN=")-1)) {
552 break;
556 /* not found, add a default */
557 if (i == env.used) {
558 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"),
559 CONST_STR_LEN("1"));
562 env.ptr[env.used] = NULL;
565 dfd = fdevent_open_dirname(host->args.ptr[0], 1); /* permit symlinks */
566 if (-1 == dfd) {
567 log_error_write(srv, __FILE__, __LINE__, "sss",
568 "open dirname failed:", strerror(errno),
569 host->args.ptr[0]);
572 /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/
573 proc->pid = (dfd >= 0)
574 ? fdevent_fork_execve(host->args.ptr[0], host->args.ptr,
575 env.ptr, gw_fd, -1, -1, dfd)
576 : -1;
578 for (i = 0; i < env.used; ++i) free(env.ptr[i]);
579 free(env.ptr);
580 if (-1 != dfd) close(dfd);
581 close(gw_fd);
583 if (-1 == proc->pid) {
584 log_error_write(srv, __FILE__, __LINE__, "sb",
585 "gw-backend failed to start:", host->bin_path);
586 proc->pid = 0;
587 proc->disabled_until = srv->cur_ts;
588 return -1;
591 /* register process */
592 proc->last_used = srv->cur_ts;
593 proc->is_local = 1;
595 /* wait */
596 select(0, NULL, NULL, NULL, &tv);
598 if (0 != gw_proc_waitpid(srv, host, proc)) {
599 log_error_write(srv, __FILE__, __LINE__, "sb",
600 "gw-backend failed to start:", host->bin_path);
601 log_error_write(srv, __FILE__, __LINE__, "s",
602 "If you're trying to run your app as a FastCGI backend, make "
603 "sure you're using the FastCGI-enabled version. If this is PHP "
604 "on Gentoo, add 'fastcgi' to the USE flags. If this is PHP, try "
605 "removing the bytecode caches for now and try again.");
606 return -1;
608 } else {
609 proc->is_local = 0;
610 proc->pid = 0;
612 if (debug) {
613 log_error_write(srv, __FILE__, __LINE__, "sb",
614 "(debug) socket is already used; won't spawn:",
615 proc->connection_name);
619 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
620 return 0;
623 static void gw_proc_spawn(server *srv, gw_host *host, int debug) {
624 gw_proc *proc;
625 for (proc = host->unused_procs; proc; proc = proc->next) {
626 /* (proc->pid <= 0 indicates PROC_STATE_DIED, not PROC_STATE_KILLED) */
627 if (proc->pid > 0) continue;
628 /* (do not attempt to spawn another proc if a proc just exited) */
629 if (proc->disabled_until >= srv->cur_ts) return;
630 break;
632 if (proc) {
633 if (proc == host->unused_procs)
634 host->unused_procs = proc->next;
635 else
636 proc->prev->next = proc->next;
638 if (proc->next) {
639 proc->next->prev = proc->prev;
640 proc->next = NULL;
643 proc->prev = NULL;
644 } else {
645 proc = gw_proc_init();
646 proc->id = host->max_id++;
649 ++host->num_procs;
651 if (buffer_string_is_empty(host->unixsocket)) {
652 proc->port = host->port + proc->id;
653 } else {
654 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
655 buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
656 buffer_append_int(proc->unixsocket, proc->id);
659 if (0 != gw_proc_sockaddr_init(srv, host, proc)) {
660 /*(should not happen if host->host validated at startup,
661 * and translated from name to IP address at startup)*/
662 log_error_write(srv, __FILE__, __LINE__, "s",
663 "ERROR: spawning backend failed.");
664 --host->num_procs;
665 if (proc->id == host->max_id-1) --host->max_id;
666 gw_proc_free(proc);
667 } else if (gw_spawn_connection(srv, host, proc, debug)) {
668 log_error_write(srv, __FILE__, __LINE__, "s",
669 "ERROR: spawning backend failed.");
670 proc->next = host->unused_procs;
671 if (host->unused_procs)
672 host->unused_procs->prev = proc;
673 host->unused_procs = proc;
674 } else {
675 proc->next = host->first;
676 if (host->first)
677 host->first->prev = proc;
678 host->first = proc;
682 static void gw_proc_kill(server *srv, gw_host *host, gw_proc *proc) {
683 UNUSED(srv);
684 if (proc->next) proc->next->prev = proc->prev;
685 if (proc->prev) proc->prev->next = proc->next;
687 if (proc->prev == NULL) host->first = proc->next;
689 proc->prev = NULL;
690 proc->next = host->unused_procs;
691 proc->disabled_until = 0;
693 if (host->unused_procs)
694 host->unused_procs->prev = proc;
695 host->unused_procs = proc;
697 kill(proc->pid, host->kill_signal);
699 gw_proc_set_state(host, proc, PROC_STATE_KILLED);
701 --host->num_procs;
704 static gw_host * unixsocket_is_dup(gw_plugin_data *p, size_t used, buffer *unixsocket) {
705 for (size_t i = 0; i < used; ++i) {
706 gw_exts *exts = p->config_storage[i]->exts;
707 if (NULL == exts) continue;
708 for (size_t j = 0; j < exts->used; ++j) {
709 gw_extension *ex = exts->exts[j];
710 for (size_t n = 0; n < ex->used; ++n) {
711 gw_host *host = ex->hosts[n];
712 if (!buffer_string_is_empty(host->unixsocket)
713 && buffer_is_equal(host->unixsocket, unixsocket)
714 && !buffer_string_is_empty(host->bin_path))
715 return host;
720 return NULL;
723 static int parse_binpath(char_array *env, buffer *b) {
724 char *start = b->ptr;
725 char c;
726 /* search for spaces */
727 for (size_t i = 0; i < buffer_string_length(b); ++i) {
728 switch(b->ptr[i]) {
729 case ' ':
730 case '\t':
731 /* a WS, stop here and copy the argument */
733 if (env->size == env->used) {
734 env->size += 16;
735 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
738 c = b->ptr[i];
739 b->ptr[i] = '\0';
740 env->ptr[env->used++] = strdup(start);
741 b->ptr[i] = c;
743 start = b->ptr + i + 1;
744 break;
745 default:
746 break;
750 if (env->size == env->used) { /*need one extra for terminating NULL*/
751 env->size += 16;
752 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
755 /* the rest */
756 env->ptr[env->used++] = strdup(start);
758 if (env->size == env->used) { /*need one extra for terminating NULL*/
759 env->size += 16;
760 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
763 /* terminate */
764 env->ptr[env->used++] = NULL;
766 return 0;
769 enum {
770 GW_BALANCE_LEAST_CONNECTION,
771 GW_BALANCE_RR,
772 GW_BALANCE_HASH,
773 GW_BALANCE_STICKY
776 static gw_host * gw_host_get(server *srv, connection *con, gw_extension *extension, int balance, int debug) {
777 gw_host *host;
778 unsigned long last_max = ULONG_MAX;
779 int max_usage = INT_MAX;
780 int ndx = -1;
781 size_t k;
783 if (extension->used <= 1) {
784 if (1 == extension->used && extension->hosts[0]->active_procs > 0) {
785 ndx = 0;
787 } else switch(balance) {
788 case GW_BALANCE_HASH:
789 /* hash balancing */
791 if (debug) {
792 log_error_write(srv, __FILE__, __LINE__, "sd",
793 "proxy - used hash balancing, hosts:",
794 extension->used);
797 for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
798 unsigned long cur_max;
799 host = extension->hosts[k];
800 if (0 == host->active_procs) continue;
802 cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path))
803 + generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */
804 + generate_crc32c(CONST_BUF_LEN(con->uri.authority));
806 if (debug) {
807 log_error_write(srv, __FILE__, __LINE__, "sbbbd",
808 "proxy - election:", con->uri.path,
809 host->host, con->uri.authority, cur_max);
812 if (last_max < cur_max || last_max == ULONG_MAX) {
813 last_max = cur_max;
814 ndx = k;
818 break;
819 case GW_BALANCE_LEAST_CONNECTION:
820 /* fair balancing */
821 if (debug) {
822 log_error_write(srv, __FILE__, __LINE__, "s",
823 "proxy - used least connection");
826 for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->used; ++k) {
827 host = extension->hosts[k];
828 if (0 == host->active_procs) continue;
830 if (host->load < max_usage) {
831 max_usage = host->load;
832 ndx = k;
836 break;
837 case GW_BALANCE_RR:
838 /* round robin */
839 if (debug) {
840 log_error_write(srv, __FILE__, __LINE__, "s",
841 "proxy - used round-robin balancing");
844 /* just to be sure */
845 force_assert(extension->used < INT_MAX);
847 host = extension->hosts[0];
849 /* Use last_used_ndx from first host in list */
850 k = extension->last_used_ndx;
851 ndx = k + 1; /* use next host after the last one */
852 if (ndx < 0) ndx = 0;
854 /* Search first active host after last_used_ndx */
855 while (ndx < (int) extension->used
856 && 0 == (host = extension->hosts[ndx])->active_procs) ++ndx;
858 if (ndx >= (int) extension->used) {
859 /* didn't find a higher id, wrap to the start */
860 for (ndx = 0; ndx <= (int) k; ++ndx) {
861 host = extension->hosts[ndx];
862 if (0 != host->active_procs) break;
865 /* No active host found */
866 if (0 == host->active_procs) ndx = -1;
869 /* Save new index for next round */
870 extension->last_used_ndx = ndx;
872 break;
873 case GW_BALANCE_STICKY:
874 /* source sticky balancing */
876 if (debug) {
877 log_error_write(srv, __FILE__, __LINE__, "sd",
878 "proxy - used sticky balancing, hosts:",
879 extension->used);
882 for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
883 unsigned long cur_max;
884 host = extension->hosts[k];
886 if (0 == host->active_procs) continue;
888 cur_max = generate_crc32c(CONST_BUF_LEN(con->dst_addr_buf))
889 + generate_crc32c(CONST_BUF_LEN(host->host))
890 + host->port;
892 if (debug) {
893 log_error_write(srv, __FILE__, __LINE__, "sbbdd",
894 "proxy - election:", con->dst_addr_buf,
895 host->host, host->port, cur_max);
898 if (last_max < cur_max || last_max == ULONG_MAX) {
899 last_max = cur_max;
900 ndx = k;
904 break;
905 default:
906 break;
909 if (-1 != ndx) {
910 /* found a server */
911 host = extension->hosts[ndx];
913 if (debug) {
914 log_error_write(srv, __FILE__, __LINE__, "sbd",
915 "gw - found a host", host->host, host->port);
918 return host;
919 } else if (0 == srv->srvconf.max_worker) {
920 /* special-case adaptive spawning and 0 == host->min_procs */
921 for (k = 0; k < extension->used; ++k) {
922 host = extension->hosts[k];
923 if (0 == host->min_procs && 0 == host->num_procs
924 && !buffer_string_is_empty(host->bin_path)) {
925 gw_proc_spawn(srv, host, debug);
926 if (host->num_procs) return host;
931 /* all hosts are down */
932 /* sorry, we don't have a server alive for this ext */
933 con->http_status = 503; /* Service Unavailable */
934 con->mode = DIRECT;
936 /* only send the 'no handler' once */
937 if (!extension->note_is_sent) {
938 extension->note_is_sent = 1;
939 log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
940 "all handlers for", con->uri.path, "?",
941 con->uri.query, "on", extension->key, "are down.");
944 return NULL;
947 static int gw_establish_connection(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) {
948 if (-1 == connect(gw_fd, proc->saddr, proc->saddrlen)) {
949 if (errno == EINPROGRESS ||
950 errno == EALREADY ||
951 errno == EINTR) {
952 if (debug > 2) {
953 log_error_write(srv, __FILE__, __LINE__, "sb",
954 "connect delayed; will continue later:",
955 proc->connection_name);
958 return 1;
959 } else {
960 gw_proc_connect_error(srv, host, proc, pid, errno, debug);
961 return -1;
965 if (debug > 1) {
966 log_error_write(srv, __FILE__, __LINE__, "sd",
967 "connect succeeded: ", gw_fd);
970 return 0;
973 static void gw_restart_dead_procs(server *srv, gw_host *host, int debug, int trigger) {
974 for (gw_proc *proc = host->first; proc; proc = proc->next) {
975 if (debug > 2) {
976 log_error_write(srv, __FILE__, __LINE__, "sbdddd",
977 "proc:", proc->connection_name, proc->state,
978 proc->is_local, proc->load, proc->pid);
981 switch (proc->state) {
982 case PROC_STATE_RUNNING:
983 break;
984 case PROC_STATE_OVERLOADED:
985 gw_proc_check_enable(srv, host, proc);
986 break;
987 case PROC_STATE_KILLED:
988 if (trigger && ++proc->disabled_until > 4) {
989 int sig = (proc->disabled_until <= 8)
990 ? host->kill_signal
991 : proc->disabled_until <= 16 ? SIGTERM : SIGKILL;
992 kill(proc->pid, sig);
994 break;
995 case PROC_STATE_DIED_WAIT_FOR_PID:
996 /*(state should not happen in workers if server.max-worker > 0)*/
997 /*(if PROC_STATE_DIED_WAIT_FOR_PID is used in future, might want
998 * to save proc->disabled_until before gw_proc_waitpid() since
999 * gw_proc_waitpid will set proc->disabled_until to srv->cur_ts,
1000 * and so process will not be restarted below until one sec later)*/
1001 if (0 == gw_proc_waitpid(srv, host, proc)) {
1002 gw_proc_check_enable(srv, host, proc);
1005 if (proc->state != PROC_STATE_DIED) break;
1006 /* fall through *//*(we have a dead proc now)*/
1008 case PROC_STATE_DIED:
1009 /* local procs get restarted by us,
1010 * remote ones hopefully by the admin */
1012 if (!buffer_string_is_empty(host->bin_path)) {
1013 /* we still have connections bound to this proc,
1014 * let them terminate first */
1015 if (proc->load != 0) break;
1017 /* avoid spinning if child exits too quickly */
1018 if (proc->disabled_until >= srv->cur_ts) break;
1020 /* restart the child */
1022 if (debug) {
1023 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
1024 "--- gw spawning",
1025 "\n\tsocket", proc->connection_name,
1026 "\n\tcurrent:", 1, "/", host->max_procs);
1029 if (gw_spawn_connection(srv, host, proc, debug)) {
1030 log_error_write(srv, __FILE__, __LINE__, "s",
1031 "ERROR: spawning gw failed.");
1033 } else {
1034 gw_proc_check_enable(srv, host, proc);
1036 break;
1044 #include "base.h"
1045 #include "connections.h"
1046 #include "joblist.h"
1047 #include "response.h"
1050 /* ok, we need a prototype */
1051 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents);
1054 static gw_handler_ctx * handler_ctx_init(size_t sz) {
1055 gw_handler_ctx *hctx = calloc(1, 0 == sz ? sizeof(*hctx) : sz);
1056 force_assert(hctx);
1058 /*hctx->response = chunk_buffer_acquire();*//*(allocated when needed)*/
1060 hctx->request_id = 0;
1061 hctx->gw_mode = GW_RESPONDER;
1062 hctx->state = GW_STATE_INIT;
1063 hctx->proc = NULL;
1065 hctx->fd = -1;
1067 hctx->reconnects = 0;
1068 hctx->send_content_body = 1;
1070 /*hctx->rb = chunkqueue_init();*//*(allocated when needed)*/
1071 hctx->wb = chunkqueue_init();
1072 hctx->wb_reqlen = 0;
1074 return hctx;
1077 static void handler_ctx_free(gw_handler_ctx *hctx) {
1078 /* caller MUST have called gw_backend_close(srv, hctx) if necessary */
1079 if (hctx->handler_ctx_free) hctx->handler_ctx_free(hctx);
1080 chunk_buffer_release(hctx->response);
1082 chunkqueue_free(hctx->rb);
1083 chunkqueue_free(hctx->wb);
1085 free(hctx);
1088 static void handler_ctx_clear(gw_handler_ctx *hctx) {
1089 /* caller MUST have called gw_backend_close(srv, hctx) if necessary */
1091 hctx->proc = NULL;
1092 hctx->host = NULL;
1093 hctx->ext = NULL;
1094 /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/
1096 hctx->gw_mode = GW_RESPONDER;
1097 hctx->state = GW_STATE_INIT;
1098 /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/
1100 if (hctx->rb) chunkqueue_reset(hctx->rb);
1101 if (hctx->wb) chunkqueue_reset(hctx->wb);
1102 hctx->wb_reqlen = 0;
1104 if (hctx->response) buffer_clear(hctx->response);
1106 hctx->fd = -1;
1107 hctx->reconnects = 0;
1108 hctx->request_id = 0;
1109 hctx->send_content_body = 1;
1111 /*plugin_config conf;*//*(no need to reset for same request)*/
1113 /*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/
1114 /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/
1118 void * gw_init(void) {
1119 return calloc(1, sizeof(gw_plugin_data));
1123 void gw_plugin_config_free(gw_plugin_config *s) {
1124 gw_exts *exts = s->exts;
1125 if (exts) {
1126 for (size_t j = 0; j < exts->used; ++j) {
1127 gw_extension *ex = exts->exts[j];
1128 for (size_t n = 0; n < ex->used; ++n) {
1129 gw_proc *proc;
1130 gw_host *host = ex->hosts[n];
1132 for (proc = host->first; proc; proc = proc->next) {
1133 if (proc->pid > 0) {
1134 kill(proc->pid, host->kill_signal);
1137 if (proc->is_local &&
1138 !buffer_string_is_empty(proc->unixsocket)) {
1139 unlink(proc->unixsocket->ptr);
1143 for (proc = host->unused_procs; proc; proc = proc->next) {
1144 if (proc->pid > 0) {
1145 kill(proc->pid, host->kill_signal);
1147 if (proc->is_local &&
1148 !buffer_string_is_empty(proc->unixsocket)) {
1149 unlink(proc->unixsocket->ptr);
1155 gw_extensions_free(s->exts);
1156 gw_extensions_free(s->exts_auth);
1157 gw_extensions_free(s->exts_resp);
1159 array_free(s->ext_mapping);
1160 free(s);
1163 handler_t gw_free(server *srv, void *p_d) {
1164 gw_plugin_data *p = p_d;
1165 if (p->config_storage) {
1166 for (size_t i = 0; i < srv->config_context->used; ++i) {
1167 gw_plugin_config *s = p->config_storage[i];
1168 if (NULL == s) continue;
1169 gw_plugin_config_free(s);
1171 free(p->config_storage);
1173 free(p);
1174 return HANDLER_GO_ON;
1177 int gw_set_defaults_backend(server *srv, gw_plugin_data *p, data_unset *du, size_t i, int sh_exec) {
1178 /* per-module plugin_config MUST have common "base class" gw_plugin_config*/
1179 /* per-module plugin_data MUST have pointer-compatible common "base class"
1180 * with gw_plugin_data (stemming from gw_plugin_config compatibility) */
1182 data_array *da = (data_array *)du;
1183 gw_plugin_config *s = p->config_storage[i];
1184 buffer *gw_mode;
1185 gw_host *host = NULL;
1187 if (NULL == da) return 1;
1189 if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) {
1190 log_error_write(srv, __FILE__, __LINE__, "s",
1191 "unexpected value for xxxxx.server; expected "
1192 "( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1193 return 0;
1196 p->srv_pid = srv->pid;
1198 gw_mode = buffer_init();
1200 s->exts = gw_extensions_init();
1201 s->exts_auth = gw_extensions_init();
1202 s->exts_resp = gw_extensions_init();
1203 /*s->balance = GW_BALANCE_LEAST_CONNECTION;*//*(default)*/
1206 * gw.server = ( "<ext>" => ( ... ),
1207 * "<ext>" => ( ... ) )
1210 for (size_t j = 0; j < da->value->used; ++j) {
1211 data_array *da_ext = (data_array *)da->value->data[j];
1214 * da_ext->key == name of the extension
1218 * gw.server = ( "<ext>" =>
1219 * ( "<host>" => ( ... ),
1220 * "<host>" => ( ... )
1221 * ),
1222 * "<ext>" => ... )
1225 for (size_t n = 0; n < da_ext->value->used; ++n) {
1226 data_array *da_host = (data_array *)da_ext->value->data[n];
1228 config_values_t fcv[] = {
1229 { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1230 { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1231 { "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
1232 { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
1233 { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
1235 { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
1236 { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
1237 { "min-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
1238 { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
1239 { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
1240 { "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
1241 { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
1243 { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
1244 { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
1246 { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
1247 { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
1248 { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
1249 { "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */
1250 { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
1251 { "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */
1252 { "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 20 */
1253 { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 21 */
1254 { "tcp-fin-propagate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 22 */
1256 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1258 unsigned short host_mode = GW_RESPONDER;
1260 if (da_host->type != TYPE_ARRAY || !array_is_kvany(da_host->value)){
1261 log_error_write(srv, __FILE__, __LINE__, "SBS",
1262 "unexpected value for gw.server near [",
1263 da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1265 goto error;
1268 host = gw_host_init();
1269 buffer_clear(gw_mode);
1271 buffer_copy_buffer(host->id, da_host->key);
1273 host->check_local = 1;
1274 host->min_procs = 4;
1275 host->max_procs = 4;
1276 host->max_load_per_proc = 1;
1277 host->idle_timeout = 60;
1278 host->disable_time = 1;
1279 host->break_scriptfilename_for_php = 0;
1280 host->kill_signal = SIGTERM;
1281 host->fix_root_path_name = 0;
1282 host->listen_backlog = 1024;
1283 host->xsendfile_allow = 0;
1284 host->refcount = 0;
1286 fcv[0].destination = host->host;
1287 fcv[1].destination = host->docroot;
1288 fcv[2].destination = gw_mode;
1289 fcv[3].destination = host->unixsocket;
1290 fcv[4].destination = host->bin_path;
1292 fcv[5].destination = &(host->check_local);
1293 fcv[6].destination = &(host->port);
1294 fcv[7].destination = &(host->min_procs);
1295 fcv[8].destination = &(host->max_procs);
1296 fcv[9].destination = &(host->max_load_per_proc);
1297 fcv[10].destination = &(host->idle_timeout);
1298 fcv[11].destination = &(host->disable_time);
1300 fcv[12].destination = host->bin_env;
1301 fcv[13].destination = host->bin_env_copy;
1302 fcv[14].destination = &(host->break_scriptfilename_for_php);
1303 fcv[15].destination = &(host->xsendfile_allow);
1304 fcv[16].destination = host->strip_request_uri;
1305 fcv[17].destination = &(host->kill_signal);
1306 fcv[18].destination = &(host->fix_root_path_name);
1307 fcv[19].destination = &(host->listen_backlog);
1308 fcv[20].destination = &(host->xsendfile_allow);
1309 fcv[21].destination = host->xsendfile_docroot;
1310 fcv[22].destination = &(host->tcp_fin_propagate);
1312 if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) {
1313 goto error;
1316 for (size_t m = 0; m < da_host->value->used; ++m) {
1317 if (NULL != strchr(da_host->value->data[m]->key->ptr, '_')) {
1318 log_error_write(srv, __FILE__, __LINE__, "sb",
1319 "incorrect directive contains underscore ('_') instead of dash ('-'):",
1320 da_host->value->data[m]->key);
1324 if ((!buffer_string_is_empty(host->host) || host->port)
1325 && !buffer_string_is_empty(host->unixsocket)) {
1326 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1327 "either host/port or socket have to be set in:",
1328 da->key, "= (",
1329 da_ext->key, " => (",
1330 da_host->key, " ( ...");
1332 goto error;
1335 if (!buffer_string_is_empty(host->host) && *host->host->ptr == '/'
1336 && buffer_string_is_empty(host->unixsocket)) {
1337 buffer_copy_buffer(host->unixsocket, host->host);
1340 if (!buffer_string_is_empty(host->unixsocket)) {
1341 /* unix domain socket */
1342 struct sockaddr_un un;
1344 if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1345 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1346 "unixsocket is too long in:",
1347 da->key, "= (",
1348 da_ext->key, " => (",
1349 da_host->key, " ( ...");
1351 goto error;
1354 if (!buffer_string_is_empty(host->bin_path)) {
1355 gw_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket);
1356 if (NULL != duplicate) {
1357 if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) {
1358 log_error_write(srv, __FILE__, __LINE__, "sb",
1359 "duplicate unixsocket path:",
1360 host->unixsocket);
1361 goto error;
1363 gw_host_free(host);
1364 host = duplicate;
1365 ++host->refcount;
1369 host->family = AF_UNIX;
1370 } else {
1371 /* tcp/ip */
1373 if (buffer_string_is_empty(host->host) &&
1374 buffer_string_is_empty(host->bin_path)) {
1375 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1376 "host or bin-path have to be set in:",
1377 da->key, "= (",
1378 da_ext->key, " => (",
1379 da_host->key, " ( ...");
1381 goto error;
1382 } else if (0 == host->port) {
1383 host->port = 80;
1386 if (buffer_string_is_empty(host->host)) {
1387 buffer_copy_string_len(host->host,
1388 CONST_STR_LEN("127.0.0.1"));
1391 host->family = (NULL != strchr(host->host->ptr, ':'))
1392 ? AF_INET6
1393 : AF_INET;
1396 if (host->refcount) {
1397 /* already init'd; skip spawning */
1398 } else if (!buffer_string_is_empty(host->bin_path)) {
1399 /* a local socket + self spawning */
1400 struct stat st;
1401 parse_binpath(&host->args, host->bin_path);
1402 if (0 != stat(host->args.ptr[0], &st) || !S_ISREG(st.st_mode)
1403 || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
1404 log_error_write(srv, __FILE__, __LINE__, "SSs",
1405 "invalid \"bin-path\" => \"", host->bin_path->ptr,
1406 "\" (check that file exists, is regular file, "
1407 "and is executable by lighttpd)");
1410 if (sh_exec) {
1411 /*(preserve prior behavior for SCGI exec of command)*/
1412 /*(admin should really prefer to put
1413 * any complex command into a script)*/
1414 for (size_t m = 0; m < host->args.used; ++m)
1415 free(host->args.ptr[m]);
1416 free(host->args.ptr);
1418 host->args.ptr = calloc(4, sizeof(char *));
1419 force_assert(host->args.ptr);
1420 host->args.used = 3;
1421 host->args.size = 4;
1422 host->args.ptr[0] = malloc(sizeof("/bin/sh"));
1423 force_assert(host->args.ptr[0]);
1424 memcpy(host->args.ptr[0], "/bin/sh", sizeof("/bin/sh"));
1425 host->args.ptr[1] = malloc(sizeof("-c"));
1426 force_assert(host->args.ptr[1]);
1427 memcpy(host->args.ptr[1], "-c", sizeof("-c"));
1428 host->args.ptr[2] =
1429 malloc(sizeof("exec ")-1
1430 + buffer_string_length(host->bin_path) + 1);
1431 force_assert(host->args.ptr[2]);
1432 memcpy(host->args.ptr[2], "exec ", sizeof("exec ")-1);
1433 memcpy(host->args.ptr[2]+sizeof("exec ")-1,
1434 host->bin_path->ptr,
1435 buffer_string_length(host->bin_path)+1);
1436 host->args.ptr[3] = NULL;
1439 if (host->min_procs > host->max_procs)
1440 host->min_procs = host->max_procs;
1441 if (host->min_procs!= host->max_procs
1442 && 0 != srv->srvconf.max_worker) {
1443 host->min_procs = host->max_procs;
1444 log_error_write(srv, __FILE__, __LINE__, "s",
1445 "adaptive backend spawning disabled "
1446 "(server.max_worker is non-zero)");
1448 if (host->max_load_per_proc < 1)
1449 host->max_load_per_proc = 0;
1451 if (s->debug) {
1452 log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",
1453 "--- gw spawning local",
1454 "\n\tproc:", host->bin_path,
1455 "\n\tport:", host->port,
1456 "\n\tsocket", host->unixsocket,
1457 "\n\tmin-procs:", host->min_procs,
1458 "\n\tmax-procs:", host->max_procs);
1461 for (size_t pno = 0; pno < host->min_procs; ++pno) {
1462 gw_proc *proc = gw_proc_init();
1463 proc->id = host->num_procs++;
1464 host->max_id++;
1466 if (buffer_string_is_empty(host->unixsocket)) {
1467 proc->port = host->port + pno;
1468 } else {
1469 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1470 buffer_append_string_len(proc->unixsocket,
1471 CONST_STR_LEN("-"));
1472 buffer_append_int(proc->unixsocket, pno);
1475 if (s->debug) {
1476 log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1477 "--- gw spawning",
1478 "\n\tport:", host->port,
1479 "\n\tsocket", host->unixsocket,
1480 "\n\tcurrent:", pno, "/", host->max_procs);
1483 if (0 != gw_proc_sockaddr_init(srv, host, proc)) {
1484 gw_proc_free(proc);
1485 goto error;
1488 if (!srv->srvconf.preflight_check
1489 && gw_spawn_connection(srv, host, proc, s->debug)) {
1490 log_error_write(srv, __FILE__, __LINE__, "s",
1491 "[ERROR]: spawning gw failed.");
1492 gw_proc_free(proc);
1493 goto error;
1496 gw_status_init(srv, host, proc);
1498 proc->next = host->first;
1499 if (host->first) host->first->prev = proc;
1501 host->first = proc;
1503 } else {
1504 gw_proc *proc;
1506 proc = gw_proc_init();
1507 proc->id = host->num_procs++;
1508 host->max_id++;
1509 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
1511 if (buffer_string_is_empty(host->unixsocket)) {
1512 proc->port = host->port;
1513 } else {
1514 buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1517 gw_status_init(srv, host, proc);
1519 host->first = proc;
1521 host->min_procs = 1;
1522 host->max_procs = 1;
1524 if (0 != gw_proc_sockaddr_init(srv, host, proc)) goto error;
1527 if (!buffer_string_is_empty(gw_mode)) {
1528 if (strcmp(gw_mode->ptr, "responder") == 0) {
1529 host_mode = GW_RESPONDER;
1530 } else if (strcmp(gw_mode->ptr, "authorizer") == 0) {
1531 host_mode = GW_AUTHORIZER;
1532 } else {
1533 log_error_write(srv, __FILE__, __LINE__, "sbs",
1534 "WARNING: unknown gw mode:",
1535 gw_mode,"(ignored, mode set to responder)");
1539 if (host->xsendfile_docroot->used) {
1540 size_t k;
1541 for (k = 0; k < host->xsendfile_docroot->used; ++k) {
1542 data_string *ds = (data_string *)host->xsendfile_docroot->data[k];
1543 if (ds->type != TYPE_STRING) {
1544 log_error_write(srv, __FILE__, __LINE__, "s",
1545 "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )");
1546 goto error;
1548 if (ds->value->ptr[0] != '/') {
1549 log_error_write(srv, __FILE__, __LINE__, "SBs",
1550 "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
1551 goto error;
1553 buffer_path_simplify(ds->value, ds->value);
1554 buffer_append_slash(ds->value);
1558 /* s->exts is list of exts -> hosts
1559 * s->exts now used as combined list
1560 * of authorizer and responder hosts (for backend maintenance)
1561 * s->exts_auth is list of exts -> authorizer hosts
1562 * s->exts_resp is list of exts -> responder hosts
1563 * For each path/extension:
1564 * there may be an independent GW_AUTHORIZER and GW_RESPONDER
1565 * (The GW_AUTHORIZER and GW_RESPONDER could be handled by the same
1566 * host, and an admin might want to do that for large uploads,
1567 * since GW_AUTHORIZER runs prior to receiving (potentially large)
1568 * request body from client and can authorizer or deny request
1569 * prior to receiving the full upload)
1571 gw_extension_insert(s->exts, da_ext->key, host);
1573 if (host_mode == GW_AUTHORIZER) {
1574 ++host->refcount;
1575 gw_extension_insert(s->exts_auth, da_ext->key, host);
1576 } else if (host_mode == GW_RESPONDER) {
1577 ++host->refcount;
1578 gw_extension_insert(s->exts_resp, da_ext->key, host);
1579 } /*(else should have been rejected above)*/
1581 host = NULL;
1585 buffer_free(gw_mode);
1586 return 1;
1588 error:
1589 if (NULL != host) gw_host_free(host);
1590 buffer_free(gw_mode);
1591 return 0;
1594 int gw_set_defaults_balance(server *srv, gw_plugin_config *s, data_unset *du) {
1595 buffer *b;
1596 if (NULL == du) {
1597 b = NULL;
1598 } else if (du->type == TYPE_STRING) {
1599 b = ((data_string *)du)->value;
1600 } else {
1601 log_error_write(srv, __FILE__, __LINE__, "s",
1602 "unexpected type for xxxxx.balance; expected string");
1603 return 0;
1605 if (buffer_string_is_empty(b)) {
1606 s->balance = GW_BALANCE_LEAST_CONNECTION;
1607 } else if (buffer_is_equal_string(b, CONST_STR_LEN("fair"))) {
1608 s->balance = GW_BALANCE_LEAST_CONNECTION;
1609 } else if (buffer_is_equal_string(b, CONST_STR_LEN("least-connection"))) {
1610 s->balance = GW_BALANCE_LEAST_CONNECTION;
1611 } else if (buffer_is_equal_string(b, CONST_STR_LEN("round-robin"))) {
1612 s->balance = GW_BALANCE_RR;
1613 } else if (buffer_is_equal_string(b, CONST_STR_LEN("hash"))) {
1614 s->balance = GW_BALANCE_HASH;
1615 } else if (buffer_is_equal_string(b, CONST_STR_LEN("sticky"))) {
1616 s->balance = GW_BALANCE_STICKY;
1617 } else {
1618 log_error_write(srv, __FILE__, __LINE__, "sb",
1619 "xxxxx.balance has to be one of: "
1620 "least-connection, round-robin, hash, sticky, but not:",
1622 return 0;
1624 return 1;
1627 static void gw_set_state(server *srv, gw_handler_ctx *hctx, gw_connection_state_t state) {
1628 hctx->state = state;
1629 hctx->state_timestamp = srv->cur_ts;
1633 void gw_set_transparent(server *srv, gw_handler_ctx *hctx) {
1634 if (AF_UNIX != hctx->host->family) {
1635 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1636 /*(error, but not critical)*/
1639 hctx->wb_reqlen = -1;
1640 gw_set_state(srv, hctx, GW_STATE_WRITE);
1644 static void gw_backend_close(server *srv, gw_handler_ctx *hctx) {
1645 if (hctx->fd >= 0) {
1646 fdevent_fdnode_event_del(srv->ev, hctx->fdn);
1647 /*fdevent_unregister(srv->ev, hctx->fd);*//*(handled below)*/
1648 fdevent_sched_close(srv->ev, hctx->fd, 1);
1649 hctx->fdn = NULL;
1650 hctx->fd = -1;
1653 if (hctx->host) {
1654 if (hctx->proc) {
1655 gw_proc_release(srv, hctx->host, hctx->proc, hctx->conf.debug);
1656 hctx->proc = NULL;
1659 gw_host_reset(srv, hctx->host);
1660 hctx->host = NULL;
1664 static void gw_connection_close(server *srv, gw_handler_ctx *hctx) {
1665 gw_plugin_data *p = hctx->plugin_data;
1666 connection *con = hctx->remote_conn;
1668 gw_backend_close(srv, hctx);
1669 handler_ctx_free(hctx);
1670 con->plugin_ctx[p->id] = NULL;
1672 if (con->mode == p->id) {
1673 http_response_backend_done(srv, con);
1677 static handler_t gw_reconnect(server *srv, gw_handler_ctx *hctx) {
1678 gw_backend_close(srv, hctx);
1680 hctx->host = gw_host_get(srv, hctx->remote_conn, hctx->ext,
1681 hctx->conf.balance, hctx->conf.debug);
1682 if (NULL == hctx->host) return HANDLER_FINISHED;
1684 gw_host_assign(srv, hctx->host);
1685 hctx->request_id = 0;
1686 hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow;
1687 hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot;
1688 gw_set_state(srv, hctx, GW_STATE_INIT);
1689 return HANDLER_COMEBACK;
1693 handler_t gw_connection_reset(server *srv, connection *con, void *p_d) {
1694 gw_plugin_data *p = p_d;
1695 gw_handler_ctx *hctx = con->plugin_ctx[p->id];
1696 if (hctx) gw_connection_close(srv, hctx);
1698 return HANDLER_GO_ON;
1702 static void gw_conditional_tcp_fin(server *srv, gw_handler_ctx *hctx) {
1703 connection *con = hctx->remote_conn;
1704 /*assert(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN);*/
1705 if (!chunkqueue_is_empty(hctx->wb)) return;
1706 if (!hctx->host->tcp_fin_propagate) return;
1707 if (hctx->gw_mode == GW_AUTHORIZER) return;
1708 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR)
1709 return;
1711 /* propagate shutdown SHUT_WR to backend if TCP half-close on con->fd */
1712 con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR;
1713 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1714 con->is_readable = 0;
1715 shutdown(hctx->fd, SHUT_WR);
1716 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1719 static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) {
1720 switch(hctx->state) {
1721 case GW_STATE_INIT:
1722 /* do we have a running process for this host (max-procs) ? */
1723 hctx->proc = NULL;
1725 for (gw_proc *proc = hctx->host->first; proc; proc = proc->next) {
1726 if (proc->state == PROC_STATE_RUNNING) {
1727 hctx->proc = proc;
1728 break;
1732 /* all children are dead */
1733 if (hctx->proc == NULL) {
1734 return HANDLER_ERROR;
1737 /* check the other procs if they have a lower load */
1738 for (gw_proc *proc = hctx->proc->next; proc; proc = proc->next) {
1739 if (proc->state != PROC_STATE_RUNNING) continue;
1740 if (proc->load < hctx->proc->load) hctx->proc = proc;
1743 gw_proc_load_inc(srv, hctx->host, hctx->proc);
1745 hctx->fd = fdevent_socket_nb_cloexec(hctx->host->family,SOCK_STREAM,0);
1746 if (-1 == hctx->fd) {
1747 if (errno == EMFILE || errno == EINTR) {
1748 log_error_write(srv, __FILE__, __LINE__, "sd",
1749 "wait for fd at connection:",
1750 hctx->remote_conn->fd);
1751 return HANDLER_WAIT_FOR_FD;
1754 log_error_write(srv, __FILE__, __LINE__, "ssdd",
1755 "socket failed:", strerror(errno),
1756 srv->cur_fds, srv->max_fds);
1757 return HANDLER_ERROR;
1760 srv->cur_fds++;
1762 hctx->fdn = fdevent_register(srv->ev,hctx->fd,gw_handle_fdevent,hctx);
1764 if (hctx->proc->is_local) {
1765 hctx->pid = hctx->proc->pid;
1768 switch (gw_establish_connection(srv, hctx->host, hctx->proc, hctx->pid,
1769 hctx->fd, hctx->conf.debug)) {
1770 case 1: /* connection is in progress */
1771 fdevent_fdnode_event_set(srv->ev, hctx->fdn, FDEVENT_OUT);
1772 gw_set_state(srv, hctx, GW_STATE_CONNECT_DELAYED);
1773 return HANDLER_WAIT_FOR_EVENT;
1774 case -1:/* connection error */
1775 return HANDLER_ERROR;
1776 case 0: /* everything is ok, go on */
1777 hctx->reconnects = 0;
1778 break;
1780 /* fall through */
1781 case GW_STATE_CONNECT_DELAYED:
1782 if (hctx->state == GW_STATE_CONNECT_DELAYED) { /*(not GW_STATE_INIT)*/
1783 int socket_error = fdevent_connect_status(hctx->fd);
1784 if (socket_error != 0) {
1785 gw_proc_connect_error(srv, hctx->host, hctx->proc, hctx->pid,
1786 socket_error, hctx->conf.debug);
1787 return HANDLER_ERROR;
1789 /* go on with preparing the request */
1792 gw_proc_connect_success(srv, hctx->host, hctx->proc, hctx->conf.debug);
1794 gw_set_state(srv, hctx, GW_STATE_PREPARE_WRITE);
1795 /* fall through */
1796 case GW_STATE_PREPARE_WRITE:
1797 /* ok, we have the connection */
1800 handler_t rc = hctx->create_env(srv, hctx);
1801 if (HANDLER_GO_ON != rc) {
1802 if (HANDLER_FINISHED != rc && HANDLER_ERROR != rc)
1803 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1804 return rc;
1808 /*(disable Nagle algorithm if streaming and content-length unknown)*/
1809 if (AF_UNIX != hctx->host->family) {
1810 connection *con = hctx->remote_conn;
1811 if (con->request.content_length < 0) {
1812 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1813 /*(error, but not critical)*/
1818 fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_IN|FDEVENT_RDHUP);
1819 gw_set_state(srv, hctx, GW_STATE_WRITE);
1820 /* fall through */
1821 case GW_STATE_WRITE:
1822 if (!chunkqueue_is_empty(hctx->wb)) {
1823 int ret;
1824 #if 0
1825 if (hctx->conf.debug > 1) {
1826 log_error_write(srv, __FILE__, __LINE__, "sdsx",
1827 "send data to backend ( fd =", hctx->fd,
1828 "), size =", chunkqueue_length(hctx->wb));
1830 #endif
1831 ret = srv->network_backend_write(srv, hctx->fd, hctx->wb,
1832 MAX_WRITE_LIMIT);
1834 if (ret < 0) {
1835 switch(errno) {
1836 case EPIPE:
1837 case ENOTCONN:
1838 case ECONNRESET:
1839 /* the connection got dropped after accept()
1840 * we don't care about that --
1841 * if you accept() it, you have to handle it.
1843 log_error_write(srv, __FILE__, __LINE__, "ssosb",
1844 "connection was dropped after accept() "
1845 "(perhaps the gw process died),",
1846 "write-offset:", hctx->wb->bytes_out,
1847 "socket:", hctx->proc->connection_name);
1848 return HANDLER_ERROR;
1849 default:
1850 log_error_write(srv, __FILE__, __LINE__, "ssd",
1851 "write failed:", strerror(errno), errno);
1852 return HANDLER_ERROR;
1857 if (hctx->wb->bytes_out == hctx->wb_reqlen) {
1858 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1859 gw_set_state(srv, hctx, GW_STATE_READ);
1860 } else {
1861 off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out;
1862 if ((hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0)
1863 && wblen < 65536 - 16384) {
1864 connection *con = hctx->remote_conn;
1865 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
1866 if (!(con->conf.stream_request_body
1867 & FDEVENT_STREAM_REQUEST_POLLIN)) {
1868 con->conf.stream_request_body |=
1869 FDEVENT_STREAM_REQUEST_POLLIN;
1870 con->is_readable = 1;/*trigger optimistic read from client*/
1873 if (0 == wblen) {
1874 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1875 } else {
1876 fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_OUT);
1880 if (hctx->remote_conn->conf.stream_request_body
1881 & FDEVENT_STREAM_REQUEST_TCP_FIN)
1882 gw_conditional_tcp_fin(srv, hctx);
1884 return HANDLER_WAIT_FOR_EVENT;
1885 case GW_STATE_READ:
1886 /* waiting for a response */
1887 return HANDLER_WAIT_FOR_EVENT;
1888 default:
1889 log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
1890 return HANDLER_ERROR;
1894 static handler_t gw_write_error(server *srv, gw_handler_ctx *hctx) {
1895 connection *con = hctx->remote_conn;
1896 int status = con->http_status;
1898 if (hctx->state == GW_STATE_INIT ||
1899 hctx->state == GW_STATE_CONNECT_DELAYED) {
1901 /* (optimization to detect backend process exit while processing a
1902 * large number of ready events; (this block could be removed)) */
1903 if (0 == srv->srvconf.max_worker)
1904 gw_restart_dead_procs(srv, hctx->host, hctx->conf.debug, 0);
1906 /* cleanup this request and let request handler start request again */
1907 if (hctx->reconnects++ < 5) return gw_reconnect(srv, hctx);
1910 if (hctx->backend_error) hctx->backend_error(hctx);
1911 gw_connection_close(srv, hctx);
1912 con->http_status = (status == 400) ? 400 : 503;
1913 return HANDLER_FINISHED;
1916 static handler_t gw_send_request(server *srv, gw_handler_ctx *hctx) {
1917 handler_t rc = gw_write_request(srv, hctx);
1918 return (HANDLER_ERROR != rc) ? rc : gw_write_error(srv, hctx);
1922 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx);
1925 handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d) {
1926 gw_plugin_data *p = p_d;
1927 gw_handler_ctx *hctx = con->plugin_ctx[p->id];
1928 if (NULL == hctx) return HANDLER_GO_ON;
1929 if (con->mode != p->id) return HANDLER_GO_ON; /* not my job */
1931 if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
1932 && con->file_started) {
1933 if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
1934 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_IN);
1936 else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) {
1937 /* optimistic read from backend */
1938 handler_t rc;
1939 rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/
1940 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
1941 fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_IN);
1945 /* (do not receive request body before GW_AUTHORIZER has run or else
1946 * the request body is discarded with handler_ctx_clear() after running
1947 * the FastCGI Authorizer) */
1949 if (hctx->gw_mode != GW_AUTHORIZER
1950 && (0 == hctx->wb->bytes_in
1951 ? (con->state == CON_STATE_READ_POST || -1 == hctx->wb_reqlen)
1952 : (hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0))) {
1953 /* leave excess data in con->request_content_queue, which is
1954 * buffered to disk if too large and backend can not keep up */
1955 /*(64k - 4k to attempt to avoid temporary files
1956 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
1957 if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096) {
1958 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) {
1959 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1961 if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT;
1963 else {
1964 handler_t r = connection_handle_read_post_state(srv, con);
1965 chunkqueue *req_cq = con->request_content_queue;
1966 #if 0 /*(not reached since we send 411 Length Required below)*/
1967 if (hctx->wb_reqlen < -1 && con->request.content_length >= 0) {
1968 /* (completed receiving Transfer-Encoding: chunked) */
1969 hctx->wb_reqlen= -hctx->wb_reqlen + con->request.content_length;
1970 if (hctx->stdin_append) {
1971 handler_t rc = hctx->stdin_append(srv, hctx);
1972 if (HANDLER_GO_ON != rc) return rc;
1975 #endif
1976 if ((0 != hctx->wb->bytes_in || -1 == hctx->wb_reqlen)
1977 && !chunkqueue_is_empty(req_cq)) {
1978 if (hctx->stdin_append) {
1979 handler_t rc = hctx->stdin_append(srv, hctx);
1980 if (HANDLER_GO_ON != rc) return rc;
1982 else
1983 chunkqueue_append_chunkqueue(hctx->wb, req_cq);
1984 if (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_OUT) {
1985 return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
1988 if (r != HANDLER_GO_ON) return r;
1991 /* XXX: create configurable flag */
1992 /* CGI environment requires that Content-Length be set.
1993 * Send 411 Length Required if Content-Length missing.
1994 * (occurs here if client sends Transfer-Encoding: chunked
1995 * and module is flagged to stream request body to backend) */
1996 /* proxy currently sends HTTP/1.0 request and ideally should send
1997 * Content-Length with request if request body is present, so
1998 * send 411 Length Required if Content-Length missing. */
1999 if (-1 == con->request.content_length) {
2000 return connection_handle_read_post_error(srv, con, 411);
2006 handler_t rc =((0==hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb))
2007 && hctx->state != GW_STATE_CONNECT_DELAYED)
2008 ? gw_send_request(srv, hctx)
2009 : HANDLER_WAIT_FOR_EVENT;
2010 if (HANDLER_WAIT_FOR_EVENT != rc) return rc;
2013 if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN)
2014 gw_conditional_tcp_fin(srv, hctx);
2016 return HANDLER_WAIT_FOR_EVENT;
2020 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx) {
2021 connection *con = hctx->remote_conn;
2022 gw_proc *proc = hctx->proc;
2023 gw_host *host = hctx->host;
2024 /*(XXX: make this a configurable flag for other protocols)*/
2025 buffer *b = hctx->opts.backend == BACKEND_FASTCGI
2026 ? chunk_buffer_acquire()
2027 : hctx->response;
2029 handler_t rc =
2030 http_response_read(srv, hctx->remote_conn, &hctx->opts, b, hctx->fdn);
2032 if (b != hctx->response) chunk_buffer_release(b);
2034 switch (rc) {
2035 default:
2036 return HANDLER_GO_ON;
2037 case HANDLER_FINISHED:
2038 if (hctx->gw_mode == GW_AUTHORIZER
2039 && (200 == con->http_status || 0 == con->http_status)) {
2041 * If we are here in AUTHORIZER mode then a request for authorizer
2042 * was processed already, and status 200 has been returned. We need
2043 * now to handle authorized request.
2045 buffer *physpath = NULL;
2047 if (!buffer_string_is_empty(host->docroot)) {
2048 buffer_copy_buffer(con->physical.doc_root, host->docroot);
2049 buffer_copy_buffer(con->physical.basedir, host->docroot);
2051 buffer_copy_buffer(con->physical.path, host->docroot);
2052 buffer_append_string_buffer(con->physical.path, con->uri.path);
2053 physpath = con->physical.path;
2056 proc->last_used = srv->cur_ts;
2057 gw_backend_close(srv, hctx);
2058 handler_ctx_clear(hctx);
2060 /* don't do more than 6 loops here; normally shouldn't happen */
2061 if (++con->loops_per_request > 5) {
2062 log_error_write(srv, __FILE__, __LINE__, "sb",
2063 "too many loops while processing request:",
2064 con->request.orig_uri);
2065 con->http_status = 500; /* Internal Server Error */
2066 con->mode = DIRECT;
2067 return HANDLER_FINISHED;
2070 /* restart the request so other handlers can process it */
2072 if (physpath) con->physical.path = NULL;
2073 connection_response_reset(srv,con);/*(includes con->http_status=0)*/
2074 /* preserve con->physical.path with modified docroot */
2075 if (physpath) con->physical.path = physpath;
2077 /*(FYI: if multiple FastCGI authorizers were to be supported,
2078 * next one could be started here instead of restarting request)*/
2080 con->mode = DIRECT;
2081 return HANDLER_COMEBACK;
2082 } else {
2083 /* we are done */
2084 gw_connection_close(srv, hctx);
2087 return HANDLER_FINISHED;
2088 case HANDLER_COMEBACK: /*(not expected; treat as error)*/
2089 case HANDLER_ERROR:
2090 /* (optimization to detect backend process exit while processing a
2091 * large number of ready events; (this block could be removed)) */
2092 if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid
2093 && proc->state != PROC_STATE_DIED && 0 == srv->srvconf.max_worker) {
2094 /* intentionally check proc->disabed_until before gw_proc_waitpid */
2095 if (proc->disabled_until < srv->cur_ts
2096 && 0 != gw_proc_waitpid(srv, host, proc)) {
2097 if (hctx->conf.debug) {
2098 log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2099 "--- gw spawning",
2100 "\n\tsocket", proc->connection_name,
2101 "\n\tcurrent:", 1, "/", host->num_procs);
2104 if (gw_spawn_connection(srv, host, proc, hctx->conf.debug)) {
2105 log_error_write(srv, __FILE__, __LINE__, "s",
2106 "respawning failed, will retry later");
2111 if (con->file_started == 0) {
2112 /* nothing has been sent out yet, try to use another child */
2114 if (hctx->wb->bytes_out == 0 &&
2115 hctx->reconnects++ < 5) {
2117 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2118 "response not received, request not sent",
2119 "on socket:", proc->connection_name,
2120 "for", con->uri.path, "?", con->uri.query, ", reconnecting");
2122 return gw_reconnect(srv, hctx);
2125 log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
2126 "response not received, request sent:", hctx->wb->bytes_out,
2127 "on socket:", proc->connection_name, "for",
2128 con->uri.path, "?", con->uri.query, ", closing connection");
2129 } else {
2130 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2131 "response already sent out, but backend returned error",
2132 "on socket:", proc->connection_name, "for",
2133 con->uri.path, "?", con->uri.query, ", terminating connection");
2136 if (hctx->backend_error) hctx->backend_error(hctx);
2137 http_response_backend_error(srv, con);
2138 gw_connection_close(srv, hctx);
2139 return HANDLER_FINISHED;
2144 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents) {
2145 gw_handler_ctx *hctx = ctx;
2146 connection *con = hctx->remote_conn;
2148 joblist_append(srv, con);
2150 if (revents & FDEVENT_IN) {
2151 handler_t rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/
2152 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
2155 if (revents & FDEVENT_OUT) {
2156 return gw_send_request(srv, hctx); /*(might invalidate hctx)*/
2159 /* perhaps this issue is already handled */
2160 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) {
2161 if (hctx->state == GW_STATE_CONNECT_DELAYED) {
2162 /* getoptsock will catch this one (right ?)
2164 * if we are in connect we might get an EINPROGRESS
2165 * in the first call and an FDEVENT_HUP in the
2166 * second round
2168 * FIXME: as it is a bit ugly.
2171 gw_send_request(srv, hctx);
2172 } else if (con->file_started) {
2173 /* drain any remaining data from kernel pipe buffers
2174 * even if (con->conf.stream_response_body
2175 * & FDEVENT_STREAM_RESPONSE_BUFMIN)
2176 * since event loop will spin on fd FDEVENT_HUP event
2177 * until unregistered. */
2178 handler_t rc;
2179 const unsigned short flags = con->conf.stream_response_body;
2180 con->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN;
2181 con->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP;
2182 do {
2183 rc = gw_recv_response(srv,hctx); /*(might invalidate hctx)*/
2184 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
2185 con->conf.stream_response_body = flags;
2186 return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
2187 } else {
2188 gw_proc *proc = hctx->proc;
2189 log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd",
2190 "error: unexpected close of gw connection for",
2191 con->uri.path, "?", con->uri.query,
2192 "(no gw process on socket:", proc->connection_name, "?)",
2193 hctx->state);
2195 gw_connection_close(srv, hctx);
2197 } else if (revents & FDEVENT_ERR) {
2198 log_error_write(srv, __FILE__, __LINE__, "s",
2199 "gw: got a FDEVENT_ERR. Don't know why.");
2201 if (hctx->backend_error) hctx->backend_error(hctx);
2202 http_response_backend_error(srv, con);
2203 gw_connection_close(srv, hctx);
2206 return HANDLER_FINISHED;
2209 handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz) {
2210 #if 0 /*(caller must handle)*/
2211 if (con->mode != DIRECT) return HANDLER_GO_ON;
2212 gw_patch_connection(srv, con, p);
2213 if (NULL == p->conf.exts) return HANDLER_GO_ON;
2214 #endif
2216 buffer *fn = uri_path_handler ? con->uri.path : con->physical.path;
2217 size_t s_len = buffer_string_length(fn);
2218 gw_extension *extension = NULL;
2219 gw_host *host = NULL;
2220 gw_handler_ctx *hctx;
2221 unsigned short gw_mode;
2223 if (0 == s_len) return HANDLER_GO_ON; /*(not expected)*/
2225 /* check p->conf.exts_auth list and then p->conf.ext_resp list
2226 * (skip p->conf.exts_auth if array is empty
2227 * or if GW_AUTHORIZER already ran in this request) */
2228 hctx = con->plugin_ctx[p->id];
2229 /*(hctx not NULL if GW_AUTHORIZER ran; hctx->ext_auth check is redundant)*/
2230 gw_mode = (NULL == hctx || NULL == hctx->ext_auth)
2231 ? 0 /*GW_AUTHORIZER p->conf.exts_auth will be searched next*/
2232 : GW_AUTHORIZER; /*GW_RESPONDER p->conf.exts_resp will be searched next*/
2234 do {
2236 gw_exts *exts;
2237 if (0 == gw_mode) {
2238 gw_mode = GW_AUTHORIZER;
2239 exts = p->conf.exts_auth;
2240 } else {
2241 gw_mode = GW_RESPONDER;
2242 exts = p->conf.exts_resp;
2245 if (0 == exts->used) continue;
2247 /* gw.map-extensions maps extensions to existing gw.server entries
2249 * gw.map-extensions = ( ".php3" => ".php" )
2251 * gw.server = ( ".php" => ... )
2253 * */
2255 /* check if extension-mapping matches */
2256 if (p->conf.ext_mapping) {
2257 data_string *ds =
2258 (data_string *)array_match_key_suffix(p->conf.ext_mapping, fn);
2259 if (NULL != ds) {
2260 /* found a mapping */
2261 /* check if we know the extension */
2262 size_t k;
2263 for (k = 0; k < exts->used; ++k) {
2264 extension = exts->exts[k];
2266 if (buffer_is_equal(ds->value, extension->key)) {
2267 break;
2271 if (k == exts->used) {
2272 /* found nothing */
2273 extension = NULL;
2278 if (extension == NULL) {
2279 size_t uri_path_len = buffer_string_length(con->uri.path);
2281 /* check if extension matches */
2282 for (size_t k = 0; k < exts->used; ++k) {
2283 gw_extension *ext = exts->exts[k];
2284 size_t ct_len = buffer_string_length(ext->key);
2286 /* check _url_ in the form "/gw_pattern" */
2287 if (ext->key->ptr[0] == '/') {
2288 if (ct_len <= uri_path_len
2289 && 0==memcmp(con->uri.path->ptr,ext->key->ptr,ct_len)){
2290 extension = ext;
2291 break;
2293 } else if (ct_len <= s_len
2294 && 0 == memcmp(fn->ptr + s_len - ct_len,
2295 ext->key->ptr, ct_len)) {
2296 /* check extension in the form ".fcg" */
2297 extension = ext;
2298 break;
2303 } while (NULL == extension && gw_mode != GW_RESPONDER);
2305 /* extension doesn't match */
2306 if (NULL == extension) {
2307 return HANDLER_GO_ON;
2310 /* check if we have at least one server for this extension up and running */
2311 host = gw_host_get(srv, con, extension, p->conf.balance, p->conf.debug);
2312 if (NULL == host) {
2313 return HANDLER_FINISHED;
2316 /* a note about no handler is not sent yet */
2317 extension->note_is_sent = 0;
2320 * if check-local is disabled, use the uri.path handler
2324 /* init handler-context */
2325 if (uri_path_handler) {
2326 if (host->check_local != 0) {
2327 return HANDLER_GO_ON;
2328 } else {
2329 /* do not split path info for authorizer */
2330 if (gw_mode != GW_AUTHORIZER) {
2331 /* the prefix is the SCRIPT_NAME,
2332 * everything from start to the next slash
2333 * this is important for check-local = "disable"
2335 * if prefix = /admin.gw
2337 * /admin.gw/foo/bar
2339 * SCRIPT_NAME = /admin.gw
2340 * PATH_INFO = /foo/bar
2342 * if prefix = /cgi-bin/
2344 * /cgi-bin/foo/bar
2346 * SCRIPT_NAME = /cgi-bin/foo
2347 * PATH_INFO = /bar
2349 * if prefix = /, and fix-root-path-name is enable
2351 * /cgi-bin/foo/bar
2353 * SCRIPT_NAME = /cgi-bin/foo
2354 * PATH_INFO = /bar
2357 char *pathinfo;
2359 /* the rewrite is only done for /prefix/? matches */
2360 if (host->fix_root_path_name && extension->key->ptr[0] == '/'
2361 && extension->key->ptr[1] == '\0'){
2362 buffer_copy_buffer(con->request.pathinfo, con->uri.path);
2363 buffer_clear(con->uri.path);
2364 } else if (extension->key->ptr[0] == '/'
2365 && buffer_string_length(con->uri.path)
2366 > buffer_string_length(extension->key)
2367 && (pathinfo =
2368 strchr(con->uri.path->ptr
2369 + buffer_string_length(extension->key),
2370 '/')) != NULL) {
2371 /* rewrite uri.path and pathinfo */
2373 buffer_copy_string(con->request.pathinfo, pathinfo);
2374 buffer_string_set_length(
2375 con->uri.path,
2376 buffer_string_length(con->uri.path)
2377 - buffer_string_length(con->request.pathinfo));
2383 if (!hctx) hctx = handler_ctx_init(hctx_sz);
2385 hctx->remote_conn = con;
2386 hctx->plugin_data = p;
2387 hctx->host = host;
2388 hctx->proc = NULL;
2389 hctx->ext = extension;
2390 gw_host_assign(srv, host);
2392 hctx->gw_mode = gw_mode;
2393 if (gw_mode == GW_AUTHORIZER) {
2394 hctx->ext_auth = hctx->ext;
2397 /*hctx->conf.exts = p->conf.exts;*/
2398 /*hctx->conf.exts_auth = p->conf.exts_auth;*/
2399 /*hctx->conf.exts_resp = p->conf.exts_resp;*/
2400 /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/
2401 hctx->conf.balance = p->conf.balance;
2402 hctx->conf.proto = p->conf.proto;
2403 hctx->conf.debug = p->conf.debug;
2405 hctx->opts.fdfmt = S_IFSOCK;
2406 hctx->opts.authorizer = (gw_mode == GW_AUTHORIZER);
2407 hctx->opts.local_redir = 0;
2408 hctx->opts.xsendfile_allow = host->xsendfile_allow;
2409 hctx->opts.xsendfile_docroot = host->xsendfile_docroot;
2411 con->plugin_ctx[p->id] = hctx;
2413 con->mode = p->id;
2415 if (con->conf.log_request_handling) {
2416 log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_gw");
2419 return HANDLER_GO_ON;
2422 static void gw_handle_trigger_host(server *srv, gw_host *host, int debug) {
2424 * TODO:
2426 * - add timeout for a connect to a non-gw process
2427 * (use state_timestamp + state)
2429 * perhaps we should kill a connect attempt after 10-15 seconds
2431 * currently we wait for the TCP timeout which is 180 seconds on Linux
2434 /* check each child proc to detect if proc exited */
2436 gw_proc *proc;
2437 time_t idle_timestamp;
2438 int overload = 1;
2440 for (proc = host->first; proc; proc = proc->next) {
2441 gw_proc_waitpid(srv, host, proc);
2444 gw_restart_dead_procs(srv, host, debug, 1);
2446 /* check if adaptive spawning enabled */
2447 if (host->min_procs == host->max_procs) return;
2448 if (buffer_string_is_empty(host->bin_path)) return;
2450 for (proc = host->first; proc; proc = proc->next) {
2451 if (proc->load <= host->max_load_per_proc) {
2452 overload = 0;
2453 break;
2457 if (overload && host->num_procs && host->num_procs < host->max_procs) {
2458 /* overload, spawn new child */
2459 if (debug) {
2460 log_error_write(srv, __FILE__, __LINE__, "s",
2461 "overload detected, spawning a new child");
2464 gw_proc_spawn(srv, host, debug);
2467 idle_timestamp = srv->cur_ts - host->idle_timeout;
2468 for (proc = host->first; proc; proc = proc->next) {
2469 if (host->num_procs <= host->min_procs) break;
2470 if (0 != proc->load) continue;
2471 if (proc->pid <= 0) continue;
2472 if (proc->last_used >= idle_timestamp) continue;
2474 /* terminate proc that has been idling for a long time */
2475 if (debug) {
2476 log_error_write(srv, __FILE__, __LINE__, "ssbsd",
2477 "idle-timeout reached, terminating child:",
2478 "socket:", proc->unixsocket, "pid", proc->pid);
2481 gw_proc_kill(srv, host, proc);
2483 /* proc is now in unused, let next second handle next process */
2484 break;
2487 for (proc = host->unused_procs; proc; proc = proc->next) {
2488 gw_proc_waitpid(srv, host, proc);
2492 static void gw_handle_trigger_exts(server *srv, gw_exts *exts, int debug) {
2493 for (size_t j = 0; j < exts->used; ++j) {
2494 gw_extension *ex = exts->exts[j];
2495 for (size_t n = 0; n < ex->used; ++n) {
2496 gw_handle_trigger_host(srv, ex->hosts[n], debug);
2501 static void gw_handle_trigger_exts_wkr(server *srv, gw_exts *exts) {
2502 for (size_t j = 0; j < exts->used; ++j) {
2503 gw_extension * const ex = exts->exts[j];
2504 for (size_t n = 0; n < ex->used; ++n) {
2505 gw_host * const host = ex->hosts[n];
2506 for (gw_proc *proc = host->first; proc; proc = proc->next) {
2507 if (proc->state == PROC_STATE_OVERLOADED)
2508 gw_proc_check_enable(srv, host, proc);
2514 handler_t gw_handle_trigger(server *srv, void *p_d) {
2515 gw_plugin_data *p = p_d;
2516 int wkr = (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid);
2518 for (size_t i = 0; i < srv->config_context->used; i++) {
2519 gw_plugin_config *conf = p->config_storage[i];
2520 gw_exts *exts = conf->exts;
2521 int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
2522 if (NULL == exts) continue;
2524 ? gw_handle_trigger_exts_wkr(srv, exts)
2525 : gw_handle_trigger_exts(srv, exts, debug);
2528 return HANDLER_GO_ON;
2531 handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
2532 gw_plugin_data *p = p_d;
2533 if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid)
2534 return HANDLER_GO_ON;
2536 for (size_t i = 0; i < srv->config_context->used; ++i) {
2537 gw_plugin_config *conf = p->config_storage[i];
2538 gw_exts *exts = conf->exts;
2539 int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
2540 if (NULL == exts) continue;
2541 for (size_t j = 0; j < exts->used; ++j) {
2542 gw_extension *ex = exts->exts[j];
2543 for (size_t n = 0; n < ex->used; ++n) {
2544 gw_host *host = ex->hosts[n];
2545 gw_proc *proc;
2546 for (proc = host->first; proc; proc = proc->next) {
2547 if (!proc->is_local || proc->pid != pid) continue;
2549 gw_proc_waitpid_log(srv, host, proc, status);
2550 gw_proc_set_state(host, proc, PROC_STATE_DIED);
2551 proc->pid = 0;
2553 /* restart, but avoid spinning if child exits too quickly */
2554 if (proc->disabled_until < srv->cur_ts) {
2555 if (proc->state != PROC_STATE_KILLED)
2556 proc->disabled_until = srv->cur_ts;
2557 if (gw_spawn_connection(srv, host, proc, debug)) {
2558 log_error_write(srv, __FILE__, __LINE__, "s",
2559 "ERROR: spawning gw failed.");
2563 return HANDLER_FINISHED;
2565 for (proc = host->unused_procs; proc; proc = proc->next) {
2566 if (!proc->is_local || proc->pid != pid) continue;
2568 gw_proc_waitpid_log(srv, host, proc, status);
2569 if (proc->state != PROC_STATE_KILLED)
2570 proc->disabled_until = srv->cur_ts;
2571 gw_proc_set_state(host, proc, PROC_STATE_DIED);
2572 proc->pid = 0;
2573 return HANDLER_FINISHED;
2579 return HANDLER_GO_ON;