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