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