]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_fcgi.c
Merge r1780328, r1780329, r1781329, r1782164, r1782166, r1782193, r1778350, r1781329...
[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 #include "ap_expr.h"
21
22 module AP_MODULE_DECLARE_DATA proxy_fcgi_module;
23
24 typedef struct {
25     ap_expr_info_t *cond;
26     ap_expr_info_t *subst;
27     const char *envname;
28 } sei_entry;
29
30 typedef struct {
31     int need_dirwalk;
32 } fcgi_req_config_t;
33
34 /* We will assume FPM, but still differentiate */
35 typedef enum {
36     BACKEND_DEFAULT_UNKNOWN = 0,
37     BACKEND_FPM,
38     BACKEND_GENERIC,
39 } fcgi_backend_t;
40
41
42 #define FCGI_MAY_BE_FPM(dconf)                              \
43         (dconf &&                                           \
44         ((dconf->backend_type == BACKEND_DEFAULT_UNKNOWN) || \
45         (dconf->backend_type == BACKEND_FPM)))
46
47 typedef struct {
48     fcgi_backend_t backend_type;
49     apr_array_header_t *env_fixups;
50 } fcgi_dirconf_t;
51
52 /*
53  * Canonicalise http-like URLs.
54  * scheme is the scheme for the URL
55  * url is the URL starting with the first '/'
56  * def_port is the default port for this scheme.
57  */
58 static int proxy_fcgi_canon(request_rec *r, char *url)
59 {
60     char *host, sport[7];
61     const char *err;
62     char *path;
63     apr_port_t port, def_port;
64     fcgi_req_config_t *rconf = NULL;
65     const char *pathinfo_type = NULL;
66
67     if (ap_cstr_casecmpn(url, "fcgi:", 5) == 0) {
68         url += 5;
69     }
70     else {
71         return DECLINED;
72     }
73
74     port = def_port = ap_proxy_port_of_scheme("fcgi");
75
76     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
77                  "canonicalising URL %s", url);
78     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
79     if (err) {
80         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01059)
81                       "error parsing URL %s: %s", url, err);
82         return HTTP_BAD_REQUEST;
83     }
84
85     if (port != def_port)
86         apr_snprintf(sport, sizeof(sport), ":%d", port);
87     else
88         sport[0] = '\0';
89
90     if (ap_strchr_c(host, ':')) {
91         /* if literal IPv6 address */
92         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
93     }
94
95     if (apr_table_get(r->notes, "proxy-nocanon")) {
96         path = url;   /* this is the raw path */
97     }
98     else {
99         path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
100                              r->proxyreq);
101     }
102     if (path == NULL)
103         return HTTP_BAD_REQUEST;
104
105     r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/",
106                               path, NULL);
107
108     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01060)
109                   "set r->filename to %s", r->filename);
110
111     rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
112     if (rconf == NULL) {
113         rconf = apr_pcalloc(r->pool, sizeof(fcgi_req_config_t));
114         ap_set_module_config(r->request_config, &proxy_fcgi_module, rconf);
115     }
116
117     if (NULL != (pathinfo_type = apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo"))) {
118         /* It has to be on disk for this to work */
119         if (!strcasecmp(pathinfo_type, "full")) {
120             rconf->need_dirwalk = 1;
121             ap_unescape_url_keep2f(path, 0);
122         }
123         else if (!strcasecmp(pathinfo_type, "first-dot")) {
124             char *split = ap_strchr(path, '.');
125             if (split) {
126                 char *slash = ap_strchr(split, '/');
127                 if (slash) {
128                     r->path_info = apr_pstrdup(r->pool, slash);
129                     ap_unescape_url_keep2f(r->path_info, 0);
130                     *slash = '\0'; /* truncate path */
131                 }
132             }
133         }
134         else if (!strcasecmp(pathinfo_type, "last-dot")) {
135             char *split = ap_strrchr(path, '.');
136             if (split) {
137                 char *slash = ap_strchr(split, '/');
138                 if (slash) {
139                     r->path_info = apr_pstrdup(r->pool, slash);
140                     ap_unescape_url_keep2f(r->path_info, 0);
141                     *slash = '\0'; /* truncate path */
142                 }
143             }
144         }
145         else {
146             /* before proxy-fcgi-pathinfo had multi-values. This requires the
147              * the FCGI server to fixup PATH_INFO because it's the entire path
148              */
149             r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
150             if (!strcasecmp(pathinfo_type, "unescape")) {
151                 ap_unescape_url_keep2f(r->path_info, 0);
152             }
153             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061)
154                     "set r->path_info to %s", r->path_info);
155         }
156     }
157
158     return OK;
159 }
160
161
162 /*
163   ProxyFCGISetEnvIf "reqenv('PATH_INFO') =~ m#/foo(\d+)\.php$#" COVENV1 "$1"
164   ProxyFCGISetEnvIf "reqenv('PATH_INFO') =~ m#/foo(\d+)\.php$#" PATH_INFO "/foo.php"
165   ProxyFCGISetEnvIf "reqenv('PATH_TRANSLATED') =~ m#(/.*foo)(\d+)(.*)#" PATH_TRANSLATED "$1$3"
166 */
167 static void fix_cgivars(request_rec *r, fcgi_dirconf_t *dconf)
168 {
169     sei_entry *entries;
170     const char *err, *src;
171     int i = 0, rc = 0;
172     ap_regmatch_t regm[AP_MAX_REG_MATCH];
173
174     entries = (sei_entry *) dconf->env_fixups->elts;
175     for (i = 0; i < dconf->env_fixups->nelts; i++) {
176         sei_entry *entry = &entries[i];
177
178         if (entry->envname[0] == '!') {
179             apr_table_unset(r->subprocess_env, entry->envname+1);
180         }
181         else if (0 < (rc = ap_expr_exec_re(r, entry->cond, AP_MAX_REG_MATCH, regm, &src, &err)))  {
182             const char *val = ap_expr_str_exec_re(r, entry->subst, AP_MAX_REG_MATCH, regm, &src, &err);
183             if (err) {
184                 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03514)
185                               "Error evaluating expression for replacement of %s: '%s'",
186                                entry->envname, err);
187                 continue;
188             }
189             if (APLOGrtrace4(r)) {
190                 const char *oldval = apr_table_get(r->subprocess_env, entry->envname);
191                 ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
192                               "fix_cgivars: override %s from '%s' to '%s'",
193                               entry->envname, oldval, val);
194
195             }
196             apr_table_setn(r->subprocess_env, entry->envname, val);
197         }
198         else {
199             ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, "fix_cgivars: Condition returned %d", rc);
200         }
201     }
202 }
203
204 /* Wrapper for apr_socket_sendv that handles updating the worker stats. */
205 static apr_status_t send_data(proxy_conn_rec *conn,
206                               struct iovec *vec,
207                               int nvec,
208                               apr_size_t *len)
209 {
210     apr_status_t rv = APR_SUCCESS;
211     apr_size_t written = 0, to_write = 0;
212     int i, offset;
213     apr_socket_t *s = conn->sock;
214
215     for (i = 0; i < nvec; i++) {
216         to_write += vec[i].iov_len;
217     }
218
219     offset = 0;
220     while (to_write) {
221         apr_size_t n = 0;
222         rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
223         if (rv != APR_SUCCESS) {
224             break;
225         }
226         if (n > 0) {
227             written += n;
228             if (written >= to_write)
229                 break;                 /* short circuit out */
230             for (i = offset; i < nvec; ) {
231                 if (n >= vec[i].iov_len) {
232                     offset++;
233                     n -= vec[i++].iov_len;
234                 } else {
235                     vec[i].iov_len -= n;
236                     vec[i].iov_base = (char *) vec[i].iov_base + n;
237                     break;
238                 }
239             }
240         }
241     }
242
243     conn->worker->s->transferred += written;
244     *len = written;
245
246     return rv;
247 }
248
249 /* Wrapper for apr_socket_recv that handles updating the worker stats. */
250 static apr_status_t get_data(proxy_conn_rec *conn,
251                              char *buffer,
252                              apr_size_t *buflen)
253 {
254     apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen);
255
256     if (rv == APR_SUCCESS) {
257         conn->worker->s->read += *buflen;
258     }
259
260     return rv;
261 }
262
263 static apr_status_t get_data_full(proxy_conn_rec *conn,
264                                   char *buffer,
265                                   apr_size_t buflen)
266 {
267     apr_size_t readlen;
268     apr_size_t cumulative_len = 0;
269     apr_status_t rv;
270
271     do {
272         readlen = buflen - cumulative_len;
273         rv = get_data(conn, buffer + cumulative_len, &readlen);
274         if (rv != APR_SUCCESS) {
275             return rv;
276         }
277         cumulative_len += readlen;
278     } while (cumulative_len < buflen);
279
280     return APR_SUCCESS;
281 }
282
283 static apr_status_t send_begin_request(proxy_conn_rec *conn,
284                                        apr_uint16_t request_id)
285 {
286     struct iovec vec[2];
287     ap_fcgi_header header;
288     unsigned char farray[AP_FCGI_HEADER_LEN];
289     ap_fcgi_begin_request_body brb;
290     unsigned char abrb[AP_FCGI_HEADER_LEN];
291     apr_size_t len;
292
293     ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id,
294                            sizeof(abrb), 0);
295
296     ap_fcgi_fill_in_request_body(&brb, AP_FCGI_RESPONDER,
297                                  ap_proxy_connection_reusable(conn)
298                                      ? AP_FCGI_KEEP_CONN : 0);
299
300     ap_fcgi_header_to_array(&header, farray);
301     ap_fcgi_begin_request_body_to_array(&brb, abrb);
302
303     vec[0].iov_base = (void *)farray;
304     vec[0].iov_len = sizeof(farray);
305     vec[1].iov_base = (void *)abrb;
306     vec[1].iov_len = sizeof(abrb);
307
308     return send_data(conn, vec, 2, &len);
309 }
310
311 static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r,
312                                      apr_pool_t *temp_pool,
313                                      apr_uint16_t request_id)
314 {
315     const apr_array_header_t *envarr;
316     const apr_table_entry_t *elts;
317     struct iovec vec[2];
318     ap_fcgi_header header;
319     unsigned char farray[AP_FCGI_HEADER_LEN];
320     char *body;
321     apr_status_t rv;
322     apr_size_t avail_len, len, required_len;
323     int next_elem, starting_elem;
324     int fpm = 0;
325     fcgi_req_config_t *rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module);
326     fcgi_dirconf_t *dconf = ap_get_module_config(r->per_dir_config, &proxy_fcgi_module);
327
328     if (rconf) {
329        if (rconf->need_dirwalk) {
330           ap_directory_walk(r);
331        }
332     }
333
334     /* Strip proxy: prefixes */
335     if (r->filename) {
336         char *newfname = NULL;
337
338         if (!strncmp(r->filename, "proxy:balancer://", 17)) {
339             newfname = apr_pstrdup(r->pool, r->filename+17);
340         }
341
342         if (!FCGI_MAY_BE_FPM(dconf))  {
343             if (!strncmp(r->filename, "proxy:fcgi://", 13)) {
344                 /* If we strip this under FPM, and any internal redirect occurs
345                  * on PATH_INFO, FPM may use PATH_TRANSLATED instead of
346                  * SCRIPT_FILENAME (a la mod_fastcgi + Action).
347                  */
348                 newfname = apr_pstrdup(r->pool, r->filename+13);
349             }
350             /* Query string in environment only */
351             if (newfname && r->args && *r->args) {
352                 char *qs = strrchr(newfname, '?');
353                 if (qs && !strcmp(qs+1, r->args)) {
354                     *qs = '\0';
355                 }
356             }
357         } else {
358             fpm = 1;
359         }
360
361         if (newfname) {
362             newfname = ap_strchr(newfname, '/');
363             r->filename = newfname;
364         }
365     }
366
367 #if 0
368     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(09999)
369                   "r->filename: %s", (r->filename ? r->filename : "nil"));
370     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(09999)
371                   "r->uri: %s", (r->uri ? r->uri : "nil"));
372     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(09999)
373                   "r->path_info: %s", (r->path_info ? r->path_info : "nil"));
374 #endif
375
376     ap_add_common_vars(r);
377     ap_add_cgi_vars(r);
378
379     if (fpm || apr_table_get(r->notes, "virtual_script")) {
380         /*
381          * Adjust SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED for PHP-FPM
382          * TODO: Right now, PATH_INFO and PATH_TRANSLATED look OK...
383          */
384         const char *pend;
385         const char *script_name = apr_table_get(r->subprocess_env, "SCRIPT_NAME");
386         pend = script_name + strlen(script_name);
387         if (r->path_info && *r->path_info) {
388             pend = script_name + ap_find_path_info(script_name, r->path_info) - 1;
389         }
390         while (pend != script_name && *pend != '/') {
391             pend--;
392         }
393         apr_table_setn(r->subprocess_env, "SCRIPT_NAME", pend);
394         ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
395                       "fpm:virtual_script: Modified SCRIPT_NAME to: %s",
396                       pend);
397     }
398
399     /* XXX are there any FastCGI specific env vars we need to send? */
400
401     /* Give admins final option to fine-tune env vars */
402     fix_cgivars(r, dconf);
403
404     /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in
405      *     the TZ value specially.  We could use that, but it would mean
406      *     parsing the key/value pairs back OUT of the allocated env array,
407      *     not to mention allocating a totally useless array in the first
408      *     place, which would suck. */
409
410     envarr = apr_table_elts(r->subprocess_env);
411     elts = (const apr_table_entry_t *) envarr->elts;
412
413     if (APLOGrtrace8(r)) {
414         int i;
415
416         for (i = 0; i < envarr->nelts; ++i) {
417             ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(01062)
418                           "sending env var '%s' value '%s'",
419                           elts[i].key, elts[i].val);
420         }
421     }
422
423     /* Send envvars over in as many FastCGI records as it takes, */
424     next_elem = 0; /* starting with the first one */
425
426     avail_len = 16 * 1024; /* our limit per record, which could have been up
427                             * to AP_FCGI_MAX_CONTENT_LEN
428                             */
429
430     while (next_elem < envarr->nelts) {
431         starting_elem = next_elem;
432         required_len = ap_fcgi_encoded_env_len(r->subprocess_env,
433                                                avail_len,
434                                                &next_elem);
435
436         if (!required_len) {
437             if (next_elem < envarr->nelts) {
438                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
439                               APLOGNO(02536) "couldn't encode envvar '%s' in %"
440                               APR_SIZE_T_FMT " bytes",
441                               elts[next_elem].key, avail_len);
442                 /* skip this envvar and continue */
443                 ++next_elem;
444                 continue;
445             }
446             /* only an unused element at the end of the array */
447             break;
448         }
449
450         body = apr_palloc(temp_pool, required_len);
451         rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len,
452                                 &starting_elem);
453         /* we pre-compute, so we can't run out of space */
454         ap_assert(rv == APR_SUCCESS);
455         /* compute and encode must be in sync */
456         ap_assert(starting_elem == next_elem);
457
458         ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id,
459                                (apr_uint16_t)required_len, 0);
460         ap_fcgi_header_to_array(&header, farray);
461
462         vec[0].iov_base = (void *)farray;
463         vec[0].iov_len = sizeof(farray);
464         vec[1].iov_base = body;
465         vec[1].iov_len = required_len;
466
467         rv = send_data(conn, vec, 2, &len);
468         apr_pool_clear(temp_pool);
469
470         if (rv) {
471             return rv;
472         }
473     }
474
475     /* Envvars sent, so say we're done */
476     ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 0, 0);
477     ap_fcgi_header_to_array(&header, farray);
478
479     vec[0].iov_base = (void *)farray;
480     vec[0].iov_len = sizeof(farray);
481
482     return send_data(conn, vec, 1, &len);
483 }
484
485 enum {
486   HDR_STATE_READING_HEADERS,
487   HDR_STATE_GOT_CR,
488   HDR_STATE_GOT_CRLF,
489   HDR_STATE_GOT_CRLFCR,
490   HDR_STATE_GOT_LF,
491   HDR_STATE_DONE_WITH_HEADERS
492 };
493
494 /* Try to find the end of the script headers in the response from the back
495  * end fastcgi server. STATE holds the current header parsing state for this
496  * request.
497  *
498  * Returns 0 if it can't find the end of the headers, and 1 if it found the
499  * end of the headers. */
500 static int handle_headers(request_rec *r, int *state,
501                           const char *readbuf, apr_size_t readlen)
502 {
503     const char *itr = readbuf;
504
505     while (readlen--) {
506         if (*itr == '\r') {
507             switch (*state) {
508                 case HDR_STATE_GOT_CRLF:
509                     *state = HDR_STATE_GOT_CRLFCR;
510                     break;
511
512                 default:
513                     *state = HDR_STATE_GOT_CR;
514                     break;
515             }
516         }
517         else if (*itr == '\n') {
518             switch (*state) {
519                  case HDR_STATE_GOT_LF:
520                      *state = HDR_STATE_DONE_WITH_HEADERS;
521                      break;
522
523                  case HDR_STATE_GOT_CR:
524                      *state = HDR_STATE_GOT_CRLF;
525                      break;
526
527                  case HDR_STATE_GOT_CRLFCR:
528                      *state = HDR_STATE_DONE_WITH_HEADERS;
529                      break;
530
531                  default:
532                      *state = HDR_STATE_GOT_LF;
533                      break;
534             }
535         }
536         else {
537             *state = HDR_STATE_READING_HEADERS;
538         }
539
540         if (*state == HDR_STATE_DONE_WITH_HEADERS)
541             break;
542
543         ++itr;
544     }
545
546     if (*state == HDR_STATE_DONE_WITH_HEADERS) {
547         return 1;
548     }
549
550     return 0;
551 }
552
553 static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf,
554                              request_rec *r, apr_pool_t *setaside_pool,
555                              apr_uint16_t request_id, const char **err,
556                              int *bad_request, int *has_responded)
557 {
558     apr_bucket_brigade *ib, *ob;
559     int seen_end_of_headers = 0, done = 0, ignore_body = 0;
560     apr_status_t rv = APR_SUCCESS;
561     int script_error_status = HTTP_OK;
562     conn_rec *c = r->connection;
563     struct iovec vec[2];
564     ap_fcgi_header header;
565     unsigned char farray[AP_FCGI_HEADER_LEN];
566     apr_pollfd_t pfd;
567     int header_state = HDR_STATE_READING_HEADERS;
568     char stack_iobuf[AP_IOBUFSIZE];
569     apr_size_t iobuf_size = AP_IOBUFSIZE;
570     char *iobuf = stack_iobuf;
571
572     *err = NULL;
573     if (conn->worker->s->io_buffer_size_set) {
574         iobuf_size = conn->worker->s->io_buffer_size;
575         iobuf = apr_palloc(r->pool, iobuf_size);
576     }
577
578     pfd.desc_type = APR_POLL_SOCKET;
579     pfd.desc.s = conn->sock;
580     pfd.p = r->pool;
581     pfd.reqevents = APR_POLLIN | APR_POLLOUT;
582
583     ib = apr_brigade_create(r->pool, c->bucket_alloc);
584     ob = apr_brigade_create(r->pool, c->bucket_alloc);
585
586     while (! done) {
587         apr_interval_time_t timeout;
588         apr_size_t len;
589         int n;
590
591         /* We need SOME kind of timeout here, or virtually anything will
592          * cause timeout errors. */
593         apr_socket_timeout_get(conn->sock, &timeout);
594
595         rv = apr_poll(&pfd, 1, &n, timeout);
596         if (rv != APR_SUCCESS) {
597             if (APR_STATUS_IS_EINTR(rv)) {
598                 continue;
599             }
600             *err = "polling";
601             break;
602         }
603
604         if (pfd.rtnevents & APR_POLLOUT) {
605             apr_size_t to_send, writebuflen;
606             int last_stdin = 0;
607             char *iobuf_cursor;
608
609             rv = ap_get_brigade(r->input_filters, ib,
610                                 AP_MODE_READBYTES, APR_BLOCK_READ,
611                                 iobuf_size);
612             if (rv != APR_SUCCESS) {
613                 *err = "reading input brigade";
614                 *bad_request = 1;
615                 break;
616             }
617
618             if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) {
619                 last_stdin = 1;
620             }
621
622             writebuflen = iobuf_size;
623
624             rv = apr_brigade_flatten(ib, iobuf, &writebuflen);
625
626             apr_brigade_cleanup(ib);
627
628             if (rv != APR_SUCCESS) {
629                 *err = "flattening brigade";
630                 break;
631             }
632
633             to_send = writebuflen;
634             iobuf_cursor = iobuf;
635             while (to_send > 0) {
636                 int nvec = 0;
637                 apr_size_t write_this_time;
638
639                 write_this_time =
640                     to_send < AP_FCGI_MAX_CONTENT_LEN ? to_send : AP_FCGI_MAX_CONTENT_LEN;
641
642                 ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id,
643                                        (apr_uint16_t)write_this_time, 0);
644                 ap_fcgi_header_to_array(&header, farray);
645
646                 vec[nvec].iov_base = (void *)farray;
647                 vec[nvec].iov_len = sizeof(farray);
648                 ++nvec;
649                 if (writebuflen) {
650                     vec[nvec].iov_base = iobuf_cursor;
651                     vec[nvec].iov_len = write_this_time;
652                     ++nvec;
653                 }
654
655                 rv = send_data(conn, vec, nvec, &len);
656                 if (rv != APR_SUCCESS) {
657                     *err = "sending stdin";
658                     break;
659                 }
660
661                 to_send -= write_this_time;
662                 iobuf_cursor += write_this_time;
663             }
664             if (rv != APR_SUCCESS) {
665                 break;
666             }
667
668             if (last_stdin) {
669                 pfd.reqevents = APR_POLLIN; /* Done with input data */
670
671                 /* signal EOF (empty FCGI_STDIN) */
672                 ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id,
673                                        0, 0);
674                 ap_fcgi_header_to_array(&header, farray);
675
676                 vec[0].iov_base = (void *)farray;
677                 vec[0].iov_len = sizeof(farray);
678
679                 rv = send_data(conn, vec, 1, &len);
680                 if (rv != APR_SUCCESS) {
681                     *err = "sending empty stdin";
682                     break;
683                 }
684             }
685         }
686
687         if (pfd.rtnevents & APR_POLLIN) {
688             apr_size_t readbuflen;
689             apr_uint16_t clen, rid;
690             apr_bucket *b;
691             unsigned char plen;
692             unsigned char type, version;
693
694             /* First, we grab the header... */
695             rv = get_data_full(conn, (char *) farray, AP_FCGI_HEADER_LEN);
696             if (rv != APR_SUCCESS) {
697                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067)
698                               "Failed to read FastCGI header");
699                 break;
700             }
701
702             ap_log_rdata(APLOG_MARK, APLOG_TRACE8, r, "FastCGI header",
703                          farray, AP_FCGI_HEADER_LEN, 0);
704
705             ap_fcgi_header_fields_from_array(&version, &type, &rid,
706                                              &clen, &plen, farray);
707
708             if (version != AP_FCGI_VERSION_1) {
709                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068)
710                               "Got bogus version %d", (int)version);
711                 rv = APR_EINVAL;
712                 break;
713             }
714
715             if (rid != request_id) {
716                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069)
717                               "Got bogus rid %d, expected %d",
718                               rid, request_id);
719                 rv = APR_EINVAL;
720                 break;
721             }
722
723 recv_again:
724             if (clen > iobuf_size) {
725                 readbuflen = iobuf_size;
726             } else {
727                 readbuflen = clen;
728             }
729
730             /* Now get the actual data.  Yes it sucks to do this in a second
731              * recv call, this will eventually change when we move to real
732              * nonblocking recv calls. */
733             if (readbuflen != 0) {
734                 rv = get_data(conn, iobuf, &readbuflen);
735                 if (rv != APR_SUCCESS) {
736                     *err = "reading response body";
737                     break;
738                 }
739             }
740
741             switch (type) {
742             case AP_FCGI_STDOUT:
743                 if (clen != 0) {
744                     b = apr_bucket_transient_create(iobuf,
745                                                     readbuflen,
746                                                     c->bucket_alloc);
747
748                     APR_BRIGADE_INSERT_TAIL(ob, b);
749
750                     if (! seen_end_of_headers) {
751                         int st = handle_headers(r, &header_state,
752                                                 iobuf, readbuflen);
753
754                         if (st == 1) {
755                             int status;
756                             seen_end_of_headers = 1;
757
758                             status = ap_scan_script_header_err_brigade_ex(r, ob,
759                                 NULL, APLOG_MODULE_INDEX);
760                             /* suck in all the rest */
761                             if (status != OK) {
762                                 apr_bucket *tmp_b;
763                                 apr_brigade_cleanup(ob);
764                                 tmp_b = apr_bucket_eos_create(c->bucket_alloc);
765                                 APR_BRIGADE_INSERT_TAIL(ob, tmp_b);
766
767                                 *has_responded = 1;
768                                 r->status = status;
769                                 rv = ap_pass_brigade(r->output_filters, ob);
770                                 if (rv != APR_SUCCESS) {
771                                     *err = "passing headers brigade to output filters";
772                                     break;
773                                 }
774                                 else if (status == HTTP_NOT_MODIFIED
775                                          || status == HTTP_PRECONDITION_FAILED) {
776                                     /* Special 'status' cases handled:
777                                      * 1) HTTP 304 response MUST NOT contain
778                                      *    a message-body, ignore it.
779                                      * 2) HTTP 412 response.
780                                      * The break is not added since there might
781                                      * be more bytes to read from the FCGI
782                                      * connection. Even if the message-body is
783                                      * ignored (and the EOS bucket has already
784                                      * been sent) we want to avoid subsequent
785                                      * bogus reads. */
786                                     ignore_body = 1;
787                                 }
788                                 else {
789                                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070)
790                                                     "Error parsing script headers");
791                                     rv = APR_EINVAL;
792                                     break;
793                                 }
794                             }
795
796                             if (conf->error_override
797                                 && ap_is_HTTP_ERROR(r->status) && ap_is_initial_req(r)) {
798                                 /*
799                                  * set script_error_status to discard
800                                  * everything after the headers
801                                  */
802                                 script_error_status = r->status;
803                                 /*
804                                  * prevent ap_die() from treating this as a
805                                  * recursive error, initially:
806                                  */
807                                 r->status = HTTP_OK;
808                             }
809
810                             if (script_error_status == HTTP_OK
811                                 && !APR_BRIGADE_EMPTY(ob) && !ignore_body) {
812                                 /* Send the part of the body that we read while
813                                  * reading the headers.
814                                  */
815                                 *has_responded = 1;
816                                 rv = ap_pass_brigade(r->output_filters, ob);
817                                 if (rv != APR_SUCCESS) {
818                                     *err = "passing brigade to output filters";
819                                     break;
820                                 }
821                             }
822                             apr_brigade_cleanup(ob);
823
824                             apr_pool_clear(setaside_pool);
825                         }
826                         else {
827                             /* We're still looking for the end of the
828                              * headers, so this part of the data will need
829                              * to persist. */
830                             apr_bucket_setaside(b, setaside_pool);
831                         }
832                     } else {
833                         /* we've already passed along the headers, so now pass
834                          * through the content.  we could simply continue to
835                          * setaside the content and not pass until we see the
836                          * 0 content-length (below, where we append the EOS),
837                          * but that could be a huge amount of data; so we pass
838                          * along smaller chunks
839                          */
840                         if (script_error_status == HTTP_OK && !ignore_body) {
841                             *has_responded = 1;
842                             rv = ap_pass_brigade(r->output_filters, ob);
843                             if (rv != APR_SUCCESS) {
844                                 *err = "passing brigade to output filters";
845                                 break;
846                             }
847                         }
848                         apr_brigade_cleanup(ob);
849                     }
850
851                     /* If we didn't read all the data, go back and get the
852                      * rest of it. */
853                     if (clen > readbuflen) {
854                         clen -= readbuflen;
855                         goto recv_again;
856                     }
857                 } else {
858                     /* XXX what if we haven't seen end of the headers yet? */
859
860                     if (script_error_status == HTTP_OK) {
861                         b = apr_bucket_eos_create(c->bucket_alloc);
862                         APR_BRIGADE_INSERT_TAIL(ob, b);
863
864                         *has_responded = 1;
865                         rv = ap_pass_brigade(r->output_filters, ob);
866                         if (rv != APR_SUCCESS) {
867                             *err = "passing brigade to output filters";
868                             break;
869                         }
870                     }
871
872                     /* XXX Why don't we cleanup here?  (logic from AJP) */
873                 }
874                 break;
875
876             case AP_FCGI_STDERR:
877                 /* TODO: Should probably clean up this logging a bit... */
878                 if (clen) {
879                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071)
880                                   "Got error '%.*s'", (int)readbuflen, iobuf);
881                 }
882
883                 if (clen > readbuflen) {
884                     clen -= readbuflen;
885                     goto recv_again;
886                 }
887                 break;
888
889             case AP_FCGI_END_REQUEST:
890                 done = 1;
891                 break;
892
893             default:
894                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01072)
895                               "Got bogus record %d", type);
896                 break;
897             }
898             /* Leave on above switch's inner error. */
899             if (rv != APR_SUCCESS) {
900                 break;
901             }
902
903             if (plen) {
904                 rv = get_data_full(conn, iobuf, plen);
905                 if (rv != APR_SUCCESS) {
906                     ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02537)
907                                   "Error occurred reading padding");
908                     break;
909                 }
910             }
911         }
912     }
913
914     apr_brigade_destroy(ib);
915     apr_brigade_destroy(ob);
916
917     if (script_error_status != HTTP_OK) {
918         ap_die(script_error_status, r); /* send ErrorDocument */
919         *has_responded = 1;
920     }
921
922     return rv;
923 }
924
925 /*
926  * process the request and write the response.
927  */
928 static int fcgi_do_request(apr_pool_t *p, request_rec *r,
929                            proxy_conn_rec *conn,
930                            conn_rec *origin,
931                            proxy_dir_conf *conf,
932                            apr_uri_t *uri,
933                            char *url, char *server_portstr)
934 {
935     /* Request IDs are arbitrary numbers that we assign to a
936      * single request. This would allow multiplex/pipelining of
937      * multiple requests to the same FastCGI connection, but
938      * we don't support that, and always use a value of '1' to
939      * keep things simple. */
940     apr_uint16_t request_id = 1;
941     apr_status_t rv;
942     apr_pool_t *temp_pool;
943     const char *err;
944     int bad_request = 0,
945         has_responded = 0;
946
947     /* Step 1: Send AP_FCGI_BEGIN_REQUEST */
948     rv = send_begin_request(conn, request_id);
949     if (rv != APR_SUCCESS) {
950         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01073)
951                       "Failed Writing Request to %s:", server_portstr);
952         conn->close = 1;
953         return HTTP_SERVICE_UNAVAILABLE;
954     }
955
956     apr_pool_create(&temp_pool, r->pool);
957
958     /* Step 2: Send Environment via FCGI_PARAMS */
959     rv = send_environment(conn, r, temp_pool, request_id);
960     if (rv != APR_SUCCESS) {
961         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01074)
962                       "Failed writing Environment to %s:", server_portstr);
963         conn->close = 1;
964         return HTTP_SERVICE_UNAVAILABLE;
965     }
966
967     /* Step 3: Read records from the back end server and handle them. */
968     rv = dispatch(conn, conf, r, temp_pool, request_id,
969                   &err, &bad_request, &has_responded);
970     if (rv != APR_SUCCESS) {
971         /* If the client aborted the connection during retrieval or (partially)
972          * sending the response, don't return a HTTP_SERVICE_UNAVAILABLE, since
973          * this is not a backend problem. */
974         if (r->connection->aborted) {
975             ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r,
976                           "The client aborted the connection.");
977             conn->close = 1;
978             return OK;
979         }
980
981         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01075)
982                       "Error dispatching request to %s: %s%s%s",
983                       server_portstr,
984                       err ? "(" : "",
985                       err ? err : "",
986                       err ? ")" : "");
987         conn->close = 1;
988         if (has_responded) {
989             return AP_FILTER_ERROR;
990         }
991         if (bad_request) {
992             return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
993         }
994         if (APR_STATUS_IS_TIMEUP(rv)) {
995             return HTTP_GATEWAY_TIME_OUT;
996         }
997         return HTTP_SERVICE_UNAVAILABLE;
998     }
999
1000     return OK;
1001 }
1002
1003 #define FCGI_SCHEME "FCGI"
1004
1005 /*
1006  * This handles fcgi:(dest) URLs
1007  */
1008 static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker,
1009                               proxy_server_conf *conf,
1010                               char *url, const char *proxyname,
1011                               apr_port_t proxyport)
1012 {
1013     int status;
1014     char server_portstr[32];
1015     conn_rec *origin = NULL;
1016     proxy_conn_rec *backend = NULL;
1017     apr_uri_t *uri;
1018
1019     proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
1020                                                  &proxy_module);
1021
1022     apr_pool_t *p = r->pool;
1023
1024
1025     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01076)
1026                   "url: %s proxyname: %s proxyport: %d",
1027                   url, proxyname, proxyport);
1028
1029     if (ap_cstr_casecmpn(url, "fcgi:", 5) != 0) {
1030         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url);
1031         return DECLINED;
1032     }
1033
1034     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01078) "serving URL %s", url);
1035
1036     /* Create space for state information */
1037     status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker,
1038                                          r->server);
1039     if (status != OK) {
1040         if (backend) {
1041             backend->close = 1;
1042             ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
1043         }
1044         return status;
1045     }
1046
1047     backend->is_ssl = 0;
1048
1049     /* Step One: Determine Who To Connect To */
1050     uri = apr_palloc(p, sizeof(*uri));
1051     status = ap_proxy_determine_connection(p, r, conf, worker, backend,
1052                                            uri, &url, proxyname, proxyport,
1053                                            server_portstr,
1054                                            sizeof(server_portstr));
1055     if (status != OK) {
1056         goto cleanup;
1057     }
1058
1059     /* This scheme handler does not reuse connections by default, to
1060      * avoid tying up a fastcgi that isn't expecting to work on
1061      * parallel requests.  But if the user went out of their way to
1062      * type the default value of disablereuse=off, we'll allow it.
1063      */
1064     backend->close = 1;
1065     if (worker->s->disablereuse_set && !worker->s->disablereuse) {
1066         backend->close = 0;
1067     }
1068
1069     /* Step Two: Make the Connection */
1070     if (ap_proxy_check_connection(FCGI_SCHEME, backend, r->server, 0,
1071                                   PROXY_CHECK_CONN_EMPTY)
1072             && ap_proxy_connect_backend(FCGI_SCHEME, backend, worker,
1073                                         r->server)) {
1074         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079)
1075                       "failed to make connection to backend: %s",
1076                       backend->hostname);
1077         status = HTTP_SERVICE_UNAVAILABLE;
1078         goto cleanup;
1079     }
1080
1081     /* Step Three: Process the Request */
1082     status = fcgi_do_request(p, r, backend, origin, dconf, uri, url,
1083                              server_portstr);
1084
1085 cleanup:
1086     ap_proxy_release_connection(FCGI_SCHEME, backend, r->server);
1087     return status;
1088 }
1089
1090 static void *fcgi_create_dconf(apr_pool_t *p, char *path)
1091 {
1092     fcgi_dirconf_t *a;
1093
1094     a = (fcgi_dirconf_t *)apr_pcalloc(p, sizeof(fcgi_dirconf_t));
1095     a->backend_type = BACKEND_DEFAULT_UNKNOWN;
1096     a->env_fixups = apr_array_make(p, 20, sizeof(sei_entry));
1097
1098     return a;
1099 }
1100
1101 static void *fcgi_merge_dconf(apr_pool_t *p, void *basev, void *overridesv)
1102 {
1103     fcgi_dirconf_t *a, *base, *over;
1104
1105     a     = (fcgi_dirconf_t *)apr_pcalloc(p, sizeof(fcgi_dirconf_t));
1106     base  = (fcgi_dirconf_t *)basev;
1107     over  = (fcgi_dirconf_t *)overridesv;
1108
1109     a->backend_type = (over->backend_type != BACKEND_DEFAULT_UNKNOWN)
1110                       ? over->backend_type
1111                       : base->backend_type;
1112     a->env_fixups = apr_array_append(p, base->env_fixups, over->env_fixups);
1113     return a;
1114 }
1115
1116 static const char *cmd_servertype(cmd_parms *cmd, void *in_dconf,
1117                                    const char *val)
1118 {
1119     fcgi_dirconf_t *dconf = in_dconf;
1120
1121     if (!strcasecmp(val, "GENERIC")) {
1122        dconf->backend_type = BACKEND_GENERIC;
1123     }
1124     else if (!strcasecmp(val, "FPM")) {
1125        dconf->backend_type = BACKEND_FPM;
1126     }
1127     else {
1128         return "ProxyFCGIBackendType requires one of the following arguments: "
1129                "'GENERIC', 'FPM'";
1130     }
1131
1132     return NULL;
1133 }
1134
1135
1136 static const char *cmd_setenv(cmd_parms *cmd, void *in_dconf,
1137                               const char *arg1, const char *arg2,
1138                               const char *arg3)
1139 {
1140     fcgi_dirconf_t *dconf = in_dconf;
1141     const char *err;
1142     sei_entry *new;
1143     const char *envvar = arg2;
1144
1145     new = apr_array_push(dconf->env_fixups);
1146     new->cond = ap_expr_parse_cmd(cmd, arg1, 0, &err, NULL);
1147     if (err) {
1148         return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s",
1149                             arg1, err);
1150     }
1151
1152     if (envvar[0] == '!') {
1153         /* Unset mode. */
1154         if (arg3) {
1155             return apr_psprintf(cmd->pool, "Third argument (\"%s\") is not "
1156                                 "allowed when using ProxyFCGISetEnvIf's unset "
1157                                 "mode (%s)", arg3, envvar);
1158         }
1159         else if (!envvar[1]) {
1160             /* i.e. someone tried to give us a name of just "!" */
1161             return "ProxyFCGISetEnvIf: \"!\" is not a valid variable name";
1162         }
1163
1164         new->subst = NULL;
1165     }
1166     else {
1167         /* Set mode. */
1168         if (!arg3) {
1169             /* A missing expr-value should be treated as empty. */
1170             arg3 = "";
1171         }
1172
1173         new->subst = ap_expr_parse_cmd(cmd, arg3, AP_EXPR_FLAG_STRING_RESULT, &err, NULL);
1174         if (err) {
1175             return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s",
1176                                 arg3, err);
1177         }
1178     }
1179
1180     new->envname = envvar;
1181
1182     return NULL;
1183 }
1184 static void register_hooks(apr_pool_t *p)
1185 {
1186     proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST);
1187     proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST);
1188 }
1189
1190 static const command_rec command_table[] = {
1191     AP_INIT_TAKE1("ProxyFCGIBackendType", cmd_servertype, NULL, OR_FILEINFO,
1192                   "Specify the type of FastCGI server: 'Generic', 'FPM'"),
1193     AP_INIT_TAKE23("ProxyFCGISetEnvIf", cmd_setenv, NULL, OR_FILEINFO,
1194                   "expr-condition env-name expr-value"),
1195     { NULL }
1196 };
1197
1198 AP_DECLARE_MODULE(proxy_fcgi) = {
1199     STANDARD20_MODULE_STUFF,
1200     fcgi_create_dconf,          /* create per-directory config structure */
1201     fcgi_merge_dconf,           /* merge per-directory config structures */
1202     NULL,                       /* create per-server config structure */
1203     NULL,                       /* merge per-server config structures */
1204     command_table,              /* command apr_table_t */
1205     register_hooks              /* register hooks */
1206 };