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