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