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