]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_fcgi.c
repeat apr_poll() while EINTR
[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_error(APLOG_MARK, APLOG_TRACE1, 0, r->server,
90                  "proxy: FCGI: 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                   "proxy: FCGI: set r->filename to %s", r->filename);
121
122     r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
123
124     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
125                   "proxy: FCGI: set r->path_info to %s", r->path_info);
126
127     return OK;
128 }
129
130 /*
131  * Fill in a fastcgi request header with the following type, request id,
132  * content length, and padding length.
133  *
134  * The header array must be at least FCGI_HEADER_LEN bytes long.
135  */
136 static void fill_in_header(fcgi_header *header,
137                            unsigned char type,
138                            apr_uint16_t request_id,
139                            apr_uint16_t content_len,
140                            unsigned char padding_len)
141 {
142     header->version = FCGI_VERSION;
143
144     header->type = type;
145
146     header->requestIdB1 = ((request_id >> 8) & 0xff);
147     header->requestIdB0 = ((request_id) & 0xff);
148
149     header->contentLengthB1 = ((content_len >> 8) & 0xff);
150     header->contentLengthB0 = ((content_len) & 0xff);
151
152     header->paddingLength = padding_len;
153
154     header->reserved = 0;
155 }
156
157 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
158 static apr_status_t send_data(proxy_conn_rec *conn,
159                               struct iovec *vec,
160                               int nvec,
161                               apr_size_t *len,
162                               int blocking)
163 {
164     apr_status_t rv = APR_SUCCESS, arv;
165     apr_size_t written = 0, to_write = 0;
166     int i, offset;
167     apr_interval_time_t old_timeout;
168     apr_socket_t *s = conn->sock;
169
170     if (!blocking) {
171         arv = apr_socket_timeout_get(s, &old_timeout);
172         if (arv != APR_SUCCESS) {
173             return arv;
174         }
175         arv = apr_socket_timeout_set(s, 0);
176         if (arv != APR_SUCCESS) {
177             return arv;
178         }
179     }
180
181     for (i = 0; i < nvec; i++) {
182         to_write += vec[i].iov_len;
183     }
184
185     offset = 0;
186     while (to_write) {
187         apr_size_t n = 0;
188         rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
189         if (rv != APR_SUCCESS) {
190             break;
191         }
192         if (n > 0) {
193             written += n;
194             if (written >= to_write)
195                 break;                 /* short circuit out */
196             for (i = offset; i < nvec; ) {
197                 if (n >= vec[i].iov_len) {
198                     offset++;
199                     n -= vec[i++].iov_len;
200                 } else {
201                     vec[i].iov_len -= n;
202                     vec[i].iov_base = (char *) vec[i].iov_base + n;
203                     break;
204                 }
205             }
206         }
207     }
208
209     conn->worker->s->transferred += written;
210     *len = written;
211
212     if (!blocking) {
213         arv = apr_socket_timeout_set(s, old_timeout);
214         if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
215             return arv;
216         }
217     }
218     return rv;
219 }
220
221 /* Wrapper for apr_socket_recv that handles updating the worker stats. */
222 static apr_status_t get_data(proxy_conn_rec *conn,
223                              char *buffer,
224                              apr_size_t *buflen)
225 {
226     apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
227
228     if (rv == APR_SUCCESS) {
229         conn->worker->s->read += *buflen;
230     }
231
232     return rv;
233 }
234
235 static apr_status_t send_begin_request(proxy_conn_rec *conn, int request_id)
236 {
237     struct iovec vec[2];
238     fcgi_header header;
239     unsigned char farray[FCGI_HEADER_LEN];
240     fcgi_begin_request_body brb;
241     unsigned char abrb[FCGI_HEADER_LEN];
242     apr_size_t len;
243
244     fill_in_header(&header, FCGI_BEGIN_REQUEST, request_id, sizeof(abrb), 0);
245
246     brb.roleB1 = ((FCGI_RESPONDER >> 8) & 0xff);
247     brb.roleB0 = ((FCGI_RESPONDER) & 0xff); 
248     brb.flags = FCGI_KEEP_CONN;
249     brb.reserved[0] = 0;
250     brb.reserved[1] = 0;
251     brb.reserved[2] = 0;
252     brb.reserved[3] = 0;
253     brb.reserved[4] = 0;
254
255     fcgi_header_to_array(&header, farray);
256     fcgi_begin_request_body_to_array(&brb, abrb);
257
258     vec[0].iov_base = farray;
259     vec[0].iov_len = sizeof(farray);
260     vec[1].iov_base = abrb;
261     vec[1].iov_len = sizeof(abrb);
262
263     return send_data(conn, vec, 2, &len, 1);
264 }
265
266 static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r, 
267                                      int request_id)
268 {
269     const apr_array_header_t *envarr;
270     const apr_table_entry_t *elts;
271     struct iovec vec[2];
272     fcgi_header header;
273     unsigned char farray[FCGI_HEADER_LEN];
274     apr_size_t bodylen, envlen;
275     char *body, *itr;
276     apr_status_t rv;
277     apr_size_t len;
278     int i, numenv;
279
280     ap_add_common_vars(r);
281     ap_add_cgi_vars(r);
282
283     /* XXX are there any FastCGI specific env vars we need to send? */
284
285     bodylen = envlen = 0;
286
287     /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
288      *     the TZ value specially.  We could use that, but it would mean
289      *     parsing the key/value pairs back OUT of the allocated env array,
290      *     not to mention allocating a totally useless array in the first
291      *     place, which would suck. */
292
293     envarr = apr_table_elts(r->subprocess_env);
294
295     elts = (const apr_table_entry_t *) envarr->elts;
296
297     for (i = 0; i < envarr->nelts; ++i) {
298         apr_size_t keylen, vallen;
299
300         if (! elts[i].key) {
301             continue;
302         }
303
304         keylen = strlen(elts[i].key);
305
306         if (keylen >> 7 == 0) {
307             envlen += 1;
308         }
309         else {
310             envlen += 4;
311         }
312
313         envlen += keylen;
314
315         vallen = strlen(elts[i].val);
316
317 #ifdef FCGI_DUMP_ENV_VARS
318         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
319                       "proxy: FCGI: sending env var '%s' value '%s'",
320                       elts[i].key, elts[i].val);
321 #endif
322
323         if (vallen >> 7 == 0) {
324             envlen += 1;
325         }
326         else {
327             envlen += 4;
328         }
329
330         envlen += vallen;
331
332         /* The cast of bodylen is safe since FCGI_MAX_ENV_SIZE is for sure an int */
333         if (envlen > FCGI_MAX_ENV_SIZE) {
334             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
335                           "proxy: FCGI: truncating environment to %d bytes and %d elements",
336                           (int)bodylen, i);
337             break;
338         }
339
340         bodylen = envlen;
341     }
342
343     numenv = i;
344
345     body = apr_pcalloc(r->pool, bodylen);
346
347     itr = body;
348
349     for (i = 0; i < numenv; ++i) {
350         apr_size_t keylen, vallen;
351        
352         if (! elts[i].key) {
353             continue;
354         }
355
356         keylen = strlen(elts[i].key);
357
358         if (keylen >> 7 == 0) {
359             itr[0] = keylen & 0xff;
360             itr += 1;
361         }
362         else {
363             itr[0] = ((keylen >> 24) & 0xff) | 0x80;
364             itr[1] = ((keylen >> 16) & 0xff);
365             itr[2] = ((keylen >> 8) & 0xff);
366             itr[3] = ((keylen) & 0xff);
367             itr += 4;
368         }
369
370         vallen = strlen(elts[i].val);
371
372         if (vallen >> 7 == 0) {
373             itr[0] = vallen & 0xff;
374             itr += 1;
375         }
376         else {
377             itr[0] = ((vallen >> 24) & 0xff) | 0x80;
378             itr[1] = ((vallen >> 16) & 0xff);
379             itr[2] = ((vallen >> 8) & 0xff);
380             itr[3] = ((vallen) & 0xff);
381             itr += 4;
382         }
383
384         memcpy(itr, elts[i].key, keylen);
385         itr += keylen;
386
387         memcpy(itr, elts[i].val, vallen);
388         itr += vallen;
389     }
390
391     fill_in_header(&header, FCGI_PARAMS, request_id, bodylen, 0);
392     fcgi_header_to_array(&header, farray);
393
394     vec[0].iov_base = farray;
395     vec[0].iov_len = sizeof(farray);
396     vec[1].iov_base = body;
397     vec[1].iov_len = bodylen;
398
399     rv = send_data(conn, vec, 2, &len, 1);
400     if (rv) {
401         return rv;
402     }
403
404     fill_in_header(&header, FCGI_PARAMS, request_id, 0, 0);
405     fcgi_header_to_array(&header, farray);
406
407     vec[0].iov_base = farray;
408     vec[0].iov_len = sizeof(farray);
409
410     return send_data(conn, vec, 1, &len, 1);
411 }
412
413 enum {
414   HDR_STATE_READING_HEADERS,
415   HDR_STATE_GOT_CR,
416   HDR_STATE_GOT_CRLF,
417   HDR_STATE_GOT_CRLFCR,
418   HDR_STATE_GOT_LF,
419   HDR_STATE_DONE_WITH_HEADERS
420 };
421
422 /* Try to parse the script headers in the response from the back end fastcgi
423  * server.  Assumes that the contents of READBUF have already been added to
424  * the end of OB.  STATE holds the current header parsing state for this
425  * request.
426  *
427  * Returns -1 on error, 0 if it can't find the end of the headers, and 1 if
428  * it found the end of the headers and scans them successfully. */
429 static int handle_headers(request_rec *r,
430                           int *state,
431                           char *readbuf,
432                           apr_bucket_brigade *ob)
433 {
434     conn_rec *c = r->connection;
435     const char *itr = readbuf;
436
437     while (*itr) {
438         if (*itr == '\r') {
439             switch (*state) {
440                 case HDR_STATE_GOT_CRLF:
441                     *state = HDR_STATE_GOT_CRLFCR;
442                     break;
443
444                 default:
445                     *state = HDR_STATE_GOT_CR;
446                     break;
447             }
448         }
449         else if (*itr == '\n') {
450             switch (*state) {
451                  case HDR_STATE_GOT_LF:
452                      *state = HDR_STATE_DONE_WITH_HEADERS;
453                      break;
454
455                  case HDR_STATE_GOT_CR:
456                      *state = HDR_STATE_GOT_CRLF;
457                      break;
458
459                  case HDR_STATE_GOT_CRLFCR:
460                      *state = HDR_STATE_DONE_WITH_HEADERS;
461                      break;
462
463                  default:
464                      *state = HDR_STATE_GOT_LF;
465                      break;
466             }
467         }
468         else {
469             *state = HDR_STATE_READING_HEADERS;
470         }
471
472         if (*state == HDR_STATE_DONE_WITH_HEADERS)
473             break;
474
475         ++itr;
476     }
477
478     if (*state == HDR_STATE_DONE_WITH_HEADERS) {
479         int status = ap_scan_script_header_err_brigade(r, ob, NULL);
480         if (status != OK) {
481             apr_bucket *b;
482
483             r->status = status;
484
485             apr_brigade_cleanup(ob);
486
487             b = apr_bucket_eos_create(c->bucket_alloc);
488
489             APR_BRIGADE_INSERT_TAIL(ob, b);
490
491             ap_pass_brigade(r->output_filters, ob);
492
493             ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
494                          "proxy: FCGI: Error parsing script headers");
495
496             return -1;
497         }
498         else {
499             return 1;
500         }
501     }
502
503     return 0;
504 }
505
506 static void dump_header_to_log(request_rec *r, unsigned char fheader[],
507                                apr_size_t length)
508 {
509 #ifdef FCGI_DUMP_HEADERS
510     apr_size_t posn = 0;
511     char asc_line[20];
512     char hex_line[60];
513     int i = 0;
514
515     memset(asc_line, 0, sizeof(asc_line));
516     memset(hex_line, 0, sizeof(hex_line));
517
518     while (posn < length) {
519         unsigned char c = fheader[posn]; 
520
521         if (i >= 20) {
522             i = 0;
523
524             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
525                          "HEADER: %s %s", asc_line, hex_line);
526
527             memset(asc_line, 0, sizeof(asc_line));
528             memset(hex_line, 0, sizeof(hex_line));
529         }
530
531         if (isprint(c)) {
532             asc_line[i] = c;
533         }
534         else {
535             asc_line[i] = '.';
536         }
537
538         if ((c >> 4) >= 10) {
539             hex_line[i * 3] = 'a' + ((c >> 4) - 10);
540         }
541         else {
542             hex_line[i * 3] = '0' + (c >> 4);
543         }
544
545         if ((c & 0x0F) >= 10) {
546             hex_line[i * 3 + 1] = 'a' + ((c & 0x0F) - 10);
547         }
548         else {
549             hex_line[i * 3 + 1] = '0' + (c & 0xF);
550         }
551
552         hex_line[i * 3 + 2] = ' ';
553
554         i++;
555         posn++;
556     }
557
558     if (i != 1) {
559         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "HEADER: %s %s",
560                      asc_line, hex_line);
561     }
562
563     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "HEADER: -EOH-");
564 #endif
565 }
566
567 static apr_status_t dispatch(proxy_conn_rec *conn, request_rec *r,
568                              int request_id)
569 {
570     apr_bucket_brigade *ib, *ob;
571     int seen_end_of_headers = 0, done = 0;
572     apr_status_t rv = APR_SUCCESS;
573     conn_rec *c = r->connection;
574     struct iovec vec[2];
575     fcgi_header header;
576     unsigned char farray[FCGI_HEADER_LEN];
577     apr_pollfd_t pfd;
578     int header_state = HDR_STATE_READING_HEADERS;
579     apr_pool_t *setaside_pool;
580
581     apr_pool_create(&setaside_pool, r->pool);
582
583     pfd.desc_type = APR_POLL_SOCKET;
584     pfd.desc.s = conn->sock;
585     pfd.p = r->pool;
586     pfd.reqevents = APR_POLLIN | APR_POLLOUT;
587
588     ib = apr_brigade_create(r->pool, c->bucket_alloc);
589     ob = apr_brigade_create(r->pool, c->bucket_alloc);
590
591     while (! done) {
592         apr_interval_time_t timeout = conn->worker->timeout;
593         apr_size_t len;
594         int n;
595
596         /* We need SOME kind of timeout here, or virtually anything will
597          * cause timeout errors. */
598         if (! conn->worker->timeout_set) {
599             timeout = apr_time_from_sec(30);
600         }
601
602         rv = apr_poll(&pfd, 1, &n, timeout);
603         if (rv != APR_SUCCESS) {
604             if (APR_STATUS_IS_EINTR(rv)) {
605                 continue;
606             }
607             break;
608         }
609
610         if (pfd.rtnevents & APR_POLLOUT) {
611             char writebuf[AP_IOBUFSIZE];
612             apr_size_t writebuflen;
613             int last_stdin = 0;
614
615             rv = ap_get_brigade(r->input_filters, ib,
616                                 AP_MODE_READBYTES, APR_BLOCK_READ,
617                                 sizeof(writebuf));
618             if (rv != APR_SUCCESS) {
619                 break;
620             }
621
622             if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) {
623                 last_stdin = 1;
624             }
625
626             writebuflen = sizeof(writebuf);
627
628             rv = apr_brigade_flatten(ib, writebuf, &writebuflen);
629
630             apr_brigade_cleanup(ib);
631
632             if (rv != APR_SUCCESS) {
633                 break;
634             }
635
636             fill_in_header(&header, FCGI_STDIN, request_id,
637                            (apr_uint16_t) writebuflen, 0);
638             fcgi_header_to_array(&header, farray);
639
640             vec[0].iov_base = farray;
641             vec[0].iov_len = sizeof(farray);
642             vec[1].iov_base = writebuf;
643             vec[1].iov_len = writebuflen;
644
645             rv = send_data(conn, vec, 2, &len, 0);
646             if (rv != APR_SUCCESS) {
647                 break;
648             }
649
650             if (last_stdin) {
651                 pfd.reqevents = APR_POLLIN; /* Done with input data */
652
653                 fill_in_header(&header, FCGI_STDIN, request_id, 0, 0);
654                 fcgi_header_to_array(&header, farray);
655
656                 vec[0].iov_base = farray;
657                 vec[0].iov_len = sizeof(farray);
658
659                 rv = send_data(conn, vec, 1, &len, 1);
660             }
661         }
662
663         if (pfd.rtnevents & APR_POLLIN) {
664             /* readbuf has one byte on the end that is always 0, so it's
665              * able to work with a strstr when we search for the end of
666              * the headers, even if we fill the entire length in the recv. */
667             char readbuf[AP_IOBUFSIZE + 1];
668             apr_size_t readbuflen;
669             apr_size_t clen;
670             int rid, type;
671             apr_bucket *b;
672             char plen;
673
674             memset(readbuf, 0, sizeof(readbuf));
675             memset(farray, 0, sizeof(farray));
676
677             /* First, we grab the header... */
678             readbuflen = FCGI_HEADER_LEN;
679
680             rv = get_data(conn, (char *) farray, &readbuflen);
681             if (rv != APR_SUCCESS) {
682                 break;
683             }
684
685             dump_header_to_log(r, farray, readbuflen);
686             
687             if (readbuflen != FCGI_HEADER_LEN) {
688                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
689                              "proxy: FCGI: Failed to read entire header "
690                              "got %" APR_SIZE_T_FMT " wanted %d", 
691                              readbuflen, FCGI_HEADER_LEN);
692                 rv = APR_EINVAL;
693                 break;
694             }
695
696             fcgi_header_from_array(&header, farray);
697
698             if (header.version != FCGI_VERSION) {
699                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
700                              "proxy: FCGI: Got bogus version %d",
701                              (int) header.version);
702                 rv = APR_EINVAL;
703                 break;
704             }
705
706             type = header.type;
707
708             rid = header.requestIdB1 << 8;
709             rid |= header.requestIdB0;
710
711             if (rid != request_id) {
712                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
713                              "proxy: FCGI: Got bogus rid %d, expected %d",
714                              rid, request_id);
715                 rv = APR_EINVAL;
716                 break;
717             }
718
719             clen = header.contentLengthB1 << 8;
720             clen |= header.contentLengthB0;
721
722             plen = header.paddingLength;
723
724 recv_again:
725             if (clen > sizeof(readbuf) - 1) {
726                 readbuflen = sizeof(readbuf) - 1;
727             } else {
728                 readbuflen = clen;
729             }
730
731             /* Now get the actual data.  Yes it sucks to do this in a second
732              * recv call, this will eventually change when we move to real
733              * nonblocking recv calls. */
734             if (readbuflen != 0) {
735                 rv = get_data(conn, readbuf, &readbuflen);
736                 if (rv != APR_SUCCESS) {
737                     break;
738                 }
739                 readbuf[readbuflen] = 0;
740             }
741
742             switch (type) {
743             case FCGI_STDOUT:
744                 if (clen != 0) {
745                     b = apr_bucket_transient_create(readbuf,
746                                                     readbuflen,
747                                                     c->bucket_alloc);
748
749                     APR_BRIGADE_INSERT_TAIL(ob, b);
750
751                     if (! seen_end_of_headers) {
752                         int st = handle_headers(r, &header_state, readbuf, ob);
753
754                         if (st == 1) {
755                             seen_end_of_headers = 1;
756
757                             rv = ap_pass_brigade(r->output_filters, ob);
758                             if (rv != APR_SUCCESS) {
759                                 break;
760                             }
761
762                             apr_brigade_cleanup(ob);
763
764                             apr_pool_clear(setaside_pool);
765                         }
766                         else if (st == -1) {
767                             rv = APR_EINVAL;
768                             break;
769                         }
770                         else {
771                             /* We're still looking for the end of the
772                              * headers, so this part of the data will need
773                              * to persist. */
774                             apr_bucket_setaside(b, setaside_pool);
775                         }
776                     } else {
777                         /* we've already passed along the headers, so now pass
778                          * through the content.  we could simply continue to
779                          * setaside the content and not pass until we see the
780                          * 0 content-length (below, where we append the EOS),
781                          * but that could be a huge amount of data; so we pass
782                          * along smaller chunks
783                          */
784                         rv = ap_pass_brigade(r->output_filters, ob);
785                         if (rv != APR_SUCCESS) {
786                             break;
787                         }
788                         apr_brigade_cleanup(ob);
789                     }
790
791                     /* If we didn't read all the data go back and get the
792                      * rest of it. */
793                     if (clen > readbuflen) {
794                         clen -= readbuflen;
795                         goto recv_again;
796                     }
797                 } else {
798                     /* XXX what if we haven't seen end of the headers yet? */
799
800                     b = apr_bucket_eos_create(c->bucket_alloc);
801
802                     APR_BRIGADE_INSERT_TAIL(ob, b);
803
804                     rv = ap_pass_brigade(r->output_filters, ob);
805                     if (rv != APR_SUCCESS) {
806                         break;
807                     }
808
809                     /* XXX Why don't we cleanup here?  (logic from AJP) */
810                 }
811                 break;
812
813             case FCGI_STDERR:
814                 /* TODO: Should probably clean up this logging a bit... */
815                 if (clen) {
816                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
817                                  "proxy: FCGI: Got error '%s'", readbuf);
818                 }
819
820                 if (clen > readbuflen) {
821                     clen -= readbuflen;
822                     goto recv_again;
823                 }
824                 break;
825
826             case FCGI_END_REQUEST:
827                 done = 1;
828                 break;
829
830             default:
831                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
832                              "proxy: FCGI: Got bogus record %d", type);
833                 break;
834             }
835
836             if (plen) {
837                 readbuflen = plen;
838
839                 rv = get_data(conn, readbuf, &readbuflen);
840                 if (rv != APR_SUCCESS) {
841                     break;
842                 }
843             }
844         }
845     }
846
847     apr_brigade_destroy(ib);
848     apr_brigade_destroy(ob);
849
850     return rv;
851 }
852
853 /*
854  * process the request and write the response.
855  */
856 static int fcgi_do_request(apr_pool_t *p, request_rec *r,
857                            proxy_conn_rec *conn,
858                            conn_rec *origin,
859                            proxy_dir_conf *conf,
860                            apr_uri_t *uri,
861                            char *url, char *server_portstr)
862 {
863     /* Request IDs are arbitrary numbers that we assign to a
864      * single request. This would allow multiplex/pipelinig of 
865      * multiple requests to the same FastCGI connection, but 
866      * we don't support that, and always use a value of '1' to
867      * keep things simple. */
868     int request_id = 1; 
869     apr_status_t rv;
870    
871     /* Step 1: Send FCGI_BEGIN_REQUEST */
872     rv = send_begin_request(conn, request_id);
873     if (rv != APR_SUCCESS) {
874         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
875                      "proxy: FCGI: Failed Writing Request to %s:",
876                      server_portstr);
877         conn->close = 1;
878         return HTTP_SERVICE_UNAVAILABLE;
879     }
880     
881     /* Step 2: Send Environment via FCGI_PARAMS */
882     rv = send_environment(conn, r, request_id);
883     if (rv != APR_SUCCESS) {
884         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
885                      "proxy: FCGI: Failed writing Environment to %s:",
886                      server_portstr);
887         conn->close = 1;
888         return HTTP_SERVICE_UNAVAILABLE;
889     }
890
891     /* Step 3: Read records from the back end server and handle them. */
892     rv = dispatch(conn, r, request_id);
893     if (rv != APR_SUCCESS) {
894         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
895                      "proxy: FCGI: Error dispatching request to %s:",
896                      server_portstr);
897         conn->close = 1;
898         return HTTP_SERVICE_UNAVAILABLE;
899     }
900
901     return OK;
902 }
903
904 #define FCGI_SCHEME "FCGI"
905
906 /*
907  * This handles fcgi:(dest) URLs
908  */
909 static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
910                               proxy_server_conf *conf,
911                               char *url, const char *proxyname,
912                               apr_port_t proxyport)
913 {
914     int status;
915     char server_portstr[32];
916     conn_rec *origin = NULL;
917     proxy_conn_rec *backend = NULL;
918
919     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
920                                                  &proxy_module);
921
922     apr_pool_t *p = r->pool;
923
924     apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri));
925
926     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
927                  "proxy: FCGI: url: %s proxyname: %s proxyport: %d",
928                  url, proxyname, proxyport);
929
930     if (strncasecmp(url, "fcgi:", 5) == 0) {
931         url += 5;
932     }
933     else {
934         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
935                      "proxy: FCGI: declining URL %s", url);
936         return DECLINED;
937     }
938     
939     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
940                  "proxy: FCGI: serving URL %s", url);
941
942     /* Create space for state information */
943     if (! backend) {
944         status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker,
945                                              r->server);
946         if (status != OK) {
947             if (backend) {
948                 backend->close = 1;
949                 ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
950             }
951             return status;
952         }
953     }
954
955     backend->is_ssl = 0;
956
957     /* XXX Setting close to 0 is a great way to end up with
958      *     timeouts at this point, since we lack good ways to manage the
959      *     back end fastcgi processes.  This should be revisited when we
960      *     have a better story on that part of things. */
961
962     backend->close = 1;
963
964     /* Step One: Determine Who To Connect To */
965     status = ap_proxy_determine_connection(p, r, conf, worker, backend,
966                                            uri, &url, proxyname, proxyport,
967                                            server_portstr,
968                                            sizeof(server_portstr));
969     if (status != OK) {
970         goto cleanup;
971     }
972
973     /* Step Two: Make the Connection */
974     if (ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, r->server)) {
975         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
976                      "proxy: FCGI: failed to make connection to backend: %s",
977                      backend->hostname);
978         status = HTTP_SERVICE_UNAVAILABLE;
979         goto cleanup;
980     }
981
982     /* Step Three: Process the Request */
983     status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
984                              server_portstr);
985
986 cleanup:
987     /* Do not close the socket */
988     ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
989     return status;
990 }
991
992 static void register_hooks(apr_pool_t *p)
993 {
994     proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST);
995     proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST);
996 }
997
998 AP_DECLARE_MODULE(proxy_fcgi) = {
999     STANDARD20_MODULE_STUFF,
1000     NULL,                       /* create per-directory config structure */
1001     NULL,                       /* merge per-directory config structures */
1002     NULL,                       /* create per-server config structure */
1003     NULL,                       /* merge per-server config structures */
1004     NULL,                       /* command apr_table_t */
1005     register_hooks              /* register hooks */
1006 };