1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "mod_proxy.h" 18#include "util_fcgi.h" 19#include "util_script.h" 20 21module AP_MODULE_DECLARE_DATA proxy_fcgi_module; 22 23/* 24 * Canonicalise http-like URLs. 25 * scheme is the scheme for the URL 26 * url is the URL starting with the first '/' 27 * def_port is the default port for this scheme. 28 */ 29static int proxy_fcgi_canon(request_rec *r, char *url) 30{ 31 char *host, sport[7]; 32 const char *err, *path; 33 apr_port_t port, def_port; 34 35 if (strncasecmp(url, "fcgi:", 5) == 0) { 36 url += 5; 37 } 38 else { 39 return DECLINED; 40 } 41 42 port = def_port = ap_proxy_port_of_scheme("fcgi"); 43 44 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 45 "canonicalising URL %s", url); 46 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); 47 if (err) { 48 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01059) 49 "error parsing URL %s: %s", url, err); 50 return HTTP_BAD_REQUEST; 51 } 52 53 if (port != def_port) 54 apr_snprintf(sport, sizeof(sport), ":%d", port); 55 else 56 sport[0] = '\0'; 57 58 if (ap_strchr_c(host, ':')) { 59 /* if literal IPv6 address */ 60 host = apr_pstrcat(r->pool, "[", host, "]", NULL); 61 } 62 63 if (apr_table_get(r->notes, "proxy-nocanon")) { 64 path = url; /* this is the raw path */ 65 } 66 else { 67 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, 68 r->proxyreq); 69 } 70 if (path == NULL) 71 return HTTP_BAD_REQUEST; 72 73 r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/", 74 path, NULL); 75 76 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01060) 77 "set r->filename to %s", r->filename); 78 79 if (apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo")) { 80 r->path_info = apr_pstrcat(r->pool, "/", path, NULL); 81 82 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061) 83 "set r->path_info to %s", r->path_info); 84 } 85 86 return OK; 87} 88 89/* Wrapper for apr_socket_sendv that handles updating the worker stats. */ 90static apr_status_t send_data(proxy_conn_rec *conn, 91 struct iovec *vec, 92 int nvec, 93 apr_size_t *len, 94 int blocking) 95{ 96 apr_status_t rv = APR_SUCCESS, arv; 97 apr_size_t written = 0, to_write = 0; 98 int i, offset; 99 apr_interval_time_t old_timeout; 100 apr_socket_t *s = conn->sock; 101 102 if (!blocking) { 103 arv = apr_socket_timeout_get(s, &old_timeout); 104 if (arv != APR_SUCCESS) { 105 return arv; 106 } 107 arv = apr_socket_timeout_set(s, 0); 108 if (arv != APR_SUCCESS) { 109 return arv; 110 } 111 } 112 113 for (i = 0; i < nvec; i++) { 114 to_write += vec[i].iov_len; 115 } 116 117 offset = 0; 118 while (to_write) { 119 apr_size_t n = 0; 120 rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n); 121 if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) { 122 break; 123 } 124 if (n > 0) { 125 written += n; 126 if (written >= to_write) 127 break; /* short circuit out */ 128 for (i = offset; i < nvec; ) { 129 if (n >= vec[i].iov_len) { 130 offset++; 131 n -= vec[i++].iov_len; 132 } else { 133 vec[i].iov_len -= n; 134 vec[i].iov_base = (char *) vec[i].iov_base + n; 135 break; 136 } 137 } 138 } 139 } 140 141 conn->worker->s->transferred += written; 142 *len = written; 143 144 if (!blocking) { 145 arv = apr_socket_timeout_set(s, old_timeout); 146 if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) { 147 return arv; 148 } 149 } 150 return rv; 151} 152 153/* Wrapper for apr_socket_recv that handles updating the worker stats. */ 154static apr_status_t get_data(proxy_conn_rec *conn, 155 char *buffer, 156 apr_size_t *buflen) 157{ 158 apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen); 159 160 if (rv == APR_SUCCESS) { 161 conn->worker->s->read += *buflen; 162 } 163 164 return rv; 165} 166 167static apr_status_t get_data_full(proxy_conn_rec *conn, 168 char *buffer, 169 apr_size_t buflen) 170{ 171 apr_size_t readlen; 172 apr_size_t cumulative_len = 0; 173 apr_status_t rv; 174 175 do { 176 readlen = buflen - cumulative_len; 177 rv = get_data(conn, buffer + cumulative_len, &readlen); 178 if (rv != APR_SUCCESS) { 179 return rv; 180 } 181 cumulative_len += readlen; 182 } while (cumulative_len < buflen); 183 184 return APR_SUCCESS; 185} 186 187static apr_status_t send_begin_request(proxy_conn_rec *conn, 188 apr_uint16_t request_id) 189{ 190 struct iovec vec[2]; 191 ap_fcgi_header header; 192 unsigned char farray[AP_FCGI_HEADER_LEN]; 193 ap_fcgi_begin_request_body brb; 194 unsigned char abrb[AP_FCGI_HEADER_LEN]; 195 apr_size_t len; 196 197 ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id, 198 sizeof(abrb), 0); 199 200 ap_fcgi_fill_in_request_body(&brb, AP_FCGI_RESPONDER, AP_FCGI_KEEP_CONN); 201 202 ap_fcgi_header_to_array(&header, farray); 203 ap_fcgi_begin_request_body_to_array(&brb, abrb); 204 205 vec[0].iov_base = (void *)farray; 206 vec[0].iov_len = sizeof(farray); 207 vec[1].iov_base = (void *)abrb; 208 vec[1].iov_len = sizeof(abrb); 209 210 return send_data(conn, vec, 2, &len, 1); 211} 212 213static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r, 214 apr_pool_t *temp_pool, 215 apr_uint16_t request_id) 216{ 217 const apr_array_header_t *envarr; 218 const apr_table_entry_t *elts; 219 struct iovec vec[2]; 220 ap_fcgi_header header; 221 unsigned char farray[AP_FCGI_HEADER_LEN]; 222 char *body; 223 apr_status_t rv; 224 apr_size_t avail_len, len, required_len; 225 int next_elem, starting_elem; 226 227 ap_add_common_vars(r); 228 ap_add_cgi_vars(r); 229 230 /* XXX are there any FastCGI specific env vars we need to send? */ 231 232 /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in 233 * the TZ value specially. We could use that, but it would mean 234 * parsing the key/value pairs back OUT of the allocated env array, 235 * not to mention allocating a totally useless array in the first 236 * place, which would suck. */ 237 238 envarr = apr_table_elts(r->subprocess_env); 239 elts = (const apr_table_entry_t *) envarr->elts; 240 241#ifdef FCGI_DUMP_ENV_VARS 242 { 243 int i; 244 245 for (i = 0; i < envarr->nelts; ++i) { 246 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01062) 247 "sending env var '%s' value '%s'", 248 elts[i].key, elts[i].val); 249 } 250 } 251#endif 252 253 /* Send envvars over in as many FastCGI records as it takes, */ 254 next_elem = 0; /* starting with the first one */ 255 256 avail_len = 16 * 1024; /* our limit per record, which could have been up 257 * to AP_FCGI_MAX_CONTENT_LEN 258 */ 259 260 while (next_elem < envarr->nelts) { 261 starting_elem = next_elem; 262 required_len = ap_fcgi_encoded_env_len(r->subprocess_env, 263 avail_len, 264 &next_elem); 265 266 if (!required_len) { 267 if (next_elem < envarr->nelts) { 268 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, 269 APLOGNO(02536) "couldn't encode envvar '%s' in %" 270 APR_SIZE_T_FMT " bytes", 271 elts[next_elem].key, avail_len); 272 /* skip this envvar and continue */ 273 ++next_elem; 274 continue; 275 } 276 /* only an unused element at the end of the array */ 277 break; 278 } 279 280 body = apr_palloc(temp_pool, required_len); 281 rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len, 282 &starting_elem); 283 /* we pre-compute, so we can't run out of space */ 284 ap_assert(rv == APR_SUCCESS); 285 /* compute and encode must be in sync */ 286 ap_assert(starting_elem == next_elem); 287 288 ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 289 (apr_uint16_t)required_len, 0); 290 ap_fcgi_header_to_array(&header, farray); 291 292 vec[0].iov_base = (void *)farray; 293 vec[0].iov_len = sizeof(farray); 294 vec[1].iov_base = body; 295 vec[1].iov_len = required_len; 296 297 rv = send_data(conn, vec, 2, &len, 1); 298 apr_pool_clear(temp_pool); 299 300 if (rv) { 301 return rv; 302 } 303 } 304 305 /* Envvars sent, so say we're done */ 306 ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 0, 0); 307 ap_fcgi_header_to_array(&header, farray); 308 309 vec[0].iov_base = (void *)farray; 310 vec[0].iov_len = sizeof(farray); 311 312 return send_data(conn, vec, 1, &len, 1); 313} 314 315enum { 316 HDR_STATE_READING_HEADERS, 317 HDR_STATE_GOT_CR, 318 HDR_STATE_GOT_CRLF, 319 HDR_STATE_GOT_CRLFCR, 320 HDR_STATE_GOT_LF, 321 HDR_STATE_DONE_WITH_HEADERS 322}; 323 324/* Try to find the end of the script headers in the response from the back 325 * end fastcgi server. STATE holds the current header parsing state for this 326 * request. 327 * 328 * Returns 0 if it can't find the end of the headers, and 1 if it found the 329 * end of the headers. */ 330static int handle_headers(request_rec *r, 331 int *state, 332 char *readbuf) 333{ 334 const char *itr = readbuf; 335 336 while (*itr) { 337 if (*itr == '\r') { 338 switch (*state) { 339 case HDR_STATE_GOT_CRLF: 340 *state = HDR_STATE_GOT_CRLFCR; 341 break; 342 343 default: 344 *state = HDR_STATE_GOT_CR; 345 break; 346 } 347 } 348 else if (*itr == '\n') { 349 switch (*state) { 350 case HDR_STATE_GOT_LF: 351 *state = HDR_STATE_DONE_WITH_HEADERS; 352 break; 353 354 case HDR_STATE_GOT_CR: 355 *state = HDR_STATE_GOT_CRLF; 356 break; 357 358 case HDR_STATE_GOT_CRLFCR: 359 *state = HDR_STATE_DONE_WITH_HEADERS; 360 break; 361 362 default: 363 *state = HDR_STATE_GOT_LF; 364 break; 365 } 366 } 367 else { 368 *state = HDR_STATE_READING_HEADERS; 369 } 370 371 if (*state == HDR_STATE_DONE_WITH_HEADERS) 372 break; 373 374 ++itr; 375 } 376 377 if (*state == HDR_STATE_DONE_WITH_HEADERS) { 378 return 1; 379 } 380 381 return 0; 382} 383 384static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, 385 request_rec *r, apr_pool_t *setaside_pool, 386 apr_uint16_t request_id) 387{ 388 apr_bucket_brigade *ib, *ob; 389 int seen_end_of_headers = 0, done = 0; 390 apr_status_t rv = APR_SUCCESS; 391 int script_error_status = HTTP_OK; 392 conn_rec *c = r->connection; 393 struct iovec vec[2]; 394 ap_fcgi_header header; 395 unsigned char farray[AP_FCGI_HEADER_LEN]; 396 apr_pollfd_t pfd; 397 int header_state = HDR_STATE_READING_HEADERS; 398 399 pfd.desc_type = APR_POLL_SOCKET; 400 pfd.desc.s = conn->sock; 401 pfd.p = r->pool; 402 pfd.reqevents = APR_POLLIN | APR_POLLOUT; 403 404 ib = apr_brigade_create(r->pool, c->bucket_alloc); 405 ob = apr_brigade_create(r->pool, c->bucket_alloc); 406 407 while (! done) { 408 apr_interval_time_t timeout; 409 apr_size_t len; 410 int n; 411 412 /* We need SOME kind of timeout here, or virtually anything will 413 * cause timeout errors. */ 414 apr_socket_timeout_get(conn->sock, &timeout); 415 416 rv = apr_poll(&pfd, 1, &n, timeout); 417 if (rv != APR_SUCCESS) { 418 if (APR_STATUS_IS_EINTR(rv)) { 419 continue; 420 } 421 break; 422 } 423 424 if (pfd.rtnevents & APR_POLLOUT) { 425 char writebuf[AP_IOBUFSIZE]; 426 apr_size_t writebuflen; 427 int last_stdin = 0; 428 int nvec = 0; 429 430 rv = ap_get_brigade(r->input_filters, ib, 431 AP_MODE_READBYTES, APR_BLOCK_READ, 432 sizeof(writebuf)); 433 if (rv != APR_SUCCESS) { 434 break; 435 } 436 437 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) { 438 last_stdin = 1; 439 } 440 441 writebuflen = sizeof(writebuf); 442 443 rv = apr_brigade_flatten(ib, writebuf, &writebuflen); 444 445 apr_brigade_cleanup(ib); 446 447 if (rv != APR_SUCCESS) { 448 break; 449 } 450 451 ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, 452 (apr_uint16_t) writebuflen, 0); 453 ap_fcgi_header_to_array(&header, farray); 454 455 vec[nvec].iov_base = (void *)farray; 456 vec[nvec].iov_len = sizeof(farray); 457 ++nvec; 458 if (writebuflen) { 459 vec[nvec].iov_base = writebuf; 460 vec[nvec].iov_len = writebuflen; 461 ++nvec; 462 } 463 464 rv = send_data(conn, vec, nvec, &len, 0); 465 if (rv != APR_SUCCESS) { 466 break; 467 } 468 469 if (last_stdin) { 470 pfd.reqevents = APR_POLLIN; /* Done with input data */ 471 472 if (writebuflen) { /* empty AP_FCGI_STDIN not already sent? */ 473 ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, 474 0, 0); 475 ap_fcgi_header_to_array(&header, farray); 476 477 vec[0].iov_base = (void *)farray; 478 vec[0].iov_len = sizeof(farray); 479 480 rv = send_data(conn, vec, 1, &len, 1); 481 } 482 } 483 } 484 485 if (pfd.rtnevents & APR_POLLIN) { 486 /* readbuf has one byte on the end that is always 0, so it's 487 * able to work with a strstr when we search for the end of 488 * the headers, even if we fill the entire length in the recv. */ 489 char readbuf[AP_IOBUFSIZE + 1]; 490 apr_size_t readbuflen; 491 apr_uint16_t clen, rid; 492 apr_bucket *b; 493 unsigned char plen; 494 unsigned char type, version; 495 496 memset(readbuf, 0, sizeof(readbuf)); 497 memset(farray, 0, sizeof(farray)); 498 499 /* First, we grab the header... */ 500 rv = get_data_full(conn, (char *) farray, AP_FCGI_HEADER_LEN); 501 if (rv != APR_SUCCESS) { 502 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067) 503 "Failed to read FastCGI header"); 504 break; 505 } 506 507#ifdef FCGI_DUMP_HEADERS 508 ap_log_rdata(APLOG_MARK, APLOG_DEBUG, r, "FastCGI header", 509 farray, AP_FCGI_HEADER_LEN, 0); 510#endif 511 512 ap_fcgi_header_fields_from_array(&version, &type, &rid, 513 &clen, &plen, farray); 514 515 if (version != AP_FCGI_VERSION_1) { 516 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068) 517 "Got bogus version %d", (int)version); 518 rv = APR_EINVAL; 519 break; 520 } 521 522 if (rid != request_id) { 523 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069) 524 "Got bogus rid %d, expected %d", 525 rid, request_id); 526 rv = APR_EINVAL; 527 break; 528 } 529 530recv_again: 531 if (clen > sizeof(readbuf) - 1) { 532 readbuflen = sizeof(readbuf) - 1; 533 } else { 534 readbuflen = clen; 535 } 536 537 /* Now get the actual data. Yes it sucks to do this in a second 538 * recv call, this will eventually change when we move to real 539 * nonblocking recv calls. */ 540 if (readbuflen != 0) { 541 rv = get_data(conn, readbuf, &readbuflen); 542 if (rv != APR_SUCCESS) { 543 break; 544 } 545 readbuf[readbuflen] = 0; 546 } 547 548 switch (type) { 549 case AP_FCGI_STDOUT: 550 if (clen != 0) { 551 b = apr_bucket_transient_create(readbuf, 552 readbuflen, 553 c->bucket_alloc); 554 555 APR_BRIGADE_INSERT_TAIL(ob, b); 556 557 if (! seen_end_of_headers) { 558 int st = handle_headers(r, &header_state, readbuf); 559 560 if (st == 1) { 561 int status; 562 seen_end_of_headers = 1; 563 564 status = ap_scan_script_header_err_brigade_ex(r, ob, 565 NULL, APLOG_MODULE_INDEX); 566 /* suck in all the rest */ 567 if (status != OK) { 568 apr_bucket *tmp_b; 569 apr_brigade_cleanup(ob); 570 tmp_b = apr_bucket_eos_create(c->bucket_alloc); 571 APR_BRIGADE_INSERT_TAIL(ob, tmp_b); 572 r->status = status; 573 ap_pass_brigade(r->output_filters, ob); 574 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070) 575 "Error parsing script headers"); 576 rv = APR_EINVAL; 577 break; 578 } 579 580 if (conf->error_override && 581 ap_is_HTTP_ERROR(r->status)) { 582 /* 583 * set script_error_status to discard 584 * everything after the headers 585 */ 586 script_error_status = r->status; 587 /* 588 * prevent ap_die() from treating this as a 589 * recursive error, initially: 590 */ 591 r->status = HTTP_OK; 592 } 593 594 if (script_error_status == HTTP_OK) { 595 rv = ap_pass_brigade(r->output_filters, ob); 596 if (rv != APR_SUCCESS) { 597 break; 598 } 599 } 600 apr_brigade_cleanup(ob); 601 602 apr_pool_clear(setaside_pool); 603 } 604 else { 605 /* We're still looking for the end of the 606 * headers, so this part of the data will need 607 * to persist. */ 608 apr_bucket_setaside(b, setaside_pool); 609 } 610 } else { 611 /* we've already passed along the headers, so now pass 612 * through the content. we could simply continue to 613 * setaside the content and not pass until we see the 614 * 0 content-length (below, where we append the EOS), 615 * but that could be a huge amount of data; so we pass 616 * along smaller chunks 617 */ 618 if (script_error_status == HTTP_OK) { 619 rv = ap_pass_brigade(r->output_filters, ob); 620 if (rv != APR_SUCCESS) { 621 break; 622 } 623 } 624 apr_brigade_cleanup(ob); 625 } 626 627 /* If we didn't read all the data go back and get the 628 * rest of it. */ 629 if (clen > readbuflen) { 630 clen -= readbuflen; 631 goto recv_again; 632 } 633 } else { 634 /* XXX what if we haven't seen end of the headers yet? */ 635 636 if (script_error_status == HTTP_OK) { 637 b = apr_bucket_eos_create(c->bucket_alloc); 638 APR_BRIGADE_INSERT_TAIL(ob, b); 639 rv = ap_pass_brigade(r->output_filters, ob); 640 if (rv != APR_SUCCESS) { 641 break; 642 } 643 } 644 645 /* XXX Why don't we cleanup here? (logic from AJP) */ 646 } 647 break; 648 649 case AP_FCGI_STDERR: 650 /* TODO: Should probably clean up this logging a bit... */ 651 if (clen) { 652 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071) 653 "Got error '%s'", readbuf); 654 } 655 656 if (clen > readbuflen) { 657 clen -= readbuflen; 658 goto recv_again; 659 } 660 break; 661 662 case AP_FCGI_END_REQUEST: 663 done = 1; 664 break; 665 666 default: 667 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01072) 668 "Got bogus record %d", type); 669 break; 670 } 671 672 if (plen) { 673 rv = get_data_full(conn, readbuf, plen); 674 if (rv != APR_SUCCESS) { 675 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, 676 APLOGNO(02537) "Error occurred reading padding"); 677 break; 678 } 679 } 680 } 681 } 682 683 apr_brigade_destroy(ib); 684 apr_brigade_destroy(ob); 685 686 if (script_error_status != HTTP_OK) { 687 ap_die(script_error_status, r); /* send ErrorDocument */ 688 } 689 690 return rv; 691} 692 693/* 694 * process the request and write the response. 695 */ 696static int fcgi_do_request(apr_pool_t *p, request_rec *r, 697 proxy_conn_rec *conn, 698 conn_rec *origin, 699 proxy_dir_conf *conf, 700 apr_uri_t *uri, 701 char *url, char *server_portstr) 702{ 703 /* Request IDs are arbitrary numbers that we assign to a 704 * single request. This would allow multiplex/pipelinig of 705 * multiple requests to the same FastCGI connection, but 706 * we don't support that, and always use a value of '1' to 707 * keep things simple. */ 708 apr_uint16_t request_id = 1; 709 apr_status_t rv; 710 apr_pool_t *temp_pool; 711 712 /* Step 1: Send AP_FCGI_BEGIN_REQUEST */ 713 rv = send_begin_request(conn, request_id); 714 if (rv != APR_SUCCESS) { 715 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01073) 716 "Failed Writing Request to %s:", server_portstr); 717 conn->close = 1; 718 return HTTP_SERVICE_UNAVAILABLE; 719 } 720 721 apr_pool_create(&temp_pool, r->pool); 722 723 /* Step 2: Send Environment via FCGI_PARAMS */ 724 rv = send_environment(conn, r, temp_pool, request_id); 725 if (rv != APR_SUCCESS) { 726 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01074) 727 "Failed writing Environment to %s:", server_portstr); 728 conn->close = 1; 729 return HTTP_SERVICE_UNAVAILABLE; 730 } 731 732 /* Step 3: Read records from the back end server and handle them. */ 733 rv = dispatch(conn, conf, r, temp_pool, request_id); 734 if (rv != APR_SUCCESS) { 735 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01075) 736 "Error dispatching request to %s:", server_portstr); 737 conn->close = 1; 738 return HTTP_SERVICE_UNAVAILABLE; 739 } 740 741 return OK; 742} 743 744#define FCGI_SCHEME "FCGI" 745 746/* 747 * This handles fcgi:(dest) URLs 748 */ 749static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, 750 proxy_server_conf *conf, 751 char *url, const char *proxyname, 752 apr_port_t proxyport) 753{ 754 int status; 755 char server_portstr[32]; 756 conn_rec *origin = NULL; 757 proxy_conn_rec *backend; 758 759 proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, 760 &proxy_module); 761 762 apr_pool_t *p = r->pool; 763 764 apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri)); 765 766 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01076) 767 "url: %s proxyname: %s proxyport: %d", 768 url, proxyname, proxyport); 769 770 if (strncasecmp(url, "fcgi:", 5) != 0) { 771 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url); 772 return DECLINED; 773 } 774 775 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01078) "serving URL %s", url); 776 777 /* Create space for state information */ 778 status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker, 779 r->server); 780 if (status != OK) { 781 if (backend) { 782 backend->close = 1; 783 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server); 784 } 785 return status; 786 } 787 788 backend->is_ssl = 0; 789 790 /* Step One: Determine Who To Connect To */ 791 status = ap_proxy_determine_connection(p, r, conf, worker, backend, 792 uri, &url, proxyname, proxyport, 793 server_portstr, 794 sizeof(server_portstr)); 795 if (status != OK) { 796 goto cleanup; 797 } 798 799 /* XXX Setting close to 0 is a great way to end up with 800 * timeouts at this point, since we lack good ways to manage the 801 * back end fastcgi processes. This should be revisited when we 802 * have a better story on that part of things. */ 803 backend->close = 1; 804 805 /* Step Two: Make the Connection */ 806 if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) { 807 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079) 808 "failed to make connection to backend: %s", 809 backend->hostname); 810 status = HTTP_SERVICE_UNAVAILABLE; 811 goto cleanup; 812 } 813 814 /* Step Three: Process the Request */ 815 status = fcgi_do_request(p, r, backend, origin, dconf, uri, url, 816 server_portstr); 817 818cleanup: 819 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server); 820 return status; 821} 822 823static void register_hooks(apr_pool_t *p) 824{ 825 proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST); 826 proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST); 827} 828 829AP_DECLARE_MODULE(proxy_fcgi) = { 830 STANDARD20_MODULE_STUFF, 831 NULL, /* create per-directory config structure */ 832 NULL, /* merge per-directory config structures */ 833 NULL, /* create per-server config structure */ 834 NULL, /* merge per-server config structures */ 835 NULL, /* command apr_table_t */ 836 register_hooks /* register hooks */ 837}; 838