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 "util_fcgi.h"
19 #include "util_script.h"
21 module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
28 * Canonicalise http-like URLs.
29 * scheme is the scheme for the URL
30 * url is the URL starting with the first '/'
31 * def_port is the default port for this scheme.
33 static int proxy_fcgi_canon(request_rec *r, char *url)
38 apr_port_t port, def_port;
39 fcgi_req_config_t *rconf = NULL;
40 const char *pathinfo_type = NULL;
42 if (strncasecmp(url, "fcgi:", 5) == 0) {
49 port = def_port = ap_proxy_port_of_scheme("fcgi");
51 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
52 "canonicalising URL %s", url);
53 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
55 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01059)
56 "error parsing URL %s: %s", url, err);
57 return HTTP_BAD_REQUEST;
61 apr_snprintf(sport, sizeof(sport), ":%d", port);
65 if (ap_strchr_c(host, ':')) {
66 /* if literal IPv6 address */
67 host = apr_pstrcat(r->pool, "[", host, "]", NULL);
70 if (apr_table_get(r->notes, "proxy-nocanon")) {
71 path = url; /* this is the raw path */
74 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
78 return HTTP_BAD_REQUEST;
80 r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/",
83 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01060)
84 "set r->filename to %s", r->filename);
86 rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
88 rconf = apr_pcalloc(r->pool, sizeof(fcgi_req_config_t));
89 ap_set_module_config(r->request_config, &proxy_fcgi_module, rconf);
92 if (NULL != (pathinfo_type = apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo"))) {
93 /* It has to be on disk for this to work */
94 if (!strcasecmp(pathinfo_type, "full")) {
95 rconf->need_dirwalk = 1;
96 ap_unescape_url_keep2f(path, 0);
98 else if (!strcasecmp(pathinfo_type, "first-dot")) {
99 char *split = ap_strchr(path, '.');
101 char *slash = ap_strchr(split, '/');
103 r->path_info = apr_pstrdup(r->pool, slash);
104 ap_unescape_url_keep2f(r->path_info, 0);
105 *slash = '\0'; /* truncate path */
109 else if (!strcasecmp(pathinfo_type, "last-dot")) {
110 char *split = ap_strrchr(path, '.');
112 char *slash = ap_strchr(split, '/');
114 r->path_info = apr_pstrdup(r->pool, slash);
115 ap_unescape_url_keep2f(r->path_info, 0);
116 *slash = '\0'; /* truncate path */
121 /* before proxy-fcgi-pathinfo had multi-values. This requires the
122 * the FCGI server to fixup PATH_INFO because it's the entire path
124 r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
125 if (!strcasecmp(pathinfo_type, "unescape")) {
126 ap_unescape_url_keep2f(r->path_info, 0);
128 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061)
129 "set r->path_info to %s", r->path_info);
136 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
137 static apr_status_t send_data(proxy_conn_rec *conn,
142 apr_status_t rv = APR_SUCCESS;
143 apr_size_t written = 0, to_write = 0;
145 apr_socket_t *s = conn->sock;
147 for (i = 0; i < nvec; i++) {
148 to_write += vec[i].iov_len;
154 rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
155 if (rv != APR_SUCCESS) {
160 if (written >= to_write)
161 break; /* short circuit out */
162 for (i = offset; i < nvec; ) {
163 if (n >= vec[i].iov_len) {
165 n -= vec[i++].iov_len;
168 vec[i].iov_base = (char *) vec[i].iov_base + n;
175 conn->worker->s->transferred += written;
181 /* Wrapper for apr_socket_recv that handles updating the worker stats. */
182 static apr_status_t get_data(proxy_conn_rec *conn,
186 apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
188 if (rv == APR_SUCCESS) {
189 conn->worker->s->read += *buflen;
195 static apr_status_t get_data_full(proxy_conn_rec *conn,
200 apr_size_t cumulative_len = 0;
204 readlen = buflen - cumulative_len;
205 rv = get_data(conn, buffer + cumulative_len, &readlen);
206 if (rv != APR_SUCCESS) {
209 cumulative_len += readlen;
210 } while (cumulative_len < buflen);
215 static apr_status_t send_begin_request(proxy_conn_rec *conn,
216 apr_uint16_t request_id)
219 ap_fcgi_header header;
220 unsigned char farray[AP_FCGI_HEADER_LEN];
221 ap_fcgi_begin_request_body brb;
222 unsigned char abrb[AP_FCGI_HEADER_LEN];
225 ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id,
228 ap_fcgi_fill_in_request_body(&brb, AP_FCGI_RESPONDER,
229 ap_proxy_connection_reusable(conn)
230 ? AP_FCGI_KEEP_CONN : 0);
232 ap_fcgi_header_to_array(&header, farray);
233 ap_fcgi_begin_request_body_to_array(&brb, abrb);
235 vec[0].iov_base = (void *)farray;
236 vec[0].iov_len = sizeof(farray);
237 vec[1].iov_base = (void *)abrb;
238 vec[1].iov_len = sizeof(abrb);
240 return send_data(conn, vec, 2, &len);
243 static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
244 apr_pool_t *temp_pool,
245 apr_uint16_t request_id)
247 const apr_array_header_t *envarr;
248 const apr_table_entry_t *elts;
250 ap_fcgi_header header;
251 unsigned char farray[AP_FCGI_HEADER_LEN];
254 apr_size_t avail_len, len, required_len;
255 int next_elem, starting_elem;
256 char *proxyfilename = r->filename;
257 fcgi_req_config_t *rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
260 if (rconf->need_dirwalk) {
261 ap_directory_walk(r);
265 /* Strip balancer prefix */
266 if (r->filename && !strncmp(r->filename, "proxy:balancer://", 17)) {
267 char *newfname = apr_pstrdup(r->pool, r->filename+17);
268 newfname = ap_strchr(newfname, '/');
269 r->filename = newfname;
272 ap_add_common_vars(r);
275 r->filename = proxyfilename;
277 /* XXX are there any FastCGI specific env vars we need to send? */
279 /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
280 * the TZ value specially. We could use that, but it would mean
281 * parsing the key/value pairs back OUT of the allocated env array,
282 * not to mention allocating a totally useless array in the first
283 * place, which would suck. */
285 envarr = apr_table_elts(r->subprocess_env);
286 elts = (const apr_table_entry_t *) envarr->elts;
288 if (APLOGrtrace8(r)) {
291 for (i = 0; i < envarr->nelts; ++i) {
292 ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(01062)
293 "sending env var '%s' value '%s'",
294 elts[i].key, elts[i].val);
298 /* Send envvars over in as many FastCGI records as it takes, */
299 next_elem = 0; /* starting with the first one */
301 avail_len = 16 * 1024; /* our limit per record, which could have been up
302 * to AP_FCGI_MAX_CONTENT_LEN
305 while (next_elem < envarr->nelts) {
306 starting_elem = next_elem;
307 required_len = ap_fcgi_encoded_env_len(r->subprocess_env,
312 if (next_elem < envarr->nelts) {
313 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
314 APLOGNO(02536) "couldn't encode envvar '%s' in %"
315 APR_SIZE_T_FMT " bytes",
316 elts[next_elem].key, avail_len);
317 /* skip this envvar and continue */
321 /* only an unused element at the end of the array */
325 body = apr_palloc(temp_pool, required_len);
326 rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len,
328 /* we pre-compute, so we can't run out of space */
329 ap_assert(rv == APR_SUCCESS);
330 /* compute and encode must be in sync */
331 ap_assert(starting_elem == next_elem);
333 ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id,
334 (apr_uint16_t)required_len, 0);
335 ap_fcgi_header_to_array(&header, farray);
337 vec[0].iov_base = (void *)farray;
338 vec[0].iov_len = sizeof(farray);
339 vec[1].iov_base = body;
340 vec[1].iov_len = required_len;
342 rv = send_data(conn, vec, 2, &len);
343 apr_pool_clear(temp_pool);
350 /* Envvars sent, so say we're done */
351 ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 0, 0);
352 ap_fcgi_header_to_array(&header, farray);
354 vec[0].iov_base = (void *)farray;
355 vec[0].iov_len = sizeof(farray);
357 return send_data(conn, vec, 1, &len);
361 HDR_STATE_READING_HEADERS,
364 HDR_STATE_GOT_CRLFCR,
366 HDR_STATE_DONE_WITH_HEADERS
369 /* Try to find the end of the script headers in the response from the back
370 * end fastcgi server. STATE holds the current header parsing state for this
373 * Returns 0 if it can't find the end of the headers, and 1 if it found the
374 * end of the headers. */
375 static int handle_headers(request_rec *r, int *state,
376 const char *readbuf, apr_size_t readlen)
378 const char *itr = readbuf;
383 case HDR_STATE_GOT_CRLF:
384 *state = HDR_STATE_GOT_CRLFCR;
388 *state = HDR_STATE_GOT_CR;
392 else if (*itr == '\n') {
394 case HDR_STATE_GOT_LF:
395 *state = HDR_STATE_DONE_WITH_HEADERS;
398 case HDR_STATE_GOT_CR:
399 *state = HDR_STATE_GOT_CRLF;
402 case HDR_STATE_GOT_CRLFCR:
403 *state = HDR_STATE_DONE_WITH_HEADERS;
407 *state = HDR_STATE_GOT_LF;
412 *state = HDR_STATE_READING_HEADERS;
415 if (*state == HDR_STATE_DONE_WITH_HEADERS)
421 if (*state == HDR_STATE_DONE_WITH_HEADERS) {
428 static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
429 request_rec *r, apr_pool_t *setaside_pool,
430 apr_uint16_t request_id, const char **err,
431 int *bad_request, int *has_responded)
433 apr_bucket_brigade *ib, *ob;
434 int seen_end_of_headers = 0, done = 0, ignore_body = 0;
435 apr_status_t rv = APR_SUCCESS;
436 int script_error_status = HTTP_OK;
437 conn_rec *c = r->connection;
439 ap_fcgi_header header;
440 unsigned char farray[AP_FCGI_HEADER_LEN];
442 int header_state = HDR_STATE_READING_HEADERS;
443 char stack_iobuf[AP_IOBUFSIZE];
444 apr_size_t iobuf_size = AP_IOBUFSIZE;
445 char *iobuf = stack_iobuf;
448 if (conn->worker->s->io_buffer_size_set) {
449 iobuf_size = conn->worker->s->io_buffer_size;
450 iobuf = apr_palloc(r->pool, iobuf_size);
453 pfd.desc_type = APR_POLL_SOCKET;
454 pfd.desc.s = conn->sock;
456 pfd.reqevents = APR_POLLIN | APR_POLLOUT;
458 ib = apr_brigade_create(r->pool, c->bucket_alloc);
459 ob = apr_brigade_create(r->pool, c->bucket_alloc);
462 apr_interval_time_t timeout;
466 /* We need SOME kind of timeout here, or virtually anything will
467 * cause timeout errors. */
468 apr_socket_timeout_get(conn->sock, &timeout);
470 rv = apr_poll(&pfd, 1, &n, timeout);
471 if (rv != APR_SUCCESS) {
472 if (APR_STATUS_IS_EINTR(rv)) {
479 if (pfd.rtnevents & APR_POLLOUT) {
480 apr_size_t to_send, writebuflen;
484 rv = ap_get_brigade(r->input_filters, ib,
485 AP_MODE_READBYTES, APR_BLOCK_READ,
487 if (rv != APR_SUCCESS) {
488 *err = "reading input brigade";
493 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) {
497 writebuflen = iobuf_size;
499 rv = apr_brigade_flatten(ib, iobuf, &writebuflen);
501 apr_brigade_cleanup(ib);
503 if (rv != APR_SUCCESS) {
504 *err = "flattening brigade";
508 to_send = writebuflen;
509 iobuf_cursor = iobuf;
510 while (to_send > 0) {
512 apr_size_t write_this_time;
515 to_send < AP_FCGI_MAX_CONTENT_LEN ? to_send : AP_FCGI_MAX_CONTENT_LEN;
517 ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id,
518 (apr_uint16_t)write_this_time, 0);
519 ap_fcgi_header_to_array(&header, farray);
521 vec[nvec].iov_base = (void *)farray;
522 vec[nvec].iov_len = sizeof(farray);
525 vec[nvec].iov_base = iobuf_cursor;
526 vec[nvec].iov_len = write_this_time;
530 rv = send_data(conn, vec, nvec, &len);
531 if (rv != APR_SUCCESS) {
532 *err = "sending stdin";
536 to_send -= write_this_time;
537 iobuf_cursor += write_this_time;
539 if (rv != APR_SUCCESS) {
544 pfd.reqevents = APR_POLLIN; /* Done with input data */
546 /* signal EOF (empty FCGI_STDIN) */
547 ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id,
549 ap_fcgi_header_to_array(&header, farray);
551 vec[0].iov_base = (void *)farray;
552 vec[0].iov_len = sizeof(farray);
554 rv = send_data(conn, vec, 1, &len);
555 if (rv != APR_SUCCESS) {
556 *err = "sending empty stdin";
562 if (pfd.rtnevents & APR_POLLIN) {
563 apr_size_t readbuflen;
564 apr_uint16_t clen, rid;
567 unsigned char type, version;
569 /* First, we grab the header... */
570 rv = get_data_full(conn, (char *) farray, AP_FCGI_HEADER_LEN);
571 if (rv != APR_SUCCESS) {
572 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067)
573 "Failed to read FastCGI header");
577 ap_log_rdata(APLOG_MARK, APLOG_TRACE8, r, "FastCGI header",
578 farray, AP_FCGI_HEADER_LEN, 0);
580 ap_fcgi_header_fields_from_array(&version, &type, &rid,
581 &clen, &plen, farray);
583 if (version != AP_FCGI_VERSION_1) {
584 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068)
585 "Got bogus version %d", (int)version);
590 if (rid != request_id) {
591 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069)
592 "Got bogus rid %d, expected %d",
599 if (clen > iobuf_size) {
600 readbuflen = iobuf_size;
605 /* Now get the actual data. Yes it sucks to do this in a second
606 * recv call, this will eventually change when we move to real
607 * nonblocking recv calls. */
608 if (readbuflen != 0) {
609 rv = get_data(conn, iobuf, &readbuflen);
610 if (rv != APR_SUCCESS) {
611 *err = "reading response body";
619 b = apr_bucket_transient_create(iobuf,
623 APR_BRIGADE_INSERT_TAIL(ob, b);
625 if (! seen_end_of_headers) {
626 int st = handle_headers(r, &header_state,
631 seen_end_of_headers = 1;
633 status = ap_scan_script_header_err_brigade_ex(r, ob,
634 NULL, APLOG_MODULE_INDEX);
635 /* suck in all the rest */
638 apr_brigade_cleanup(ob);
639 tmp_b = apr_bucket_eos_create(c->bucket_alloc);
640 APR_BRIGADE_INSERT_TAIL(ob, tmp_b);
644 rv = ap_pass_brigade(r->output_filters, ob);
645 if (rv != APR_SUCCESS) {
646 *err = "passing headers brigade to output filters";
648 else if (status == HTTP_NOT_MODIFIED) {
649 /* The 304 response MUST NOT contain
650 * a message-body, ignore it. */
654 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070)
655 "Error parsing script headers");
661 if (conf->error_override &&
662 ap_is_HTTP_ERROR(r->status)) {
664 * set script_error_status to discard
665 * everything after the headers
667 script_error_status = r->status;
669 * prevent ap_die() from treating this as a
670 * recursive error, initially:
675 if (script_error_status == HTTP_OK
676 && !APR_BRIGADE_EMPTY(ob) && !ignore_body) {
677 /* Send the part of the body that we read while
678 * reading the headers.
681 rv = ap_pass_brigade(r->output_filters, ob);
682 if (rv != APR_SUCCESS) {
683 *err = "passing brigade to output filters";
687 apr_brigade_cleanup(ob);
689 apr_pool_clear(setaside_pool);
692 /* We're still looking for the end of the
693 * headers, so this part of the data will need
695 apr_bucket_setaside(b, setaside_pool);
698 /* we've already passed along the headers, so now pass
699 * through the content. we could simply continue to
700 * setaside the content and not pass until we see the
701 * 0 content-length (below, where we append the EOS),
702 * but that could be a huge amount of data; so we pass
703 * along smaller chunks
705 if (script_error_status == HTTP_OK && !ignore_body) {
707 rv = ap_pass_brigade(r->output_filters, ob);
708 if (rv != APR_SUCCESS) {
709 *err = "passing brigade to output filters";
713 apr_brigade_cleanup(ob);
716 /* If we didn't read all the data, go back and get the
718 if (clen > readbuflen) {
723 /* XXX what if we haven't seen end of the headers yet? */
725 if (script_error_status == HTTP_OK) {
726 b = apr_bucket_eos_create(c->bucket_alloc);
727 APR_BRIGADE_INSERT_TAIL(ob, b);
730 rv = ap_pass_brigade(r->output_filters, ob);
731 if (rv != APR_SUCCESS) {
732 *err = "passing brigade to output filters";
737 /* XXX Why don't we cleanup here? (logic from AJP) */
742 /* TODO: Should probably clean up this logging a bit... */
744 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071)
745 "Got error '%.*s'", (int)readbuflen, iobuf);
748 if (clen > readbuflen) {
754 case AP_FCGI_END_REQUEST:
759 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01072)
760 "Got bogus record %d", type);
763 /* Leave on above switch's inner error. */
764 if (rv != APR_SUCCESS) {
769 rv = get_data_full(conn, iobuf, plen);
770 if (rv != APR_SUCCESS) {
771 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02537)
772 "Error occurred reading padding");
779 apr_brigade_destroy(ib);
780 apr_brigade_destroy(ob);
782 if (script_error_status != HTTP_OK) {
783 ap_die(script_error_status, r); /* send ErrorDocument */
791 * process the request and write the response.
793 static int fcgi_do_request(apr_pool_t *p, request_rec *r,
794 proxy_conn_rec *conn,
796 proxy_dir_conf *conf,
798 char *url, char *server_portstr)
800 /* Request IDs are arbitrary numbers that we assign to a
801 * single request. This would allow multiplex/pipelining of
802 * multiple requests to the same FastCGI connection, but
803 * we don't support that, and always use a value of '1' to
804 * keep things simple. */
805 apr_uint16_t request_id = 1;
807 apr_pool_t *temp_pool;
812 /* Step 1: Send AP_FCGI_BEGIN_REQUEST */
813 rv = send_begin_request(conn, request_id);
814 if (rv != APR_SUCCESS) {
815 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01073)
816 "Failed Writing Request to %s:", server_portstr);
818 return HTTP_SERVICE_UNAVAILABLE;
821 apr_pool_create(&temp_pool, r->pool);
823 /* Step 2: Send Environment via FCGI_PARAMS */
824 rv = send_environment(conn, r, temp_pool, request_id);
825 if (rv != APR_SUCCESS) {
826 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01074)
827 "Failed writing Environment to %s:", server_portstr);
829 return HTTP_SERVICE_UNAVAILABLE;
832 /* Step 3: Read records from the back end server and handle them. */
833 rv = dispatch(conn, conf, r, temp_pool, request_id,
834 &err, &bad_request, &has_responded);
835 if (rv != APR_SUCCESS) {
836 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01075)
837 "Error dispatching request to %s: %s%s%s",
844 return AP_FILTER_ERROR;
847 return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
849 return HTTP_SERVICE_UNAVAILABLE;
855 #define FCGI_SCHEME "FCGI"
858 * This handles fcgi:(dest) URLs
860 static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
861 proxy_server_conf *conf,
862 char *url, const char *proxyname,
863 apr_port_t proxyport)
866 char server_portstr[32];
867 conn_rec *origin = NULL;
868 proxy_conn_rec *backend = NULL;
870 proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
873 apr_pool_t *p = r->pool;
875 apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri));
877 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01076)
878 "url: %s proxyname: %s proxyport: %d",
879 url, proxyname, proxyport);
881 if (strncasecmp(url, "fcgi:", 5) != 0) {
882 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url);
886 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01078) "serving URL %s", url);
888 /* Create space for state information */
889 status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker,
894 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
901 /* Step One: Determine Who To Connect To */
902 status = ap_proxy_determine_connection(p, r, conf, worker, backend,
903 uri, &url, proxyname, proxyport,
905 sizeof(server_portstr));
910 /* This scheme handler does not reuse connections by default, to
911 * avoid tying up a fastcgi that isn't expecting to work on
912 * parallel requests. But if the user went out of their way to
913 * type the default value of disablereuse=off, we'll allow it.
916 if (worker->s->disablereuse_set && !worker->s->disablereuse) {
920 /* Step Two: Make the Connection */
921 if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) {
922 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079)
923 "failed to make connection to backend: %s",
925 status = HTTP_SERVICE_UNAVAILABLE;
929 /* Step Three: Process the Request */
930 status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
934 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
938 static void register_hooks(apr_pool_t *p)
940 proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST);
941 proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST);
944 AP_DECLARE_MODULE(proxy_fcgi) = {
945 STANDARD20_MODULE_STUFF,
946 NULL, /* create per-directory config structure */
947 NULL, /* merge per-directory config structures */
948 NULL, /* create per-server config structure */
949 NULL, /* merge per-server config structures */
950 NULL, /* command apr_table_t */
951 register_hooks /* register hooks */