]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_fcgi.c
b8d9b84237ac31dbb04bd7f602445c0711987680
[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 "fcgi_protocol.h"
19 #include "util_script.h"
20
21 module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
22
23 /*
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
27  * structures.
28  *
29  * NOTE: These have specific internal knowledge of the
30  *       layout of the fcgi_header and fcgi_begin_request_body
31  *       structs!
32  */
33 static void fcgi_header_to_array(fcgi_header *h, unsigned char a[])
34 {
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;
43 }
44
45 static void fcgi_header_from_array(fcgi_header *h, unsigned char a[])
46 {
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];
55 }
56
57 static void fcgi_begin_request_body_to_array(fcgi_begin_request_body *h,
58                                              unsigned char a[])
59 {
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];
68 }
69
70 /*
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.
75  */
76 static int proxy_fcgi_canon(request_rec *r, char *url)
77 {
78     char *host, sport[7];
79     const char *err, *path;
80     apr_port_t port = 8000;
81
82     if (strncasecmp(url, "fcgi:", 5) == 0) {
83         url += 5;
84     }
85     else {
86         return DECLINED;
87     }
88
89     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
90                  "canonicalising URL %s", url);
91
92     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
93     if (err) {
94         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
95                       "error parsing URL %s: %s", url, err);
96         return HTTP_BAD_REQUEST;
97     }
98
99     apr_snprintf(sport, sizeof(sport), ":%d", port);
100
101     if (ap_strchr_c(host, ':')) {
102         /* if literal IPv6 address */
103         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
104     }
105
106     if (apr_table_get(r->notes, "proxy-nocanon")) {
107         path = url;   /* this is the raw path */
108     }
109     else {
110         path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
111                              r->proxyreq);
112     }
113     if (path == NULL)
114         return HTTP_BAD_REQUEST;
115
116     r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/",
117                               path, NULL);
118
119     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
120                   "set r->filename to %s", r->filename);
121
122     if (apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo")) {
123         r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
124
125         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
126                       "set r->path_info to %s", r->path_info);
127     }
128
129     return OK;
130 }
131
132 /*
133  * Fill in a fastcgi request header with the following type, request id,
134  * content length, and padding length.
135  *
136  * The header array must be at least FCGI_HEADER_LEN bytes long.
137  */
138 static void fill_in_header(fcgi_header *header,
139                            unsigned char type,
140                            apr_uint16_t request_id,
141                            apr_uint16_t content_len,
142                            unsigned char padding_len)
143 {
144     header->version = FCGI_VERSION;
145
146     header->type = type;
147
148     header->requestIdB1 = ((request_id >> 8) & 0xff);
149     header->requestIdB0 = ((request_id) & 0xff);
150
151     header->contentLengthB1 = ((content_len >> 8) & 0xff);
152     header->contentLengthB0 = ((content_len) & 0xff);
153
154     header->paddingLength = padding_len;
155
156     header->reserved = 0;
157 }
158
159 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
160 static apr_status_t send_data(proxy_conn_rec *conn,
161                               struct iovec *vec,
162                               int nvec,
163                               apr_size_t *len,
164                               int blocking)
165 {
166     apr_status_t rv = APR_SUCCESS, arv;
167     apr_size_t written = 0, to_write = 0;
168     int i, offset;
169     apr_interval_time_t old_timeout;
170     apr_socket_t *s = conn->sock;
171
172     if (!blocking) {
173         arv = apr_socket_timeout_get(s, &old_timeout);
174         if (arv != APR_SUCCESS) {
175             return arv;
176         }
177         arv = apr_socket_timeout_set(s, 0);
178         if (arv != APR_SUCCESS) {
179             return arv;
180         }
181     }
182
183     for (i = 0; i < nvec; i++) {
184         to_write += vec[i].iov_len;
185     }
186
187     offset = 0;
188     while (to_write) {
189         apr_size_t n = 0;
190         rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
191         if (rv != APR_SUCCESS) {
192             break;
193         }
194         if (n > 0) {
195             written += n;
196             if (written >= to_write)
197                 break;                 /* short circuit out */
198             for (i = offset; i < nvec; ) {
199                 if (n >= vec[i].iov_len) {
200                     offset++;
201                     n -= vec[i++].iov_len;
202                 } else {
203                     vec[i].iov_len -= n;
204                     vec[i].iov_base = (char *) vec[i].iov_base + n;
205                     break;
206                 }
207             }
208         }
209     }
210
211     conn->worker->s->transferred += written;
212     *len = written;
213
214     if (!blocking) {
215         arv = apr_socket_timeout_set(s, old_timeout);
216         if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
217             return arv;
218         }
219     }
220     return rv;
221 }
222
223 /* Wrapper for apr_socket_recv that handles updating the worker stats. */
224 static apr_status_t get_data(proxy_conn_rec *conn,
225                              char *buffer,
226                              apr_size_t *buflen)
227 {
228     apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
229
230     if (rv == APR_SUCCESS) {
231         conn->worker->s->read += *buflen;
232     }
233
234     return rv;
235 }
236
237 static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id)
238 {
239     struct iovec vec[2];
240     fcgi_header header;
241     unsigned char farray[FCGI_HEADER_LEN];
242     fcgi_begin_request_body brb;
243     unsigned char abrb[FCGI_HEADER_LEN];
244     apr_size_t len;
245
246     fill_in_header(&header, FCGI_BEGIN_REQUEST, request_id, sizeof(abrb), 0);
247
248     brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff);
249     brb.roleB0 = ((FCGI_RESPONDER) & 0xff);
250     brb.flags = FCGI_KEEP_CONN;
251     brb.reserved[0] = 0;
252     brb.reserved[1] = 0;
253     brb.reserved[2] = 0;
254     brb.reserved[3] = 0;
255     brb.reserved[4] = 0;
256
257     fcgi_header_to_array(&header, farray);
258     fcgi_begin_request_body_to_array(&brb, abrb);
259
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);
264
265     return send_data(conn, vec, 2, &len, 1);
266 }
267
268 static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
269                                      int request_id)
270 {
271     const apr_array_header_t *envarr;
272     const apr_table_entry_t *elts;
273     struct iovec vec[2];
274     fcgi_header header;
275     unsigned char farray[FCGI_HEADER_LEN];
276     apr_size_t bodylen, envlen;
277     char *body, *itr;
278     apr_status_t rv;
279     apr_size_t len;
280     int i, numenv;
281
282     ap_add_common_vars(r);
283     ap_add_cgi_vars(r);
284
285     /* XXX are there any FastCGI specific env vars we need to send? */
286
287     bodylen = envlen = 0;
288
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. */
294
295     envarr = apr_table_elts(r->subprocess_env);
296
297     elts = (const apr_table_entry_t *) envarr->elts;
298
299     for (i = 0; i < envarr->nelts; ++i) {
300         apr_size_t keylen, vallen;
301
302         if (! elts[i].key) {
303             continue;
304         }
305
306         keylen = strlen(elts[i].key);
307
308         if (keylen >> 7 == 0) {
309             envlen += 1;
310         }
311         else {
312             envlen += 4;
313         }
314
315         envlen += keylen;
316
317         vallen = strlen(elts[i].val);
318
319 #ifdef FCGI_DUMP_ENV_VARS
320         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
321                       "sending env var '%s' value '%s'",
322                       elts[i].key, elts[i].val);
323 #endif
324
325         if (vallen >> 7 == 0) {
326             envlen += 1;
327         }
328         else {
329             envlen += 4;
330         }
331
332         envlen += vallen;
333
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,
337                           "truncating environment to %d bytes and %d elements",
338                           (int)bodylen, i);
339             break;
340         }
341
342         bodylen = envlen;
343     }
344
345     numenv = i;
346
347     body = apr_pcalloc(r->pool, bodylen);
348
349     itr = body;
350
351     for (i = 0; i < numenv; ++i) {
352         apr_size_t keylen, vallen;
353
354         if (! elts[i].key) {
355             continue;
356         }
357
358         keylen = strlen(elts[i].key);
359
360         if (keylen >> 7 == 0) {
361             itr[0] = keylen & 0xff;
362             itr += 1;
363         }
364         else {
365             itr[0] = ((keylen >> 24) & 0xff) | 0x80;
366             itr[1] = ((keylen >> 16) & 0xff);
367             itr[2] = ((keylen >> 8) & 0xff);
368             itr[3] = ((keylen) & 0xff);
369             itr += 4;
370         }
371
372         vallen = strlen(elts[i].val);
373
374         if (vallen >> 7 == 0) {
375             itr[0] = vallen & 0xff;
376             itr += 1;
377         }
378         else {
379             itr[0] = ((vallen >> 24) & 0xff) | 0x80;
380             itr[1] = ((vallen >> 16) & 0xff);
381             itr[2] = ((vallen >> 8) & 0xff);
382             itr[3] = ((vallen) & 0xff);
383             itr += 4;
384         }
385
386         memcpy(itr, elts[i].key, keylen);
387         itr += keylen;
388
389         memcpy(itr, elts[i].val, vallen);
390         itr += vallen;
391     }
392
393     fill_in_header(&header, FCGI_PARAMS, request_id, bodylen, 0);
394     fcgi_header_to_array(&header, farray);
395
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;
400
401     rv = send_data(conn, vec, 2, &len, 1);
402     if (rv) {
403         return rv;
404     }
405
406     fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0);
407     fcgi_header_to_array(&header, farray);
408
409     vec[0].iov_base = (void *)farray;
410     vec[0].iov_len = sizeof(farray);
411
412     return send_data(conn, vec, 1, &len, 1);
413 }
414
415 enum {
416   HDR_STATE_READING_HEADERS,
417   HDR_STATE_GOT_CR,
418   HDR_STATE_GOT_CRLF,
419   HDR_STATE_GOT_CRLFCR,
420   HDR_STATE_GOT_LF,
421   HDR_STATE_DONE_WITH_HEADERS
422 };
423
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
426  * request.
427  *
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,
431                           int *state,
432                           char *readbuf)
433 {
434     const char *itr = readbuf;
435
436     while (*itr) {
437         if (*itr == '\r') {
438             switch (*state) {
439                 case HDR_STATE_GOT_CRLF:
440                     *state = HDR_STATE_GOT_CRLFCR;
441                     break;
442
443                 default:
444                     *state = HDR_STATE_GOT_CR;
445                     break;
446             }
447         }
448         else if (*itr == '\n') {
449             switch (*state) {
450                  case HDR_STATE_GOT_LF:
451                      *state = HDR_STATE_DONE_WITH_HEADERS;
452                      break;
453
454                  case HDR_STATE_GOT_CR:
455                      *state = HDR_STATE_GOT_CRLF;
456                      break;
457
458                  case HDR_STATE_GOT_CRLFCR:
459                      *state = HDR_STATE_DONE_WITH_HEADERS;
460                      break;
461
462                  default:
463                      *state = HDR_STATE_GOT_LF;
464                      break;
465             }
466         }
467         else {
468             *state = HDR_STATE_READING_HEADERS;
469         }
470
471         if (*state == HDR_STATE_DONE_WITH_HEADERS)
472             break;
473
474         ++itr;
475     }
476
477     if (*state == HDR_STATE_DONE_WITH_HEADERS) {
478         return 1;
479     }
480
481     return 0;
482 }
483
484 static void dump_header_to_log(request_rec *r, unsigned char fheader[],
485                                apr_size_t length)
486 {
487 #ifdef FCGI_DUMP_HEADERS
488     apr_size_t posn = 0;
489     char asc_line[20];
490     char hex_line[60];
491     int i = 0;
492
493     memset(asc_line, 0, sizeof(asc_line));
494     memset(hex_line, 0, sizeof(hex_line));
495
496     while (posn < length) {
497         unsigned char c = fheader[posn];
498
499         if (i >= 20) {
500             i = 0;
501
502             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
503                           "HEADER: %s %s", asc_line, hex_line);
504
505             memset(asc_line, 0, sizeof(asc_line));
506             memset(hex_line, 0, sizeof(hex_line));
507         }
508
509         if (isprint(c)) {
510             asc_line[i] = c;
511         }
512         else {
513             asc_line[i] = '.';
514         }
515
516         if ((c >> 4) >= 10) {
517             hex_line[i * 3] = 'a' + ((c >> 4) - 10);
518         }
519         else {
520             hex_line[i * 3] = '0' + (c >> 4);
521         }
522
523         if ((c & 0x0F) >= 10) {
524             hex_line[i * 3 + 1] = 'a' + ((c & 0x0F) - 10);
525         }
526         else {
527             hex_line[i * 3 + 1] = '0' + (c & 0xF);
528         }
529
530         hex_line[i * 3 + 2] = ' ';
531
532         i++;
533         posn++;
534     }
535
536     if (i != 1) {
537         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "HEADER: %s %s",
538                       asc_line, hex_line);
539     }
540
541     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "HEADER: -EOH-");
542 #endif
543 }
544
545 static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
546                              request_rec *r, int request_id)
547 {
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;
553     struct iovec vec[2];
554     fcgi_header header;
555     unsigned char farray[FCGI_HEADER_LEN];
556     apr_pollfd_t pfd;
557     int header_state = HDR_STATE_READING_HEADERS;
558     apr_pool_t *setaside_pool;
559
560     apr_pool_create(&setaside_pool, r->pool);
561
562     pfd.desc_type = APR_POLL_SOCKET;
563     pfd.desc.s = conn->sock;
564     pfd.p = r->pool;
565     pfd.reqevents = APR_POLLIN | APR_POLLOUT;
566
567     ib = apr_brigade_create(r->pool, c->bucket_alloc);
568     ob = apr_brigade_create(r->pool, c->bucket_alloc);
569
570     while (! done) {
571         apr_interval_time_t timeout = conn->worker->s->timeout;
572         apr_size_t len;
573         int n;
574
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);
579         }
580
581         rv = apr_poll(&pfd, 1, &n, timeout);
582         if (rv != APR_SUCCESS) {
583             if (APR_STATUS_IS_EINTR(rv)) {
584                 continue;
585             }
586             break;
587         }
588
589         if (pfd.rtnevents & APR_POLLOUT) {
590             char writebuf[AP_IOBUFSIZE];
591             apr_size_t writebuflen;
592             int last_stdin = 0;
593             int nvec = 0;
594
595             rv = ap_get_brigade(r->input_filters, ib,
596                                 AP_MODE_READBYTES, APR_BLOCK_READ,
597                                 sizeof(writebuf));
598             if (rv != APR_SUCCESS) {
599                 break;
600             }
601
602             if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) {
603                 last_stdin = 1;
604             }
605
606             writebuflen = sizeof(writebuf);
607
608             rv = apr_brigade_flatten(ib, writebuf, &writebuflen);
609
610             apr_brigade_cleanup(ib);
611
612             if (rv != APR_SUCCESS) {
613                 break;
614             }
615
616             fill_in_header(&header, FCGI_STDIN, request_id,
617                            (apr_uint16_t) writebuflen, 0);
618             fcgi_header_to_array(&header, farray);
619
620             vec[nvec].iov_base = (void *)farray;
621             vec[nvec].iov_len = sizeof(farray);
622             ++nvec;
623             if (writebuflen) {
624                 vec[nvec].iov_base = writebuf;
625                 vec[nvec].iov_len = writebuflen;
626                 ++nvec;
627             }
628
629             rv = send_data(conn, vec, nvec, &len, 0);
630             if (rv != APR_SUCCESS) {
631                 break;
632             }
633
634             if (last_stdin) {
635                 pfd.reqevents = APR_POLLIN; /* Done with input data */
636
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);
640
641                     vec[0].iov_base = (void *)farray;
642                     vec[0].iov_len = sizeof(farray);
643
644                     rv = send_data(conn, vec, 1, &len, 1);
645                 }
646             }
647         }
648
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;
655             apr_size_t clen;
656             int rid, type;
657             apr_bucket *b;
658             char plen;
659
660             memset(readbuf, 0, sizeof(readbuf));
661             memset(farray, 0, sizeof(farray));
662
663             /* First, we grab the header... */
664             readbuflen = FCGI_HEADER_LEN;
665
666             rv = get_data(conn, (char *) farray, &readbuflen);
667             if (rv != APR_SUCCESS) {
668                 break;
669             }
670
671             dump_header_to_log(r, farray, readbuflen);
672
673             if (readbuflen != FCGI_HEADER_LEN) {
674                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
675                               "Failed to read entire header "
676                               "got %" APR_SIZE_T_FMT " wanted %d",
677                               readbuflen, FCGI_HEADER_LEN);
678                 rv = APR_EINVAL;
679                 break;
680             }
681
682             fcgi_header_from_array(&header, farray);
683
684             if (header.version != FCGI_VERSION) {
685                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
686                               "Got bogus version %d", (int) header.version);
687                 rv = APR_EINVAL;
688                 break;
689             }
690
691             type = header.type;
692
693             rid = header.requestIdB1 << 8;
694             rid |= header.requestIdB0;
695
696             if (rid != request_id) {
697                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
698                               "Got bogus rid %d, expected %d",
699                               rid, request_id);
700                 rv = APR_EINVAL;
701                 break;
702             }
703
704             clen = header.contentLengthB1 << 8;
705             clen |= header.contentLengthB0;
706
707             plen = header.paddingLength;
708
709 recv_again:
710             if (clen > sizeof(readbuf) - 1) {
711                 readbuflen = sizeof(readbuf) - 1;
712             } else {
713                 readbuflen = clen;
714             }
715
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) {
722                     break;
723                 }
724                 readbuf[readbuflen] = 0;
725             }
726
727             switch (type) {
728             case FCGI_STDOUT:
729                 if (clen != 0) {
730                     b = apr_bucket_transient_create(readbuf,
731                                                     readbuflen,
732                                                     c->bucket_alloc);
733
734                     APR_BRIGADE_INSERT_TAIL(ob, b);
735
736                     if (! seen_end_of_headers) {
737                         int st = handle_headers(r, &header_state, readbuf);
738
739                         if (st == 1) {
740                             int status;
741                             seen_end_of_headers = 1;
742
743                             status = ap_scan_script_header_err_brigade_ex(r, ob,
744                                 NULL, APLOG_MODULE_INDEX);
745                             /* suck in all the rest */
746                             if (status != OK) {
747                                 apr_bucket *tmp_b;
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,
753                                               "Error parsing script headers");
754                                 r->status = status;
755                                 rv = APR_EINVAL;
756                                 break;
757                             }
758
759                             if (conf->error_override &&
760                                 ap_is_HTTP_ERROR(r->status)) {
761                                 /*
762                                  * set script_error_status to discard
763                                  * everything after the headers
764                                  */
765                                 script_error_status = r->status;
766                                 /*
767                                  * prevent ap_die() from treating this as a
768                                  * recursive error, initially:
769                                  */
770                                 r->status = HTTP_OK;
771                             }
772
773                             if (script_error_status == HTTP_OK) {
774                                 rv = ap_pass_brigade(r->output_filters, ob);
775                                 if (rv != APR_SUCCESS) {
776                                     break;
777                                 }
778                             }
779                             apr_brigade_cleanup(ob);
780
781                             apr_pool_clear(setaside_pool);
782                         }
783                         else {
784                             /* We're still looking for the end of the
785                              * headers, so this part of the data will need
786                              * to persist. */
787                             apr_bucket_setaside(b, setaside_pool);
788                         }
789                     } else {
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
796                          */
797                         if (script_error_status == HTTP_OK) {
798                             rv = ap_pass_brigade(r->output_filters, ob);
799                             if (rv != APR_SUCCESS) {
800                                 break;
801                             }
802                         }
803                         apr_brigade_cleanup(ob);
804                     }
805
806                     /* If we didn't read all the data go back and get the
807                      * rest of it. */
808                     if (clen > readbuflen) {
809                         clen -= readbuflen;
810                         goto recv_again;
811                     }
812                 } else {
813                     /* XXX what if we haven't seen end of the headers yet? */
814
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) {
820                             break;
821                         }
822                     }
823
824                     /* XXX Why don't we cleanup here?  (logic from AJP) */
825                 }
826                 break;
827
828             case FCGI_STDERR:
829                 /* TODO: Should probably clean up this logging a bit... */
830                 if (clen) {
831                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
832                                   "Got error '%s'", readbuf);
833                 }
834
835                 if (clen > readbuflen) {
836                     clen -= readbuflen;
837                     goto recv_again;
838                 }
839                 break;
840
841             case FCGI_END_REQUEST:
842                 done = 1;
843                 break;
844
845             default:
846                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
847                               "Got bogus record %d", type);
848                 break;
849             }
850
851             if (plen) {
852                 readbuflen = plen;
853
854                 rv = get_data(conn, readbuf, &readbuflen);
855                 if (rv != APR_SUCCESS) {
856                     break;
857                 }
858             }
859         }
860     }
861
862     apr_brigade_destroy(ib);
863     apr_brigade_destroy(ob);
864
865     if (script_error_status != HTTP_OK) {
866         ap_die(script_error_status, r); /* send ErrorDocument */
867     }
868
869     return rv;
870 }
871
872 /*
873  * process the request and write the response.
874  */
875 static int fcgi_do_request(apr_pool_t *p, request_rec *r,
876                            proxy_conn_rec *conn,
877                            conn_rec *origin,
878                            proxy_dir_conf *conf,
879                            apr_uri_t *uri,
880                            char *url, char *server_portstr)
881 {
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. */
887     int request_id = 1;
888     apr_status_t rv;
889
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,
894                       "Failed Writing Request to %s:", server_portstr);
895         conn->close = 1;
896         return HTTP_SERVICE_UNAVAILABLE;
897     }
898
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,
903                       "Failed writing Environment to %s:", server_portstr);
904         conn->close = 1;
905         return HTTP_SERVICE_UNAVAILABLE;
906     }
907
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,
912                       "Error dispatching request to %s:", server_portstr);
913         conn->close = 1;
914         return HTTP_SERVICE_UNAVAILABLE;
915     }
916
917     return OK;
918 }
919
920 #define FCGI_SCHEME "FCGI"
921
922 /*
923  * This handles fcgi:(dest) URLs
924  */
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)
929 {
930     int status;
931     char server_portstr[32];
932     conn_rec *origin = NULL;
933     proxy_conn_rec *backend = NULL;
934
935     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
936                                                  &proxy_module);
937
938     apr_pool_t *p = r->pool;
939
940     apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri));
941
942     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
943                   "url: %s proxyname: %s proxyport: %d",
944                  url, proxyname, proxyport);
945
946     if (strncasecmp(url, "fcgi:", 5) == 0) {
947         url += 5;
948     }
949     else {
950         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "declining URL %s", url);
951         return DECLINED;
952     }
953
954     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "serving URL %s", url);
955
956     /* Create space for state information */
957     if (! backend) {
958         status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker,
959                                              r->server);
960         if (status != OK) {
961             if (backend) {
962                 backend->close = 1;
963                 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
964             }
965             return status;
966         }
967     }
968
969     backend->is_ssl = 0;
970
971     /* Step One: Determine Who To Connect To */
972     status = ap_proxy_determine_connection(p, r, conf, worker, backend,
973                                            uri, &url, proxyname, proxyport,
974                                            server_portstr,
975                                            sizeof(server_portstr));
976     if (status != OK) {
977         goto cleanup;
978     }
979
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. */
984     backend->close = 1;
985
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,
989                       "failed to make connection to backend: %s",
990                       backend->hostname);
991         status = HTTP_SERVICE_UNAVAILABLE;
992         goto cleanup;
993     }
994
995     /* Step Three: Process the Request */
996     status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
997                              server_portstr);
998
999 cleanup:
1000     ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
1001     return status;
1002 }
1003
1004 static void register_hooks(apr_pool_t *p)
1005 {
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);
1008 }
1009
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 */
1018 };