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