xref: /lighttpd1.4/src/gw_backend.c (revision e11f72b1)
1 #include "first.h"
2 
3 #include "gw_backend.h"
4 
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
14 
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>
22 
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"
30 #include "settings.h"   /* MAX_WRITE_LIMIT */
31 
32 
33 
34 
35 #include "status_counter.h"
36 
37 __attribute_noinline__
38 static int * gw_status_get_counter(gw_host *host, gw_proc *proc, const char *tag, size_t tlen) {
39     /*(At the cost of some memory, could prepare strings for host and for proc
40      * so that here we would copy ready made string for proc (or if NULL,
41      * for host), and then append tag to produce key)*/
42     char label[288];
43     size_t llen = sizeof("gw.backend.")-1, len;
44     memcpy(label, "gw.backend.", llen);
45 
46     len = buffer_string_length(host->id);
47     force_assert(len < sizeof(label) - llen);
48     memcpy(label+llen, host->id->ptr, len);
49     llen += len;
50 
51     if (proc) {
52         force_assert(llen < sizeof(label) - (LI_ITOSTRING_LENGTH + 1));
53         label[llen++] = '.';
54         len = li_utostrn(label+llen, LI_ITOSTRING_LENGTH, proc->id);
55         llen += len;
56     }
57 
58     force_assert(tlen < sizeof(label) - llen);
59     memcpy(label+llen, tag, tlen);
60     llen += tlen;
61     label[llen] = '\0';
62 
63     return status_counter_get_counter(label, llen);
64 }
65 
66 static void gw_proc_tag_inc(gw_host *host, gw_proc *proc, const char *tag, size_t len) {
67     ++(*gw_status_get_counter(host, proc, tag, len));
68 }
69 
70 static void gw_proc_load_inc(gw_host *host, gw_proc *proc) {
71     *gw_status_get_counter(host, proc, CONST_STR_LEN(".load")) = ++proc->load;
72 
73     status_counter_inc(CONST_STR_LEN("gw.active-requests"));
74 }
75 
76 static void gw_proc_load_dec(gw_host *host, gw_proc *proc) {
77     *gw_status_get_counter(host, proc, CONST_STR_LEN(".load")) = --proc->load;
78 
79     status_counter_dec(CONST_STR_LEN("gw.active-requests"));
80 }
81 
82 static void gw_host_assign(gw_host *host) {
83     *gw_status_get_counter(host, NULL, CONST_STR_LEN(".load")) = ++host->load;
84 }
85 
86 static void gw_host_reset(gw_host *host) {
87     *gw_status_get_counter(host, NULL, CONST_STR_LEN(".load")) = --host->load;
88 }
89 
90 static int gw_status_init(gw_host *host, gw_proc *proc) {
91     *gw_status_get_counter(host, proc, CONST_STR_LEN(".disabled")) = 0;
92     *gw_status_get_counter(host, proc, CONST_STR_LEN(".died")) = 0;
93     *gw_status_get_counter(host, proc, CONST_STR_LEN(".overloaded")) = 0;
94     *gw_status_get_counter(host, proc, CONST_STR_LEN(".connected")) = 0;
95     *gw_status_get_counter(host, proc, CONST_STR_LEN(".load")) = 0;
96 
97     *gw_status_get_counter(host, NULL, CONST_STR_LEN(".load")) = 0;
98 
99     return 0;
100 }
101 
102 
103 
104 
105 static void gw_proc_set_state(gw_host *host, gw_proc *proc, int state) {
106     if ((int)proc->state == state) return;
107     if (proc->state == PROC_STATE_RUNNING) {
108         --host->active_procs;
109     } else if (state == PROC_STATE_RUNNING) {
110         ++host->active_procs;
111     }
112     proc->state = state;
113 }
114 
115 
116 static gw_proc *gw_proc_init(void) {
117     gw_proc *f = calloc(1, sizeof(*f));
118     force_assert(f);
119 
120     f->unixsocket = buffer_init();
121     f->connection_name = buffer_init();
122 
123     f->prev = NULL;
124     f->next = NULL;
125     f->state = PROC_STATE_DIED;
126 
127     return f;
128 }
129 
130 static void gw_proc_free(gw_proc *f) {
131     if (!f) return;
132 
133     gw_proc_free(f->next);
134 
135     buffer_free(f->unixsocket);
136     buffer_free(f->connection_name);
137     free(f->saddr);
138 
139     free(f);
140 }
141 
142 static gw_host *gw_host_init(void) {
143     gw_host *f = calloc(1, sizeof(*f));
144     force_assert(f);
145     return f;
146 }
147 
148 static void gw_host_free(gw_host *h) {
149     if (!h) return;
150     if (h->refcount) {
151         --h->refcount;
152         return;
153     }
154 
155     gw_proc_free(h->first);
156     gw_proc_free(h->unused_procs);
157 
158     for (uint32_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]);
159     free(h->args.ptr);
160     free(h);
161 }
162 
163 static gw_exts *gw_extensions_init(void) {
164     gw_exts *f = calloc(1, sizeof(*f));
165     force_assert(f);
166     return f;
167 }
168 
169 static void gw_extensions_free(gw_exts *f) {
170     if (!f) return;
171     for (uint32_t i = 0; i < f->used; ++i) {
172         gw_extension *fe = f->exts+i;
173         for (uint32_t j = 0; j < fe->used; ++j) {
174             gw_host_free(fe->hosts[j]);
175         }
176         free(fe->hosts);
177     }
178     free(f->exts);
179     free(f);
180 }
181 
182 static int gw_extension_insert(gw_exts *ext, const buffer *key, gw_host *fh) {
183     gw_extension *fe = NULL;
184     for (uint32_t i = 0; i < ext->used; ++i) {
185         if (buffer_is_equal(key, &ext->exts[i].key)) {
186             fe = ext->exts+i;
187             break;
188         }
189     }
190 
191     if (NULL == fe) {
192         if (ext->used == ext->size) {
193             ext->size += 8;
194             ext->exts = realloc(ext->exts, ext->size * sizeof(gw_extension));
195             force_assert(ext->exts);
196             memset(ext->exts + ext->used, 0, 8 * sizeof(gw_extension));
197         }
198         fe = ext->exts + ext->used++;
199         fe->last_used_ndx = -1;
200         buffer *b;
201         *(const buffer **)&b = &fe->key;
202         memcpy(b, key, sizeof(buffer)); /*(copy; not later free'd)*/
203     }
204 
205     if (fe->size == fe->used) {
206         fe->size += 4;
207         fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
208         force_assert(fe->hosts);
209     }
210 
211     fe->hosts[fe->used++] = fh;
212     return 0;
213 }
214 
215 static void gw_proc_connect_success(gw_host *host, gw_proc *proc, int debug, request_st * const r) {
216     gw_proc_tag_inc(host, proc, CONST_STR_LEN(".connected"));
217     proc->last_used = log_epoch_secs;
218 
219     if (debug) {
220         log_error(r->conf.errh, __FILE__, __LINE__,
221           "got proc: pid: %d socket: %s load: %d",
222           proc->pid, proc->connection_name->ptr, proc->load);
223     }
224 }
225 
226 __attribute_cold__
227 static void gw_proc_connect_error(request_st * const r, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) {
228     const time_t cur_ts = log_epoch_secs;
229     log_error_st * const errh = r->conf.errh;
230     log_error(errh, __FILE__, __LINE__,
231       "establishing connection failed: socket: %s: %s",
232       proc->connection_name->ptr, strerror(errnum));
233 
234     if (!proc->is_local) {
235         proc->disabled_until = cur_ts + host->disable_time;
236         gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
237     }
238     else if (proc->pid == pid && proc->state == PROC_STATE_RUNNING) {
239         /* several requests from lighttpd might reference the same proc
240          *
241          * Only one of them should mark the proc
242          * and all other ones should just take a new one.
243          *
244          * If a new proc was started with the old struct, this might
245          * otherwise lead to marking a perfectly good proc as dead
246          */
247         log_error(errh, __FILE__, __LINE__,
248           "backend error; we'll disable for %d"
249           "secs and send the request to another backend instead:"
250           "load: %d", host->disable_time, host->load);
251         if (EAGAIN == errnum) {
252             /* - EAGAIN: cool down the backend; it is overloaded */
253           #ifdef __linux__
254             log_error(errh, __FILE__, __LINE__,
255               "If this happened on Linux: You have run out of local ports. "
256               "Check the manual, section Performance how to handle this.");
257           #endif
258             if (debug) {
259                 log_error(errh, __FILE__, __LINE__,
260                   "This means that you have more incoming requests than your "
261                   "FastCGI backend can handle in parallel.  It might help to "
262                   "spawn more FastCGI backends or PHP children; if not, "
263                   "decrease server.max-connections.  The load for this FastCGI "
264                   "backend %s is %d", proc->connection_name->ptr, proc->load);
265             }
266             proc->disabled_until = cur_ts + host->disable_time;
267             gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
268         }
269         else {
270             /* we got a hard error from the backend like
271              * - ECONNREFUSED for tcp-ip sockets
272              * - ENOENT for unix-domain-sockets
273              */
274           #if 0
275             gw_proc_set_state(host, proc, PROC_STATE_DIED_WAIT_FOR_PID);
276           #else  /* treat as overloaded (future: unless we send kill() signal)*/
277             proc->disabled_until = cur_ts + host->disable_time;
278             gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
279           #endif
280         }
281     }
282 
283     if (EAGAIN == errnum) {
284         gw_proc_tag_inc(host, proc, CONST_STR_LEN(".overloaded"));
285     }
286     else {
287         gw_proc_tag_inc(host, proc, CONST_STR_LEN(".died"));
288     }
289 }
290 
291 static void gw_proc_release(gw_host *host, gw_proc *proc, int debug, log_error_st *errh) {
292     gw_proc_load_dec(host, proc);
293 
294     if (debug) {
295         log_error(errh, __FILE__, __LINE__,
296           "released proc: pid: %d socket: %s load: %u",
297           proc->pid, proc->connection_name->ptr, proc->load);
298     }
299 }
300 
301 static void gw_proc_check_enable(gw_host * const host, gw_proc * const proc, log_error_st * const errh) {
302     if (log_epoch_secs <= proc->disabled_until) return;
303     if (proc->state != PROC_STATE_OVERLOADED) return;
304 
305     gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
306 
307     log_error(errh, __FILE__, __LINE__,
308       "gw-server re-enabled: %s %s %hu %s",
309       proc->connection_name->ptr, host->host->ptr, host->port,
310       host->unixsocket->ptr ? host->unixsocket->ptr : "");
311 }
312 
313 static void gw_proc_waitpid_log(const gw_host * const host, const gw_proc * const proc, log_error_st * const errh, const int status) {
314     if (WIFEXITED(status)) {
315         if (proc->state != PROC_STATE_KILLED) {
316             log_error(errh, __FILE__, __LINE__,
317               "child exited: %d %s",
318               WEXITSTATUS(status), proc->connection_name->ptr);
319         }
320     } else if (WIFSIGNALED(status)) {
321         if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT
322             && WTERMSIG(status) != host->kill_signal) {
323             log_error(errh, __FILE__, __LINE__,
324               "child signalled: %d", WTERMSIG(status));
325         }
326     } else {
327         log_error(errh, __FILE__, __LINE__,
328           "child died somehow: %d", status);
329     }
330 }
331 
332 static int gw_proc_waitpid(gw_host *host, gw_proc *proc, log_error_st *errh) {
333     int rc, status;
334 
335     if (!proc->is_local) return 0;
336     if (proc->pid <= 0) return 0;
337 
338     do {
339         rc = waitpid(proc->pid, &status, WNOHANG);
340     } while (-1 == rc && errno == EINTR);
341     if (0 == rc) return 0; /* child still running */
342 
343     /* child terminated */
344     if (-1 == rc) {
345         /* EINVAL or ECHILD no child processes */
346         /* should not happen; someone else has cleaned up for us */
347         log_perror(errh, __FILE__, __LINE__,
348           "pid %d %d not found", proc->pid, proc->state);
349     }
350     else {
351         gw_proc_waitpid_log(host, proc, errh, status);
352     }
353 
354     proc->pid = 0;
355     if (proc->state != PROC_STATE_KILLED)
356         proc->disabled_until = log_epoch_secs;
357     gw_proc_set_state(host, proc, PROC_STATE_DIED);
358     return 1;
359 }
360 
361 static int gw_proc_sockaddr_init(gw_host * const host, gw_proc * const proc, log_error_st * const errh) {
362     sock_addr addr;
363     socklen_t addrlen;
364 
365     if (!buffer_string_is_empty(proc->unixsocket)) {
366         if (1 != sock_addr_from_str_hints(&addr,&addrlen,proc->unixsocket->ptr,
367                                           AF_UNIX, 0, errh)) {
368             errno = EINVAL;
369             return -1;
370         }
371         buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
372         buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
373     }
374     else {
375         /*(note: name resolution here is *blocking* if IP string not supplied)*/
376         if (1 != sock_addr_from_str_hints(&addr, &addrlen, host->host->ptr,
377                                           0, proc->port, errh)) {
378             errno = EINVAL;
379             return -1;
380         }
381         else {
382             /* overwrite host->host buffer with IP addr string so that
383              * any further use of gw_host does not block on DNS lookup */
384             buffer *h;
385             *(const buffer **)&h = host->host;
386             sock_addr_inet_ntop_copy_buffer(h, &addr);
387             host->family = sock_addr_get_family(&addr);
388         }
389         buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
390         buffer_append_string_buffer(proc->connection_name, host->host);
391         buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
392         buffer_append_int(proc->connection_name, proc->port);
393     }
394 
395     if (NULL != proc->saddr && proc->saddrlen < addrlen) {
396         free(proc->saddr);
397         proc->saddr = NULL;
398     }
399     if (NULL == proc->saddr) {
400         proc->saddr = (struct sockaddr *)malloc(addrlen);
401         force_assert(proc->saddr);
402     }
403     proc->saddrlen = addrlen;
404     memcpy(proc->saddr, &addr, addrlen);
405     return 0;
406 }
407 
408 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
409     char *dst;
410 
411     if (!key || !val) return -1;
412 
413     dst = malloc(key_len + val_len + 3);
414     force_assert(dst);
415     memcpy(dst, key, key_len);
416     dst[key_len] = '=';
417     memcpy(dst + key_len + 1, val, val_len + 1); /* add the \0 from the value */
418 
419     for (uint32_t i = 0; i < env->used; ++i) {
420         if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
421             free(env->ptr[i]);
422             env->ptr[i] = dst;
423             return 0;
424         }
425     }
426 
427     if (env->size <= env->used + 1) {
428         env->size += 16;
429         env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
430         force_assert(env->ptr);
431     }
432 
433     env->ptr[env->used++] = dst;
434 
435     return 0;
436 }
437 
438 static int gw_spawn_connection(gw_host * const host, gw_proc * const proc, log_error_st * const errh, int debug) {
439     int gw_fd;
440     int status;
441     struct timeval tv = { 0, 10 * 1000 };
442 
443     if (debug) {
444         log_error(errh, __FILE__, __LINE__,
445           "new proc, socket: %hu %s",
446           proc->port, proc->unixsocket->ptr ? proc->unixsocket->ptr : "");
447     }
448 
449     gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
450     if (-1 == gw_fd) {
451         log_perror(errh, __FILE__, __LINE__, "socket()");
452         return -1;
453     }
454 
455     do {
456         status = connect(gw_fd, proc->saddr, proc->saddrlen);
457     } while (-1 == status && errno == EINTR);
458 
459     if (-1 == status && errno != ENOENT
460         && !buffer_string_is_empty(proc->unixsocket)) {
461         log_perror(errh, __FILE__, __LINE__,
462           "unlink %s after connect failed", proc->unixsocket->ptr);
463         unlink(proc->unixsocket->ptr);
464     }
465 
466     close(gw_fd);
467 
468     if (-1 == status) {
469         /* server is not up, spawn it  */
470         char_array env;
471         uint32_t i;
472         int dfd = -1;
473 
474         /* reopen socket */
475         gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
476         if (-1 == gw_fd) {
477             log_perror(errh, __FILE__, __LINE__, "socket()");
478             return -1;
479         }
480 
481         if (fdevent_set_so_reuseaddr(gw_fd, 1) < 0) {
482             log_perror(errh, __FILE__, __LINE__, "socketsockopt()");
483             close(gw_fd);
484             return -1;
485         }
486 
487         /* create socket */
488         if (-1 == bind(gw_fd, proc->saddr, proc->saddrlen)) {
489             log_perror(errh, __FILE__, __LINE__,
490               "bind failed for: %s", proc->connection_name->ptr);
491             close(gw_fd);
492             return -1;
493         }
494 
495         if (-1 == listen(gw_fd, host->listen_backlog)) {
496             log_perror(errh, __FILE__, __LINE__, "listen()");
497             close(gw_fd);
498             return -1;
499         }
500 
501         {
502             /* create environment */
503             env.ptr = NULL;
504             env.size = 0;
505             env.used = 0;
506 
507             /* build clean environment */
508             if (host->bin_env_copy && host->bin_env_copy->used) {
509                 for (i = 0; i < host->bin_env_copy->used; ++i) {
510                     data_string *ds=(data_string *)host->bin_env_copy->data[i];
511                     char *ge;
512 
513                     if (NULL != (ge = getenv(ds->value.ptr))) {
514                         env_add(&env,CONST_BUF_LEN(&ds->value),ge,strlen(ge));
515                     }
516                 }
517             } else {
518                 char ** const e = fdevent_environ();
519                 for (i = 0; e[i]; ++i) {
520                     char *eq;
521 
522                     if (NULL != (eq = strchr(e[i], '='))) {
523                         env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1));
524                     }
525                 }
526             }
527 
528             /* create environment */
529             if (host->bin_env) {
530                 for (i = 0; i < host->bin_env->used; ++i) {
531                     data_string *ds = (data_string *)host->bin_env->data[i];
532                     env_add(&env, CONST_BUF_LEN(&ds->key),
533                                   CONST_BUF_LEN(&ds->value));
534                 }
535             }
536 
537             for (i = 0; i < env.used; ++i) {
538                 /* search for PHP_FCGI_CHILDREN */
539                 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=",
540                                       sizeof("PHP_FCGI_CHILDREN=")-1)) {
541                     break;
542                 }
543             }
544 
545             /* not found, add a default */
546             if (i == env.used) {
547                 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"),
548                               CONST_STR_LEN("1"));
549             }
550 
551             env.ptr[env.used] = NULL;
552         }
553 
554         dfd = fdevent_open_dirname(host->args.ptr[0], 1); /* permit symlinks */
555         if (-1 == dfd) {
556             log_perror(errh, __FILE__, __LINE__,
557               "open dirname failed: %s", host->args.ptr[0]);
558         }
559 
560         /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/
561         proc->pid = (dfd >= 0)
562           ? fdevent_fork_execve(host->args.ptr[0], host->args.ptr,
563                                 env.ptr, gw_fd, -1, -1, dfd)
564           : -1;
565 
566         for (i = 0; i < env.used; ++i) free(env.ptr[i]);
567         free(env.ptr);
568         if (-1 != dfd) close(dfd);
569         close(gw_fd);
570 
571         if (-1 == proc->pid) {
572             log_error(errh, __FILE__, __LINE__,
573               "gw-backend failed to start: %s", host->bin_path->ptr);
574             proc->pid = 0;
575             proc->disabled_until = log_epoch_secs;
576             return -1;
577         }
578 
579         /* register process */
580         proc->last_used = log_epoch_secs;
581         proc->is_local = 1;
582 
583         /* wait */
584         select(0, NULL, NULL, NULL, &tv);
585 
586         if (0 != gw_proc_waitpid(host, proc, errh)) {
587             log_error(errh, __FILE__, __LINE__,
588               "gw-backend failed to start: %s", host->bin_path->ptr);
589             log_error(errh, __FILE__, __LINE__,
590               "If you're trying to run your app as a FastCGI backend, make "
591               "sure you're using the FastCGI-enabled version.  If this is PHP "
592               "on Gentoo, add 'fastcgi' to the USE flags.  If this is PHP, try "
593               "removing the bytecode caches for now and try again.");
594             return -1;
595         }
596     } else {
597         proc->is_local = 0;
598         proc->pid = 0;
599 
600         if (debug) {
601             log_error(errh, __FILE__, __LINE__,
602               "(debug) socket is already used; won't spawn: %s",
603               proc->connection_name->ptr);
604         }
605     }
606 
607     gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
608     return 0;
609 }
610 
611 static void gw_proc_spawn(gw_host * const host, log_error_st * const errh, const int debug) {
612     gw_proc *proc;
613     for (proc = host->unused_procs; proc; proc = proc->next) {
614         /* (proc->pid <= 0 indicates PROC_STATE_DIED, not PROC_STATE_KILLED) */
615         if (proc->pid > 0) continue;
616         /* (do not attempt to spawn another proc if a proc just exited) */
617         if (proc->disabled_until >= log_epoch_secs) return;
618         break;
619     }
620     if (proc) {
621         if (proc == host->unused_procs)
622             host->unused_procs = proc->next;
623         else
624             proc->prev->next = proc->next;
625 
626         if (proc->next) {
627             proc->next->prev = proc->prev;
628             proc->next = NULL;
629         }
630 
631         proc->prev = NULL;
632     } else {
633         proc = gw_proc_init();
634         proc->id = host->max_id++;
635     }
636 
637     ++host->num_procs;
638 
639     if (buffer_string_is_empty(host->unixsocket)) {
640         proc->port = host->port + proc->id;
641     } else {
642         buffer_copy_buffer(proc->unixsocket, host->unixsocket);
643         buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
644         buffer_append_int(proc->unixsocket, proc->id);
645     }
646 
647     if (0 != gw_proc_sockaddr_init(host, proc, errh)) {
648         /*(should not happen if host->host validated at startup,
649          * and translated from name to IP address at startup)*/
650         log_error(errh, __FILE__, __LINE__,
651           "ERROR: spawning backend failed.");
652         --host->num_procs;
653         if (proc->id == host->max_id-1) --host->max_id;
654         gw_proc_free(proc);
655     } else if (gw_spawn_connection(host, proc, errh, debug)) {
656         log_error(errh, __FILE__, __LINE__,
657           "ERROR: spawning backend failed.");
658         proc->next = host->unused_procs;
659         if (host->unused_procs)
660             host->unused_procs->prev = proc;
661         host->unused_procs = proc;
662     } else {
663         proc->next = host->first;
664         if (host->first)
665             host->first->prev = proc;
666         host->first = proc;
667     }
668 }
669 
670 static void gw_proc_kill(gw_host *host, gw_proc *proc) {
671     if (proc->next) proc->next->prev = proc->prev;
672     if (proc->prev) proc->prev->next = proc->next;
673 
674     if (proc->prev == NULL) host->first = proc->next;
675 
676     proc->prev = NULL;
677     proc->next = host->unused_procs;
678     proc->disabled_until = 0;
679 
680     if (host->unused_procs)
681         host->unused_procs->prev = proc;
682     host->unused_procs = proc;
683 
684     kill(proc->pid, host->kill_signal);
685 
686     gw_proc_set_state(host, proc, PROC_STATE_KILLED);
687 
688     --host->num_procs;
689 }
690 
691 static gw_host * unixsocket_is_dup(gw_plugin_data *p, const buffer *unixsocket) {
692     if (NULL == p->cvlist) return NULL;
693     /* (init i to 0 if global context; to 1 to skip empty global context) */
694     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
695         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
696         gw_plugin_config *conf = NULL;
697         for (; -1 != cpv->k_id; ++cpv) {
698             switch (cpv->k_id) {
699               case 0: /* xxxxx.server */
700                 if (cpv->vtype == T_CONFIG_LOCAL) conf = cpv->v.v;
701                 break;
702               default:
703                 break;
704             }
705         }
706 
707         if (NULL == conf || NULL == conf->exts) continue;
708 
709         gw_exts *exts = conf->exts;
710         for (uint32_t j = 0; j < exts->used; ++j) {
711             gw_extension *ex = exts->exts+j;
712             for (uint32_t n = 0; n < ex->used; ++n) {
713                 gw_host *host = ex->hosts[n];
714                 if (!buffer_string_is_empty(host->unixsocket)
715                     && buffer_is_equal(host->unixsocket, unixsocket)
716                     && !buffer_string_is_empty(host->bin_path))
717                     return host;
718             }
719         }
720     }
721 
722     return NULL;
723 }
724 
725 static int parse_binpath(char_array *env, const buffer *b) {
726     char *start = b->ptr;
727     char c;
728     /* search for spaces */
729     for (size_t i = 0; i < buffer_string_length(b); ++i) {
730         switch(b->ptr[i]) {
731         case ' ':
732         case '\t':
733             /* a WS, stop here and copy the argument */
734 
735             if (env->size == env->used) {
736                 env->size += 16;
737                 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
738             }
739 
740             c = b->ptr[i];
741             b->ptr[i] = '\0';
742             env->ptr[env->used++] = strdup(start);
743             b->ptr[i] = c;
744 
745             start = b->ptr + i + 1;
746             break;
747         default:
748             break;
749         }
750     }
751 
752     if (env->size == env->used) { /*need one extra for terminating NULL*/
753         env->size += 16;
754         env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
755     }
756 
757     /* the rest */
758     env->ptr[env->used++] = strdup(start);
759 
760     if (env->size == env->used) { /*need one extra for terminating NULL*/
761         env->size += 16;
762         env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
763     }
764 
765     /* terminate */
766     env->ptr[env->used++] = NULL;
767 
768     return 0;
769 }
770 
771 enum {
772   GW_BALANCE_LEAST_CONNECTION,
773   GW_BALANCE_RR,
774   GW_BALANCE_HASH,
775   GW_BALANCE_STICKY
776 };
777 
778 static gw_host * gw_host_get(request_st * const r, gw_extension *extension, int balance, int debug) {
779     gw_host *host;
780     buffer *dst_addr_buf;
781     unsigned long last_max = ULONG_MAX;
782     unsigned long base_crc32c;
783     int max_usage = INT_MAX;
784     int ndx = -1;
785     uint32_t k;
786 
787     if (extension->used <= 1) {
788         if (1 == extension->used && extension->hosts[0]->active_procs > 0) {
789             ndx = 0;
790         }
791     } else switch(balance) {
792     case GW_BALANCE_HASH:
793         /* hash balancing */
794 
795         if (debug) {
796             log_error(r->conf.errh, __FILE__, __LINE__,
797               "proxy - used hash balancing, hosts: %u", extension->used);
798         }
799 
800         base_crc32c = generate_crc32c(CONST_BUF_LEN(&r->uri.path))
801                     + generate_crc32c(CONST_BUF_LEN(&r->uri.authority));
802 
803         for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
804             unsigned long cur_max;
805             host = extension->hosts[k];
806             if (0 == host->active_procs) continue;
807 
808             cur_max = base_crc32c
809                     + generate_crc32c(CONST_BUF_LEN(host->host)); /* cachable */
810 
811             if (debug) {
812                 log_error(r->conf.errh, __FILE__, __LINE__,
813                   "proxy - election: %s %s %s %lu", r->uri.path.ptr,
814                   host->host->ptr, r->uri.authority.ptr, cur_max);
815             }
816 
817             if (last_max < cur_max || last_max == ULONG_MAX) {
818                 last_max = cur_max;
819                 ndx = k;
820             }
821         }
822 
823         break;
824     case GW_BALANCE_LEAST_CONNECTION:
825         /* fair balancing */
826         if (debug) {
827             log_error(r->conf.errh, __FILE__, __LINE__,
828               "proxy - used least connection");
829         }
830 
831         for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->used; ++k) {
832             host = extension->hosts[k];
833             if (0 == host->active_procs) continue;
834 
835             if (host->load < max_usage) {
836                 max_usage = host->load;
837                 ndx = k;
838             }
839         }
840 
841         break;
842     case GW_BALANCE_RR:
843         /* round robin */
844         if (debug) {
845             log_error(r->conf.errh, __FILE__, __LINE__,
846               "proxy - used round-robin balancing");
847         }
848 
849         /* just to be sure */
850         force_assert(extension->used < INT_MAX);
851 
852         host = extension->hosts[0];
853 
854         /* Use last_used_ndx from first host in list */
855         k = extension->last_used_ndx;
856         ndx = k + 1; /* use next host after the last one */
857         if (ndx < 0) ndx = 0;
858 
859         /* Search first active host after last_used_ndx */
860         while (ndx < (int) extension->used
861                && 0 == (host = extension->hosts[ndx])->active_procs) ++ndx;
862 
863         if (ndx >= (int) extension->used) {
864             /* didn't find a higher id, wrap to the start */
865             for (ndx = 0; ndx <= (int) k; ++ndx) {
866                 host = extension->hosts[ndx];
867                 if (0 != host->active_procs) break;
868             }
869 
870             /* No active host found */
871             if (0 == host->active_procs) ndx = -1;
872         }
873 
874         /* Save new index for next round */
875         extension->last_used_ndx = ndx;
876 
877         break;
878     case GW_BALANCE_STICKY:
879         /* source sticky balancing */
880         dst_addr_buf = r->con->dst_addr_buf;
881 
882         if (debug) {
883             log_error(r->conf.errh, __FILE__, __LINE__,
884               "proxy - used sticky balancing, hosts: %u", extension->used);
885         }
886 
887         base_crc32c = generate_crc32c(CONST_BUF_LEN(dst_addr_buf));
888 
889         for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
890             unsigned long cur_max;
891             host = extension->hosts[k];
892 
893             if (0 == host->active_procs) continue;
894 
895             cur_max = base_crc32c
896                     + generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */
897                     + host->port;
898 
899             if (debug) {
900                 log_error(r->conf.errh, __FILE__, __LINE__,
901                   "proxy - election: %s %s %hu %ld", dst_addr_buf->ptr,
902                   host->host->ptr, host->port, cur_max);
903             }
904 
905             if (last_max < cur_max || last_max == ULONG_MAX) {
906                 last_max = cur_max;
907                 ndx = k;
908             }
909         }
910 
911         break;
912     default:
913         break;
914     }
915 
916     if (-1 != ndx) {
917         /* found a server */
918         host = extension->hosts[ndx];
919 
920         if (debug) {
921             log_error(r->conf.errh, __FILE__, __LINE__,
922               "gw - found a host %s %hu", host->host->ptr, host->port);
923         }
924 
925         return host;
926     } else if (0 == r->con->srv->srvconf.max_worker) {
927         /* special-case adaptive spawning and 0 == host->min_procs */
928         for (k = 0; k < extension->used; ++k) {
929             host = extension->hosts[k];
930             if (0 == host->min_procs && 0 == host->num_procs
931                 && !buffer_string_is_empty(host->bin_path)) {
932                 gw_proc_spawn(host, r->con->srv->errh, debug);
933                 if (host->num_procs) return host;
934             }
935         }
936     }
937 
938     /* all hosts are down */
939     /* sorry, we don't have a server alive for this ext */
940     r->http_status = 503; /* Service Unavailable */
941     r->handler_module = NULL;
942 
943     /* only send the 'no handler' once */
944     if (!extension->note_is_sent) {
945         extension->note_is_sent = 1;
946         log_error(r->conf.errh, __FILE__, __LINE__,
947           "all handlers for %s?%.*s on %s are down.",
948           r->uri.path.ptr, BUFFER_INTLEN_PTR(&r->uri.query),
949           extension->key.ptr);
950     }
951 
952     return NULL;
953 }
954 
955 static int gw_establish_connection(request_st * const r, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) {
956     if (-1 == connect(gw_fd, proc->saddr, proc->saddrlen)) {
957         if (errno == EINPROGRESS || errno == EALREADY || errno == EINTR) {
958             if (debug > 2) {
959                 log_error(r->conf.errh, __FILE__, __LINE__,
960                   "connect delayed; will continue later: %s",
961                   proc->connection_name->ptr);
962             }
963 
964             return 1;
965         } else {
966             gw_proc_connect_error(r, host, proc, pid, errno, debug);
967             return -1;
968         }
969     }
970 
971     if (debug > 1) {
972         log_error(r->conf.errh, __FILE__, __LINE__,
973           "connect succeeded: %d", gw_fd);
974     }
975 
976     return 0;
977 }
978 
979 static void gw_restart_dead_procs(gw_host * const host, log_error_st * const errh, const int debug, const int trigger) {
980     for (gw_proc *proc = host->first; proc; proc = proc->next) {
981         if (debug > 2) {
982             log_error(errh, __FILE__, __LINE__,
983               "proc: %s %d %d %d %d", proc->connection_name->ptr,
984               proc->state, proc->is_local, proc->load, proc->pid);
985         }
986 
987         switch (proc->state) {
988         case PROC_STATE_RUNNING:
989             break;
990         case PROC_STATE_OVERLOADED:
991             gw_proc_check_enable(host, proc, errh);
992             break;
993         case PROC_STATE_KILLED:
994             if (trigger && ++proc->disabled_until > 4) {
995                 int sig = (proc->disabled_until <= 8)
996                   ? host->kill_signal
997                   : proc->disabled_until <= 16 ? SIGTERM : SIGKILL;
998                 kill(proc->pid, sig);
999             }
1000             break;
1001         case PROC_STATE_DIED_WAIT_FOR_PID:
1002             /*(state should not happen in workers if server.max-worker > 0)*/
1003             /*(if PROC_STATE_DIED_WAIT_FOR_PID is used in future, might want
1004              * to save proc->disabled_until before gw_proc_waitpid() since
1005              * gw_proc_waitpid will set proc->disabled_until to log_epoch_secs,
1006              * and so process will not be restarted below until one sec later)*/
1007             if (0 == gw_proc_waitpid(host, proc, errh)) {
1008                 gw_proc_check_enable(host, proc, errh);
1009             }
1010 
1011             if (proc->state != PROC_STATE_DIED) break;
1012             /* fall through *//*(we have a dead proc now)*/
1013 
1014         case PROC_STATE_DIED:
1015             /* local procs get restarted by us,
1016              * remote ones hopefully by the admin */
1017 
1018             if (!buffer_string_is_empty(host->bin_path)) {
1019                 /* we still have connections bound to this proc,
1020                  * let them terminate first */
1021                 if (proc->load != 0) break;
1022 
1023                 /* avoid spinning if child exits too quickly */
1024                 if (proc->disabled_until >= log_epoch_secs) break;
1025 
1026                 /* restart the child */
1027 
1028                 if (debug) {
1029                     log_error(errh, __FILE__, __LINE__,
1030                       "--- gw spawning"
1031                       "\n\tsocket %s"
1032                       "\n\tcurrent: 1 / %u",
1033                       proc->connection_name->ptr, host->max_procs);
1034                 }
1035 
1036                 if (gw_spawn_connection(host, proc, errh, debug)) {
1037                     log_error(errh, __FILE__, __LINE__,
1038                       "ERROR: spawning gw failed.");
1039                 }
1040             } else {
1041                 gw_proc_check_enable(host, proc, errh);
1042             }
1043             break;
1044         }
1045     }
1046 }
1047 
1048 
1049 
1050 
1051 #include "base.h"
1052 #include "connections.h"
1053 #include "response.h"
1054 
1055 
1056 /* ok, we need a prototype */
1057 static handler_t gw_handle_fdevent(void *ctx, int revents);
1058 
1059 
1060 static gw_handler_ctx * handler_ctx_init(size_t sz) {
1061     gw_handler_ctx *hctx = calloc(1, 0 == sz ? sizeof(*hctx) : sz);
1062     force_assert(hctx);
1063 
1064     /*hctx->response = chunk_buffer_acquire();*//*(allocated when needed)*/
1065 
1066     hctx->request_id = 0;
1067     hctx->gw_mode = GW_RESPONDER;
1068     hctx->state = GW_STATE_INIT;
1069     hctx->proc = NULL;
1070 
1071     hctx->fd = -1;
1072 
1073     hctx->reconnects = 0;
1074     hctx->send_content_body = 1;
1075 
1076     /*hctx->rb = chunkqueue_init();*//*(allocated when needed)*/
1077     hctx->wb = chunkqueue_init();
1078     hctx->wb_reqlen = 0;
1079 
1080     return hctx;
1081 }
1082 
1083 static void handler_ctx_free(gw_handler_ctx *hctx) {
1084     /* caller MUST have called gw_backend_close(hctx, r) if necessary */
1085     if (hctx->handler_ctx_free) hctx->handler_ctx_free(hctx);
1086     chunk_buffer_release(hctx->response);
1087 
1088     chunkqueue_free(hctx->rb);
1089     chunkqueue_free(hctx->wb);
1090 
1091     free(hctx);
1092 }
1093 
1094 static void handler_ctx_clear(gw_handler_ctx *hctx) {
1095     /* caller MUST have called gw_backend_close(hctx, r) if necessary */
1096 
1097     hctx->proc = NULL;
1098     hctx->host = NULL;
1099     hctx->ext  = NULL;
1100     /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/
1101 
1102     hctx->gw_mode = GW_RESPONDER;
1103     hctx->state = GW_STATE_INIT;
1104     /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/
1105 
1106     if (hctx->rb) chunkqueue_reset(hctx->rb);
1107     if (hctx->wb) chunkqueue_reset(hctx->wb);
1108     hctx->wb_reqlen = 0;
1109 
1110     if (hctx->response) buffer_clear(hctx->response);
1111 
1112     hctx->fd = -1;
1113     hctx->reconnects = 0;
1114     hctx->request_id = 0;
1115     hctx->send_content_body = 1;
1116 
1117     /*plugin_config conf;*//*(no need to reset for same request)*/
1118 
1119     /*hctx->r           = NULL;*//*(no need to reset for same request)*/
1120     /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/
1121 }
1122 
1123 
1124 void * gw_init(void) {
1125     return calloc(1, sizeof(gw_plugin_data));
1126 }
1127 
1128 
1129 void gw_plugin_config_free(gw_plugin_config *s) {
1130     gw_exts *exts = s->exts;
1131     if (exts) {
1132         for (uint32_t j = 0; j < exts->used; ++j) {
1133             gw_extension *ex = exts->exts+j;
1134             for (uint32_t n = 0; n < ex->used; ++n) {
1135                 gw_proc *proc;
1136                 gw_host *host = ex->hosts[n];
1137 
1138                 for (proc = host->first; proc; proc = proc->next) {
1139                     if (proc->pid > 0) {
1140                         kill(proc->pid, host->kill_signal);
1141                     }
1142 
1143                     if (proc->is_local &&
1144                         !buffer_string_is_empty(proc->unixsocket)) {
1145                         unlink(proc->unixsocket->ptr);
1146                     }
1147                 }
1148 
1149                 for (proc = host->unused_procs; proc; proc = proc->next) {
1150                     if (proc->pid > 0) {
1151                         kill(proc->pid, host->kill_signal);
1152                     }
1153                     if (proc->is_local &&
1154                         !buffer_string_is_empty(proc->unixsocket)) {
1155                         unlink(proc->unixsocket->ptr);
1156                     }
1157                 }
1158             }
1159         }
1160 
1161         gw_extensions_free(s->exts);
1162         gw_extensions_free(s->exts_auth);
1163         gw_extensions_free(s->exts_resp);
1164     }
1165     free(s);
1166 }
1167 
1168 void gw_free(void *p_d) {
1169     gw_plugin_data * const p = p_d;
1170     if (NULL == p->cvlist) return;
1171     /* (init i to 0 if global context; to 1 to skip empty global context) */
1172     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
1173         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1174         for (; -1 != cpv->k_id; ++cpv) {
1175             switch (cpv->k_id) {
1176               case 0: /* xxxxx.server */
1177                 if (cpv->vtype == T_CONFIG_LOCAL)
1178                     gw_plugin_config_free(cpv->v.v);
1179                 break;
1180               default:
1181                 break;
1182             }
1183         }
1184     }
1185 }
1186 
1187 void gw_exts_clear_check_local(gw_exts *exts) {
1188     for (uint32_t j = 0; j < exts->used; ++j) {
1189         gw_extension *ex = exts->exts+j;
1190         for (uint32_t n = 0; n < ex->used; ++n) {
1191             ex->hosts[n]->check_local = 0;
1192         }
1193     }
1194 }
1195 
1196 int gw_set_defaults_backend(server *srv, gw_plugin_data *p, const array *a, gw_plugin_config *s, int sh_exec, const char *cpkkey) {
1197     /* per-module plugin_config MUST have common "base class" gw_plugin_config*/
1198     /* per-module plugin_data MUST have pointer-compatible common "base class"
1199      * with gw_plugin_data (stemming from gw_plugin_config compatibility) */
1200 
1201     static const config_plugin_keys_t cpk[] = {
1202       { CONST_STR_LEN("host"),
1203         T_CONFIG_STRING,
1204         T_CONFIG_SCOPE_CONNECTION }
1205      ,{ CONST_STR_LEN("port"),
1206         T_CONFIG_SHORT,
1207         T_CONFIG_SCOPE_CONNECTION }
1208      ,{ CONST_STR_LEN("socket"),
1209         T_CONFIG_STRING,
1210         T_CONFIG_SCOPE_CONNECTION }
1211      ,{ CONST_STR_LEN("listen-backlog"),
1212         T_CONFIG_INT,
1213         T_CONFIG_SCOPE_CONNECTION }
1214      ,{ CONST_STR_LEN("bin-path"),
1215         T_CONFIG_STRING,
1216         T_CONFIG_SCOPE_CONNECTION }
1217      ,{ CONST_STR_LEN("kill-signal"),
1218         T_CONFIG_SHORT,
1219         T_CONFIG_SCOPE_CONNECTION }
1220      ,{ CONST_STR_LEN("check-local"),
1221         T_CONFIG_BOOL,
1222         T_CONFIG_SCOPE_CONNECTION }
1223      ,{ CONST_STR_LEN("mode"),
1224         T_CONFIG_STRING,
1225         T_CONFIG_SCOPE_CONNECTION }
1226      ,{ CONST_STR_LEN("docroot"),
1227         T_CONFIG_STRING,
1228         T_CONFIG_SCOPE_CONNECTION }
1229      ,{ CONST_STR_LEN("min-procs"),
1230         T_CONFIG_SHORT,
1231         T_CONFIG_SCOPE_CONNECTION }
1232      ,{ CONST_STR_LEN("max-procs"),
1233         T_CONFIG_SHORT,
1234         T_CONFIG_SCOPE_CONNECTION }
1235      ,{ CONST_STR_LEN("max-load-per-proc"),
1236         T_CONFIG_SHORT,
1237         T_CONFIG_SCOPE_CONNECTION }
1238      ,{ CONST_STR_LEN("idle-timeout"),
1239         T_CONFIG_SHORT,
1240         T_CONFIG_SCOPE_CONNECTION }
1241      ,{ CONST_STR_LEN("disable-time"),
1242         T_CONFIG_SHORT,
1243         T_CONFIG_SCOPE_CONNECTION }
1244      ,{ CONST_STR_LEN("bin-environment"),
1245         T_CONFIG_ARRAY_KVSTRING,
1246         T_CONFIG_SCOPE_CONNECTION }
1247      ,{ CONST_STR_LEN("bin-copy-environment"),
1248         T_CONFIG_ARRAY_VLIST,
1249         T_CONFIG_SCOPE_CONNECTION }
1250      ,{ CONST_STR_LEN("broken-scriptfilename"),
1251         T_CONFIG_BOOL,
1252         T_CONFIG_SCOPE_CONNECTION }
1253      ,{ CONST_STR_LEN("strip-request-uri"),
1254         T_CONFIG_STRING,
1255         T_CONFIG_SCOPE_CONNECTION }
1256      ,{ CONST_STR_LEN("fix-root-scriptname"),
1257         T_CONFIG_BOOL,
1258         T_CONFIG_SCOPE_CONNECTION }
1259      ,{ CONST_STR_LEN("allow-x-send-file"),
1260         T_CONFIG_BOOL,
1261         T_CONFIG_SCOPE_CONNECTION }
1262      ,{ CONST_STR_LEN("x-sendfile"),
1263         T_CONFIG_BOOL,
1264         T_CONFIG_SCOPE_CONNECTION }
1265      ,{ CONST_STR_LEN("x-sendfile-docroot"),
1266         T_CONFIG_ARRAY_VLIST,
1267         T_CONFIG_SCOPE_CONNECTION }
1268      ,{ CONST_STR_LEN("tcp-fin-propagate"),
1269         T_CONFIG_BOOL,
1270         T_CONFIG_SCOPE_CONNECTION }
1271      ,{ NULL, 0,
1272         T_CONFIG_UNSET,
1273         T_CONFIG_SCOPE_UNSET }
1274     };
1275 
1276     gw_host *host = NULL;
1277 
1278     p->srv_pid = srv->pid;
1279 
1280     s->exts      = gw_extensions_init();
1281     s->exts_auth = gw_extensions_init();
1282     s->exts_resp = gw_extensions_init();
1283     /*s->balance = GW_BALANCE_LEAST_CONNECTION;*//*(default)*/
1284 
1285     /*
1286      * gw.server = ( "<ext>" => ( ... ),
1287      *               "<ext>" => ( ... ) )
1288      */
1289 
1290     for (uint32_t j = 0; j < a->used; ++j) {
1291         data_array *da_ext = (data_array *)a->data[j];
1292 
1293         /*
1294          * da_ext->key == name of the extension
1295          */
1296 
1297         /*
1298          * gw.server = ( "<ext>" =>
1299          *                     ( "<host>" => ( ... ),
1300          *                       "<host>" => ( ... )
1301          *                     ),
1302          *               "<ext>" => ... )
1303          */
1304 
1305         for (uint32_t n = 0; n < da_ext->value.used; ++n) {
1306             data_array * const da_host = (data_array *)da_ext->value.data[n];
1307 
1308             if (da_host->type != TYPE_ARRAY
1309                 || !array_is_kvany(&da_host->value)){
1310                 log_error(srv->errh, __FILE__, __LINE__,
1311                   "unexpected value for gw.server near [%s](string); "
1312                   "expected ( \"ext\" => "
1313                   "( \"backend-label\" => ( \"key\" => \"value\" )))",
1314                   da_host->key.ptr ? da_host->key.ptr : "");
1315                 goto error;
1316             }
1317 
1318             config_plugin_value_t cvlist[sizeof(cpk)/sizeof(cpk[0])+1];
1319             memset(cvlist, 0, sizeof(cvlist));
1320 
1321             array *ca = &da_host->value;
1322             if (!config_plugin_values_init_block(srv, ca, cpk, cpkkey, cvlist))
1323                 goto error;
1324 
1325             unsigned short host_mode = GW_RESPONDER;
1326 
1327             host = gw_host_init();
1328             host->id = &da_host->key;
1329             host->check_local  = 1;
1330             host->min_procs    = 4;
1331             host->max_procs    = 4;
1332             host->max_load_per_proc = 1;
1333             host->idle_timeout = 60;
1334             host->disable_time = 1;
1335             host->break_scriptfilename_for_php = 0;
1336             host->kill_signal = SIGTERM;
1337             host->fix_root_path_name = 0;
1338             host->listen_backlog = 1024;
1339             host->xsendfile_allow = 0;
1340             host->refcount = 0;
1341 
1342             config_plugin_value_t *cpv = cvlist;
1343             for (; -1 != cpv->k_id; ++cpv) {
1344                 switch (cpv->k_id) {
1345                   case 0: /* host */
1346                     host->host = cpv->v.b;
1347                     break;
1348                   case 1: /* port */
1349                     host->port = cpv->v.shrt;
1350                     break;
1351                   case 2: /* socket */
1352                     host->unixsocket = cpv->v.b;
1353                     break;
1354                   case 3: /* listen-backlog */
1355                     host->listen_backlog = cpv->v.u;
1356                     break;
1357                   case 4: /* bin-path */
1358                     host->bin_path = cpv->v.b;
1359                     break;
1360                   case 5: /* kill-signal */
1361                     host->kill_signal = cpv->v.shrt;
1362                     break;
1363                   case 6: /* check-local */
1364                     host->check_local = (0 != cpv->v.u);
1365                     break;
1366                   case 7: /* mode */
1367                     if (!buffer_string_is_empty(cpv->v.b)) {
1368                         const buffer *b = cpv->v.b;
1369                         if (buffer_eq_slen(b, CONST_STR_LEN("responder")))
1370                             host_mode = GW_RESPONDER;
1371                         else if (buffer_eq_slen(b, CONST_STR_LEN("authorizer")))
1372                             host_mode = GW_AUTHORIZER;
1373                         else
1374                             log_error(srv->errh, __FILE__, __LINE__,
1375                               "WARNING: unknown gw mode: %s "
1376                               "(ignored, mode set to responder)", b->ptr);
1377                     }
1378                     break;
1379                   case 8: /* docroot */
1380                     host->docroot = cpv->v.b;
1381                     break;
1382                   case 9: /* min-procs */
1383                     host->min_procs = cpv->v.shrt;
1384                     break;
1385                   case 10:/* max-procs */
1386                     host->max_procs = cpv->v.shrt;
1387                     break;
1388                   case 11:/* max-load-per-proc */
1389                     host->max_load_per_proc = cpv->v.shrt;
1390                     break;
1391                   case 12:/* idle-timeout */
1392                     host->idle_timeout = cpv->v.shrt;
1393                     break;
1394                   case 13:/* disable-time */
1395                     host->disable_time = cpv->v.shrt;
1396                     break;
1397                   case 14:/* bin-environment */
1398                     host->bin_env = cpv->v.a;
1399                     break;
1400                   case 15:/* bin-copy-environment */
1401                     host->bin_env_copy = cpv->v.a;
1402                     break;
1403                   case 16:/* broken-scriptfilename */
1404                     host->break_scriptfilename_for_php = (0 != cpv->v.u);
1405                     break;
1406                   case 17:/* strip-request-uri */
1407                     host->strip_request_uri = cpv->v.b;
1408                     break;
1409                   case 18:/* fix-root-scriptname */
1410                     host->fix_root_path_name = (0 != cpv->v.u);
1411                     break;
1412                   case 19:/* allow-x-send-file */
1413                     host->xsendfile_allow = (0 != cpv->v.u);
1414                     break;
1415                   case 20:/* x-sendfile */
1416                     host->xsendfile_allow = (0 != cpv->v.u);
1417                     break;
1418                   case 21:/* x-sendfile-docroot */
1419                     host->xsendfile_docroot = cpv->v.a;
1420                     if (cpv->v.a->used) {
1421                         for (uint32_t k = 0; k < cpv->v.a->used; ++k) {
1422                             data_string *ds = (data_string *)cpv->v.a->data[k];
1423                             if (ds->type != TYPE_STRING) {
1424                                 log_error(srv->errh, __FILE__, __LINE__,
1425                                   "unexpected type for x-sendfile-docroot; "
1426                                   "expected: \"x-sendfile-docroot\" => "
1427                                   "( \"/allowed/path\", ... )");
1428                                 goto error;
1429                             }
1430                             if (ds->value.ptr[0] != '/') {
1431                                 log_error(srv->errh, __FILE__, __LINE__,
1432                                   "x-sendfile-docroot paths must begin with "
1433                                   "'/'; invalid: \"%s\"", ds->value.ptr);
1434                                 goto error;
1435                             }
1436                             buffer_path_simplify(&ds->value, &ds->value);
1437                             buffer_append_slash(&ds->value);
1438                         }
1439                     }
1440                     break;
1441                   case 22:/* tcp-fin-propagate */
1442                     host->tcp_fin_propagate = (0 != cpv->v.u);
1443                     break;
1444                   default:
1445                     break;
1446                 }
1447             }
1448 
1449             for (uint32_t m = 0; m < da_host->value.used; ++m) {
1450                 if (NULL != strchr(da_host->value.data[m]->key.ptr, '_')) {
1451                     log_error(srv->errh, __FILE__, __LINE__,
1452                       "incorrect directive contains underscore ('_') instead of dash ('-'): %s",
1453                       da_host->value.data[m]->key.ptr);
1454                 }
1455             }
1456 
1457             if ((!buffer_string_is_empty(host->host) || host->port)
1458                 && !buffer_string_is_empty(host->unixsocket)) {
1459                 log_error(srv->errh, __FILE__, __LINE__,
1460                   "either host/port or socket have to be set in: "
1461                   "%s = (%s => (%s ( ...", cpkkey, da_ext->key.ptr,
1462                   da_host->key.ptr);
1463 
1464                 goto error;
1465             }
1466 
1467             if (!buffer_string_is_empty(host->host) && *host->host->ptr == '/'
1468                 && buffer_string_is_empty(host->unixsocket)) {
1469                 host->unixsocket = host->host;
1470             }
1471 
1472             if (!buffer_string_is_empty(host->unixsocket)) {
1473                 /* unix domain socket */
1474                 struct sockaddr_un un;
1475 
1476                 if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1477                     log_error(srv->errh, __FILE__, __LINE__,
1478                       "unixsocket is too long in: %s = (%s => (%s ( ...",
1479                       cpkkey, da_ext->key.ptr, da_host->key.ptr);
1480 
1481                     goto error;
1482                 }
1483 
1484                 if (!buffer_string_is_empty(host->bin_path)) {
1485                     gw_host *duplicate = unixsocket_is_dup(p, host->unixsocket);
1486                     if (NULL != duplicate) {
1487                         if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) {
1488                             log_error(srv->errh, __FILE__, __LINE__,
1489                               "duplicate unixsocket path: %s",
1490                               host->unixsocket->ptr);
1491                             goto error;
1492                         }
1493                         gw_host_free(host);
1494                         host = duplicate;
1495                         ++host->refcount;
1496                     }
1497                 }
1498 
1499                 host->family = AF_UNIX;
1500             } else {
1501                 /* tcp/ip */
1502 
1503                 if (buffer_string_is_empty(host->host) &&
1504                     buffer_string_is_empty(host->bin_path)) {
1505                     log_error(srv->errh, __FILE__, __LINE__,
1506                       "host or bin-path have to be set in: "
1507                       "%s = (%s => (%s ( ...", cpkkey, da_ext->key.ptr,
1508                       da_host->key.ptr);
1509 
1510                     goto error;
1511                 } else if (0 == host->port) {
1512                     host->port = 80;
1513                 }
1514 
1515                 if (buffer_string_is_empty(host->host)) {
1516                     static const buffer lhost = {CONST_STR_LEN("127.0.0.1"), 0};
1517                     host->host = &lhost;
1518                 }
1519 
1520                 host->family = (NULL != strchr(host->host->ptr, ':'))
1521                   ? AF_INET6
1522                   : AF_INET;
1523             }
1524 
1525             if (host->refcount) {
1526                 /* already init'd; skip spawning */
1527             } else if (!buffer_string_is_empty(host->bin_path)) {
1528                 /* a local socket + self spawning */
1529                 struct stat st;
1530                 parse_binpath(&host->args, host->bin_path);
1531                 if (0 != stat(host->args.ptr[0], &st) || !S_ISREG(st.st_mode)
1532                     || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
1533                     log_error(srv->errh, __FILE__, __LINE__,
1534                       "invalid \"bin-path\" => \"%s\" "
1535                       "(check that file exists, is regular file, "
1536                       "and is executable by lighttpd)", host->bin_path->ptr);
1537                 }
1538 
1539                 if (sh_exec) {
1540                     /*(preserve prior behavior for SCGI exec of command)*/
1541                     /*(admin should really prefer to put
1542                      * any complex command into a script)*/
1543                     for (uint32_t m = 0; m < host->args.used; ++m)
1544                         free(host->args.ptr[m]);
1545                     free(host->args.ptr);
1546 
1547                     host->args.ptr = calloc(4, sizeof(char *));
1548                     force_assert(host->args.ptr);
1549                     host->args.used = 3;
1550                     host->args.size = 4;
1551                     host->args.ptr[0] = malloc(sizeof("/bin/sh"));
1552                     force_assert(host->args.ptr[0]);
1553                     memcpy(host->args.ptr[0], "/bin/sh", sizeof("/bin/sh"));
1554                     host->args.ptr[1] = malloc(sizeof("-c"));
1555                     force_assert(host->args.ptr[1]);
1556                     memcpy(host->args.ptr[1], "-c", sizeof("-c"));
1557                     host->args.ptr[2] =
1558                       malloc(sizeof("exec ")-1
1559                              + buffer_string_length(host->bin_path) + 1);
1560                     force_assert(host->args.ptr[2]);
1561                     memcpy(host->args.ptr[2], "exec ", sizeof("exec ")-1);
1562                     memcpy(host->args.ptr[2]+sizeof("exec ")-1,
1563                            host->bin_path->ptr,
1564                            buffer_string_length(host->bin_path)+1);
1565                     host->args.ptr[3] = NULL;
1566                 }
1567 
1568                 if (host->min_procs > host->max_procs)
1569                     host->min_procs = host->max_procs;
1570                 if (host->min_procs!= host->max_procs
1571                     && 0 != srv->srvconf.max_worker) {
1572                     host->min_procs = host->max_procs;
1573                     log_error(srv->errh, __FILE__, __LINE__,
1574                       "adaptive backend spawning disabled "
1575                       "(server.max_worker is non-zero)");
1576                 }
1577                 if (host->max_load_per_proc < 1)
1578                     host->max_load_per_proc = 0;
1579 
1580                 if (s->debug) {
1581                     log_error(srv->errh, __FILE__, __LINE__,
1582                       "--- gw spawning local"
1583                       "\n\tproc: %s"
1584                       "\n\tport: %hu"
1585                       "\n\tsocket %s"
1586                       "\n\tmin-procs: %d"
1587                       "\n\tmax-procs: %d",
1588                       host->bin_path->ptr,
1589                       host->port,
1590                       host->unixsocket && host->unixsocket->ptr
1591                         ? host->unixsocket->ptr
1592                         : "",
1593                       host->min_procs,
1594                       host->max_procs);
1595                 }
1596 
1597                 for (uint32_t pno = 0; pno < host->min_procs; ++pno) {
1598                     gw_proc *proc = gw_proc_init();
1599                     proc->id = host->num_procs++;
1600                     host->max_id++;
1601 
1602                     if (buffer_string_is_empty(host->unixsocket)) {
1603                         proc->port = host->port + pno;
1604                     } else {
1605                         buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1606                         buffer_append_string_len(proc->unixsocket,
1607                                                  CONST_STR_LEN("-"));
1608                         buffer_append_int(proc->unixsocket, pno);
1609                     }
1610 
1611                     if (s->debug) {
1612                         log_error(srv->errh, __FILE__, __LINE__,
1613                           "--- gw spawning"
1614                           "\n\tport: %hu"
1615                           "\n\tsocket %s"
1616                           "\n\tcurrent: %u / %u",
1617                           host->port,
1618                           host->unixsocket && host->unixsocket->ptr
1619                             ? host->unixsocket->ptr
1620                             : "",
1621                           pno, host->max_procs);
1622                     }
1623 
1624                     if (0 != gw_proc_sockaddr_init(host, proc, srv->errh)) {
1625                         gw_proc_free(proc);
1626                         goto error;
1627                     }
1628 
1629                     if (!srv->srvconf.preflight_check
1630                         && gw_spawn_connection(host, proc, srv->errh, s->debug)) {
1631                         log_error(srv->errh, __FILE__, __LINE__,
1632                           "[ERROR]: spawning gw failed.");
1633                         gw_proc_free(proc);
1634                         goto error;
1635                     }
1636 
1637                     gw_status_init(host, proc);
1638 
1639                     proc->next = host->first;
1640                     if (host->first) host->first->prev = proc;
1641 
1642                     host->first = proc;
1643                 }
1644             } else {
1645                 gw_proc *proc;
1646 
1647                 proc = gw_proc_init();
1648                 proc->id = host->num_procs++;
1649                 host->max_id++;
1650                 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
1651 
1652                 if (buffer_string_is_empty(host->unixsocket)) {
1653                     proc->port = host->port;
1654                 } else {
1655                     buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1656                 }
1657 
1658                 gw_status_init(host, proc);
1659 
1660                 host->first = proc;
1661 
1662                 host->min_procs = 1;
1663                 host->max_procs = 1;
1664 
1665                 if (0 != gw_proc_sockaddr_init(host, proc, srv->errh)) goto error;
1666             }
1667 
1668             /* s->exts is list of exts -> hosts
1669              * s->exts now used as combined list
1670              *   of authorizer and responder hosts (for backend maintenance)
1671              * s->exts_auth is list of exts -> authorizer hosts
1672              * s->exts_resp is list of exts -> responder hosts
1673              * For each path/extension:
1674              * there may be an independent GW_AUTHORIZER and GW_RESPONDER
1675              * (The GW_AUTHORIZER and GW_RESPONDER could be handled by the same
1676              *  host, and an admin might want to do that for large uploads,
1677              *  since GW_AUTHORIZER runs prior to receiving (potentially large)
1678              *  request body from client and can authorizer or deny request
1679              *  prior to receiving the full upload)
1680              */
1681             gw_extension_insert(s->exts, &da_ext->key, host);
1682 
1683             if (host_mode == GW_AUTHORIZER) {
1684                 ++host->refcount;
1685                 gw_extension_insert(s->exts_auth, &da_ext->key, host);
1686             } else if (host_mode == GW_RESPONDER) {
1687                 ++host->refcount;
1688                 gw_extension_insert(s->exts_resp, &da_ext->key, host);
1689             } /*(else should have been rejected above)*/
1690 
1691             host = NULL;
1692         }
1693     }
1694 
1695     return 1;
1696 
1697 error:
1698     if (NULL != host) gw_host_free(host);
1699     return 0;
1700 }
1701 
1702 int gw_get_defaults_balance(server *srv, const buffer *b) {
1703     if (buffer_string_is_empty(b))
1704         return GW_BALANCE_LEAST_CONNECTION;
1705     if (buffer_eq_slen(b, CONST_STR_LEN("fair")))
1706         return GW_BALANCE_LEAST_CONNECTION;
1707     if (buffer_eq_slen(b, CONST_STR_LEN("least-connection")))
1708         return GW_BALANCE_LEAST_CONNECTION;
1709     if (buffer_eq_slen(b, CONST_STR_LEN("round-robin")))
1710         return GW_BALANCE_RR;
1711     if (buffer_eq_slen(b, CONST_STR_LEN("hash")))
1712         return GW_BALANCE_HASH;
1713     if (buffer_eq_slen(b, CONST_STR_LEN("sticky")))
1714         return GW_BALANCE_STICKY;
1715 
1716     log_error(srv->errh, __FILE__, __LINE__,
1717       "xxxxx.balance has to be one of: "
1718       "least-connection, round-robin, hash, sticky, but not: %s", b->ptr);
1719     return GW_BALANCE_LEAST_CONNECTION;
1720 }
1721 
1722 
1723 static void gw_set_state(gw_handler_ctx *hctx, gw_connection_state_t state) {
1724     hctx->state = state;
1725     /*hctx->state_timestamp = log_epoch_secs;*/
1726 }
1727 
1728 
1729 void gw_set_transparent(gw_handler_ctx *hctx) {
1730     if (AF_UNIX != hctx->host->family) {
1731         if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1732             /*(error, but not critical)*/
1733         }
1734     }
1735     hctx->wb_reqlen = -1;
1736     gw_set_state(hctx, GW_STATE_WRITE);
1737 }
1738 
1739 
1740 static void gw_backend_close(gw_handler_ctx * const hctx, request_st * const r) {
1741     if (hctx->fd >= 0) {
1742         fdevent_fdnode_event_del(hctx->ev, hctx->fdn);
1743         /*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/
1744         fdevent_sched_close(hctx->ev, hctx->fd, 1);
1745         hctx->fdn = NULL;
1746         hctx->fd = -1;
1747     }
1748 
1749     if (hctx->host) {
1750         if (hctx->proc) {
1751             gw_proc_release(hctx->host, hctx->proc, hctx->conf.debug,
1752                             r->conf.errh);
1753             hctx->proc = NULL;
1754         }
1755 
1756         gw_host_reset(hctx->host);
1757         hctx->host = NULL;
1758     }
1759 }
1760 
1761 static void gw_connection_close(gw_handler_ctx * const hctx, request_st * const r) {
1762     gw_plugin_data *p = hctx->plugin_data;
1763 
1764     gw_backend_close(hctx, r);
1765     handler_ctx_free(hctx);
1766     r->plugin_ctx[p->id] = NULL;
1767 
1768     if (r->handler_module == p->self) {
1769         http_response_backend_done(r);
1770     }
1771 }
1772 
1773 static handler_t gw_reconnect(gw_handler_ctx * const hctx, request_st * const r) {
1774     gw_backend_close(hctx, r);
1775 
1776     hctx->host = gw_host_get(r,hctx->ext,hctx->conf.balance,hctx->conf.debug);
1777     if (NULL == hctx->host) return HANDLER_FINISHED;
1778 
1779     gw_host_assign(hctx->host);
1780     hctx->request_id = 0;
1781     hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow;
1782     hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot;
1783     gw_set_state(hctx, GW_STATE_INIT);
1784     return HANDLER_COMEBACK;
1785 }
1786 
1787 
1788 handler_t gw_connection_reset(request_st * const r, void *p_d) {
1789     gw_plugin_data *p = p_d;
1790     gw_handler_ctx *hctx = r->plugin_ctx[p->id];
1791     if (hctx) gw_connection_close(hctx, r);
1792 
1793     return HANDLER_GO_ON;
1794 }
1795 
1796 
1797 static void gw_conditional_tcp_fin(gw_handler_ctx * const hctx, request_st * const r) {
1798     /*assert(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN);*/
1799     if (!chunkqueue_is_empty(hctx->wb)) return;
1800     if (!hctx->host->tcp_fin_propagate) return;
1801     if (hctx->gw_mode == GW_AUTHORIZER) return;
1802     if (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR)
1803         return;
1804 
1805     /* propagate shutdown SHUT_WR to backend if TCP half-close on con->fd */
1806     r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR;
1807     r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1808     r->con->is_readable = 0;
1809     shutdown(hctx->fd, SHUT_WR);
1810     fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_OUT);
1811 }
1812 
1813 static handler_t gw_write_request(gw_handler_ctx * const hctx, request_st * const r) {
1814     switch(hctx->state) {
1815     case GW_STATE_INIT:
1816         /* do we have a running process for this host (max-procs) ? */
1817         hctx->proc = NULL;
1818 
1819         for (gw_proc *proc = hctx->host->first; proc; proc = proc->next) {
1820              if (proc->state == PROC_STATE_RUNNING) {
1821                  hctx->proc = proc;
1822                  break;
1823              }
1824         }
1825 
1826         /* all children are dead */
1827         if (hctx->proc == NULL) {
1828             return HANDLER_ERROR;
1829         }
1830 
1831         /* check the other procs if they have a lower load */
1832         for (gw_proc *proc = hctx->proc->next; proc; proc = proc->next) {
1833             if (proc->state != PROC_STATE_RUNNING) continue;
1834             if (proc->load < hctx->proc->load) hctx->proc = proc;
1835         }
1836 
1837         gw_proc_load_inc(hctx->host, hctx->proc);
1838 
1839         hctx->fd = fdevent_socket_nb_cloexec(hctx->host->family,SOCK_STREAM,0);
1840         if (-1 == hctx->fd) {
1841             log_error_st * const errh = r->conf.errh;
1842             if (errno == EMFILE || errno == EINTR) {
1843                 log_error(errh, __FILE__, __LINE__,
1844                   "wait for fd at connection: %d", r->con->fd);
1845                 return HANDLER_WAIT_FOR_FD;
1846             }
1847 
1848             log_perror(errh, __FILE__, __LINE__,
1849               "socket failed %d %d",r->con->srv->cur_fds,r->con->srv->max_fds);
1850             return HANDLER_ERROR;
1851         }
1852 
1853         ++r->con->srv->cur_fds;
1854 
1855         hctx->fdn = fdevent_register(hctx->ev,hctx->fd,gw_handle_fdevent,hctx);
1856 
1857         if (hctx->proc->is_local) {
1858             hctx->pid = hctx->proc->pid;
1859         }
1860 
1861         switch (gw_establish_connection(r, hctx->host, hctx->proc, hctx->pid,
1862                                         hctx->fd, hctx->conf.debug)) {
1863         case 1: /* connection is in progress */
1864             fdevent_fdnode_event_set(hctx->ev, hctx->fdn, FDEVENT_OUT);
1865             gw_set_state(hctx, GW_STATE_CONNECT_DELAYED);
1866             return HANDLER_WAIT_FOR_EVENT;
1867         case -1:/* connection error */
1868             return HANDLER_ERROR;
1869         case 0: /* everything is ok, go on */
1870             hctx->reconnects = 0;
1871             break;
1872         }
1873         /* fall through */
1874     case GW_STATE_CONNECT_DELAYED:
1875         if (hctx->state == GW_STATE_CONNECT_DELAYED) { /*(not GW_STATE_INIT)*/
1876             int socket_error = fdevent_connect_status(hctx->fd);
1877             if (socket_error != 0) {
1878                 gw_proc_connect_error(r, hctx->host, hctx->proc, hctx->pid,
1879                                       socket_error, hctx->conf.debug);
1880                 return HANDLER_ERROR;
1881             }
1882             /* go on with preparing the request */
1883         }
1884 
1885         gw_proc_connect_success(hctx->host, hctx->proc, hctx->conf.debug, r);
1886 
1887         gw_set_state(hctx, GW_STATE_PREPARE_WRITE);
1888         /* fall through */
1889     case GW_STATE_PREPARE_WRITE:
1890         /* ok, we have the connection */
1891 
1892         {
1893             handler_t rc = hctx->create_env(hctx);
1894             if (HANDLER_GO_ON != rc) {
1895                 if (HANDLER_FINISHED != rc && HANDLER_ERROR != rc)
1896                     fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_OUT);
1897                 return rc;
1898             }
1899         }
1900 
1901         /*(disable Nagle algorithm if streaming and content-length unknown)*/
1902         if (AF_UNIX != hctx->host->family) {
1903             if (r->reqbody_length < 0) {
1904                 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1905                     /*(error, but not critical)*/
1906                 }
1907             }
1908         }
1909 
1910         fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN|FDEVENT_RDHUP);
1911         gw_set_state(hctx, GW_STATE_WRITE);
1912         /* fall through */
1913     case GW_STATE_WRITE:
1914         if (!chunkqueue_is_empty(hctx->wb)) {
1915             log_error_st * const errh = r->conf.errh;
1916           #if 0
1917             if (hctx->conf.debug > 1) {
1918                 log_error(errh, __FILE__, __LINE__, "sdsx",
1919                   "send data to backend (fd=%d), size=%zu",
1920                   hctx->fd, chunkqueue_length(hctx->wb));
1921             }
1922           #endif
1923             if (r->con->srv->network_backend_write(hctx->fd, hctx->wb,
1924                                                    MAX_WRITE_LIMIT, errh) < 0) {
1925                 switch(errno) {
1926                 case EPIPE:
1927                 case ENOTCONN:
1928                 case ECONNRESET:
1929                     /* the connection got dropped after accept()
1930                      * we don't care about that --
1931                      * if you accept() it, you have to handle it.
1932                      */
1933                     log_error(errh, __FILE__, __LINE__,
1934                       "connection was dropped after accept() "
1935                       "(perhaps the gw process died), "
1936                       "write-offset: %lld socket: %s",
1937                       (long long)hctx->wb->bytes_out,
1938                       hctx->proc->connection_name->ptr);
1939                     return HANDLER_ERROR;
1940                 default:
1941                     log_perror(errh, __FILE__, __LINE__, "write failed");
1942                     return HANDLER_ERROR;
1943                 }
1944             }
1945         }
1946 
1947         if (hctx->wb->bytes_out == hctx->wb_reqlen) {
1948             fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_OUT);
1949             gw_set_state(hctx, GW_STATE_READ);
1950         } else {
1951             off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out;
1952             if ((hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0)
1953                 && wblen < 65536 - 16384) {
1954                 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
1955                 if (!(r->conf.stream_request_body
1956                       & FDEVENT_STREAM_REQUEST_POLLIN)) {
1957                     r->conf.stream_request_body |=
1958                         FDEVENT_STREAM_REQUEST_POLLIN;
1959                     r->con->is_readable = 1; /* trigger optimistic client read */
1960                 }
1961             }
1962             if (0 == wblen) {
1963                 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_OUT);
1964             } else {
1965                 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_OUT);
1966             }
1967         }
1968 
1969         if (r->conf.stream_request_body
1970             & FDEVENT_STREAM_REQUEST_TCP_FIN)
1971             gw_conditional_tcp_fin(hctx, r);
1972 
1973         return HANDLER_WAIT_FOR_EVENT;
1974     case GW_STATE_READ:
1975         /* waiting for a response */
1976         return HANDLER_WAIT_FOR_EVENT;
1977     default:
1978         log_error(r->conf.errh, __FILE__, __LINE__,
1979           "(debug) unknown state");
1980         return HANDLER_ERROR;
1981     }
1982 }
1983 
1984 __attribute_cold__
1985 static handler_t gw_write_error(gw_handler_ctx * const hctx, request_st * const r) {
1986     int status = r->http_status;
1987 
1988     if (hctx->state == GW_STATE_INIT ||
1989         hctx->state == GW_STATE_CONNECT_DELAYED) {
1990 
1991         /* (optimization to detect backend process exit while processing a
1992          *  large number of ready events; (this block could be removed)) */
1993         server * const srv = r->con->srv;
1994         if (0 == srv->srvconf.max_worker)
1995             gw_restart_dead_procs(hctx->host, srv->errh, hctx->conf.debug, 0);
1996 
1997         /* cleanup this request and let request handler start request again */
1998         if (hctx->reconnects++ < 5) return gw_reconnect(hctx, r);
1999     }
2000 
2001     if (hctx->backend_error) hctx->backend_error(hctx);
2002     gw_connection_close(hctx, r);
2003     r->http_status = (status == 400) ? 400 : 503;
2004     return HANDLER_FINISHED;
2005 }
2006 
2007 static handler_t gw_send_request(gw_handler_ctx * const hctx, request_st * const r) {
2008     handler_t rc = gw_write_request(hctx, r);
2009     return (HANDLER_ERROR != rc) ? rc : gw_write_error(hctx, r);
2010 }
2011 
2012 
2013 static handler_t gw_recv_response(gw_handler_ctx *hctx, request_st *r);
2014 
2015 
2016 handler_t gw_handle_subrequest(request_st * const r, void *p_d) {
2017     gw_plugin_data *p = p_d;
2018     gw_handler_ctx *hctx = r->plugin_ctx[p->id];
2019     if (NULL == hctx) return HANDLER_GO_ON;
2020 
2021     if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
2022         && r->resp_body_started) {
2023         if (chunkqueue_length(r->write_queue) > 65536 - 4096) {
2024             fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN);
2025         }
2026         else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) {
2027             /* optimistic read from backend */
2028             handler_t rc;
2029             rc = gw_recv_response(hctx, r);          /*(might invalidate hctx)*/
2030             if (rc != HANDLER_GO_ON) return rc;      /*(unless HANDLER_GO_ON)*/
2031             fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN);
2032         }
2033     }
2034 
2035     /* (do not receive request body before GW_AUTHORIZER has run or else
2036      *  the request body is discarded with handler_ctx_clear() after running
2037      *  the FastCGI Authorizer) */
2038 
2039     if (hctx->gw_mode != GW_AUTHORIZER
2040         && (0 == hctx->wb->bytes_in
2041             ? (r->state == CON_STATE_READ_POST || -1 == hctx->wb_reqlen)
2042             : (hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0))) {
2043         /* leave excess data in r->reqbody_queue, which is
2044          * buffered to disk if too large and backend can not keep up */
2045         /*(64k - 4k to attempt to avoid temporary files
2046          * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
2047         if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096) {
2048             if (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) {
2049                 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
2050             }
2051             if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT;
2052         }
2053         else {
2054             handler_t rc = connection_handle_read_post_state(r);
2055             chunkqueue *req_cq = r->reqbody_queue;
2056           #if 0 /*(not reached since we send 411 Length Required below)*/
2057             if (hctx->wb_reqlen < -1 && r->reqbody_length >= 0) {
2058                 /* (completed receiving Transfer-Encoding: chunked) */
2059                 hctx->wb_reqlen = -hctx->wb_reqlen + r->reqbody_length;
2060                 if (hctx->stdin_append) {
2061                     handler_t rca = hctx->stdin_append(hctx);
2062                     if (HANDLER_GO_ON != rca) return rca;
2063                 }
2064             }
2065           #endif
2066             if ((0 != hctx->wb->bytes_in || -1 == hctx->wb_reqlen)
2067                 && !chunkqueue_is_empty(req_cq)) {
2068                 if (hctx->stdin_append) {
2069                     handler_t rca = hctx->stdin_append(hctx);
2070                     if (HANDLER_GO_ON != rca) return rca;
2071                 }
2072                 else
2073                     chunkqueue_append_chunkqueue(hctx->wb, req_cq);
2074                 if (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_OUT) {
2075                     return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc;
2076                 }
2077             }
2078             if (rc != HANDLER_GO_ON) return rc;
2079 
2080 
2081             /* XXX: create configurable flag */
2082             /* CGI environment requires that Content-Length be set.
2083              * Send 411 Length Required if Content-Length missing.
2084              * (occurs here if client sends Transfer-Encoding: chunked
2085              *  and module is flagged to stream request body to backend) */
2086             /* proxy currently sends HTTP/1.0 request and ideally should send
2087              * Content-Length with request if request body is present, so
2088              * send 411 Length Required if Content-Length missing. */
2089             if (-1 == r->reqbody_length) {
2090                 return connection_handle_read_post_error(r, 411);
2091             }
2092         }
2093     }
2094 
2095     {
2096         handler_t rc =((0==hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb))
2097                        && hctx->state != GW_STATE_CONNECT_DELAYED)
2098           ? gw_send_request(hctx, r)
2099           : HANDLER_WAIT_FOR_EVENT;
2100         if (HANDLER_WAIT_FOR_EVENT != rc) return rc;
2101     }
2102 
2103     if (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN)
2104         gw_conditional_tcp_fin(hctx, r);
2105 
2106     return HANDLER_WAIT_FOR_EVENT;
2107 }
2108 
2109 
2110 static handler_t gw_recv_response(gw_handler_ctx * const hctx, request_st * const r) {
2111     gw_proc *proc = hctx->proc;
2112     gw_host *host = hctx->host;
2113     /*(XXX: make this a configurable flag for other protocols)*/
2114     buffer *b = hctx->opts.backend == BACKEND_FASTCGI
2115       ? chunk_buffer_acquire()
2116       : hctx->response;
2117 
2118     handler_t rc = http_response_read(r, &hctx->opts, b, hctx->fdn);
2119 
2120     if (b != hctx->response) chunk_buffer_release(b);
2121 
2122     switch (rc) {
2123     default:
2124         return HANDLER_GO_ON;
2125     case HANDLER_FINISHED:
2126         if (hctx->gw_mode == GW_AUTHORIZER
2127             && (200 == r->http_status || 0 == r->http_status)) {
2128             /*
2129              * If we are here in AUTHORIZER mode then a request for authorizer
2130              * was processed already, and status 200 has been returned. We need
2131              * now to handle authorized request.
2132              */
2133             char *physpath = NULL;
2134 
2135             if (!buffer_string_is_empty(host->docroot)) {
2136                 buffer_copy_buffer(&r->physical.doc_root, host->docroot);
2137                 buffer_copy_buffer(&r->physical.basedir, host->docroot);
2138 
2139                 buffer_copy_buffer(&r->physical.path, host->docroot);
2140                 buffer_append_string_buffer(&r->physical.path, &r->uri.path);
2141                 physpath = r->physical.path.ptr;
2142             }
2143 
2144             proc->last_used = log_epoch_secs;
2145             gw_backend_close(hctx, r);
2146             handler_ctx_clear(hctx);
2147 
2148             /* don't do more than 6 loops here; normally shouldn't happen */
2149             if (++r->loops_per_request > 5) {
2150                 log_error(r->conf.errh, __FILE__, __LINE__,
2151                   "too many loops while processing request: %s",
2152                   r->target_orig.ptr);
2153                 r->http_status = 500; /* Internal Server Error */
2154                 r->handler_module = NULL;
2155                 return HANDLER_FINISHED;
2156             }
2157 
2158             /* restart the request so other handlers can process it */
2159 
2160             if (physpath) r->physical.path.ptr = NULL;
2161             connection_response_reset(r); /*(includes r->http_status=0)*/
2162             /* preserve r->physical.path.ptr with modified docroot */
2163             if (physpath) r->physical.path.ptr = physpath;
2164 
2165             /*(FYI: if multiple FastCGI authorizers were to be supported,
2166              * next one could be started here instead of restarting request)*/
2167 
2168             r->handler_module = NULL;
2169             return HANDLER_COMEBACK;
2170         } else {
2171             /* we are done */
2172             gw_connection_close(hctx, r);
2173         }
2174 
2175         return HANDLER_FINISHED;
2176     case HANDLER_COMEBACK: /*(not expected; treat as error)*/
2177     case HANDLER_ERROR:
2178         /* (optimization to detect backend process exit while processing a
2179          *  large number of ready events; (this block could be removed)) */
2180         if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid
2181             && proc->state != PROC_STATE_DIED
2182             && 0 == r->con->srv->srvconf.max_worker) {
2183             /* intentionally check proc->disabed_until before gw_proc_waitpid */
2184             log_error_st * const errh = r->con->srv->errh;
2185             if (proc->disabled_until < log_epoch_secs
2186                 && 0 != gw_proc_waitpid(host, proc, errh)) {
2187                 if (hctx->conf.debug) {
2188                     log_error(errh, __FILE__, __LINE__,
2189                       "--- gw spawning\n\tsocket %s\n\tcurrent: 1/%d",
2190                       proc->connection_name->ptr, host->num_procs);
2191                 }
2192 
2193                 if (gw_spawn_connection(host, proc, errh, hctx->conf.debug)) {
2194                     log_error(errh, __FILE__, __LINE__,
2195                       "respawning failed, will retry later");
2196                 }
2197             }
2198         }
2199 
2200         if (r->resp_body_started == 0) {
2201             /* nothing has been sent out yet, try to use another child */
2202 
2203             if (hctx->wb->bytes_out == 0 &&
2204                 hctx->reconnects++ < 5) {
2205 
2206                 log_error(r->conf.errh, __FILE__, __LINE__,
2207                   "response not received, request not sent on "
2208                   "socket: %s for %s?%.*s, reconnecting",
2209                   proc->connection_name->ptr,
2210                   r->uri.path.ptr, BUFFER_INTLEN_PTR(&r->uri.query));
2211 
2212                 return gw_reconnect(hctx, r);
2213             }
2214 
2215             log_error(r->conf.errh, __FILE__, __LINE__,
2216               "response not received, request sent: %lld on "
2217               "socket: %s for %s?%.*s, closing connection",
2218               (long long)hctx->wb->bytes_out, proc->connection_name->ptr,
2219               r->uri.path.ptr, BUFFER_INTLEN_PTR(&r->uri.query));
2220         } else {
2221             log_error(r->conf.errh, __FILE__, __LINE__,
2222               "response already sent out, but backend returned error on "
2223               "socket: %s for %s?%.*s, terminating connection",
2224               proc->connection_name->ptr,
2225               r->uri.path.ptr, BUFFER_INTLEN_PTR(&r->uri.query));
2226         }
2227 
2228         if (hctx->backend_error) hctx->backend_error(hctx);
2229         http_response_backend_error(r);
2230         gw_connection_close(hctx, r);
2231         return HANDLER_FINISHED;
2232     }
2233 }
2234 
2235 
2236 static handler_t gw_handle_fdevent(void *ctx, int revents) {
2237     gw_handler_ctx *hctx = ctx;
2238     request_st * const r = hctx->r;
2239 
2240     joblist_append(r->con);
2241 
2242     if (revents & FDEVENT_IN) {
2243         handler_t rc = gw_recv_response(hctx, r);   /*(might invalidate hctx)*/
2244         if (rc != HANDLER_GO_ON) return rc;         /*(unless HANDLER_GO_ON)*/
2245     }
2246 
2247     if (revents & FDEVENT_OUT) {
2248         return gw_send_request(hctx, r); /*(might invalidate hctx)*/
2249     }
2250 
2251     /* perhaps this issue is already handled */
2252     if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) {
2253         if (hctx->state == GW_STATE_CONNECT_DELAYED) {
2254             /* getoptsock will catch this one (right ?)
2255              *
2256              * if we are in connect we might get an EINPROGRESS
2257              * in the first call and an FDEVENT_HUP in the
2258              * second round
2259              *
2260              * FIXME: as it is a bit ugly.
2261              *
2262              */
2263             gw_send_request(hctx, r);
2264         } else if (r->resp_body_started) {
2265             /* drain any remaining data from kernel pipe buffers
2266              * even if (r->conf.stream_response_body
2267              *          & FDEVENT_STREAM_RESPONSE_BUFMIN)
2268              * since event loop will spin on fd FDEVENT_HUP event
2269              * until unregistered. */
2270             handler_t rc;
2271             const unsigned short flags = r->conf.stream_response_body;
2272             r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN;
2273             r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP;
2274             do {
2275                 rc = gw_recv_response(hctx, r);  /*(might invalidate hctx)*/
2276             } while (rc == HANDLER_GO_ON);       /*(unless HANDLER_GO_ON)*/
2277             r->conf.stream_response_body = flags;
2278             return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
2279         } else {
2280             gw_proc *proc = hctx->proc;
2281             log_error(r->conf.errh, __FILE__, __LINE__,
2282               "error: unexpected close of gw connection for %s?%.*s "
2283               "(no gw process on socket: %s ?) %d",
2284               r->uri.path.ptr, BUFFER_INTLEN_PTR(&r->uri.query),
2285               proc->connection_name->ptr, hctx->state);
2286 
2287             gw_connection_close(hctx, r);
2288         }
2289     } else if (revents & FDEVENT_ERR) {
2290         log_error(r->conf.errh, __FILE__, __LINE__,
2291           "gw: got a FDEVENT_ERR. Don't know why.");
2292 
2293         if (hctx->backend_error) hctx->backend_error(hctx);
2294         http_response_backend_error(r);
2295         gw_connection_close(hctx, r);
2296     }
2297 
2298     return HANDLER_FINISHED;
2299 }
2300 
2301 handler_t gw_check_extension(request_st * const r, gw_plugin_data * const p, int uri_path_handler, size_t hctx_sz) {
2302   #if 0 /*(caller must handle)*/
2303     if (NULL != r->handler_module) return HANDLER_GO_ON;
2304     gw_patch_connection(r, p);
2305     if (NULL == p->conf.exts) return HANDLER_GO_ON;
2306   #endif
2307 
2308     buffer *fn = uri_path_handler ? &r->uri.path : &r->physical.path;
2309     size_t s_len = buffer_string_length(fn);
2310     gw_extension *extension = NULL;
2311     gw_host *host = NULL;
2312     gw_handler_ctx *hctx;
2313     unsigned short gw_mode;
2314 
2315     if (0 == s_len) return HANDLER_GO_ON; /*(not expected)*/
2316 
2317     /* check p->conf.exts_auth list and then p->conf.ext_resp list
2318      * (skip p->conf.exts_auth if array is empty
2319      *  or if GW_AUTHORIZER already ran in this request) */
2320     hctx = r->plugin_ctx[p->id];
2321     /*(hctx not NULL if GW_AUTHORIZER ran; hctx->ext_auth check is redundant)*/
2322     gw_mode = (NULL == hctx || NULL == hctx->ext_auth)
2323       ? 0              /*GW_AUTHORIZER p->conf.exts_auth will be searched next*/
2324       : GW_AUTHORIZER; /*GW_RESPONDER p->conf.exts_resp will be searched next*/
2325 
2326     do {
2327 
2328         gw_exts *exts;
2329         if (0 == gw_mode) {
2330             gw_mode = GW_AUTHORIZER;
2331             exts = p->conf.exts_auth;
2332         } else {
2333             gw_mode = GW_RESPONDER;
2334             exts = p->conf.exts_resp;
2335         }
2336 
2337         if (0 == exts->used) continue;
2338 
2339         /* gw.map-extensions maps extensions to existing gw.server entries
2340          *
2341          * gw.map-extensions = ( ".php3" => ".php" )
2342          *
2343          * gw.server = ( ".php" => ... )
2344          *
2345          * */
2346 
2347         /* check if extension-mapping matches */
2348         if (p->conf.ext_mapping) {
2349             data_string *ds =
2350               (data_string *)array_match_key_suffix(p->conf.ext_mapping, fn);
2351             if (NULL != ds) {
2352                 /* found a mapping */
2353                     /* check if we know the extension */
2354                     uint32_t k;
2355                     for (k = 0; k < exts->used; ++k) {
2356                         extension = exts->exts+k;
2357 
2358                         if (buffer_is_equal(&ds->value, &extension->key)) {
2359                             break;
2360                         }
2361                     }
2362 
2363                     if (k == exts->used) {
2364                         /* found nothing */
2365                         extension = NULL;
2366                     }
2367             }
2368         }
2369 
2370         if (extension == NULL) {
2371             size_t uri_path_len = buffer_string_length(&r->uri.path);
2372 
2373             /* check if extension matches */
2374             for (uint32_t k = 0; k < exts->used; ++k) {
2375                 gw_extension *ext = exts->exts+k;
2376                 size_t ct_len = buffer_string_length(&ext->key);
2377 
2378                 /* check _url_ in the form "/gw_pattern" */
2379                 if (ext->key.ptr[0] == '/') {
2380                     if (ct_len <= uri_path_len
2381                         && 0 == memcmp(r->uri.path.ptr, ext->key.ptr, ct_len)) {
2382                         extension = ext;
2383                         break;
2384                     }
2385                 } else if (ct_len <= s_len
2386                            && 0 == memcmp(fn->ptr + s_len - ct_len,
2387                                           ext->key.ptr, ct_len)) {
2388                     /* check extension in the form ".fcg" */
2389                     extension = ext;
2390                     break;
2391                 }
2392             }
2393         }
2394 
2395     } while (NULL == extension && gw_mode != GW_RESPONDER);
2396 
2397     /* extension doesn't match */
2398     if (NULL == extension) {
2399         return HANDLER_GO_ON;
2400     }
2401 
2402     /* check if we have at least one server for this extension up and running */
2403     host = gw_host_get(r, extension, p->conf.balance, p->conf.debug);
2404     if (NULL == host) {
2405         return HANDLER_FINISHED;
2406     }
2407 
2408     /* a note about no handler is not sent yet */
2409     extension->note_is_sent = 0;
2410 
2411     /*
2412      * if check-local is disabled, use the uri.path handler
2413      *
2414      */
2415 
2416     /* init handler-context */
2417     if (uri_path_handler) {
2418         if (host->check_local != 0) {
2419             return HANDLER_GO_ON;
2420         } else {
2421             /* do not split path info for authorizer */
2422             if (gw_mode != GW_AUTHORIZER) {
2423                 /* the prefix is the SCRIPT_NAME,
2424                 * everything from start to the next slash
2425                 * this is important for check-local = "disable"
2426                 *
2427                 * if prefix = /admin.gw
2428                 *
2429                 * /admin.gw/foo/bar
2430                 *
2431                 * SCRIPT_NAME = /admin.gw
2432                 * PATH_INFO   = /foo/bar
2433                 *
2434                 * if prefix = /cgi-bin/
2435                 *
2436                 * /cgi-bin/foo/bar
2437                 *
2438                 * SCRIPT_NAME = /cgi-bin/foo
2439                 * PATH_INFO   = /bar
2440                 *
2441                 * if prefix = /, and fix-root-path-name is enable
2442                 *
2443                 * /cgi-bin/foo/bar
2444                 *
2445                 * SCRIPT_NAME = /cgi-bin/foo
2446                 * PATH_INFO   = /bar
2447                 *
2448                 */
2449                 char *pathinfo;
2450 
2451                 /* the rewrite is only done for /prefix/? matches */
2452                 if (host->fix_root_path_name && extension->key.ptr[0] == '/'
2453                                              && extension->key.ptr[1] == '\0') {
2454                     buffer_copy_buffer(&r->pathinfo, &r->uri.path);
2455                     buffer_string_set_length(&r->uri.path, 0);
2456                 } else if (extension->key.ptr[0] == '/'
2457                            && buffer_string_length(&r->uri.path)
2458                               > buffer_string_length(&extension->key)
2459                            && (pathinfo =
2460                                  strchr(r->uri.path.ptr
2461                                         + buffer_string_length(&extension->key),
2462                                         '/')) != NULL) {
2463                     /* rewrite uri.path and pathinfo */
2464 
2465                     buffer_copy_string(&r->pathinfo, pathinfo);
2466                     buffer_string_set_length(
2467                       &r->uri.path,
2468                       buffer_string_length(&r->uri.path)
2469                       - buffer_string_length(&r->pathinfo));
2470                 }
2471             }
2472         }
2473     }
2474 
2475     if (!hctx) hctx = handler_ctx_init(hctx_sz);
2476 
2477     hctx->ev               = r->con->srv->ev;
2478     hctx->r                = r;
2479     hctx->plugin_data      = p;
2480     hctx->host             = host;
2481     hctx->proc             = NULL;
2482     hctx->ext              = extension;
2483     gw_host_assign(host);
2484 
2485     hctx->gw_mode = gw_mode;
2486     if (gw_mode == GW_AUTHORIZER) {
2487         hctx->ext_auth = hctx->ext;
2488     }
2489 
2490     /*hctx->conf.exts        = p->conf.exts;*/
2491     /*hctx->conf.exts_auth   = p->conf.exts_auth;*/
2492     /*hctx->conf.exts_resp   = p->conf.exts_resp;*/
2493     /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/
2494     hctx->conf.balance     = p->conf.balance;
2495     hctx->conf.proto       = p->conf.proto;
2496     hctx->conf.debug       = p->conf.debug;
2497 
2498     hctx->opts.fdfmt = S_IFSOCK;
2499     hctx->opts.authorizer = (gw_mode == GW_AUTHORIZER);
2500     hctx->opts.local_redir = 0;
2501     hctx->opts.xsendfile_allow = host->xsendfile_allow;
2502     hctx->opts.xsendfile_docroot = host->xsendfile_docroot;
2503 
2504     r->plugin_ctx[p->id] = hctx;
2505 
2506     r->handler_module = p->self;
2507 
2508     if (r->conf.log_request_handling) {
2509         log_error(r->conf.errh, __FILE__, __LINE__, "handling it in mod_gw");
2510     }
2511 
2512     return HANDLER_GO_ON;
2513 }
2514 
2515 static void gw_handle_trigger_host(gw_host * const host, log_error_st * const errh, const int debug) {
2516     /*
2517      * TODO:
2518      *
2519      * - add timeout for a connect to a non-gw process
2520      *   (use state_timestamp + state)
2521      *
2522      * perhaps we should kill a connect attempt after 10-15 seconds
2523      *
2524      * currently we wait for the TCP timeout which is 180 seconds on Linux
2525      */
2526 
2527     /* check each child proc to detect if proc exited */
2528 
2529     gw_proc *proc;
2530     time_t idle_timestamp;
2531     int overload = 1;
2532 
2533     for (proc = host->first; proc; proc = proc->next) {
2534         gw_proc_waitpid(host, proc, errh);
2535     }
2536 
2537     gw_restart_dead_procs(host, errh, debug, 1);
2538 
2539     /* check if adaptive spawning enabled */
2540     if (host->min_procs == host->max_procs) return;
2541     if (buffer_string_is_empty(host->bin_path)) return;
2542 
2543     for (proc = host->first; proc; proc = proc->next) {
2544         if (proc->load <= host->max_load_per_proc) {
2545             overload = 0;
2546             break;
2547         }
2548     }
2549 
2550     if (overload && host->num_procs && host->num_procs < host->max_procs) {
2551         /* overload, spawn new child */
2552         if (debug) {
2553             log_error(errh, __FILE__, __LINE__,
2554               "overload detected, spawning a new child");
2555         }
2556 
2557         gw_proc_spawn(host, errh, debug);
2558     }
2559 
2560     idle_timestamp = log_epoch_secs - host->idle_timeout;
2561     for (proc = host->first; proc; proc = proc->next) {
2562         if (host->num_procs <= host->min_procs) break;
2563         if (0 != proc->load) continue;
2564         if (proc->pid <= 0) continue;
2565         if (proc->last_used >= idle_timestamp) continue;
2566 
2567         /* terminate proc that has been idling for a long time */
2568         if (debug) {
2569             log_error(errh, __FILE__, __LINE__,
2570               "idle-timeout reached, terminating child: socket: %s pid %d",
2571               proc->unixsocket->ptr, proc->pid);
2572         }
2573 
2574         gw_proc_kill(host, proc);
2575 
2576         /* proc is now in unused, let next second handle next process */
2577         break;
2578     }
2579 
2580     for (proc = host->unused_procs; proc; proc = proc->next) {
2581         gw_proc_waitpid(host, proc, errh);
2582     }
2583 }
2584 
2585 static void gw_handle_trigger_exts(gw_exts * const exts, log_error_st * const errh, const int debug) {
2586     for (uint32_t j = 0; j < exts->used; ++j) {
2587         gw_extension *ex = exts->exts+j;
2588         for (uint32_t n = 0; n < ex->used; ++n) {
2589             gw_handle_trigger_host(ex->hosts[n], errh, debug);
2590         }
2591     }
2592 }
2593 
2594 static void gw_handle_trigger_exts_wkr(gw_exts *exts, log_error_st *errh) {
2595     for (uint32_t j = 0; j < exts->used; ++j) {
2596         gw_extension * const ex = exts->exts+j;
2597         for (uint32_t n = 0; n < ex->used; ++n) {
2598             gw_host * const host = ex->hosts[n];
2599             for (gw_proc *proc = host->first; proc; proc = proc->next) {
2600                 if (proc->state == PROC_STATE_OVERLOADED)
2601                     gw_proc_check_enable(host, proc, errh);
2602             }
2603         }
2604     }
2605 }
2606 
2607 handler_t gw_handle_trigger(server *srv, void *p_d) {
2608     gw_plugin_data * const p = p_d;
2609     int wkr = (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid);
2610     log_error_st * const errh = srv->errh;
2611     int global_debug = 0;
2612 
2613     if (NULL == p->cvlist) return HANDLER_GO_ON;
2614     /* (init i to 0 if global context; to 1 to skip empty global context) */
2615     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
2616         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
2617         gw_plugin_config *conf = NULL;
2618         int debug = global_debug;
2619         for (; -1 != cpv->k_id; ++cpv) {
2620             switch (cpv->k_id) {
2621               case 0: /* xxxxx.server */
2622                 if (cpv->vtype == T_CONFIG_LOCAL) conf = cpv->v.v;
2623                 break;
2624               case 2: /* xxxxx.debug */
2625                 debug = (int)cpv->v.u;
2626                 if (0 == i) global_debug = (int)cpv->v.u;
2627               default:
2628                 break;
2629             }
2630         }
2631 
2632         if (NULL == conf || NULL == conf->exts) continue;
2633 
2634         /* (debug flag is only active if set in same scope as xxxxx.server
2635          *  or global scope (for convenience))
2636          * (unable to use p->defaults.debug since gw_plugin_config
2637          *  might be part of a larger plugin_config) */
2638         wkr
2639           ? gw_handle_trigger_exts_wkr(conf->exts, errh)
2640           : gw_handle_trigger_exts(conf->exts, errh, debug);
2641     }
2642 
2643     return HANDLER_GO_ON;
2644 }
2645 
2646 handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
2647     gw_plugin_data * const p = p_d;
2648     if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid)
2649         return HANDLER_GO_ON;
2650     log_error_st * const errh = srv->errh;
2651     int global_debug = 0;
2652 
2653     if (NULL == p->cvlist) return HANDLER_GO_ON;
2654     /* (init i to 0 if global context; to 1 to skip empty global context) */
2655     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
2656         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
2657         gw_plugin_config *conf = NULL;
2658         int debug = global_debug;
2659         for (; -1 != cpv->k_id; ++cpv) {
2660             switch (cpv->k_id) {
2661               case 0: /* xxxxx.server */
2662                 if (cpv->vtype == T_CONFIG_LOCAL) conf = cpv->v.v;
2663                 break;
2664               case 2: /* xxxxx.debug */
2665                 debug = (int)cpv->v.u;
2666                 if (0 == i) global_debug = (int)cpv->v.u;
2667               default:
2668                 break;
2669             }
2670         }
2671 
2672         if (NULL == conf || NULL == conf->exts) continue;
2673 
2674         /* (debug flag is only active if set in same scope as xxxxx.server
2675          *  or global scope (for convenience))
2676          * (unable to use p->defaults.debug since gw_plugin_config
2677          *  might be part of a larger plugin_config) */
2678         const time_t cur_ts = log_epoch_secs;
2679         gw_exts *exts = conf->exts;
2680         for (uint32_t j = 0; j < exts->used; ++j) {
2681             gw_extension *ex = exts->exts+j;
2682             for (uint32_t n = 0; n < ex->used; ++n) {
2683                 gw_host *host = ex->hosts[n];
2684                 gw_proc *proc;
2685                 for (proc = host->first; proc; proc = proc->next) {
2686                     if (!proc->is_local || proc->pid != pid) continue;
2687 
2688                     gw_proc_waitpid_log(host, proc, errh, status);
2689                     gw_proc_set_state(host, proc, PROC_STATE_DIED);
2690                     proc->pid = 0;
2691 
2692                     /* restart, but avoid spinning if child exits too quickly */
2693                     if (proc->disabled_until < cur_ts) {
2694                         if (proc->state != PROC_STATE_KILLED)
2695                             proc->disabled_until = cur_ts;
2696                         if (gw_spawn_connection(host, proc, errh, debug)) {
2697                             log_error(errh, __FILE__, __LINE__,
2698                               "ERROR: spawning gw failed.");
2699                         }
2700                     }
2701 
2702                     return HANDLER_FINISHED;
2703                 }
2704                 for (proc = host->unused_procs; proc; proc = proc->next) {
2705                     if (!proc->is_local || proc->pid != pid) continue;
2706 
2707                     gw_proc_waitpid_log(host, proc, errh, status);
2708                     if (proc->state != PROC_STATE_KILLED)
2709                         proc->disabled_until = cur_ts;
2710                     gw_proc_set_state(host, proc, PROC_STATE_DIED);
2711                     proc->pid = 0;
2712                     return HANDLER_FINISHED;
2713                 }
2714             }
2715         }
2716     }
2717 
2718     return HANDLER_GO_ON;
2719 }
2720