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_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
90 "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, APLOGNO(01059)
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, APLOGNO(01060)
120 "set r->filename to %s", r->filename);
122 if (apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo")) {
123 r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
125 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061)
126 "set r->path_info to %s", r->path_info);
133 * Fill in a fastcgi request header with the following type, request id,
134 * content length, and padding length.
136 * The header array must be at least FCGI_HEADER_LEN bytes long.
138 static void fill_in_header(fcgi_header *header,
140 apr_uint16_t request_id,
141 apr_uint16_t content_len,
142 unsigned char padding_len)
144 header->version = FCGI_VERSION;
148 header->requestIdB1 = ((request_id >> 8) & 0xff);
149 header->requestIdB0 = ((request_id) & 0xff);
151 header->contentLengthB1 = ((content_len >> 8) & 0xff);
152 header->contentLengthB0 = ((content_len) & 0xff);
154 header->paddingLength = padding_len;
156 header->reserved = 0;
159 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
160 static apr_status_t send_data(proxy_conn_rec *conn,
166 apr_status_t rv = APR_SUCCESS, arv;
167 apr_size_t written = 0, to_write = 0;
169 apr_interval_time_t old_timeout;
170 apr_socket_t *s = conn->sock;
173 arv = apr_socket_timeout_get(s, &old_timeout);
174 if (arv != APR_SUCCESS) {
177 arv = apr_socket_timeout_set(s, 0);
178 if (arv != APR_SUCCESS) {
183 for (i = 0; i < nvec; i++) {
184 to_write += vec[i].iov_len;
190 rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
191 if (rv != APR_SUCCESS) {
196 if (written >= to_write)
197 break; /* short circuit out */
198 for (i = offset; i < nvec; ) {
199 if (n >= vec[i].iov_len) {
201 n -= vec[i++].iov_len;
204 vec[i].iov_base = (char *) vec[i].iov_base + n;
211 conn->worker->s->transferred += written;
215 arv = apr_socket_timeout_set(s, old_timeout);
216 if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
223 /* Wrapper for apr_socket_recv that handles updating the worker stats. */
224 static apr_status_t get_data(proxy_conn_rec *conn,
228 apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
230 if (rv == APR_SUCCESS) {
231 conn->worker->s->read += *buflen;
237 static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id)
241 unsigned char farray[FCGI_HEADER_LEN];
242 fcgi_begin_request_body brb;
243 unsigned char abrb[FCGI_HEADER_LEN];
246 fill_in_header(&header, FCGI_BEGIN_REQUEST, request_id, sizeof(abrb), 0);
248 brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff);
249 brb.roleB0 = ((FCGI_RESPONDER) & 0xff);
250 brb.flags = FCGI_KEEP_CONN;
257 fcgi_header_to_array(&header, farray);
258 fcgi_begin_request_body_to_array(&brb, abrb);
260 vec[0].iov_base = (void *)farray;
261 vec[0].iov_len = sizeof(farray);
262 vec[1].iov_base = (void *)abrb;
263 vec[1].iov_len = sizeof(abrb);
265 return send_data(conn, vec, 2, &len, 1);
268 static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
271 const apr_array_header_t *envarr;
272 const apr_table_entry_t *elts;
275 unsigned char farray[FCGI_HEADER_LEN];
276 apr_size_t bodylen, envlen;
282 ap_add_common_vars(r);
285 /* XXX are there any FastCGI specific env vars we need to send? */
287 bodylen = envlen = 0;
289 /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
290 * the TZ value specially. We could use that, but it would mean
291 * parsing the key/value pairs back OUT of the allocated env array,
292 * not to mention allocating a totally useless array in the first
293 * place, which would suck. */
295 envarr = apr_table_elts(r->subprocess_env);
297 elts = (const apr_table_entry_t *) envarr->elts;
299 for (i = 0; i < envarr->nelts; ++i) {
300 apr_size_t keylen, vallen;
306 keylen = strlen(elts[i].key);
308 if (keylen >> 7 == 0) {
317 vallen = strlen(elts[i].val);
319 #ifdef FCGI_DUMP_ENV_VARS
320 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01062)
321 "sending env var '%s' value '%s'",
322 elts[i].key, elts[i].val);
325 if (vallen >> 7 == 0) {
334 /* The cast of bodylen is safe since FCGI_MAX_ENV_SIZE is for sure an int */
335 if (envlen > FCGI_MAX_ENV_SIZE) {
336 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01063)
337 "truncating environment to %d bytes and %d elements",
347 body = apr_pcalloc(r->pool, bodylen);
351 for (i = 0; i < numenv; ++i) {
352 apr_size_t keylen, vallen;
358 keylen = strlen(elts[i].key);
360 if (keylen >> 7 == 0) {
361 itr[0] = keylen & 0xff;
365 itr[0] = ((keylen >> 24) & 0xff) | 0x80;
366 itr[1] = ((keylen >> 16) & 0xff);
367 itr[2] = ((keylen >> 8) & 0xff);
368 itr[3] = ((keylen) & 0xff);
372 vallen = strlen(elts[i].val);
374 if (vallen >> 7 == 0) {
375 itr[0] = vallen & 0xff;
379 itr[0] = ((vallen >> 24) & 0xff) | 0x80;
380 itr[1] = ((vallen >> 16) & 0xff);
381 itr[2] = ((vallen >> 8) & 0xff);
382 itr[3] = ((vallen) & 0xff);
386 memcpy(itr, elts[i].key, keylen);
389 memcpy(itr, elts[i].val, vallen);
393 fill_in_header(&header, FCGI_PARAMS, request_id, bodylen, 0);
394 fcgi_header_to_array(&header, farray);
396 vec[0].iov_base = (void *)farray;
397 vec[0].iov_len = sizeof(farray);
398 vec[1].iov_base = body;
399 vec[1].iov_len = bodylen;
401 rv = send_data(conn, vec, 2, &len, 1);
406 fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0);
407 fcgi_header_to_array(&header, farray);
409 vec[0].iov_base = (void *)farray;
410 vec[0].iov_len = sizeof(farray);
412 return send_data(conn, vec, 1, &len, 1);
416 HDR_STATE_READING_HEADERS,
419 HDR_STATE_GOT_CRLFCR,
421 HDR_STATE_DONE_WITH_HEADERS
424 /* Try to find the end of the script headers in the response from the back
425 * end fastcgi server. STATE holds the current header parsing state for this
428 * Returns 0 if it can't find the end of the headers, and 1 if it found the
429 * end of the headers. */
430 static int handle_headers(request_rec *r,
434 const char *itr = readbuf;
439 case HDR_STATE_GOT_CRLF:
440 *state = HDR_STATE_GOT_CRLFCR;
444 *state = HDR_STATE_GOT_CR;
448 else if (*itr == '\n') {
450 case HDR_STATE_GOT_LF:
451 *state = HDR_STATE_DONE_WITH_HEADERS;
454 case HDR_STATE_GOT_CR:
455 *state = HDR_STATE_GOT_CRLF;
458 case HDR_STATE_GOT_CRLFCR:
459 *state = HDR_STATE_DONE_WITH_HEADERS;
463 *state = HDR_STATE_GOT_LF;
468 *state = HDR_STATE_READING_HEADERS;
471 if (*state == HDR_STATE_DONE_WITH_HEADERS)
477 if (*state == HDR_STATE_DONE_WITH_HEADERS) {
484 static void dump_header_to_log(request_rec *r, unsigned char fheader[],
487 #ifdef FCGI_DUMP_HEADERS
493 memset(asc_line, 0, sizeof(asc_line));
494 memset(hex_line, 0, sizeof(hex_line));
496 while (posn < length) {
497 unsigned char c = fheader[posn];
502 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01064)
503 "HEADER: %s %s", asc_line, hex_line);
505 memset(asc_line, 0, sizeof(asc_line));
506 memset(hex_line, 0, sizeof(hex_line));
516 if ((c >> 4) >= 10) {
517 hex_line[i * 3] = 'a' + ((c >> 4) - 10);
520 hex_line[i * 3] = '0' + (c >> 4);
523 if ((c & 0x0F) >= 10) {
524 hex_line[i * 3 + 1] = 'a' + ((c & 0x0F) - 10);
527 hex_line[i * 3 + 1] = '0' + (c & 0xF);
530 hex_line[i * 3 + 2] = ' ';
537 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01065) "HEADER: %s %s",
541 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01066) "HEADER: -EOH-");
545 static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
546 request_rec *r, int request_id)
548 apr_bucket_brigade *ib, *ob;
549 int seen_end_of_headers = 0, done = 0;
550 apr_status_t rv = APR_SUCCESS;
551 int script_error_status = HTTP_OK;
552 conn_rec *c = r->connection;
555 unsigned char farray[FCGI_HEADER_LEN];
557 int header_state = HDR_STATE_READING_HEADERS;
558 apr_pool_t *setaside_pool;
560 apr_pool_create(&setaside_pool, r->pool);
562 pfd.desc_type = APR_POLL_SOCKET;
563 pfd.desc.s = conn->sock;
565 pfd.reqevents = APR_POLLIN | APR_POLLOUT;
567 ib = apr_brigade_create(r->pool, c->bucket_alloc);
568 ob = apr_brigade_create(r->pool, c->bucket_alloc);
571 apr_interval_time_t timeout = conn->worker->s->timeout;
575 /* We need SOME kind of timeout here, or virtually anything will
576 * cause timeout errors. */
577 if (! conn->worker->s->timeout_set) {
578 timeout = apr_time_from_sec(30);
581 rv = apr_poll(&pfd, 1, &n, timeout);
582 if (rv != APR_SUCCESS) {
583 if (APR_STATUS_IS_EINTR(rv)) {
589 if (pfd.rtnevents & APR_POLLOUT) {
590 char writebuf[AP_IOBUFSIZE];
591 apr_size_t writebuflen;
595 rv = ap_get_brigade(r->input_filters, ib,
596 AP_MODE_READBYTES, APR_BLOCK_READ,
598 if (rv != APR_SUCCESS) {
602 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) {
606 writebuflen = sizeof(writebuf);
608 rv = apr_brigade_flatten(ib, writebuf, &writebuflen);
610 apr_brigade_cleanup(ib);
612 if (rv != APR_SUCCESS) {
616 fill_in_header(&header, FCGI_STDIN, request_id,
617 (apr_uint16_t) writebuflen, 0);
618 fcgi_header_to_array(&header, farray);
620 vec[nvec].iov_base = (void *)farray;
621 vec[nvec].iov_len = sizeof(farray);
624 vec[nvec].iov_base = writebuf;
625 vec[nvec].iov_len = writebuflen;
629 rv = send_data(conn, vec, nvec, &len, 0);
630 if (rv != APR_SUCCESS) {
635 pfd.reqevents = APR_POLLIN; /* Done with input data */
637 if (writebuflen) { /* empty FCGI_STDIN not already sent? */
638 fill_in_header(&header, FCGI_STDIN, request_id, 0, 0);
639 fcgi_header_to_array(&header, farray);
641 vec[0].iov_base = (void *)farray;
642 vec[0].iov_len = sizeof(farray);
644 rv = send_data(conn, vec, 1, &len, 1);
649 if (pfd.rtnevents & APR_POLLIN) {
650 /* readbuf has one byte on the end that is always 0, so it's
651 * able to work with a strstr when we search for the end of
652 * the headers, even if we fill the entire length in the recv. */
653 char readbuf[AP_IOBUFSIZE + 1];
654 apr_size_t readbuflen;
660 memset(readbuf, 0, sizeof(readbuf));
661 memset(farray, 0, sizeof(farray));
663 /* First, we grab the header... */
664 readbuflen = FCGI_HEADER_LEN;
666 rv = get_data(conn, (char *) farray, &readbuflen);
667 if (rv != APR_SUCCESS) {
671 dump_header_to_log(r, farray, readbuflen);
673 if (readbuflen != FCGI_HEADER_LEN) {
674 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067)
675 "Failed to read entire header "
676 "got %" APR_SIZE_T_FMT " wanted %d",
677 readbuflen, FCGI_HEADER_LEN);
682 fcgi_header_from_array(&header, farray);
684 if (header.version != FCGI_VERSION) {
685 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068)
686 "Got bogus version %d", (int) header.version);
693 rid = header.requestIdB1 << 8;
694 rid |= header.requestIdB0;
696 if (rid != request_id) {
697 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069)
698 "Got bogus rid %d, expected %d",
704 clen = header.contentLengthB1 << 8;
705 clen |= header.contentLengthB0;
707 plen = header.paddingLength;
710 if (clen > sizeof(readbuf) - 1) {
711 readbuflen = sizeof(readbuf) - 1;
716 /* Now get the actual data. Yes it sucks to do this in a second
717 * recv call, this will eventually change when we move to real
718 * nonblocking recv calls. */
719 if (readbuflen != 0) {
720 rv = get_data(conn, readbuf, &readbuflen);
721 if (rv != APR_SUCCESS) {
724 readbuf[readbuflen] = 0;
730 b = apr_bucket_transient_create(readbuf,
734 APR_BRIGADE_INSERT_TAIL(ob, b);
736 if (! seen_end_of_headers) {
737 int st = handle_headers(r, &header_state, readbuf);
741 seen_end_of_headers = 1;
743 status = ap_scan_script_header_err_brigade_ex(r, ob,
744 NULL, APLOG_MODULE_INDEX);
745 /* suck in all the rest */
748 apr_brigade_cleanup(ob);
749 tmp_b = apr_bucket_eos_create(c->bucket_alloc);
750 APR_BRIGADE_INSERT_TAIL(ob, tmp_b);
751 ap_pass_brigade(r->output_filters, ob);
752 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070)
753 "Error parsing script headers");
759 if (conf->error_override &&
760 ap_is_HTTP_ERROR(r->status)) {
762 * set script_error_status to discard
763 * everything after the headers
765 script_error_status = r->status;
767 * prevent ap_die() from treating this as a
768 * recursive error, initially:
773 if (script_error_status == HTTP_OK) {
774 rv = ap_pass_brigade(r->output_filters, ob);
775 if (rv != APR_SUCCESS) {
779 apr_brigade_cleanup(ob);
781 apr_pool_clear(setaside_pool);
784 /* We're still looking for the end of the
785 * headers, so this part of the data will need
787 apr_bucket_setaside(b, setaside_pool);
790 /* we've already passed along the headers, so now pass
791 * through the content. we could simply continue to
792 * setaside the content and not pass until we see the
793 * 0 content-length (below, where we append the EOS),
794 * but that could be a huge amount of data; so we pass
795 * along smaller chunks
797 if (script_error_status == HTTP_OK) {
798 rv = ap_pass_brigade(r->output_filters, ob);
799 if (rv != APR_SUCCESS) {
803 apr_brigade_cleanup(ob);
806 /* If we didn't read all the data go back and get the
808 if (clen > readbuflen) {
813 /* XXX what if we haven't seen end of the headers yet? */
815 if (script_error_status == HTTP_OK) {
816 b = apr_bucket_eos_create(c->bucket_alloc);
817 APR_BRIGADE_INSERT_TAIL(ob, b);
818 rv = ap_pass_brigade(r->output_filters, ob);
819 if (rv != APR_SUCCESS) {
824 /* XXX Why don't we cleanup here? (logic from AJP) */
829 /* TODO: Should probably clean up this logging a bit... */
831 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071)
832 "Got error '%s'", readbuf);
835 if (clen > readbuflen) {
841 case FCGI_END_REQUEST:
846 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01072)
847 "Got bogus record %d", type);
854 rv = get_data(conn, readbuf, &readbuflen);
855 if (rv != APR_SUCCESS) {
862 apr_brigade_destroy(ib);
863 apr_brigade_destroy(ob);
865 if (script_error_status != HTTP_OK) {
866 ap_die(script_error_status, r); /* send ErrorDocument */
873 * process the request and write the response.
875 static int fcgi_do_request(apr_pool_t *p, request_rec *r,
876 proxy_conn_rec *conn,
878 proxy_dir_conf *conf,
880 char *url, char *server_portstr)
882 /* Request IDs are arbitrary numbers that we assign to a
883 * single request. This would allow multiplex/pipelinig of
884 * multiple requests to the same FastCGI connection, but
885 * we don't support that, and always use a value of '1' to
886 * keep things simple. */
890 /* Step 1: Send FCGI_BEGIN_REQUEST */
891 rv = send_begin_request(conn, request_id);
892 if (rv != APR_SUCCESS) {
893 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01073)
894 "Failed Writing Request to %s:", server_portstr);
896 return HTTP_SERVICE_UNAVAILABLE;
899 /* Step 2: Send Environment via FCGI_PARAMS */
900 rv = send_environment(conn, r, request_id);
901 if (rv != APR_SUCCESS) {
902 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01074)
903 "Failed writing Environment to %s:", server_portstr);
905 return HTTP_SERVICE_UNAVAILABLE;
908 /* Step 3: Read records from the back end server and handle them. */
909 rv = dispatch(conn, conf, r, request_id);
910 if (rv != APR_SUCCESS) {
911 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01075)
912 "Error dispatching request to %s:", server_portstr);
914 return HTTP_SERVICE_UNAVAILABLE;
920 #define FCGI_SCHEME "FCGI"
923 * This handles fcgi:(dest) URLs
925 static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
926 proxy_server_conf *conf,
927 char *url, const char *proxyname,
928 apr_port_t proxyport)
931 char server_portstr[32];
932 conn_rec *origin = NULL;
933 proxy_conn_rec *backend = NULL;
935 proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
938 apr_pool_t *p = r->pool;
940 apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri));
942 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01076)
943 "url: %s proxyname: %s proxyport: %d",
944 url, proxyname, proxyport);
946 if (strncasecmp(url, "fcgi:", 5) == 0) {
950 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url);
954 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01078) "serving URL %s", url);
956 /* Create space for state information */
958 status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker,
963 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
971 /* Step One: Determine Who To Connect To */
972 status = ap_proxy_determine_connection(p, r, conf, worker, backend,
973 uri, &url, proxyname, proxyport,
975 sizeof(server_portstr));
980 /* XXX Setting close to 0 is a great way to end up with
981 * timeouts at this point, since we lack good ways to manage the
982 * back end fastcgi processes. This should be revisited when we
983 * have a better story on that part of things. */
986 /* Step Two: Make the Connection */
987 if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) {
988 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079)
989 "failed to make connection to backend: %s",
991 status = HTTP_SERVICE_UNAVAILABLE;
995 /* Step Three: Process the Request */
996 status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
1000 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
1004 static void register_hooks(apr_pool_t *p)
1006 proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST);
1007 proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST);
1010 AP_DECLARE_MODULE(proxy_fcgi) = {
1011 STANDARD20_MODULE_STUFF,
1012 NULL, /* create per-directory config structure */
1013 NULL, /* merge per-directory config structures */
1014 NULL, /* create per-server config structure */
1015 NULL, /* merge per-server config structures */
1016 NULL, /* command apr_table_t */
1017 register_hooks /* register hooks */