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