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