]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_http.c
Proxy: remove Warning headers with wrong date
[apache] / modules / proxy / mod_proxy_http.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 /* HTTP routines for Apache proxy */
18
19 #include "mod_proxy.h"
20
21 module AP_MODULE_DECLARE_DATA proxy_http_module;
22
23 static apr_status_t ap_proxy_http_cleanup(const char *scheme,
24                                           request_rec *r,
25                                           proxy_conn_rec *backend);
26
27 /*
28  * Canonicalise http-like URLs.
29  *  scheme is the scheme for the URL
30  *  url    is the URL starting with the first '/'
31  *  def_port is the default port for this scheme.
32  */
33 static int proxy_http_canon(request_rec *r, char *url)
34 {
35     char *host, *path, *search, sport[7];
36     const char *err;
37     const char *scheme;
38     apr_port_t port, def_port;
39
40     /* ap_port_of_scheme() */
41     if (strncasecmp(url, "http:", 5) == 0) {
42         url += 5;
43         scheme = "http";
44     }
45     else if (strncasecmp(url, "https:", 6) == 0) {
46         url += 6;
47         scheme = "https";
48     }
49     else {
50         return DECLINED;
51     }
52     def_port = apr_uri_port_of_scheme(scheme);
53
54     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
55              "proxy: HTTP: canonicalising URL %s", url);
56
57     /* do syntatic check.
58      * We break the URL into host, port, path, search
59      */
60     port = def_port;
61     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
62     if (err) {
63         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
64                       "error parsing URL %s: %s",
65                       url, err);
66         return HTTP_BAD_REQUEST;
67     }
68
69     /* now parse path/search args, according to rfc1738 */
70     /* N.B. if this isn't a true proxy request, then the URL _path_
71      * has already been decoded.  True proxy requests have r->uri
72      * == r->unparsed_uri, and no others have that property.
73      */
74     if (r->uri == r->unparsed_uri) {
75         search = strchr(url, '?');
76         if (search != NULL)
77             *(search++) = '\0';
78     }
79     else
80         search = r->args;
81
82     /* process path */
83     path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq);
84     if (path == NULL)
85         return HTTP_BAD_REQUEST;
86
87     if (port != def_port)
88         apr_snprintf(sport, sizeof(sport), ":%d", port);
89     else
90         sport[0] = '\0';
91
92     if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
93         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
94     }
95     r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
96             "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
97     return OK;
98 }
99
100 /* Clear all connection-based headers from the incoming headers table */
101 typedef struct foo {
102     apr_pool_t *pool;
103     apr_table_t *table;
104     apr_time_t time;
105 } foo;
106 static ap_regex_t *warn_rx;
107 static int clean_warning_headers(void *data, const char *key, const char *val)
108 {
109     apr_table_t *headers = ((foo*)data)->table;
110     apr_pool_t *pool = ((foo*)data)->pool;
111     char *warning;
112     char *date;
113     apr_time_t warn_time;
114     const int nmatch = 3;
115     ap_regmatch_t pmatch[3];
116
117     if (headers == NULL) {
118         ((foo*)data)->table = headers = apr_table_make(pool, 2);
119     }
120 /*
121  * Parse this, suckers!
122  *
123  *    Warning    = "Warning" ":" 1#warning-value
124  *
125  *    warning-value = warn-code SP warn-agent SP warn-text
126  *                                             [SP warn-date]
127  *
128  *    warn-code  = 3DIGIT
129  *    warn-agent = ( host [ ":" port ] ) | pseudonym
130  *                    ; the name or pseudonym of the server adding
131  *                    ; the Warning header, for use in debugging
132  *    warn-text  = quoted-string
133  *    warn-date  = <"> HTTP-date <">
134  *
135  * Buggrit, use a bloomin' regexp!
136  * (\d{3}\s+\S+\s+\".*?\"(\s+\"(.*?)\")?)  --> whole in $1, date in $3
137  */
138     while (!ap_regexec(warn_rx, val, nmatch, pmatch, 0)) {
139         warning = apr_pstrndup(pool, val+pmatch[0].rm_so,
140                                pmatch[0].rm_eo - pmatch[0].rm_so);
141         warn_time = 0;
142         if (pmatch[2].rm_eo > pmatch[2].rm_so) {
143             /* OK, we have a date here */
144             date = apr_pstrndup(pool, val+pmatch[2].rm_so,
145                                 pmatch[2].rm_eo - pmatch[2].rm_so);
146             warn_time = apr_date_parse_http(date);
147         }
148         if (!warn_time || (warn_time == ((foo*)data)->time)) {
149             apr_table_addn(headers, key, warning);
150         }
151         val += pmatch[0].rm_eo;
152     }
153     return 1;
154 }
155 static apr_table_t *ap_proxy_clean_warnings(apr_pool_t *p, apr_table_t *headers)
156 {
157    foo x;
158    x.pool = p;
159    x.table = NULL;
160    x.time = apr_date_parse_http(apr_table_get(headers, "Date"));
161    apr_table_do(clean_warning_headers, &x, headers, "Warning", NULL);
162    if (x.table != NULL) {
163        apr_table_unset(headers, "Warning");
164        return apr_table_overlay(p, headers, x.table);
165    }
166    else {
167         return headers;
168    }
169 }
170 static int clear_conn_headers(void *data, const char *key, const char *val)
171 {
172     apr_table_t *headers = ((foo*)data)->table;
173     apr_pool_t *pool = ((foo*)data)->pool;
174     const char *name;
175     char *next = apr_pstrdup(pool, val);
176     while (*next) {
177         name = next;
178         while (*next && !apr_isspace(*next) && (*next != ',')) {
179             ++next;
180         }
181         while (*next && (apr_isspace(*next) || (*next == ','))) {
182             *next++ = '\0';
183         }
184         apr_table_unset(headers, name);
185     }
186     return 1;
187 }
188 static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
189 {
190     foo x;
191     x.pool = p;
192     x.table = headers;
193     apr_table_unset(headers, "Proxy-Connection");
194     apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL);
195     apr_table_unset(headers, "Connection");
196 }
197 static void add_te_chunked(apr_pool_t *p,
198                            apr_bucket_alloc_t *bucket_alloc,
199                            apr_bucket_brigade *header_brigade)
200 {
201     apr_bucket *e;
202     char *buf;
203     const char te_hdr[] = "Transfer-Encoding: chunked" CRLF;
204
205     buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1);
206     ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1);
207
208     e = apr_bucket_pool_create(buf, sizeof(te_hdr)-1, p, bucket_alloc);
209     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
210 }
211
212 static void add_cl(apr_pool_t *p,
213                    apr_bucket_alloc_t *bucket_alloc,
214                    apr_bucket_brigade *header_brigade,
215                    const char *cl_val)
216 {
217     apr_bucket *e;
218     char *buf;
219
220     buf = apr_pstrcat(p, "Content-Length: ",
221                       cl_val,
222                       CRLF,
223                       NULL);
224     ap_xlate_proto_to_ascii(buf, strlen(buf));
225     e = apr_bucket_pool_create(buf, strlen(buf), p, bucket_alloc);
226     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
227 }
228
229 #define ASCII_CRLF  "\015\012"
230 #define ASCII_ZERO  "\060"
231
232 static void terminate_headers(apr_bucket_alloc_t *bucket_alloc,
233                               apr_bucket_brigade *header_brigade)
234 {
235     apr_bucket *e;
236
237     /* add empty line at the end of the headers */
238     e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
239     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
240 }
241
242 static apr_status_t pass_brigade(apr_bucket_alloc_t *bucket_alloc,
243                                  request_rec *r, proxy_conn_rec *conn,
244                                  conn_rec *origin, apr_bucket_brigade *bb,
245                                  int flush)
246 {
247     apr_status_t status;
248     apr_off_t transferred;
249
250     if (flush) {
251         apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
252         APR_BRIGADE_INSERT_TAIL(bb, e);
253     }
254     apr_brigade_length(bb, 0, &transferred);
255     if (transferred != -1)
256         conn->worker->s->transferred += transferred;
257     status = ap_pass_brigade(origin->output_filters, bb);
258     if (status != APR_SUCCESS) {
259         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
260                      "proxy: pass request body failed to %pI (%s)",
261                      conn->addr, conn->hostname);
262         return status;
263     }
264     apr_brigade_cleanup(bb);
265     return APR_SUCCESS;
266 }
267
268 #define MAX_MEM_SPOOL 16384
269
270 static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
271                                            request_rec *r,
272                                            proxy_conn_rec *p_conn,
273                                            conn_rec *origin,
274                                            apr_bucket_brigade *header_brigade,
275                                            apr_bucket_brigade *input_brigade)
276 {
277     int seen_eos = 0;
278     apr_size_t hdr_len;
279     apr_off_t bytes;
280     apr_status_t status;
281     apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
282     apr_bucket_brigade *bb;
283     apr_bucket *e;
284
285     add_te_chunked(p, bucket_alloc, header_brigade);
286     terminate_headers(bucket_alloc, header_brigade);
287
288     while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
289     {
290         char chunk_hdr[20];  /* must be here due to transient bucket. */
291
292         /* If this brigade contains EOS, either stop or remove it. */
293         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
294             seen_eos = 1;
295
296             /* We can't pass this EOS to the output_filters. */
297             e = APR_BRIGADE_LAST(input_brigade);
298             apr_bucket_delete(e);
299         }
300
301         apr_brigade_length(input_brigade, 1, &bytes);
302
303         hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
304                                "%" APR_UINT64_T_HEX_FMT CRLF,
305                                (apr_uint64_t)bytes);
306
307         ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
308         e = apr_bucket_transient_create(chunk_hdr, hdr_len,
309                                         bucket_alloc);
310         APR_BRIGADE_INSERT_HEAD(input_brigade, e);
311
312         /*
313          * Append the end-of-chunk CRLF
314          */
315         e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
316         APR_BRIGADE_INSERT_TAIL(input_brigade, e);
317
318         if (header_brigade) {
319             /* we never sent the header brigade, so go ahead and
320              * take care of that now
321              */
322             bb = header_brigade;
323
324             /*
325              * Save input_brigade in bb brigade. (At least) in the SSL case
326              * input_brigade contains transient buckets whose data would get
327              * overwritten during the next call of ap_get_brigade in the loop.
328              * ap_save_brigade ensures these buckets to be set aside.
329              * Calling ap_save_brigade with NULL as filter is OK, because
330              * bb brigade already has been created and does not need to get
331              * created by ap_save_brigade.
332              */
333             status = ap_save_brigade(NULL, &bb, &input_brigade, p);
334             if (status != APR_SUCCESS) {
335                 return status;
336             }
337
338             header_brigade = NULL;
339         }
340         else {
341             bb = input_brigade;
342         }
343
344         /* The request is flushed below this loop with chunk EOS header */
345         status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0);
346         if (status != APR_SUCCESS) {
347             return status;
348         }
349
350         if (seen_eos) {
351             break;
352         }
353
354         status = ap_get_brigade(r->input_filters, input_brigade,
355                                 AP_MODE_READBYTES, APR_BLOCK_READ,
356                                 HUGE_STRING_LEN);
357
358         if (status != APR_SUCCESS) {
359             return status;
360         }
361     }
362
363     if (header_brigade) {
364         /* we never sent the header brigade because there was no request body;
365          * send it now
366          */
367         bb = header_brigade;
368     }
369     else {
370         if (!APR_BRIGADE_EMPTY(input_brigade)) {
371             /* input brigade still has an EOS which we can't pass to the output_filters. */
372             e = APR_BRIGADE_LAST(input_brigade);
373             AP_DEBUG_ASSERT(APR_BUCKET_IS_EOS(e));
374             apr_bucket_delete(e);
375         }
376         bb = input_brigade;
377     }
378
379     e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
380                                    /* <trailers> */
381                                    ASCII_CRLF,
382                                    5, bucket_alloc);
383     APR_BRIGADE_INSERT_TAIL(bb, e);
384
385     if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
386         e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
387         APR_BRIGADE_INSERT_TAIL(bb, e);
388     }
389
390     /* Now we have headers-only, or the chunk EOS mark; flush it */
391     status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
392     return status;
393 }
394
395 static apr_status_t stream_reqbody_cl(apr_pool_t *p,
396                                       request_rec *r,
397                                       proxy_conn_rec *p_conn,
398                                       conn_rec *origin,
399                                       apr_bucket_brigade *header_brigade,
400                                       apr_bucket_brigade *input_brigade,
401                                       const char *old_cl_val)
402 {
403     int seen_eos = 0;
404     apr_status_t status = APR_SUCCESS;
405     apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
406     apr_bucket_brigade *bb;
407     apr_bucket *e;
408     apr_off_t cl_val = 0;
409     apr_off_t bytes;
410     apr_off_t bytes_streamed = 0;
411
412     if (old_cl_val) {
413         add_cl(p, bucket_alloc, header_brigade, old_cl_val);
414         if (APR_SUCCESS != (status = apr_strtoff(&cl_val, old_cl_val, NULL,
415                                                  0))) {
416             return status;
417         }
418     }
419     terminate_headers(bucket_alloc, header_brigade);
420
421     while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
422     {
423         apr_brigade_length(input_brigade, 1, &bytes);
424         bytes_streamed += bytes;
425
426         /* If this brigade contains EOS, either stop or remove it. */
427         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
428             seen_eos = 1;
429
430             /* We can't pass this EOS to the output_filters. */
431             e = APR_BRIGADE_LAST(input_brigade);
432             apr_bucket_delete(e);
433
434             if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
435                 e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
436                 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
437             }
438         }
439
440         /* C-L < bytes streamed?!?
441          * We will error out after the body is completely
442          * consumed, but we can't stream more bytes at the
443          * back end since they would in part be interpreted
444          * as another request!  If nothing is sent, then
445          * just send nothing.
446          *
447          * Prevents HTTP Response Splitting.
448          */
449         if (bytes_streamed > cl_val)
450              continue;
451
452         if (header_brigade) {
453             /* we never sent the header brigade, so go ahead and
454              * take care of that now
455              */
456             bb = header_brigade;
457
458             /*
459              * Save input_brigade in bb brigade. (At least) in the SSL case
460              * input_brigade contains transient buckets whose data would get
461              * overwritten during the next call of ap_get_brigade in the loop.
462              * ap_save_brigade ensures these buckets to be set aside.
463              * Calling ap_save_brigade with NULL as filter is OK, because
464              * bb brigade already has been created and does not need to get
465              * created by ap_save_brigade.
466              */
467             status = ap_save_brigade(NULL, &bb, &input_brigade, p);
468             if (status != APR_SUCCESS) {
469                 return status;
470             }
471
472             header_brigade = NULL;
473         }
474         else {
475             bb = input_brigade;
476         }
477
478         /* Once we hit EOS, we are ready to flush. */
479         status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos);
480         if (status != APR_SUCCESS) {
481             return status;
482         }
483
484         if (seen_eos) {
485             break;
486         }
487
488         status = ap_get_brigade(r->input_filters, input_brigade,
489                                 AP_MODE_READBYTES, APR_BLOCK_READ,
490                                 HUGE_STRING_LEN);
491
492         if (status != APR_SUCCESS) {
493             return status;
494         }
495     }
496
497     if (bytes_streamed != cl_val) {
498         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
499                      "proxy: client %s given Content-Length did not match"
500                      " number of body bytes read", r->connection->remote_ip);
501         return APR_EOF;
502     }
503
504     if (header_brigade) {
505         /* we never sent the header brigade since there was no request
506          * body; send it now with the flush flag
507          */
508         bb = header_brigade;
509         status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
510     }
511     return status;
512 }
513
514 static apr_status_t spool_reqbody_cl(apr_pool_t *p,
515                                      request_rec *r,
516                                      proxy_conn_rec *p_conn,
517                                      conn_rec *origin,
518                                      apr_bucket_brigade *header_brigade,
519                                      apr_bucket_brigade *input_brigade,
520                                      int force_cl)
521 {
522     int seen_eos = 0;
523     apr_status_t status;
524     apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
525     apr_bucket_brigade *body_brigade;
526     apr_bucket *e;
527     apr_off_t bytes, bytes_spooled = 0, fsize = 0;
528     apr_file_t *tmpfile = NULL;
529
530     body_brigade = apr_brigade_create(p, bucket_alloc);
531
532     while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
533     {
534         /* If this brigade contains EOS, either stop or remove it. */
535         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
536             seen_eos = 1;
537
538             /* We can't pass this EOS to the output_filters. */
539             e = APR_BRIGADE_LAST(input_brigade);
540             apr_bucket_delete(e);
541         }
542
543         apr_brigade_length(input_brigade, 1, &bytes);
544
545         if (bytes_spooled + bytes > MAX_MEM_SPOOL) {
546             /* can't spool any more in memory; write latest brigade to disk */
547             if (tmpfile == NULL) {
548                 const char *temp_dir;
549                 char *template;
550
551                 status = apr_temp_dir_get(&temp_dir, p);
552                 if (status != APR_SUCCESS) {
553                     ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
554                                  "proxy: search for temporary directory failed");
555                     return status;
556                 }
557                 apr_filepath_merge(&template, temp_dir,
558                                    "modproxy.tmp.XXXXXX",
559                                    APR_FILEPATH_NATIVE, p);
560                 status = apr_file_mktemp(&tmpfile, template, 0, p);
561                 if (status != APR_SUCCESS) {
562                     ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
563                                  "proxy: creation of temporary file in directory %s failed",
564                                  temp_dir);
565                     return status;
566                 }
567             }
568             for (e = APR_BRIGADE_FIRST(input_brigade);
569                  e != APR_BRIGADE_SENTINEL(input_brigade);
570                  e = APR_BUCKET_NEXT(e)) {
571                 const char *data;
572                 apr_size_t bytes_read, bytes_written;
573
574                 apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
575                 status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
576                 if (status != APR_SUCCESS) {
577                     const char *tmpfile_name;
578
579                     if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
580                         tmpfile_name = "(unknown)";
581                     }
582                     ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
583                                  "proxy: write to temporary file %s failed",
584                                  tmpfile_name);
585                     return status;
586                 }
587                 AP_DEBUG_ASSERT(bytes_read == bytes_written);
588                 fsize += bytes_written;
589             }
590             apr_brigade_cleanup(input_brigade);
591         }
592         else {
593
594             /*
595              * Save input_brigade in body_brigade. (At least) in the SSL case
596              * input_brigade contains transient buckets whose data would get
597              * overwritten during the next call of ap_get_brigade in the loop.
598              * ap_save_brigade ensures these buckets to be set aside.
599              * Calling ap_save_brigade with NULL as filter is OK, because
600              * body_brigade already has been created and does not need to get
601              * created by ap_save_brigade.
602              */
603             status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
604             if (status != APR_SUCCESS) {
605                 return status;
606             }
607
608         }
609
610         bytes_spooled += bytes;
611
612         if (seen_eos) {
613             break;
614         }
615
616         status = ap_get_brigade(r->input_filters, input_brigade,
617                                 AP_MODE_READBYTES, APR_BLOCK_READ,
618                                 HUGE_STRING_LEN);
619
620         if (status != APR_SUCCESS) {
621             return status;
622         }
623     }
624
625     if (bytes_spooled || force_cl) {
626         add_cl(p, bucket_alloc, header_brigade, apr_off_t_toa(p, bytes_spooled));
627     }
628     terminate_headers(bucket_alloc, header_brigade);
629     APR_BRIGADE_CONCAT(header_brigade, body_brigade);
630     if (tmpfile) {
631         apr_brigade_insert_file(header_brigade, tmpfile, 0, fsize, p);
632     }
633     if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
634         e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
635         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
636     }
637     /* This is all a single brigade, pass with flush flagged */
638     status = pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1);
639     return status;
640 }
641
642 static
643 apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
644                                    proxy_conn_rec *p_conn, conn_rec *origin,
645                                    proxy_server_conf *conf,
646                                    apr_uri_t *uri,
647                                    char *url, char *server_portstr)
648 {
649     conn_rec *c = r->connection;
650     apr_bucket_alloc_t *bucket_alloc = c->bucket_alloc;
651     apr_bucket_brigade *header_brigade;
652     apr_bucket_brigade *input_brigade;
653     apr_bucket_brigade *temp_brigade;
654     apr_bucket *e;
655     char *buf;
656     const apr_array_header_t *headers_in_array;
657     const apr_table_entry_t *headers_in;
658     int counter;
659     apr_status_t status;
660     enum rb_methods {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED, RB_SPOOL_CL};
661     enum rb_methods rb_method = RB_INIT;
662     const char *old_cl_val = NULL;
663     const char *old_te_val = NULL;
664     apr_off_t bytes_read = 0;
665     apr_off_t bytes;
666     int force10;
667     apr_table_t *headers_in_copy;
668
669     header_brigade = apr_brigade_create(p, origin->bucket_alloc);
670
671     /*
672      * Send the HTTP/1.1 request to the remote server
673      */
674
675     if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
676         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
677         force10 = 1;
678         p_conn->close++;
679     } else {
680         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
681         force10 = 0;
682     }
683     if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
684         origin->keepalive = AP_CONN_CLOSE;
685         p_conn->close++;
686     }
687     ap_xlate_proto_to_ascii(buf, strlen(buf));
688     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
689     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
690     if (conf->preserve_host == 0) {
691         if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
692             buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str,
693                               CRLF, NULL);
694         } else {
695             buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
696         }
697     }
698     else {
699         /* don't want to use r->hostname, as the incoming header might have a
700          * port attached
701          */
702         const char* hostname = apr_table_get(r->headers_in,"Host");
703         if (!hostname) {
704             hostname =  r->server->server_hostname;
705             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
706                           "proxy: no HTTP 0.9 request (with no host line) "
707                           "on incoming request and preserve host set "
708                           "forcing hostname to be %s for uri %s",
709                           hostname,
710                           r->uri );
711         }
712         buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
713     }
714     ap_xlate_proto_to_ascii(buf, strlen(buf));
715     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
716     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
717
718     /* handle Via */
719     if (conf->viaopt == via_block) {
720         /* Block all outgoing Via: headers */
721         apr_table_unset(r->headers_in, "Via");
722     } else if (conf->viaopt != via_off) {
723         const char *server_name = ap_get_server_name(r);
724         /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
725          * then the server name returned by ap_get_server_name() is the
726          * origin server name (which does make too much sense with Via: headers)
727          * so we use the proxy vhost's name instead.
728          */
729         if (server_name == r->hostname)
730             server_name = r->server->server_hostname;
731         /* Create a "Via:" request header entry and merge it */
732         /* Generate outgoing Via: header with/without server comment: */
733         apr_table_mergen(r->headers_in, "Via",
734                          (conf->viaopt == via_full)
735                          ? apr_psprintf(p, "%d.%d %s%s (%s)",
736                                         HTTP_VERSION_MAJOR(r->proto_num),
737                                         HTTP_VERSION_MINOR(r->proto_num),
738                                         server_name, server_portstr,
739                                         AP_SERVER_BASEVERSION)
740                          : apr_psprintf(p, "%d.%d %s%s",
741                                         HTTP_VERSION_MAJOR(r->proto_num),
742                                         HTTP_VERSION_MINOR(r->proto_num),
743                                         server_name, server_portstr)
744         );
745     }
746
747     /* X-Forwarded-*: handling
748      *
749      * XXX Privacy Note:
750      * -----------------
751      *
752      * These request headers are only really useful when the mod_proxy
753      * is used in a reverse proxy configuration, so that useful info
754      * about the client can be passed through the reverse proxy and on
755      * to the backend server, which may require the information to
756      * function properly.
757      *
758      * In a forward proxy situation, these options are a potential
759      * privacy violation, as information about clients behind the proxy
760      * are revealed to arbitrary servers out there on the internet.
761      *
762      * The HTTP/1.1 Via: header is designed for passing client
763      * information through proxies to a server, and should be used in
764      * a forward proxy configuation instead of X-Forwarded-*. See the
765      * ProxyVia option for details.
766      */
767
768     if (PROXYREQ_REVERSE == r->proxyreq) {
769         const char *buf;
770
771         /* Add X-Forwarded-For: so that the upstream has a chance to
772          * determine, where the original request came from.
773          */
774         apr_table_mergen(r->headers_in, "X-Forwarded-For",
775                          c->remote_ip);
776
777         /* Add X-Forwarded-Host: so that upstream knows what the
778          * original request hostname was.
779          */
780         if ((buf = apr_table_get(r->headers_in, "Host"))) {
781             apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
782         }
783
784         /* Add X-Forwarded-Server: so that upstream knows what the
785          * name of this proxy server is (if there are more than one)
786          * XXX: This duplicates Via: - do we strictly need it?
787          */
788         apr_table_mergen(r->headers_in, "X-Forwarded-Server",
789                          r->server->server_hostname);
790     }
791
792     proxy_run_fixups(r);
793     /*
794      * Make a copy of the headers_in table before clearing the connection
795      * headers as we need the connection headers later in the http output
796      * filter to prepare the correct response headers.
797      *
798      * Note: We need to take r->pool for apr_table_copy as the key / value
799      * pairs in r->headers_in have been created out of r->pool and
800      * p might be (and actually is) a longer living pool.
801      * This would trigger the bad pool ancestry abort in apr_table_copy if
802      * apr is compiled with APR_POOL_DEBUG.
803      */
804     headers_in_copy = apr_table_copy(r->pool, r->headers_in);
805     ap_proxy_clear_connection(p, headers_in_copy);
806     /* send request headers */
807     headers_in_array = apr_table_elts(headers_in_copy);
808     headers_in = (const apr_table_entry_t *) headers_in_array->elts;
809     for (counter = 0; counter < headers_in_array->nelts; counter++) {
810         if (headers_in[counter].key == NULL
811              || headers_in[counter].val == NULL
812
813             /* Already sent */
814              || !strcasecmp(headers_in[counter].key, "Host")
815
816             /* Clear out hop-by-hop request headers not to send
817              * RFC2616 13.5.1 says we should strip these headers
818              */
819              || !strcasecmp(headers_in[counter].key, "Keep-Alive")
820              || !strcasecmp(headers_in[counter].key, "TE")
821              || !strcasecmp(headers_in[counter].key, "Trailer")
822              || !strcasecmp(headers_in[counter].key, "Upgrade")
823
824              ) {
825             continue;
826         }
827         /* Do we want to strip Proxy-Authorization ?
828          * If we haven't used it, then NO
829          * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
830          * So let's make it configurable by env.
831          */
832         if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) {
833             if (r->user != NULL) { /* we've authenticated */
834                 if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
835                     continue;
836                 }
837             }
838         }
839
840
841         /* Skip Transfer-Encoding and Content-Length for now.
842          */
843         if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) {
844             old_te_val = headers_in[counter].val;
845             continue;
846         }
847         if (!strcasecmp(headers_in[counter].key, "Content-Length")) {
848             old_cl_val = headers_in[counter].val;
849             continue;
850         }
851
852         /* for sub-requests, ignore freshness/expiry headers */
853         if (r->main) {
854             if (    !strcasecmp(headers_in[counter].key, "If-Match")
855                  || !strcasecmp(headers_in[counter].key, "If-Modified-Since")
856                  || !strcasecmp(headers_in[counter].key, "If-Range")
857                  || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since")
858                  || !strcasecmp(headers_in[counter].key, "If-None-Match")) {
859                 continue;
860             }
861         }
862
863         buf = apr_pstrcat(p, headers_in[counter].key, ": ",
864                           headers_in[counter].val, CRLF,
865                           NULL);
866         ap_xlate_proto_to_ascii(buf, strlen(buf));
867         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
868         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
869     }
870
871     /* We have headers, let's figure out our request body... */
872     input_brigade = apr_brigade_create(p, bucket_alloc);
873
874     /* sub-requests never use keepalives, and mustn't pass request bodies.
875      * Because the new logic looks at input_brigade, we will self-terminate
876      * input_brigade and jump past all of the request body logic...
877      * Reading anything with ap_get_brigade is likely to consume the
878      * main request's body or read beyond EOS - which would be unplesant.
879      */
880     if (r->main) {
881         /* XXX: Why DON'T sub-requests use keepalives? */
882         p_conn->close++;
883         if (old_cl_val) {
884             old_cl_val = NULL;
885             apr_table_unset(r->headers_in, "Content-Length");
886         }
887         if (old_te_val) {
888             old_te_val = NULL;
889             apr_table_unset(r->headers_in, "Transfer-Encoding");
890         }
891         rb_method = RB_STREAM_CL;
892         e = apr_bucket_eos_create(input_brigade->bucket_alloc);
893         APR_BRIGADE_INSERT_TAIL(input_brigade, e);
894         goto skip_body;
895     }
896
897     /* WE only understand chunked.  Other modules might inject
898      * (and therefore, decode) other flavors but we don't know
899      * that the can and have done so unless they they remove
900      * their decoding from the headers_in T-E list.
901      * XXX: Make this extensible, but in doing so, presume the
902      * encoding has been done by the extensions' handler, and
903      * do not modify add_te_chunked's logic
904      */
905     if (old_te_val && strcmp(old_te_val, "chunked") != 0) {
906         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
907                      "proxy: %s Transfer-Encoding is not supported",
908                      old_te_val);
909         return APR_EINVAL;
910     }
911
912     if (old_cl_val && old_te_val) {
913         ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_ENOTIMPL, r->server,
914                      "proxy: client %s (%s) requested Transfer-Encoding "
915                      "chunked body with Content-Length (C-L ignored)",
916                      c->remote_ip, c->remote_host ? c->remote_host: "");
917         apr_table_unset(r->headers_in, "Content-Length");
918         old_cl_val = NULL;
919         origin->keepalive = AP_CONN_CLOSE;
920         p_conn->close++;
921     }
922
923     /* Prefetch MAX_MEM_SPOOL bytes
924      *
925      * This helps us avoid any election of C-L v.s. T-E
926      * request bodies, since we are willing to keep in
927      * memory this much data, in any case.  This gives
928      * us an instant C-L election if the body is of some
929      * reasonable size.
930      */
931     temp_brigade = apr_brigade_create(p, bucket_alloc);
932     do {
933         status = ap_get_brigade(r->input_filters, temp_brigade,
934                                 AP_MODE_READBYTES, APR_BLOCK_READ,
935                                 MAX_MEM_SPOOL - bytes_read);
936         if (status != APR_SUCCESS) {
937             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
938                          "proxy: prefetch request body failed to %pI (%s)"
939                          " from %s (%s)",
940                          p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
941                          c->remote_ip, c->remote_host ? c->remote_host: "");
942             return status;
943         }
944
945         apr_brigade_length(temp_brigade, 1, &bytes);
946         bytes_read += bytes;
947
948         /*
949          * Save temp_brigade in input_brigade. (At least) in the SSL case
950          * temp_brigade contains transient buckets whose data would get
951          * overwritten during the next call of ap_get_brigade in the loop.
952          * ap_save_brigade ensures these buckets to be set aside.
953          * Calling ap_save_brigade with NULL as filter is OK, because
954          * input_brigade already has been created and does not need to get
955          * created by ap_save_brigade.
956          */
957         status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p);
958         if (status != APR_SUCCESS) {
959             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
960                          "proxy: processing prefetched request body failed"
961                          " to %pI (%s) from %s (%s)",
962                          p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
963                          c->remote_ip, c->remote_host ? c->remote_host: "");
964             return status;
965         }
966
967     /* Ensure we don't hit a wall where we have a buffer too small
968      * for ap_get_brigade's filters to fetch us another bucket,
969      * surrender once we hit 80 bytes less than MAX_MEM_SPOOL
970      * (an arbitrary value.)
971      */
972     } while ((bytes_read < MAX_MEM_SPOOL - 80)
973               && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)));
974
975     /* Use chunked request body encoding or send a content-length body?
976      *
977      * Prefer C-L when:
978      *
979      *   We have no request body (handled by RB_STREAM_CL)
980      *
981      *   We have a request body length <= MAX_MEM_SPOOL
982      *
983      *   The administrator has setenv force-proxy-request-1.0
984      *
985      *   The client sent a C-L body, and the administrator has
986      *   not setenv proxy-sendchunked or has set setenv proxy-sendcl
987      *
988      *   The client sent a T-E body, and the administrator has
989      *   setenv proxy-sendcl, and not setenv proxy-sendchunked
990      *
991      * If both proxy-sendcl and proxy-sendchunked are set, the
992      * behavior is the same as if neither were set, large bodies
993      * that can't be read will be forwarded in their original
994      * form of C-L, or T-E.
995      *
996      * To ensure maximum compatibility, setenv proxy-sendcl
997      * To reduce server resource use,   setenv proxy-sendchunked
998      *
999      * Then address specific servers with conditional setenv
1000      * options to restore the default behavior where desireable.
1001      *
1002      * We have to compute content length by reading the entire request
1003      * body; if request body is not small, we'll spool the remaining
1004      * input to a temporary file.  Chunked is always preferable.
1005      *
1006      * We can only trust the client-provided C-L if the T-E header
1007      * is absent, and the filters are unchanged (the body won't
1008      * be resized by another content filter).
1009      */
1010     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
1011         /* The whole thing fit, so our decision is trivial, use
1012          * the filtered bytes read from the client for the request
1013          * body Content-Length.
1014          *
1015          * If we expected no body, and read no body, do not set
1016          * the Content-Length.
1017          */
1018         if (old_cl_val || old_te_val || bytes_read) {
1019             old_cl_val = apr_off_t_toa(r->pool, bytes_read);
1020         }
1021         rb_method = RB_STREAM_CL;
1022     }
1023     else if (old_te_val) {
1024         if (force10
1025              || (apr_table_get(r->subprocess_env, "proxy-sendcl")
1026                   && !apr_table_get(r->subprocess_env, "proxy-sendchunks")
1027                   && !apr_table_get(r->subprocess_env, "proxy-sendchunked"))) {
1028             rb_method = RB_SPOOL_CL;
1029         }
1030         else {
1031             rb_method = RB_STREAM_CHUNKED;
1032         }
1033     }
1034     else if (old_cl_val) {
1035         if (r->input_filters == r->proto_input_filters) {
1036             rb_method = RB_STREAM_CL;
1037         }
1038         else if (!force10
1039                   && (apr_table_get(r->subprocess_env, "proxy-sendchunks")
1040                       || apr_table_get(r->subprocess_env, "proxy-sendchunked"))
1041                   && !apr_table_get(r->subprocess_env, "proxy-sendcl")) {
1042             rb_method = RB_STREAM_CHUNKED;
1043         }
1044         else {
1045             rb_method = RB_SPOOL_CL;
1046         }
1047     }
1048     else {
1049         /* This is an appropriate default; very efficient for no-body
1050          * requests, and has the behavior that it will not add any C-L
1051          * when the old_cl_val is NULL.
1052          */
1053         rb_method = RB_SPOOL_CL;
1054     }
1055
1056 /* Yes I hate gotos.  This is the subrequest shortcut */
1057 skip_body:
1058     /*
1059      * Handle Connection: header if we do HTTP/1.1 request:
1060      * If we plan to close the backend connection sent Connection: close
1061      * otherwise sent Connection: Keep-Alive.
1062      */
1063     if (!force10) {
1064         if (p_conn->close) {
1065             buf = apr_pstrdup(p, "Connection: close" CRLF);
1066         }
1067         else {
1068             buf = apr_pstrdup(p, "Connection: Keep-Alive" CRLF);
1069         }
1070         ap_xlate_proto_to_ascii(buf, strlen(buf));
1071         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
1072         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
1073     }
1074
1075     /* send the request body, if any. */
1076     switch(rb_method) {
1077     case RB_STREAM_CHUNKED:
1078         status = stream_reqbody_chunked(p, r, p_conn, origin, header_brigade,
1079                                         input_brigade);
1080         break;
1081     case RB_STREAM_CL:
1082         status = stream_reqbody_cl(p, r, p_conn, origin, header_brigade,
1083                                    input_brigade, old_cl_val);
1084         break;
1085     case RB_SPOOL_CL:
1086         status = spool_reqbody_cl(p, r, p_conn, origin, header_brigade,
1087                                   input_brigade, (old_cl_val != NULL)
1088                                               || (old_te_val != NULL)
1089                                               || (bytes_read > 0));
1090         break;
1091     default:
1092         /* shouldn't be possible */
1093         status = APR_EINVAL;
1094         break;
1095     }
1096
1097     if (status != APR_SUCCESS) {
1098         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
1099                      "proxy: pass request body failed to %pI (%s)"
1100                      " from %s (%s)",
1101                      p_conn->addr,
1102                      p_conn->hostname ? p_conn->hostname: "",
1103                      c->remote_ip,
1104                      c->remote_host ? c->remote_host: "");
1105         return status;
1106     }
1107
1108     return APR_SUCCESS;
1109 }
1110
1111 static void process_proxy_header(request_rec *r, proxy_dir_conf *c,
1112                                  const char *key, const char *value)
1113 {
1114     static const char *date_hdrs[]
1115         = { "Date", "Expires", "Last-Modified", NULL };
1116     static const struct {
1117         const char *name;
1118         ap_proxy_header_reverse_map_fn func;
1119     } transform_hdrs[] = {
1120         { "Location", ap_proxy_location_reverse_map },
1121         { "Content-Location", ap_proxy_location_reverse_map },
1122         { "URI", ap_proxy_location_reverse_map },
1123         { "Destination", ap_proxy_location_reverse_map },
1124         { "Set-Cookie", ap_proxy_cookie_reverse_map },
1125         { NULL, NULL }
1126     };
1127     int i;
1128     for (i = 0; date_hdrs[i]; ++i) {
1129         if (!strcasecmp(date_hdrs[i], key)) {
1130             apr_table_add(r->headers_out, key,
1131                           ap_proxy_date_canon(r->pool, value));
1132             return;
1133         }
1134     }
1135     for (i = 0; transform_hdrs[i].name; ++i) {
1136         if (!strcasecmp(transform_hdrs[i].name, key)) {
1137             apr_table_add(r->headers_out, key,
1138                           (*transform_hdrs[i].func)(r, c, value));
1139             return;
1140        }
1141     }
1142     apr_table_add(r->headers_out, key, value);
1143     return;
1144 }
1145
1146 /*
1147  * Note: pread_len is the length of the response that we've  mistakenly
1148  * read (assuming that we don't consider that an  error via
1149  * ProxyBadHeader StartBody). This depends on buffer actually being
1150  * local storage to the calling code in order for pread_len to make
1151  * any sense at all, since we depend on buffer still containing
1152  * what was read by ap_getline() upon return.
1153  */
1154 static void ap_proxy_read_headers(request_rec *r, request_rec *rr,
1155                                   char *buffer, int size,
1156                                   conn_rec *c, int *pread_len)
1157 {
1158     int len;
1159     char *value, *end;
1160     char field[MAX_STRING_LEN];
1161     int saw_headers = 0;
1162     void *sconf = r->server->module_config;
1163     proxy_server_conf *psc;
1164     proxy_dir_conf *dconf;
1165
1166     dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
1167     psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
1168
1169     r->headers_out = apr_table_make(r->pool, 20);
1170     *pread_len = 0;
1171
1172     /*
1173      * Read header lines until we get the empty separator line, a read error,
1174      * the connection closes (EOF), or we timeout.
1175      */
1176     while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
1177
1178         if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
1179
1180             /* We may encounter invalid headers, usually from buggy
1181              * MS IIS servers, so we need to determine just how to handle
1182              * them. We can either ignore them, assume that they mark the
1183              * start-of-body (eg: a missing CRLF) or (the default) mark
1184              * the headers as totally bogus and return a 500. The sole
1185              * exception is an extra "HTTP/1.0 200, OK" line sprinkled
1186              * in between the usual MIME headers, which is a favorite
1187              * IIS bug.
1188              */
1189              /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
1190
1191             if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
1192                 if (psc->badopt == bad_error) {
1193                     /* Nope, it wasn't even an extra HTTP header. Give up. */
1194                     r->headers_out = NULL;
1195                     return ;
1196                 }
1197                 else if (psc->badopt == bad_body) {
1198                     /* if we've already started loading headers_out, then
1199                      * return what we've accumulated so far, in the hopes
1200                      * that they are useful; also note that we likely pre-read
1201                      * the first line of the response.
1202                      */
1203                     if (saw_headers) {
1204                         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
1205                          "proxy: Starting body due to bogus non-header in headers "
1206                          "returned by %s (%s)", r->uri, r->method);
1207                         *pread_len = len;
1208                         return ;
1209                     } else {
1210                          ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
1211                          "proxy: No HTTP headers "
1212                          "returned by %s (%s)", r->uri, r->method);
1213                         return ;
1214                     }
1215                 }
1216             }
1217             /* this is the psc->badopt == bad_ignore case */
1218             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
1219                          "proxy: Ignoring bogus HTTP header "
1220                          "returned by %s (%s)", r->uri, r->method);
1221             continue;
1222         }
1223
1224         *value = '\0';
1225         ++value;
1226         /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
1227          * wrong... and so are many others probably.
1228          */
1229         while (apr_isspace(*value))
1230             ++value;            /* Skip to start of value   */
1231
1232         /* should strip trailing whitespace as well */
1233         for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --
1234 end)
1235             *end = '\0';
1236
1237         /* make sure we add so as not to destroy duplicated headers
1238          * Modify headers requiring canonicalisation and/or affected
1239          * by ProxyPassReverse and family with process_proxy_header
1240          */
1241         process_proxy_header(r, dconf, buffer, value) ;
1242         saw_headers = 1;
1243
1244         /* the header was too long; at the least we should skip extra data */
1245         if (len >= size - 1) {
1246             while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
1247                     >= MAX_STRING_LEN - 1) {
1248                 /* soak up the extra data */
1249             }
1250             if (len == 0) /* time to exit the larger loop as well */
1251                 break;
1252         }
1253     }
1254 }
1255
1256
1257
1258 static int addit_dammit(void *v, const char *key, const char *val)
1259 {
1260     apr_table_addn(v, key, val);
1261     return 1;
1262 }
1263
1264 static
1265 apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, request_rec *r,
1266                              int fold, int *writen)
1267 {
1268     char *tmp_s = s;
1269     apr_status_t rv;
1270     apr_size_t len;
1271
1272     rv = ap_rgetline(&tmp_s, n, &len, r, fold, bb);
1273     apr_brigade_cleanup(bb);
1274
1275     if (rv == APR_SUCCESS) {
1276         *writen = (int) len;
1277     } else if (rv == APR_ENOSPC) {
1278         *writen = n;
1279     } else {
1280         *writen = -1;
1281     }
1282
1283     return rv;
1284 }
1285
1286 static
1287 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
1288                                             proxy_conn_rec *backend,
1289                                             conn_rec *origin,
1290                                             proxy_server_conf *conf,
1291                                             char *server_portstr) {
1292     conn_rec *c = r->connection;
1293     char buffer[HUGE_STRING_LEN];
1294     const char *buf;
1295     char keepchar;
1296     request_rec *rp;
1297     apr_bucket *e;
1298     apr_bucket_brigade *bb, *tmp_bb;
1299     int len, backasswards;
1300     int interim_response; /* non-zero whilst interim 1xx responses
1301                            * are being read. */
1302     int pread_len = 0;
1303     apr_table_t *save_table;
1304     int backend_broke = 0;
1305     static const char *hop_by_hop_hdrs[] =
1306         {"Keep-Alive", "Proxy-Authenticate", "TE", "Trailer", "Upgrade", NULL};
1307     int i;
1308
1309     bb = apr_brigade_create(p, c->bucket_alloc);
1310
1311     /* Get response from the remote server, and pass it up the
1312      * filter chain
1313      */
1314
1315     rp = ap_proxy_make_fake_req(origin, r);
1316     /* In case anyone needs to know, this is a fake request that is really a
1317      * response.
1318      */
1319     rp->proxyreq = PROXYREQ_RESPONSE;
1320     tmp_bb = apr_brigade_create(p, c->bucket_alloc);
1321     do {
1322         apr_status_t rc;
1323
1324         apr_brigade_cleanup(bb);
1325
1326         rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), rp, 0, &len);
1327         if (len == 0) {
1328             /* handle one potential stray CRLF */
1329             rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), rp, 0, &len);
1330         }
1331         if (len <= 0) {
1332             ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
1333                           "proxy: error reading status line from remote "
1334                           "server %s", backend->hostname);
1335             return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1336                                  "Error reading from remote server");
1337         }
1338         /* XXX: Is this a real headers length send from remote? */
1339         backend->worker->s->read += len;
1340
1341         /* Is it an HTTP/1 response?
1342          * This is buggy if we ever see an HTTP/1.10
1343          */
1344         if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
1345             int major, minor;
1346
1347             if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
1348                 major = 1;
1349                 minor = 1;
1350             }
1351             /* If not an HTTP/1 message or
1352              * if the status line was > 8192 bytes
1353              */
1354             else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) {
1355                 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1356                 apr_pstrcat(p, "Corrupt status line returned by remote "
1357                             "server: ", buffer, NULL));
1358             }
1359             backasswards = 0;
1360
1361             keepchar = buffer[12];
1362             buffer[12] = '\0';
1363             r->status = atoi(&buffer[9]);
1364
1365             if (keepchar != '\0') {
1366                 buffer[12] = keepchar;
1367             } else {
1368                 /* 2616 requires the space in Status-Line; the origin
1369                  * server may have sent one but ap_rgetline_core will
1370                  * have stripped it. */
1371                 buffer[12] = ' ';
1372                 buffer[13] = '\0';
1373             }
1374             r->status_line = apr_pstrdup(p, &buffer[9]);
1375
1376
1377             /* read the headers. */
1378             /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
1379             /* Also, take care with headers with multiple occurences. */
1380
1381             /* First, tuck away all already existing cookies */
1382             save_table = apr_table_make(r->pool, 2);
1383             apr_table_do(addit_dammit, save_table, r->headers_out,
1384                          "Set-Cookie", NULL);
1385
1386             /* shove the headers direct into r->headers_out */
1387             ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin,
1388                                   &pread_len);
1389
1390             if (r->headers_out == NULL) {
1391                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
1392                              r->server, "proxy: bad HTTP/%d.%d header "
1393                              "returned by %s (%s)", major, minor, r->uri,
1394                              r->method);
1395                 backend->close += 1;
1396                 /*
1397                  * ap_send_error relies on a headers_out to be present. we
1398                  * are in a bad position here.. so force everything we send out
1399                  * to have nothing to do with the incoming packet
1400                  */
1401                 r->headers_out = apr_table_make(r->pool,1);
1402                 r->status = HTTP_BAD_GATEWAY;
1403                 r->status_line = "bad gateway";
1404                 return r->status;
1405             }
1406
1407             /* Now, add in the just read cookies */
1408             apr_table_do(addit_dammit, save_table, r->headers_out,
1409                          "Set-Cookie", NULL);
1410
1411             /* and now load 'em all in */
1412             if (!apr_is_empty_table(save_table)) {
1413                 apr_table_unset(r->headers_out, "Set-Cookie");
1414                 r->headers_out = apr_table_overlay(r->pool,
1415                                                    r->headers_out,
1416                                                    save_table);
1417             }
1418
1419             /* can't have both Content-Length and Transfer-Encoding */
1420             if (apr_table_get(r->headers_out, "Transfer-Encoding")
1421                     && apr_table_get(r->headers_out, "Content-Length")) {
1422                 /*
1423                  * 2616 section 4.4, point 3: "if both Transfer-Encoding
1424                  * and Content-Length are received, the latter MUST be
1425                  * ignored";
1426                  *
1427                  * To help mitigate HTTP Splitting, unset Content-Length
1428                  * and shut down the backend server connection
1429                  * XXX: We aught to treat such a response as uncachable
1430                  */
1431                 apr_table_unset(r->headers_out, "Content-Length");
1432                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1433                              "proxy: server %s returned Transfer-Encoding"
1434                              " and Content-Length", backend->hostname);
1435                 backend->close += 1;
1436             }
1437
1438             /* strip connection listed hop-by-hop headers from response */
1439             backend->close += ap_proxy_liststr(apr_table_get(r->headers_out,
1440                                                              "Connection"),
1441                                               "close");
1442             ap_proxy_clear_connection(p, r->headers_out);
1443             if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
1444                 ap_set_content_type(r, apr_pstrdup(p, buf));
1445             }
1446             ap_proxy_pre_http_request(origin,rp);
1447
1448             /* Clear hop-by-hop headers */
1449             for (i=0; hop_by_hop_hdrs[i]; ++i) {
1450                 apr_table_unset(r->headers_out, hop_by_hop_hdrs[i]);
1451             }
1452             /* Delete warnings with wrong date */
1453             r->headers_out = ap_proxy_clean_warnings(p, r->headers_out);
1454
1455             /* handle Via header in response */
1456             if (conf->viaopt != via_off && conf->viaopt != via_block) {
1457                 const char *server_name = ap_get_server_name(r);
1458                 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
1459                  * then the server name returned by ap_get_server_name() is the
1460                  * origin server name (which does make too much sense with Via: headers)
1461                  * so we use the proxy vhost's name instead.
1462                  */
1463                 if (server_name == r->hostname)
1464                     server_name = r->server->server_hostname;
1465                 /* create a "Via:" response header entry and merge it */
1466                 apr_table_addn(r->headers_out, "Via",
1467                                (conf->viaopt == via_full)
1468                                      ? apr_psprintf(p, "%d.%d %s%s (%s)",
1469                                            HTTP_VERSION_MAJOR(r->proto_num),
1470                                            HTTP_VERSION_MINOR(r->proto_num),
1471                                            server_name,
1472                                            server_portstr,
1473                                            AP_SERVER_BASEVERSION)
1474                                      : apr_psprintf(p, "%d.%d %s%s",
1475                                            HTTP_VERSION_MAJOR(r->proto_num),
1476                                            HTTP_VERSION_MINOR(r->proto_num),
1477                                            server_name,
1478                                            server_portstr)
1479                 );
1480             }
1481
1482             /* cancel keepalive if HTTP/1.0 or less */
1483             if ((major < 1) || (minor < 1)) {
1484                 backend->close += 1;
1485                 origin->keepalive = AP_CONN_CLOSE;
1486             }
1487         } else {
1488             /* an http/0.9 response */
1489             backasswards = 1;
1490             r->status = 200;
1491             r->status_line = "200 OK";
1492             backend->close += 1;
1493         }
1494
1495         interim_response = ap_is_HTTP_INFO(r->status);
1496         if (interim_response) {
1497             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1498                          "proxy: HTTP: received interim %d response",
1499                          r->status);
1500         }
1501         /* Moved the fixups of Date headers and those affected by
1502          * ProxyPassReverse/etc from here to ap_proxy_read_headers
1503          */
1504
1505         if ((r->status == 401) && (conf->error_override)) {
1506             const char *buf;
1507             const char *wa = "WWW-Authenticate";
1508             if ((buf = apr_table_get(r->headers_out, wa))) {
1509                 apr_table_set(r->err_headers_out, wa, buf);
1510             } else {
1511                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1512                              "proxy: origin server sent 401 without WWW-Authenticate header");
1513             }
1514         }
1515
1516         r->sent_bodyct = 1;
1517         /*
1518          * Is it an HTTP/0.9 response or did we maybe preread the 1st line of
1519          * the response? If so, load the extra data. These are 2 mutually
1520          * exclusive possibilities, that just happen to require very
1521          * similar behavior.
1522          */
1523         if (backasswards || pread_len) {
1524             apr_ssize_t cntr = (apr_ssize_t)pread_len;
1525             if (backasswards) {
1526                 /*@@@FIXME:
1527                  * At this point in response processing of a 0.9 response,
1528                  * we don't know yet whether data is binary or not.
1529                  * mod_charset_lite will get control later on, so it cannot
1530                  * decide on the conversion of this buffer full of data.
1531                  * However, chances are that we are not really talking to an
1532                  * HTTP/0.9 server, but to some different protocol, therefore
1533                  * the best guess IMHO is to always treat the buffer as "text/x":
1534                  */
1535                 ap_xlate_proto_to_ascii(buffer, len);
1536                 cntr = (apr_ssize_t)len;
1537             }
1538             e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
1539             APR_BRIGADE_INSERT_TAIL(bb, e);
1540         }
1541
1542         /* send body - but only if a body is expected */
1543         if ((!r->header_only) &&                   /* not HEAD request */
1544             !interim_response &&                   /* not any 1xx response */
1545             (r->status != HTTP_NO_CONTENT) &&      /* not 204 */
1546             (r->status != HTTP_NOT_MODIFIED)) {    /* not 304 */
1547
1548             /* We need to copy the output headers and treat them as input
1549              * headers as well.  BUT, we need to do this before we remove
1550              * TE, so that they are preserved accordingly for
1551              * ap_http_filter to know where to end.
1552              */
1553             rp->headers_in = apr_table_copy(r->pool, r->headers_out);
1554
1555             apr_table_unset(r->headers_out,"Transfer-Encoding");
1556
1557             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1558                          "proxy: start body send");
1559
1560             /*
1561              * if we are overriding the errors, we can't put the content
1562              * of the page into the brigade
1563              */
1564             if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) {
1565                 /* read the body, pass it to the output filters */
1566                 apr_read_type_e mode = APR_NONBLOCK_READ;
1567                 int finish = FALSE;
1568
1569                 do {
1570                     apr_off_t readbytes;
1571                     apr_status_t rv;
1572
1573                     rv = ap_get_brigade(rp->input_filters, bb,
1574                                         AP_MODE_READBYTES, mode,
1575                                         conf->io_buffer_size);
1576
1577                     /* ap_get_brigade will return success with an empty brigade
1578                      * for a non-blocking read which would block: */
1579                     if (APR_STATUS_IS_EAGAIN(rv)
1580                         || (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))) {
1581                         /* flush to the client and switch to blocking mode */
1582                         e = apr_bucket_flush_create(c->bucket_alloc);
1583                         APR_BRIGADE_INSERT_TAIL(bb, e);
1584                         if (ap_pass_brigade(r->output_filters, bb)
1585                             || c->aborted) {
1586                             backend->close = 1;
1587                             break;
1588                         }
1589                         apr_brigade_cleanup(bb);
1590                         mode = APR_BLOCK_READ;
1591                         continue;
1592                     }
1593                     else if (rv == APR_EOF) {
1594                         break;
1595                     }
1596                     else if (rv != APR_SUCCESS) {
1597                         /* In this case, we are in real trouble because
1598                          * our backend bailed on us. Pass along a 502 error
1599                          * error bucket
1600                          */
1601                         ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c,
1602                                       "proxy: error reading response");
1603                         ap_proxy_backend_broke(r, bb);
1604                         ap_pass_brigade(r->output_filters, bb);
1605                         backend_broke = 1;
1606                         backend->close = 1;
1607                         break;
1608                     }
1609                     /* next time try a non-blocking read */
1610                     mode = APR_NONBLOCK_READ;
1611
1612                     apr_brigade_length(bb, 0, &readbytes);
1613                     backend->worker->s->read += readbytes;
1614 #if DEBUGGING
1615                     {
1616                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1617                                  r->server, "proxy (PID %d): readbytes: %#x",
1618                                  getpid(), readbytes);
1619                     }
1620 #endif
1621                     /* sanity check */
1622                     if (APR_BRIGADE_EMPTY(bb)) {
1623                         apr_brigade_cleanup(bb);
1624                         break;
1625                     }
1626
1627                     /* found the last brigade? */
1628                     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
1629                         /* signal that we must leave */
1630                         finish = TRUE;
1631                     }
1632
1633                     /* try send what we read */
1634                     if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS
1635                         || c->aborted) {
1636                         /* Ack! Phbtt! Die! User aborted! */
1637                         backend->close = 1;  /* this causes socket close below */
1638                         finish = TRUE;
1639                     }
1640
1641                     /* make sure we always clean up after ourselves */
1642                     apr_brigade_cleanup(bb);
1643
1644                 } while (!finish);
1645             }
1646             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1647                          "proxy: end body send");
1648         }
1649         else if (!interim_response) {
1650             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1651                          "proxy: header only");
1652
1653             /* Pass EOS bucket down the filter chain. */
1654             e = apr_bucket_eos_create(c->bucket_alloc);
1655             APR_BRIGADE_INSERT_TAIL(bb, e);
1656             if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS
1657                 || c->aborted) {
1658                 /* Ack! Phbtt! Die! User aborted! */
1659                 backend->close = 1;  /* this causes socket close below */
1660             }
1661
1662             apr_brigade_cleanup(bb);
1663         }
1664     } while (interim_response);
1665
1666     /* If our connection with the client is to be aborted, return DONE. */
1667     if (c->aborted || backend_broke) {
1668         return DONE;
1669     }
1670
1671     if (conf->error_override) {
1672         /* the code above this checks for 'OK' which is what the hook expects */
1673         if (!ap_is_HTTP_ERROR(r->status))
1674             return OK;
1675         else {
1676             /* clear r->status for override error, otherwise ErrorDocument
1677              * thinks that this is a recursive error, and doesn't find the
1678              * custom error page
1679              */
1680             int status = r->status;
1681             r->status = HTTP_OK;
1682             /* Discard body, if one is expected */
1683             if (!r->header_only && /* not HEAD request */
1684                 (status != HTTP_NO_CONTENT) && /* not 204 */
1685                 (status != HTTP_NOT_MODIFIED)) { /* not 304 */
1686                ap_discard_request_body(rp);
1687            }
1688             return status;
1689         }
1690     } else
1691         return OK;
1692 }
1693
1694 static
1695 apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r,
1696                                    proxy_conn_rec *backend)
1697 {
1698     ap_proxy_release_connection(scheme, backend, r->server);
1699     return OK;
1700 }
1701
1702 /*
1703  * This handles http:// URLs, and other URLs using a remote proxy over http
1704  * If proxyhost is NULL, then contact the server directly, otherwise
1705  * go via the proxy.
1706  * Note that if a proxy is used, then URLs other than http: can be accessed,
1707  * also, if we have trouble which is clearly specific to the proxy, then
1708  * we return DECLINED so that we can try another proxy. (Or the direct
1709  * route.)
1710  */
1711 static int proxy_http_handler(request_rec *r, proxy_worker *worker,
1712                               proxy_server_conf *conf,
1713                               char *url, const char *proxyname,
1714                               apr_port_t proxyport)
1715 {
1716     int status;
1717     char server_portstr[32];
1718     char *scheme;
1719     const char *proxy_function;
1720     const char *u;
1721     proxy_conn_rec *backend = NULL;
1722     int is_ssl = 0;
1723
1724     /* Note: Memory pool allocation.
1725      * A downstream keepalive connection is always connected to the existence
1726      * (or not) of an upstream keepalive connection. If this is not done then
1727      * load balancing against multiple backend servers breaks (one backend
1728      * server ends up taking 100% of the load), and the risk is run of
1729      * downstream keepalive connections being kept open unnecessarily. This
1730      * keeps webservers busy and ties up resources.
1731      *
1732      * As a result, we allocate all sockets out of the upstream connection
1733      * pool, and when we want to reuse a socket, we check first whether the
1734      * connection ID of the current upstream connection is the same as that
1735      * of the connection when the socket was opened.
1736      */
1737     apr_pool_t *p = r->connection->pool;
1738     conn_rec *c = r->connection;
1739     apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
1740
1741     /* find the scheme */
1742     u = strchr(url, ':');
1743     if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
1744        return DECLINED;
1745     if ((u - url) > 14)
1746         return HTTP_BAD_REQUEST;
1747     scheme = apr_pstrndup(c->pool, url, u - url);
1748     /* scheme is lowercase */
1749     ap_str_tolower(scheme);
1750     /* is it for us? */
1751     if (strcmp(scheme, "https") == 0) {
1752         if (!ap_proxy_ssl_enable(NULL)) {
1753             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1754                          "proxy: HTTPS: declining URL %s"
1755                          " (mod_ssl not configured?)", url);
1756             return DECLINED;
1757         }
1758         is_ssl = 1;
1759         proxy_function = "HTTPS";
1760     }
1761     else if (!(strcmp(scheme, "http") == 0 || (strcmp(scheme, "ftp") == 0 && proxyname))) {
1762         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1763                      "proxy: HTTP: declining URL %s", url);
1764         return DECLINED; /* only interested in HTTP, or FTP via proxy */
1765     }
1766     else {
1767         if (*scheme == 'h')
1768             proxy_function = "HTTP";
1769         else
1770             proxy_function = "FTP";
1771     }
1772     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1773              "proxy: HTTP: serving URL %s", url);
1774
1775
1776     /* create space for state information */
1777     if ((status = ap_proxy_acquire_connection(proxy_function, &backend,
1778                                               worker, r->server)) != OK)
1779         goto cleanup;
1780
1781
1782     backend->is_ssl = is_ssl;
1783     /*
1784      * TODO: Currently we cannot handle persistent SSL backend connections,
1785      * because we recreate backend->connection for each request and thus
1786      * try to initialize an already existing SSL connection. This does
1787      * not work.
1788      */
1789     if (is_ssl)
1790         backend->close = 1;
1791
1792     /* Step One: Determine Who To Connect To */
1793     if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend,
1794                                                 uri, &url, proxyname,
1795                                                 proxyport, server_portstr,
1796                                                 sizeof(server_portstr))) != OK)
1797         goto cleanup;
1798
1799     /* Step Two: Make the Connection */
1800     if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
1801         if (r->proxyreq == PROXYREQ_PROXY)
1802             status = HTTP_NOT_FOUND;
1803         else
1804             status = HTTP_SERVICE_UNAVAILABLE;
1805         goto cleanup;
1806     }
1807
1808     /* Step Three: Create conn_rec */
1809     if (!backend->connection) {
1810         if ((status = ap_proxy_connection_create(proxy_function, backend,
1811                                                  c, r->server)) != OK)
1812             goto cleanup;
1813     }
1814
1815     /* Step Four: Send the Request */
1816     if ((status = ap_proxy_http_request(p, r, backend, backend->connection,
1817                                         conf, uri, url, server_portstr)) != OK)
1818         goto cleanup;
1819
1820     /* Step Five: Receive the Response */
1821     if ((status = ap_proxy_http_process_response(p, r, backend,
1822                                                  backend->connection,
1823                                                  conf, server_portstr)) != OK)
1824         goto cleanup;
1825
1826     /* Step Six: Clean Up */
1827
1828 cleanup:
1829     if (backend) {
1830         if (status != OK)
1831             backend->close = 1;
1832         ap_proxy_http_cleanup(proxy_function, r, backend);
1833     }
1834     return status;
1835 }
1836 static apr_status_t warn_rx_free(void *p)
1837 {
1838     ap_pregfree((apr_pool_t*)p, warn_rx);
1839     return APR_SUCCESS;
1840 }
1841 static void ap_proxy_http_register_hook(apr_pool_t *p)
1842 {
1843     proxy_hook_scheme_handler(proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
1844     proxy_hook_canon_handler(proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
1845     warn_rx = ap_pregcomp(p, "[0-9]{3}[ \t]+[^ \t]+[ \t]+\"[^\"]*\"([ \t]+\"([^\"]+)\")?", 0);
1846     apr_pool_cleanup_register(p, p, warn_rx_free, NULL);
1847 }
1848
1849 module AP_MODULE_DECLARE_DATA proxy_http_module = {
1850     STANDARD20_MODULE_STUFF,
1851     NULL,              /* create per-directory config structure */
1852     NULL,              /* merge per-directory config structures */
1853     NULL,              /* create per-server config structure */
1854     NULL,              /* merge per-server config structures */
1855     NULL,              /* command apr_table_t */
1856     ap_proxy_http_register_hook/* register hooks */
1857 };
1858