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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "mod_proxy.h"
18 #include "fcgi_protocol.h"
19 #include "util_script.h"
21 module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
24 * The below 3 functions serve to map the FCGI structs
25 * back and forth between an 8 byte array. We do this to avoid
26 * any potential padding issues when we send or read these
29 * NOTE: These have specific internal knowledge of the
30 * layout of the fcgi_header and fcgi_begin_request_body
33 static void fcgi_header_to_array(fcgi_header *h, unsigned char a[])
35 a[FCGI_HDR_VERSION_OFFSET] = h->version;
36 a[FCGI_HDR_TYPE_OFFSET] = h->type;
37 a[FCGI_HDR_REQUEST_ID_B1_OFFSET] = h->requestIdB1;
38 a[FCGI_HDR_REQUEST_ID_B0_OFFSET] = h->requestIdB0;
39 a[FCGI_HDR_CONTENT_LEN_B1_OFFSET] = h->contentLengthB1;
40 a[FCGI_HDR_CONTENT_LEN_B0_OFFSET] = h->contentLengthB0;
41 a[FCGI_HDR_PADDING_LEN_OFFSET] = h->paddingLength;
42 a[FCGI_HDR_RESERVED_OFFSET] = h->reserved;
45 static void fcgi_header_from_array(fcgi_header *h, unsigned char a[])
47 h->version = a[FCGI_HDR_VERSION_OFFSET];
48 h->type = a[FCGI_HDR_TYPE_OFFSET];
49 h->requestIdB1 = a[FCGI_HDR_REQUEST_ID_B1_OFFSET];
50 h->requestIdB0 = a[FCGI_HDR_REQUEST_ID_B0_OFFSET];
51 h->contentLengthB1 = a[FCGI_HDR_CONTENT_LEN_B1_OFFSET];
52 h->contentLengthB0 = a[FCGI_HDR_CONTENT_LEN_B0_OFFSET];
53 h->paddingLength = a[FCGI_HDR_PADDING_LEN_OFFSET];
54 h->reserved = a[FCGI_HDR_RESERVED_OFFSET];
57 static void fcgi_begin_request_body_to_array(fcgi_begin_request_body *h,
60 a[FCGI_BRB_ROLEB1_OFFSET] = h->roleB1;
61 a[FCGI_BRB_ROLEB0_OFFSET] = h->roleB0;
62 a[FCGI_BRB_FLAGS_OFFSET] = h->flags;
63 a[FCGI_BRB_RESERVED0_OFFSET] = h->reserved[0];
64 a[FCGI_BRB_RESERVED1_OFFSET] = h->reserved[1];
65 a[FCGI_BRB_RESERVED2_OFFSET] = h->reserved[2];
66 a[FCGI_BRB_RESERVED3_OFFSET] = h->reserved[3];
67 a[FCGI_BRB_RESERVED4_OFFSET] = h->reserved[4];
71 * Canonicalise http-like URLs.
72 * scheme is the scheme for the URL
73 * url is the URL starting with the first '/'
74 * def_port is the default port for this scheme.
76 static int proxy_fcgi_canon(request_rec *r, char *url)
79 const char *err, *path;
80 apr_port_t port = 8000;
82 if (strncasecmp(url, "fcgi:", 5) == 0) {
89 ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, r->server,
90 "proxy: FCGI: canonicalising URL %s", url);
92 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
94 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
95 "error parsing URL %s: %s", url, err);
96 return HTTP_BAD_REQUEST;
99 apr_snprintf(sport, sizeof(sport), ":%d", port);
101 if (ap_strchr_c(host, ':')) {
102 /* if literal IPv6 address */
103 host = apr_pstrcat(r->pool, "[", host, "]", NULL);
106 if (apr_table_get(r->notes, "proxy-nocanon")) {
107 path = url; /* this is the raw path */
110 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
114 return HTTP_BAD_REQUEST;
116 r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/",
119 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
120 "proxy: FCGI: set r->filename to %s", r->filename);
122 r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
124 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
125 "proxy: FCGI: set r->path_info to %s", r->path_info);
131 * Fill in a fastcgi request header with the following type, request id,
132 * content length, and padding length.
134 * The header array must be at least FCGI_HEADER_LEN bytes long.
136 static void fill_in_header(fcgi_header *header,
138 apr_uint16_t request_id,
139 apr_uint16_t content_len,
140 unsigned char padding_len)
142 header->version = FCGI_VERSION;
146 header->requestIdB1 = ((request_id >> 8) & 0xff);
147 header->requestIdB0 = ((request_id) & 0xff);
149 header->contentLengthB1 = ((content_len >> 8) & 0xff);
150 header->contentLengthB0 = ((content_len) & 0xff);
152 header->paddingLength = padding_len;
154 header->reserved = 0;
157 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
158 static apr_status_t send_data(proxy_conn_rec *conn,
164 apr_status_t rv = APR_SUCCESS, arv;
165 apr_size_t written = 0, to_write = 0;
167 apr_interval_time_t old_timeout;
168 apr_socket_t *s = conn->sock;
171 arv = apr_socket_timeout_get(s, &old_timeout);
172 if (arv != APR_SUCCESS) {
175 arv = apr_socket_timeout_set(s, 0);
176 if (arv != APR_SUCCESS) {
181 for (i = 0; i < nvec; i++) {
182 to_write += vec[i].iov_len;
188 rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
189 if (rv != APR_SUCCESS) {
194 if (written >= to_write)
195 break; /* short circuit out */
196 for (i = offset; i < nvec; ) {
197 if (n >= vec[i].iov_len) {
199 n -= vec[i++].iov_len;
202 vec[i].iov_base = (char *) vec[i].iov_base + n;
209 conn->worker->s->transferred += written;
213 arv = apr_socket_timeout_set(s, old_timeout);
214 if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
221 /* Wrapper for apr_socket_recv that handles updating the worker stats. */
222 static apr_status_t get_data(proxy_conn_rec *conn,
226 apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
228 if (rv == APR_SUCCESS) {
229 conn->worker->s->read += *buflen;
235 static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id)
239 unsigned char farray[FCGI_HEADER_LEN];
240 fcgi_begin_request_body brb;
241 unsigned char abrb[FCGI_HEADER_LEN];
244 fill_in_header(&header, FCGI_BEGIN_REQUEST, request_id, sizeof(abrb), 0);
246 brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff);
247 brb.roleB0 = ((FCGI_RESPONDER) & 0xff);
248 brb.flags = FCGI_KEEP_CONN;
255 fcgi_header_to_array(&header, farray);
256 fcgi_begin_request_body_to_array(&brb, abrb);
258 vec[0].iov_base = farray;
259 vec[0].iov_len = sizeof(farray);
260 vec[1].iov_base = abrb;
261 vec[1].iov_len = sizeof(abrb);
263 return send_data(conn, vec, 2, &len, 1);
266 static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
269 const apr_array_header_t *envarr;
270 const apr_table_entry_t *elts;
273 unsigned char farray[FCGI_HEADER_LEN];
274 apr_size_t bodylen, envlen;
280 ap_add_common_vars(r);
283 /* XXX are there any FastCGI specific env vars we need to send? */
285 bodylen = envlen = 0;
287 /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
288 * the TZ value specially. We could use that, but it would mean
289 * parsing the key/value pairs back OUT of the allocated env array,
290 * not to mention allocating a totally useless array in the first
291 * place, which would suck. */
293 envarr = apr_table_elts(r->subprocess_env);
295 elts = (const apr_table_entry_t *) envarr->elts;
297 for (i = 0; i < envarr->nelts; ++i) {
298 apr_size_t keylen, vallen;
304 keylen = strlen(elts[i].key);
306 if (keylen >> 7 == 0) {
315 vallen = strlen(elts[i].val);
317 #ifdef FCGI_DUMP_ENV_VARS
318 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
319 "proxy: FCGI: sending env var '%s' value '%s'",
320 elts[i].key, elts[i].val);
323 if (vallen >> 7 == 0) {
332 /* The cast of bodylen is safe since FCGI_MAX_ENV_SIZE is for sure an int */
333 if (envlen > FCGI_MAX_ENV_SIZE) {
334 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
335 "proxy: FCGI: truncating environment to %d bytes and %d elements",
345 body = apr_pcalloc(r->pool, bodylen);
349 for (i = 0; i < numenv; ++i) {
350 apr_size_t keylen, vallen;
356 keylen = strlen(elts[i].key);
358 if (keylen >> 7 == 0) {
359 itr[0] = keylen & 0xff;
363 itr[0] = ((keylen >> 24) & 0xff) | 0x80;
364 itr[1] = ((keylen >> 16) & 0xff);
365 itr[2] = ((keylen >> 8) & 0xff);
366 itr[3] = ((keylen) & 0xff);
370 vallen = strlen(elts[i].val);
372 if (vallen >> 7 == 0) {
373 itr[0] = vallen & 0xff;
377 itr[0] = ((vallen >> 24) & 0xff) | 0x80;
378 itr[1] = ((vallen >> 16) & 0xff);
379 itr[2] = ((vallen >> 8) & 0xff);
380 itr[3] = ((vallen) & 0xff);
384 memcpy(itr, elts[i].key, keylen);
387 memcpy(itr, elts[i].val, vallen);
391 fill_in_header(&header, FCGI_PARAMS, request_id, bodylen, 0);
392 fcgi_header_to_array(&header, farray);
394 vec[0].iov_base = farray;
395 vec[0].iov_len = sizeof(farray);
396 vec[1].iov_base = body;
397 vec[1].iov_len = bodylen;
399 rv = send_data(conn, vec, 2, &len, 1);
404 fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0);
405 fcgi_header_to_array(&header, farray);
407 vec[0].iov_base = farray;
408 vec[0].iov_len = sizeof(farray);
410 return send_data(conn, vec, 1, &len, 1);
414 HDR_STATE_READING_HEADERS,
417 HDR_STATE_GOT_CRLFCR,
419 HDR_STATE_DONE_WITH_HEADERS
422 /* Try to parse the script headers in the response from the back end fastcgi
423 * server. Assumes that the contents of READBUF have already been added to
424 * the end of OB. STATE holds the current header parsing state for this
427 * Returns -1 on error, 0 if it can't find the end of the headers, and 1 if
428 * it found the end of the headers and scans them successfully. */
429 static int handle_headers(request_rec *r,
432 apr_bucket_brigade *ob)
434 conn_rec *c = r->connection;
435 const char *itr = readbuf;
440 case HDR_STATE_GOT_CRLF:
441 *state = HDR_STATE_GOT_CRLFCR;
445 *state = HDR_STATE_GOT_CR;
449 else if (*itr == '\n') {
451 case HDR_STATE_GOT_LF:
452 *state = HDR_STATE_DONE_WITH_HEADERS;
455 case HDR_STATE_GOT_CR:
456 *state = HDR_STATE_GOT_CRLF;
459 case HDR_STATE_GOT_CRLFCR:
460 *state = HDR_STATE_DONE_WITH_HEADERS;
464 *state = HDR_STATE_GOT_LF;
469 *state = HDR_STATE_READING_HEADERS;
472 if (*state == HDR_STATE_DONE_WITH_HEADERS)
478 if (*state == HDR_STATE_DONE_WITH_HEADERS) {
479 int status = ap_scan_script_header_err_brigade(r, ob, NULL);
485 apr_brigade_cleanup(ob);
487 b = apr_bucket_eos_create(c->bucket_alloc);
489 APR_BRIGADE_INSERT_TAIL(ob, b);
491 ap_pass_brigade(r->output_filters, ob);
493 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
494 "proxy: FCGI: Error parsing script headers");
506 static void dump_header_to_log(request_rec *r, unsigned char fheader[],
509 #ifdef FCGI_DUMP_HEADERS
515 memset(asc_line, 0, sizeof(asc_line));
516 memset(hex_line, 0, sizeof(hex_line));
518 while (posn < length) {
519 unsigned char c = fheader[posn];
524 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
525 "HEADER: %s %s", asc_line, hex_line);
527 memset(asc_line, 0, sizeof(asc_line));
528 memset(hex_line, 0, sizeof(hex_line));
538 if ((c >> 4) >= 10) {
539 hex_line[i * 3] = 'a' + ((c >> 4) - 10);
542 hex_line[i * 3] = '0' + (c >> 4);
545 if ((c & 0x0F) >= 10) {
546 hex_line[i * 3 + 1] = 'a' + ((c & 0x0F) - 10);
549 hex_line[i * 3 + 1] = '0' + (c & 0xF);
552 hex_line[i * 3 + 2] = ' ';
559 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "HEADER: %s %s",
563 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "HEADER: -EOH-");
567 static apr_status_t dispatch(proxy_conn_rec *conn, request_rec *r,
570 apr_bucket_brigade *ib, *ob;
571 int seen_end_of_headers = 0, done = 0;
572 apr_status_t rv = APR_SUCCESS;
573 conn_rec *c = r->connection;
576 unsigned char farray[FCGI_HEADER_LEN];
578 int header_state = HDR_STATE_READING_HEADERS;
579 apr_pool_t *setaside_pool;
581 apr_pool_create(&setaside_pool, r->pool);
583 pfd.desc_type = APR_POLL_SOCKET;
584 pfd.desc.s = conn->sock;
586 pfd.reqevents = APR_POLLIN | APR_POLLOUT;
588 ib = apr_brigade_create(r->pool, c->bucket_alloc);
589 ob = apr_brigade_create(r->pool, c->bucket_alloc);
592 apr_interval_time_t timeout = conn->worker->timeout;
596 /* We need SOME kind of timeout here, or virtually anything will
597 * cause timeout errors. */
598 if (! conn->worker->timeout_set) {
599 timeout = apr_time_from_sec(30);
602 rv = apr_poll(&pfd, 1, &n, timeout);
603 if (rv != APR_SUCCESS) {
604 if (APR_STATUS_IS_EINTR(rv)) {
610 if (pfd.rtnevents & APR_POLLOUT) {
611 char writebuf[AP_IOBUFSIZE];
612 apr_size_t writebuflen;
615 rv = ap_get_brigade(r->input_filters, ib,
616 AP_MODE_READBYTES, APR_BLOCK_READ,
618 if (rv != APR_SUCCESS) {
622 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) {
626 writebuflen = sizeof(writebuf);
628 rv = apr_brigade_flatten(ib, writebuf, &writebuflen);
630 apr_brigade_cleanup(ib);
632 if (rv != APR_SUCCESS) {
636 fill_in_header(&header, FCGI_STDIN, request_id,
637 (apr_uint16_t) writebuflen, 0);
638 fcgi_header_to_array(&header, farray);
640 vec[0].iov_base = farray;
641 vec[0].iov_len = sizeof(farray);
642 vec[1].iov_base = writebuf;
643 vec[1].iov_len = writebuflen;
645 rv = send_data(conn, vec, 2, &len, 0);
646 if (rv != APR_SUCCESS) {
651 pfd.reqevents = APR_POLLIN; /* Done with input data */
653 fill_in_header(&header, FCGI_STDIN, request_id, 0, 0);
654 fcgi_header_to_array(&header, farray);
656 vec[0].iov_base = farray;
657 vec[0].iov_len = sizeof(farray);
659 rv = send_data(conn, vec, 1, &len, 1);
663 if (pfd.rtnevents & APR_POLLIN) {
664 /* readbuf has one byte on the end that is always 0, so it's
665 * able to work with a strstr when we search for the end of
666 * the headers, even if we fill the entire length in the recv. */
667 char readbuf[AP_IOBUFSIZE + 1];
668 apr_size_t readbuflen;
674 memset(readbuf, 0, sizeof(readbuf));
675 memset(farray, 0, sizeof(farray));
677 /* First, we grab the header... */
678 readbuflen = FCGI_HEADER_LEN;
680 rv = get_data(conn, (char *) farray, &readbuflen);
681 if (rv != APR_SUCCESS) {
685 dump_header_to_log(r, farray, readbuflen);
687 if (readbuflen != FCGI_HEADER_LEN) {
688 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
689 "proxy: FCGI: Failed to read entire header "
690 "got %" APR_SIZE_T_FMT " wanted %d",
691 readbuflen, FCGI_HEADER_LEN);
696 fcgi_header_from_array(&header, farray);
698 if (header.version != FCGI_VERSION) {
699 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
700 "proxy: FCGI: Got bogus version %d",
701 (int) header.version);
708 rid = header.requestIdB1 << 8;
709 rid |= header.requestIdB0;
711 if (rid != request_id) {
712 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
713 "proxy: FCGI: Got bogus rid %d, expected %d",
719 clen = header.contentLengthB1 << 8;
720 clen |= header.contentLengthB0;
722 plen = header.paddingLength;
725 if (clen > sizeof(readbuf) - 1) {
726 readbuflen = sizeof(readbuf) - 1;
731 /* Now get the actual data. Yes it sucks to do this in a second
732 * recv call, this will eventually change when we move to real
733 * nonblocking recv calls. */
734 if (readbuflen != 0) {
735 rv = get_data(conn, readbuf, &readbuflen);
736 if (rv != APR_SUCCESS) {
739 readbuf[readbuflen] = 0;
745 b = apr_bucket_transient_create(readbuf,
749 APR_BRIGADE_INSERT_TAIL(ob, b);
751 if (! seen_end_of_headers) {
752 int st = handle_headers(r, &header_state, readbuf, ob);
755 seen_end_of_headers = 1;
757 rv = ap_pass_brigade(r->output_filters, ob);
758 if (rv != APR_SUCCESS) {
762 apr_brigade_cleanup(ob);
764 apr_pool_clear(setaside_pool);
771 /* We're still looking for the end of the
772 * headers, so this part of the data will need
774 apr_bucket_setaside(b, setaside_pool);
777 /* we've already passed along the headers, so now pass
778 * through the content. we could simply continue to
779 * setaside the content and not pass until we see the
780 * 0 content-length (below, where we append the EOS),
781 * but that could be a huge amount of data; so we pass
782 * along smaller chunks
784 rv = ap_pass_brigade(r->output_filters, ob);
785 if (rv != APR_SUCCESS) {
788 apr_brigade_cleanup(ob);
791 /* If we didn't read all the data go back and get the
793 if (clen > readbuflen) {
798 /* XXX what if we haven't seen end of the headers yet? */
800 b = apr_bucket_eos_create(c->bucket_alloc);
802 APR_BRIGADE_INSERT_TAIL(ob, b);
804 rv = ap_pass_brigade(r->output_filters, ob);
805 if (rv != APR_SUCCESS) {
809 /* XXX Why don't we cleanup here? (logic from AJP) */
814 /* TODO: Should probably clean up this logging a bit... */
816 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
817 "proxy: FCGI: Got error '%s'", readbuf);
820 if (clen > readbuflen) {
826 case FCGI_END_REQUEST:
831 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
832 "proxy: FCGI: Got bogus record %d", type);
839 rv = get_data(conn, readbuf, &readbuflen);
840 if (rv != APR_SUCCESS) {
847 apr_brigade_destroy(ib);
848 apr_brigade_destroy(ob);
854 * process the request and write the response.
856 static int fcgi_do_request(apr_pool_t *p, request_rec *r,
857 proxy_conn_rec *conn,
859 proxy_dir_conf *conf,
861 char *url, char *server_portstr)
863 /* Request IDs are arbitrary numbers that we assign to a
864 * single request. This would allow multiplex/pipelinig of
865 * multiple requests to the same FastCGI connection, but
866 * we don't support that, and always use a value of '1' to
867 * keep things simple. */
871 /* Step 1: Send FCGI_BEGIN_REQUEST */
872 rv = send_begin_request(conn, request_id);
873 if (rv != APR_SUCCESS) {
874 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
875 "proxy: FCGI: Failed Writing Request to %s:",
878 return HTTP_SERVICE_UNAVAILABLE;
881 /* Step 2: Send Environment via FCGI_PARAMS */
882 rv = send_environment(conn, r, request_id);
883 if (rv != APR_SUCCESS) {
884 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
885 "proxy: FCGI: Failed writing Environment to %s:",
888 return HTTP_SERVICE_UNAVAILABLE;
891 /* Step 3: Read records from the back end server and handle them. */
892 rv = dispatch(conn, r, request_id);
893 if (rv != APR_SUCCESS) {
894 ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
895 "proxy: FCGI: Error dispatching request to %s:",
898 return HTTP_SERVICE_UNAVAILABLE;
904 #define FCGI_SCHEME "FCGI"
907 * This handles fcgi:(dest) URLs
909 static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
910 proxy_server_conf *conf,
911 char *url, const char *proxyname,
912 apr_port_t proxyport)
915 char server_portstr[32];
916 conn_rec *origin = NULL;
917 proxy_conn_rec *backend = NULL;
919 proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
922 apr_pool_t *p = r->pool;
924 apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri));
926 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
927 "proxy: FCGI: url: %s proxyname: %s proxyport: %d",
928 url, proxyname, proxyport);
930 if (strncasecmp(url, "fcgi:", 5) == 0) {
934 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
935 "proxy: FCGI: declining URL %s", url);
939 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
940 "proxy: FCGI: serving URL %s", url);
942 /* Create space for state information */
944 status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker,
949 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
957 /* XXX Setting close to 0 is a great way to end up with
958 * timeouts at this point, since we lack good ways to manage the
959 * back end fastcgi processes. This should be revisited when we
960 * have a better story on that part of things. */
964 /* Step One: Determine Who To Connect To */
965 status = ap_proxy_determine_connection(p, r, conf, worker, backend,
966 uri, &url, proxyname, proxyport,
968 sizeof(server_portstr));
973 /* Step Two: Make the Connection */
974 if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) {
975 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
976 "proxy: FCGI: failed to make connection to backend: %s",
978 status = HTTP_SERVICE_UNAVAILABLE;
982 /* Step Three: Process the Request */
983 status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
987 /* Do not close the socket */
988 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
992 static void register_hooks(apr_pool_t *p)
994 proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST);
995 proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST);
998 AP_DECLARE_MODULE(proxy_fcgi) = {
999 STANDARD20_MODULE_STUFF,
1000 NULL, /* create per-directory config structure */
1001 NULL, /* merge per-directory config structures */
1002 NULL, /* create per-server config structure */
1003 NULL, /* merge per-server config structures */
1004 NULL, /* command apr_table_t */
1005 register_hooks /* register hooks */