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