]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_fcgi.c
Merge r1700317, r1700318, r1700319, r1700320, r1700321, r1700322, r1700326, r1700328...
[apache] / modules / proxy / mod_proxy_fcgi.c
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
21 module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
22
23 typedef struct {
24     int need_dirwalk;
25 } fcgi_req_config_t;
26
27 /*
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.
32  */
33 static int proxy_fcgi_canon(request_rec *r, char *url)
34 {
35     char *host, sport[7];
36     const char *err;
37     char *path;
38     apr_port_t port, def_port;
39     fcgi_req_config_t *rconf = NULL;
40     const char *pathinfo_type = NULL;
41
42     if (strncasecmp(url, "fcgi:", 5) == 0) {
43         url += 5;
44     }
45     else {
46         return DECLINED;
47     }
48
49     port = def_port = ap_proxy_port_of_scheme("fcgi");
50
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);
54     if (err) {
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;
58     }
59
60     if (port != def_port)
61         apr_snprintf(sport, sizeof(sport), ":%d", port);
62     else
63         sport[0] = '\0';
64
65     if (ap_strchr_c(host, ':')) {
66         /* if literal IPv6 address */
67         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
68     }
69
70     if (apr_table_get(r->notes, "proxy-nocanon")) {
71         path = url;   /* this is the raw path */
72     }
73     else {
74         path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
75                              r->proxyreq);
76     }
77     if (path == NULL)
78         return HTTP_BAD_REQUEST;
79
80     r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/",
81                               path, NULL);
82
83     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01060)
84                   "set r->filename to %s", r->filename);
85
86     rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
87     if (rconf == NULL) { 
88         rconf = apr_pcalloc(r->pool, sizeof(fcgi_req_config_t));
89         ap_set_module_config(r->request_config, &proxy_fcgi_module, rconf);
90     }
91
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);
97         }
98         else if (!strcasecmp(pathinfo_type, "first-dot")) { 
99             char *split = ap_strchr(path, '.');
100             if (split) { 
101                 char *slash = ap_strchr(split, '/');
102                 if (slash) { 
103                     r->path_info = apr_pstrdup(r->pool, slash);
104                     ap_unescape_url_keep2f(r->path_info, 0);
105                     *slash = '\0'; /* truncate path */
106                 }
107             }
108         }
109         else if (!strcasecmp(pathinfo_type, "last-dot")) { 
110             char *split = ap_strrchr(path, '.');
111             if (split) { 
112                 char *slash = ap_strchr(split, '/');
113                 if (slash) { 
114                     r->path_info = apr_pstrdup(r->pool, slash);
115                     ap_unescape_url_keep2f(r->path_info, 0);
116                     *slash = '\0'; /* truncate path */
117                 }
118             }
119         }
120         else { 
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
123              */
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);
127             }
128             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061)
129                     "set r->path_info to %s", r->path_info);
130         }
131     }
132
133     return OK;
134 }
135
136 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
137 static apr_status_t send_data(proxy_conn_rec *conn,
138                               struct iovec *vec,
139                               int nvec,
140                               apr_size_t *len)
141 {
142     apr_status_t rv = APR_SUCCESS;
143     apr_size_t written = 0, to_write = 0;
144     int i, offset;
145     apr_socket_t *s = conn->sock;
146
147     for (i = 0; i < nvec; i++) {
148         to_write += vec[i].iov_len;
149     }
150
151     offset = 0;
152     while (to_write) {
153         apr_size_t n = 0;
154         rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
155         if (rv != APR_SUCCESS) {
156             break;
157         }
158         if (n > 0) {
159             written += n;
160             if (written >= to_write)
161                 break;                 /* short circuit out */
162             for (i = offset; i < nvec; ) {
163                 if (n >= vec[i].iov_len) {
164                     offset++;
165                     n -= vec[i++].iov_len;
166                 } else {
167                     vec[i].iov_len -= n;
168                     vec[i].iov_base = (char *) vec[i].iov_base + n;
169                     break;
170                 }
171             }
172         }
173     }
174
175     conn->worker->s->transferred += written;
176     *len = written;
177
178     return rv;
179 }
180
181 /* Wrapper for apr_socket_recv that handles updating the worker stats. */
182 static apr_status_t get_data(proxy_conn_rec *conn,
183                              char *buffer,
184                              apr_size_t *buflen)
185 {
186     apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
187
188     if (rv == APR_SUCCESS) {
189         conn->worker->s->read += *buflen;
190     }
191
192     return rv;
193 }
194
195 static apr_status_t get_data_full(proxy_conn_rec *conn,
196                                   char *buffer,
197                                   apr_size_t buflen)
198 {
199     apr_size_t readlen;
200     apr_size_t cumulative_len = 0;
201     apr_status_t rv;
202
203     do {
204         readlen = buflen - cumulative_len;
205         rv = get_data(conn, buffer + cumulative_len, &readlen);
206         if (rv != APR_SUCCESS) {
207             return rv;
208         }
209         cumulative_len += readlen;
210     } while (cumulative_len < buflen);
211
212     return APR_SUCCESS;
213 }
214
215 static apr_status_t send_begin_request(proxy_conn_rec *conn,
216                                        apr_uint16_t request_id)
217 {
218     struct iovec vec[2];
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];
223     apr_size_t len;
224
225     ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id,
226                            sizeof(abrb), 0);
227
228     ap_fcgi_fill_in_request_body(&brb, AP_FCGI_RESPONDER,
229                                  ap_proxy_connection_reusable(conn)
230                                      ? AP_FCGI_KEEP_CONN : 0);
231
232     ap_fcgi_header_to_array(&header, farray);
233     ap_fcgi_begin_request_body_to_array(&brb, abrb);
234
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);
239
240     return send_data(conn, vec, 2, &len);
241 }
242
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)
246 {
247     const apr_array_header_t *envarr;
248     const apr_table_entry_t *elts;
249     struct iovec vec[2];
250     ap_fcgi_header header;
251     unsigned char farray[AP_FCGI_HEADER_LEN];
252     char *body;
253     apr_status_t rv;
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);
258
259     if (rconf) { 
260        if (rconf->need_dirwalk) { 
261           ap_directory_walk(r);
262        }
263     }
264
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;
270     }
271
272     ap_add_common_vars(r);
273     ap_add_cgi_vars(r);
274  
275     r->filename = proxyfilename;
276
277     /* XXX are there any FastCGI specific env vars we need to send? */
278
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. */
284
285     envarr = apr_table_elts(r->subprocess_env);
286     elts = (const apr_table_entry_t *) envarr->elts;
287
288     if (APLOGrtrace8(r)) {
289         int i;
290         
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);
295         }
296     }
297
298     /* Send envvars over in as many FastCGI records as it takes, */
299     next_elem = 0; /* starting with the first one */
300
301     avail_len = 16 * 1024; /* our limit per record, which could have been up
302                             * to AP_FCGI_MAX_CONTENT_LEN
303                             */
304
305     while (next_elem < envarr->nelts) {
306         starting_elem = next_elem;
307         required_len = ap_fcgi_encoded_env_len(r->subprocess_env,
308                                                avail_len,
309                                                &next_elem);
310
311         if (!required_len) {
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 */
318                 ++next_elem;
319                 continue;
320             }
321             /* only an unused element at the end of the array */
322             break;
323         }
324
325         body = apr_palloc(temp_pool, required_len);
326         rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len,
327                                 &starting_elem);
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);
332
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);
336
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;
341
342         rv = send_data(conn, vec, 2, &len);
343         apr_pool_clear(temp_pool);
344
345         if (rv) {
346             return rv;
347         }
348     }
349
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);
353
354     vec[0].iov_base = (void *)farray;
355     vec[0].iov_len = sizeof(farray);
356
357     return send_data(conn, vec, 1, &len);
358 }
359
360 enum {
361   HDR_STATE_READING_HEADERS,
362   HDR_STATE_GOT_CR,
363   HDR_STATE_GOT_CRLF,
364   HDR_STATE_GOT_CRLFCR,
365   HDR_STATE_GOT_LF,
366   HDR_STATE_DONE_WITH_HEADERS
367 };
368
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
371  * request.
372  *
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)
377 {
378     const char *itr = readbuf;
379
380     while (readlen--) {
381         if (*itr == '\r') {
382             switch (*state) {
383                 case HDR_STATE_GOT_CRLF:
384                     *state = HDR_STATE_GOT_CRLFCR;
385                     break;
386
387                 default:
388                     *state = HDR_STATE_GOT_CR;
389                     break;
390             }
391         }
392         else if (*itr == '\n') {
393             switch (*state) {
394                  case HDR_STATE_GOT_LF:
395                      *state = HDR_STATE_DONE_WITH_HEADERS;
396                      break;
397
398                  case HDR_STATE_GOT_CR:
399                      *state = HDR_STATE_GOT_CRLF;
400                      break;
401
402                  case HDR_STATE_GOT_CRLFCR:
403                      *state = HDR_STATE_DONE_WITH_HEADERS;
404                      break;
405
406                  default:
407                      *state = HDR_STATE_GOT_LF;
408                      break;
409             }
410         }
411         else {
412             *state = HDR_STATE_READING_HEADERS;
413         }
414
415         if (*state == HDR_STATE_DONE_WITH_HEADERS)
416             break;
417
418         ++itr;
419     }
420
421     if (*state == HDR_STATE_DONE_WITH_HEADERS) {
422         return 1;
423     }
424
425     return 0;
426 }
427
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)
432 {
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;
438     struct iovec vec[2];
439     ap_fcgi_header header;
440     unsigned char farray[AP_FCGI_HEADER_LEN];
441     apr_pollfd_t pfd;
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;
446
447     *err = NULL;
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);
451     }
452
453     pfd.desc_type = APR_POLL_SOCKET;
454     pfd.desc.s = conn->sock;
455     pfd.p = r->pool;
456     pfd.reqevents = APR_POLLIN | APR_POLLOUT;
457
458     ib = apr_brigade_create(r->pool, c->bucket_alloc);
459     ob = apr_brigade_create(r->pool, c->bucket_alloc);
460
461     while (! done) {
462         apr_interval_time_t timeout;
463         apr_size_t len;
464         int n;
465
466         /* We need SOME kind of timeout here, or virtually anything will
467          * cause timeout errors. */
468         apr_socket_timeout_get(conn->sock, &timeout);
469
470         rv = apr_poll(&pfd, 1, &n, timeout);
471         if (rv != APR_SUCCESS) {
472             if (APR_STATUS_IS_EINTR(rv)) {
473                 continue;
474             }
475             *err = "polling";
476             break;
477         }
478
479         if (pfd.rtnevents & APR_POLLOUT) {
480             apr_size_t to_send, writebuflen;
481             int last_stdin = 0;
482             char *iobuf_cursor;
483
484             rv = ap_get_brigade(r->input_filters, ib,
485                                 AP_MODE_READBYTES, APR_BLOCK_READ,
486                                 iobuf_size);
487             if (rv != APR_SUCCESS) {
488                 *err = "reading input brigade";
489                 *bad_request = 1;
490                 break;
491             }
492
493             if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) {
494                 last_stdin = 1;
495             }
496
497             writebuflen = iobuf_size;
498
499             rv = apr_brigade_flatten(ib, iobuf, &writebuflen);
500
501             apr_brigade_cleanup(ib);
502
503             if (rv != APR_SUCCESS) {
504                 *err = "flattening brigade";
505                 break;
506             }
507
508             to_send = writebuflen;
509             iobuf_cursor = iobuf;
510             while (to_send > 0) {
511                 int nvec = 0;
512                 apr_size_t write_this_time;
513
514                 write_this_time =
515                     to_send < AP_FCGI_MAX_CONTENT_LEN ? to_send : AP_FCGI_MAX_CONTENT_LEN;
516
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);
520
521                 vec[nvec].iov_base = (void *)farray;
522                 vec[nvec].iov_len = sizeof(farray);
523                 ++nvec;
524                 if (writebuflen) {
525                     vec[nvec].iov_base = iobuf_cursor;
526                     vec[nvec].iov_len = write_this_time;
527                     ++nvec;
528                 }
529
530                 rv = send_data(conn, vec, nvec, &len);
531                 if (rv != APR_SUCCESS) {
532                     *err = "sending stdin";
533                     break;
534                 }
535
536                 to_send -= write_this_time;
537                 iobuf_cursor += write_this_time;
538             }
539             if (rv != APR_SUCCESS) {
540                 break;
541             }
542
543             if (last_stdin) {
544                 pfd.reqevents = APR_POLLIN; /* Done with input data */
545
546                 /* signal EOF (empty FCGI_STDIN) */
547                 ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id,
548                                        0, 0);
549                 ap_fcgi_header_to_array(&header, farray);
550
551                 vec[0].iov_base = (void *)farray;
552                 vec[0].iov_len = sizeof(farray);
553
554                 rv = send_data(conn, vec, 1, &len);
555                 if (rv != APR_SUCCESS) {
556                     *err = "sending empty stdin";
557                     break;
558                 }
559             }
560         }
561
562         if (pfd.rtnevents & APR_POLLIN) {
563             apr_size_t readbuflen;
564             apr_uint16_t clen, rid;
565             apr_bucket *b;
566             unsigned char plen;
567             unsigned char type, version;
568
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");
574                 break;
575             }
576
577             ap_log_rdata(APLOG_MARK, APLOG_TRACE8, r, "FastCGI header",
578                          farray, AP_FCGI_HEADER_LEN, 0);
579
580             ap_fcgi_header_fields_from_array(&version, &type, &rid,
581                                              &clen, &plen, farray);
582
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);
586                 rv = APR_EINVAL;
587                 break;
588             }
589
590             if (rid != request_id) {
591                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069)
592                               "Got bogus rid %d, expected %d",
593                               rid, request_id);
594                 rv = APR_EINVAL;
595                 break;
596             }
597
598 recv_again:
599             if (clen > iobuf_size) {
600                 readbuflen = iobuf_size;
601             } else {
602                 readbuflen = clen;
603             }
604
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";
612                     break;
613                 }
614             }
615
616             switch (type) {
617             case AP_FCGI_STDOUT:
618                 if (clen != 0) {
619                     b = apr_bucket_transient_create(iobuf,
620                                                     readbuflen,
621                                                     c->bucket_alloc);
622
623                     APR_BRIGADE_INSERT_TAIL(ob, b);
624
625                     if (! seen_end_of_headers) {
626                         int st = handle_headers(r, &header_state,
627                                                 iobuf, readbuflen);
628
629                         if (st == 1) {
630                             int status;
631                             seen_end_of_headers = 1;
632
633                             status = ap_scan_script_header_err_brigade_ex(r, ob,
634                                 NULL, APLOG_MODULE_INDEX);
635                             /* suck in all the rest */
636                             if (status != OK) {
637                                 apr_bucket *tmp_b;
638                                 apr_brigade_cleanup(ob);
639                                 tmp_b = apr_bucket_eos_create(c->bucket_alloc);
640                                 APR_BRIGADE_INSERT_TAIL(ob, tmp_b);
641
642                                 *has_responded = 1;
643                                 r->status = status;
644                                 rv = ap_pass_brigade(r->output_filters, ob);
645                                 if (rv != APR_SUCCESS) {
646                                     *err = "passing headers brigade to output filters";
647                                 }
648                                 else if (status == HTTP_NOT_MODIFIED) {
649                                     /* The 304 response MUST NOT contain
650                                      * a message-body, ignore it. */
651                                     ignore_body = 1;
652                                 }
653                                 else {
654                                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070)
655                                                     "Error parsing script headers");
656                                     rv = APR_EINVAL;
657                                 }
658                                 break;
659                             }
660
661                             if (conf->error_override &&
662                                 ap_is_HTTP_ERROR(r->status)) {
663                                 /*
664                                  * set script_error_status to discard
665                                  * everything after the headers
666                                  */
667                                 script_error_status = r->status;
668                                 /*
669                                  * prevent ap_die() from treating this as a
670                                  * recursive error, initially:
671                                  */
672                                 r->status = HTTP_OK;
673                             }
674
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.
679                                  */
680                                 *has_responded = 1;
681                                 rv = ap_pass_brigade(r->output_filters, ob);
682                                 if (rv != APR_SUCCESS) {
683                                     *err = "passing brigade to output filters";
684                                     break;
685                                 }
686                             }
687                             apr_brigade_cleanup(ob);
688
689                             apr_pool_clear(setaside_pool);
690                         }
691                         else {
692                             /* We're still looking for the end of the
693                              * headers, so this part of the data will need
694                              * to persist. */
695                             apr_bucket_setaside(b, setaside_pool);
696                         }
697                     } else {
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
704                          */
705                         if (script_error_status == HTTP_OK && !ignore_body) {
706                             *has_responded = 1;
707                             rv = ap_pass_brigade(r->output_filters, ob);
708                             if (rv != APR_SUCCESS) {
709                                 *err = "passing brigade to output filters";
710                                 break;
711                             }
712                         }
713                         apr_brigade_cleanup(ob);
714                     }
715
716                     /* If we didn't read all the data, go back and get the
717                      * rest of it. */
718                     if (clen > readbuflen) {
719                         clen -= readbuflen;
720                         goto recv_again;
721                     }
722                 } else {
723                     /* XXX what if we haven't seen end of the headers yet? */
724
725                     if (script_error_status == HTTP_OK) {
726                         b = apr_bucket_eos_create(c->bucket_alloc);
727                         APR_BRIGADE_INSERT_TAIL(ob, b);
728
729                         *has_responded = 1;
730                         rv = ap_pass_brigade(r->output_filters, ob);
731                         if (rv != APR_SUCCESS) {
732                             *err = "passing brigade to output filters";
733                             break;
734                         }
735                     }
736
737                     /* XXX Why don't we cleanup here?  (logic from AJP) */
738                 }
739                 break;
740
741             case AP_FCGI_STDERR:
742                 /* TODO: Should probably clean up this logging a bit... */
743                 if (clen) {
744                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071)
745                                   "Got error '%.*s'", (int)readbuflen, iobuf);
746                 }
747
748                 if (clen > readbuflen) {
749                     clen -= readbuflen;
750                     goto recv_again;
751                 }
752                 break;
753
754             case AP_FCGI_END_REQUEST:
755                 done = 1;
756                 break;
757
758             default:
759                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01072)
760                               "Got bogus record %d", type);
761                 break;
762             }
763             /* Leave on above switch's inner error. */
764             if (rv != APR_SUCCESS) {
765                 break;
766             }
767
768             if (plen) {
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");
773                     break;
774                 }
775             }
776         }
777     }
778
779     apr_brigade_destroy(ib);
780     apr_brigade_destroy(ob);
781
782     if (script_error_status != HTTP_OK) {
783         ap_die(script_error_status, r); /* send ErrorDocument */
784         *has_responded = 1;
785     }
786
787     return rv;
788 }
789
790 /*
791  * process the request and write the response.
792  */
793 static int fcgi_do_request(apr_pool_t *p, request_rec *r,
794                            proxy_conn_rec *conn,
795                            conn_rec *origin,
796                            proxy_dir_conf *conf,
797                            apr_uri_t *uri,
798                            char *url, char *server_portstr)
799 {
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;
806     apr_status_t rv;
807     apr_pool_t *temp_pool;
808     const char *err;
809     int bad_request = 0,
810         has_responded = 0;
811
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);
817         conn->close = 1;
818         return HTTP_SERVICE_UNAVAILABLE;
819     }
820
821     apr_pool_create(&temp_pool, r->pool);
822
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);
828         conn->close = 1;
829         return HTTP_SERVICE_UNAVAILABLE;
830     }
831
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",
838                       server_portstr,
839                       err ? "(" : "",
840                       err ? err : "",
841                       err ? ")" : "");
842         conn->close = 1;
843         if (has_responded) {
844             return AP_FILTER_ERROR;
845         }
846         if (bad_request) {
847             return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
848         }
849         return HTTP_SERVICE_UNAVAILABLE;
850     }
851
852     return OK;
853 }
854
855 #define FCGI_SCHEME "FCGI"
856
857 /*
858  * This handles fcgi:(dest) URLs
859  */
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)
864 {
865     int status;
866     char server_portstr[32];
867     conn_rec *origin = NULL;
868     proxy_conn_rec *backend = NULL;
869
870     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
871                                                  &proxy_module);
872
873     apr_pool_t *p = r->pool;
874
875     apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri));
876
877     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01076)
878                   "url: %s proxyname: %s proxyport: %d",
879                  url, proxyname, proxyport);
880
881     if (strncasecmp(url, "fcgi:", 5) != 0) {
882         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url);
883         return DECLINED;
884     }
885
886     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01078) "serving URL %s", url);
887
888     /* Create space for state information */
889     status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker,
890                                          r->server);
891     if (status != OK) {
892         if (backend) {
893             backend->close = 1;
894             ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
895         }
896         return status;
897     }
898
899     backend->is_ssl = 0;
900
901     /* Step One: Determine Who To Connect To */
902     status = ap_proxy_determine_connection(p, r, conf, worker, backend,
903                                            uri, &url, proxyname, proxyport,
904                                            server_portstr,
905                                            sizeof(server_portstr));
906     if (status != OK) {
907         goto cleanup;
908     }
909
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.
914      */  
915     backend->close = 1;
916     if (worker->s->disablereuse_set && !worker->s->disablereuse) { 
917         backend->close = 0;
918     }
919
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",
924                       backend->hostname);
925         status = HTTP_SERVICE_UNAVAILABLE;
926         goto cleanup;
927     }
928
929     /* Step Three: Process the Request */
930     status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
931                              server_portstr);
932
933 cleanup:
934     ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
935     return status;
936 }
937
938 static void register_hooks(apr_pool_t *p)
939 {
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);
942 }
943
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 */
952 };