]> granicus.if.org Git - apache/blob - modules/proxy/proxy_http.c
ff0207b8716abc2acfd0a1f893b893911f7b0549
[apache] / modules / proxy / proxy_http.c
1 /* Copyright 1999-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /* HTTP routines for Apache proxy */
17
18 #include "mod_proxy.h"
19
20 module AP_MODULE_DECLARE_DATA proxy_http_module;
21
22 int ap_proxy_http_canon(request_rec *r, char *url);
23 int ap_proxy_http_handler(request_rec *r, proxy_worker *worker,
24                           proxy_server_conf *conf,
25                           char *url, const char *proxyname, 
26                           apr_port_t proxyport);
27
28 static apr_status_t ap_proxy_http_cleanup(const char *scheme,
29                                           request_rec *r,
30                                           proxy_conn_rec *backend);
31
32 /*
33  * Canonicalise http-like URLs.
34  *  scheme is the scheme for the URL
35  *  url    is the URL starting with the first '/'
36  *  def_port is the default port for this scheme.
37  */
38 int ap_proxy_http_canon(request_rec *r, char *url)
39 {
40     char *host, *path, *search, sport[7];
41     const char *err;
42     const char *scheme;
43     apr_port_t port, def_port;
44
45     /* ap_port_of_scheme() */
46     if (strncasecmp(url, "http:", 5) == 0) {
47         url += 5;
48         scheme = "http";
49     }
50     else if (strncasecmp(url, "https:", 6) == 0) {
51         url += 6;
52         scheme = "https";
53     }
54     else {
55         return DECLINED;
56     }
57     def_port = apr_uri_port_of_scheme(scheme);
58
59     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
60              "proxy: HTTP: canonicalising URL %s", url);
61
62     /* do syntatic check.
63      * We break the URL into host, port, path, search
64      */
65     port = def_port;
66     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
67     if (err) {
68         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
69                       "error parsing URL %s: %s",
70                       url, err);
71         return HTTP_BAD_REQUEST;
72     }
73
74     /* now parse path/search args, according to rfc1738 */
75     /* N.B. if this isn't a true proxy request, then the URL _path_
76      * has already been decoded.  True proxy requests have r->uri
77      * == r->unparsed_uri, and no others have that property.
78      */
79     if (r->uri == r->unparsed_uri) {
80         search = strchr(url, '?');
81         if (search != NULL)
82             *(search++) = '\0';
83     }
84     else
85         search = r->args;
86
87     /* process path */
88     path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
89     if (path == NULL)
90         return HTTP_BAD_REQUEST;
91
92     if (port != def_port)
93         apr_snprintf(sport, sizeof(sport), ":%d", port);
94     else
95         sport[0] = '\0';
96
97     if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
98         host = apr_pstrcat(r->pool, "[", host, "]", NULL);
99     }
100     r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, 
101             "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
102     return OK;
103 }
104  
105 static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
106 {
107     struct proxy_alias *ent;
108     int i, l1, l2;
109     char *u;
110
111     /* XXX FIXME: Make sure this handled the ambiguous case of the :80
112      * after the hostname */
113
114     l1 = strlen(url);
115     ent = (struct proxy_alias *)conf->raliases->elts;
116     for (i = 0; i < conf->raliases->nelts; i++) {
117         l2 = strlen(ent[i].real);
118         if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) {
119             u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
120             return ap_construct_url(r->pool, u, r);
121         }
122     }
123     return url;
124 }
125 /* cookies are a bit trickier to match: we've got two substrings to worry
126  * about, and we can't just find them with strstr 'cos of case.  Regexp
127  * matching would be an easy fix, but for better consistency with all the
128  * other matches we'll refrain and use apr_strmatch to find path=/domain=
129  * and stick to plain strings for the config values.
130  */
131 static const char *proxy_cookie_reverse_map(request_rec *r,
132                           proxy_server_conf *conf, const char *str)
133 {
134     struct proxy_alias *ent;
135     size_t len = strlen(str);
136     const char* newpath = NULL ;
137     const char* newdomain = NULL ;
138     const char* pathp ;
139     const char* domainp ;
140     const char* pathe = NULL;
141     const char* domaine = NULL;
142     size_t l1, l2, poffs = 0, doffs = 0 ;
143     int i;
144     int ddiff = 0 ;
145     int pdiff = 0 ;
146     char* ret ;
147
148 /* find the match and replacement, but save replacing until we've done
149    both path and domain so we know the new strlen
150 */
151     if ( pathp = apr_strmatch(conf->cookie_path_str, str, len) , pathp ) {
152         pathp += 5 ;
153         poffs = pathp - str ;
154         pathe = ap_strchr_c(pathp, ';') ;
155         l1 = pathe ? (pathe-pathp) : strlen(pathp) ;
156         pathe = pathp + l1 ;
157         ent = (struct proxy_alias *)conf->cookie_paths->elts;
158         for (i = 0; i < conf->cookie_paths->nelts; i++) {
159             l2 = strlen(ent[i].fake);
160             if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
161                 newpath = ent[i].real ;
162                 pdiff = strlen(newpath) - l1 ;
163                 break ;
164             }
165         }
166     }
167     if ( domainp = apr_strmatch(conf->cookie_domain_str, str, len) , domainp ) {
168         domainp += 7 ;
169         doffs = domainp - str ;
170         domaine = ap_strchr_c(domainp, ';') ;
171         l1 = domaine ? (domaine-domainp) : strlen(domainp) ;
172         domaine = domainp + l1 ;
173         ent = (struct proxy_alias *)conf->cookie_domains->elts;
174         for (i = 0; i < conf->cookie_domains->nelts; i++) {
175             l2 = strlen(ent[i].fake);
176             if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
177                 newdomain = ent[i].real ;
178                 ddiff = strlen(newdomain) - l1 ;
179                 break ;
180             }
181         }
182     }
183     if ( newpath ) {
184         ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ;
185         l1 = strlen(newpath) ;
186         if ( newdomain ) {
187             l2 = strlen(newdomain) ;
188             if ( doffs > poffs ) {
189                 memcpy(ret, str, poffs) ;
190                 memcpy(ret+poffs, newpath, l1) ;
191                 memcpy(ret+poffs+l1, pathe, domainp-pathe) ;
192                 memcpy(ret+doffs+pdiff, newdomain, l2) ;
193                 strcpy(ret+doffs+pdiff+l2, domaine) ;
194             } else {
195                 memcpy(ret, str, doffs) ;
196                 memcpy(ret+doffs, newdomain, l2) ;
197                 memcpy(ret+doffs+l2, domaine, pathp-domaine) ;
198                 memcpy(ret+poffs+ddiff, newpath, l1) ;
199                 strcpy(ret+poffs+ddiff+l1, pathe) ;
200             }
201         } else {
202             memcpy(ret, str, poffs) ;
203             memcpy(ret+poffs, newpath, l1) ;
204             strcpy(ret+poffs+l1, pathe) ;
205         }
206     } else {
207         if ( newdomain ) {
208             ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ;
209             l2 = strlen(newdomain) ;
210             memcpy(ret, str, doffs) ;
211             memcpy(ret+doffs, newdomain, l2) ;
212             strcpy(ret+doffs+l2, domaine) ;
213         } else {
214             ret = (char*) str ;        /* no change */
215         }
216     }
217     return ret ;
218 }
219
220 /* Clear all connection-based headers from the incoming headers table */
221 static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
222 {
223     const char *name;
224     char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
225
226     apr_table_unset(headers, "Proxy-Connection");
227     if (!next)
228         return;
229
230     while (*next) {
231         name = next;
232         while (*next && !apr_isspace(*next) && (*next != ',')) {
233             ++next;
234         }
235         while (*next && (apr_isspace(*next) || (*next == ','))) {
236             *next = '\0';
237             ++next;
238         }
239         apr_table_unset(headers, name);
240     }
241     apr_table_unset(headers, "Connection");
242 }
243
244 static
245 apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
246                                    proxy_conn_rec *conn, conn_rec *origin, 
247                                    proxy_server_conf *conf,
248                                    apr_uri_t *uri,
249                                    char *url, char *server_portstr)
250 {
251     conn_rec *c = r->connection;
252     char *buf;
253     apr_bucket *e, *last_header_bucket = NULL;
254     const apr_array_header_t *headers_in_array;
255     const apr_table_entry_t *headers_in;
256     int counter, seen_eos, send_chunks;
257     apr_status_t status;
258     apr_bucket_brigade *header_brigade, *body_brigade, *input_brigade;
259
260     header_brigade = apr_brigade_create(p, origin->bucket_alloc);
261     body_brigade = apr_brigade_create(p, origin->bucket_alloc);
262     input_brigade = apr_brigade_create(p, origin->bucket_alloc);
263
264     /*
265      * Send the HTTP/1.1 request to the remote server
266      */
267
268     /* strip connection listed hop-by-hop headers from the request */
269     /* even though in theory a connection: close coming from the client
270      * should not affect the connection to the server, it's unlikely
271      * that subsequent client requests will hit this thread/process, so
272      * we cancel server keepalive if the client does.
273      */
274     conn->close += ap_proxy_liststr(apr_table_get(r->headers_in,
275                                                   "Connection"), "close");
276
277     /* sub-requests never use keepalives */
278     if (r->main) {
279         conn->close++;
280     }
281
282     ap_proxy_clear_connection(p, r->headers_in);
283     if (conn->close) {
284         apr_table_setn(r->headers_in, "Connection", "close");
285         origin->keepalive = AP_CONN_CLOSE;
286     }
287
288     /* By default, we can not send chunks. That means we must buffer
289      * the entire request before sending it along to ensure we have
290      * the correct Content-Length attached.
291      */
292     send_chunks = 0;
293
294     if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
295         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
296     } else {
297         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
298         if (apr_table_get(r->subprocess_env, "proxy-sendchunks")) {
299             send_chunks = 1;
300         }
301     }
302     if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
303         apr_table_unset(r->headers_in, "Connection");
304         origin->keepalive = AP_CONN_CLOSE;
305     }
306     ap_xlate_proto_to_ascii(buf, strlen(buf));
307     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
308     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
309     if (conf->preserve_host == 0) {
310         if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
311             buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str,
312                               CRLF, NULL);
313         } else {
314             buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
315         }
316     } 
317     else {
318         /* don't want to use r->hostname, as the incoming header might have a 
319          * port attached 
320          */
321         const char* hostname = apr_table_get(r->headers_in,"Host");        
322         if (!hostname) {
323             hostname =  r->server->server_hostname;
324             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
325                           "proxy: no HTTP 0.9 request (with no host line) "
326                           "on incoming request and preserve host set "
327                           "forcing hostname to be %s for uri %s", 
328                           hostname, 
329                           r->uri );
330         }
331         buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
332     }
333     ap_xlate_proto_to_ascii(buf, strlen(buf));
334     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);        
335     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
336
337     /* handle Via */
338     if (conf->viaopt == via_block) {
339         /* Block all outgoing Via: headers */
340         apr_table_unset(r->headers_in, "Via");
341     } else if (conf->viaopt != via_off) {
342         const char *server_name = ap_get_server_name(r);
343         /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
344          * then the server name returned by ap_get_server_name() is the
345          * origin server name (which does make too much sense with Via: headers)
346          * so we use the proxy vhost's name instead.
347          */
348         if (server_name == r->hostname)
349             server_name = r->server->server_hostname;
350         /* Create a "Via:" request header entry and merge it */
351         /* Generate outgoing Via: header with/without server comment: */
352         apr_table_mergen(r->headers_in, "Via",
353                          (conf->viaopt == via_full)
354                          ? apr_psprintf(p, "%d.%d %s%s (%s)",
355                                         HTTP_VERSION_MAJOR(r->proto_num),
356                                         HTTP_VERSION_MINOR(r->proto_num),
357                                         server_name, server_portstr,
358                                         AP_SERVER_BASEVERSION)
359                          : apr_psprintf(p, "%d.%d %s%s",
360                                         HTTP_VERSION_MAJOR(r->proto_num),
361                                         HTTP_VERSION_MINOR(r->proto_num),
362                                         server_name, server_portstr)
363         );
364     }
365
366     /* X-Forwarded-*: handling
367      *
368      * XXX Privacy Note:
369      * -----------------
370      *
371      * These request headers are only really useful when the mod_proxy
372      * is used in a reverse proxy configuration, so that useful info
373      * about the client can be passed through the reverse proxy and on
374      * to the backend server, which may require the information to
375      * function properly.
376      *
377      * In a forward proxy situation, these options are a potential
378      * privacy violation, as information about clients behind the proxy
379      * are revealed to arbitrary servers out there on the internet.
380      *
381      * The HTTP/1.1 Via: header is designed for passing client
382      * information through proxies to a server, and should be used in
383      * a forward proxy configuation instead of X-Forwarded-*. See the
384      * ProxyVia option for details.
385      */
386
387     if (PROXYREQ_REVERSE == r->proxyreq) {
388         const char *buf;
389
390         /* Add X-Forwarded-For: so that the upstream has a chance to
391          * determine, where the original request came from.
392          */
393         apr_table_mergen(r->headers_in, "X-Forwarded-For",
394                        r->connection->remote_ip);
395
396         /* Add X-Forwarded-Host: so that upstream knows what the
397          * original request hostname was.
398          */
399         if ((buf = apr_table_get(r->headers_in, "Host"))) {
400             apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
401         }
402
403         /* Add X-Forwarded-Server: so that upstream knows what the
404          * name of this proxy server is (if there are more than one)
405          * XXX: This duplicates Via: - do we strictly need it?
406          */
407         apr_table_mergen(r->headers_in, "X-Forwarded-Server",
408                        r->server->server_hostname);
409     }
410
411     /* send request headers */
412     proxy_run_fixups(r);
413     headers_in_array = apr_table_elts(r->headers_in);
414     headers_in = (const apr_table_entry_t *) headers_in_array->elts;
415     for (counter = 0; counter < headers_in_array->nelts; counter++) {
416         if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
417
418         /* Clear out hop-by-hop request headers not to send
419          * RFC2616 13.5.1 says we should strip these headers
420          */
421                 /* Already sent */
422             || !apr_strnatcasecmp(headers_in[counter].key, "Host")
423
424             || !apr_strnatcasecmp(headers_in[counter].key, "Keep-Alive")
425             || !apr_strnatcasecmp(headers_in[counter].key, "TE")
426             || !apr_strnatcasecmp(headers_in[counter].key, "Trailer")
427             || !apr_strnatcasecmp(headers_in[counter].key, "Transfer-Encoding")
428             || !apr_strnatcasecmp(headers_in[counter].key, "Upgrade")
429
430             /* We have no way of knowing whether this Content-Length will
431              * be accurate, so we must not include it.
432              */
433             || !apr_strnatcasecmp(headers_in[counter].key, "Content-Length")
434         /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
435          * suppressed if THIS server requested the authentication,
436          * not when a frontend proxy requested it!
437          *
438          * The solution to this problem is probably to strip out
439          * the Proxy-Authorisation header in the authorisation
440          * code itself, not here. This saves us having to signal
441          * somehow whether this request was authenticated or not.
442          */
443             || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authorization")
444             || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authenticate")) {
445             continue;
446         }
447         /* for sub-requests, ignore freshness/expiry headers */
448         if (r->main) {
449                 if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
450                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Match")
451                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Modified-Since")
452                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Range")
453                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Unmodified-Since")                     
454                      || !apr_strnatcasecmp(headers_in[counter].key, "If-None-Match")) {
455                     continue;
456                 }
457         }
458
459
460         buf = apr_pstrcat(p, headers_in[counter].key, ": ",
461                           headers_in[counter].val, CRLF,
462                           NULL);
463         ap_xlate_proto_to_ascii(buf, strlen(buf));
464         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
465         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
466     }
467
468     /* If we can send chunks, do so! */
469     if (send_chunks) {
470         const char te_hdr[] = "Transfer-Encoding: chunked" CRLF;
471
472         buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1);
473         ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1);
474
475         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
476         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
477     }
478     else {
479         last_header_bucket = APR_BRIGADE_LAST(header_brigade);
480     }
481
482     /* add empty line at the end of the headers */
483 #if APR_CHARSET_EBCDIC
484     e = apr_bucket_immortal_create("\015\012", 2, c->bucket_alloc);
485 #else
486     e = apr_bucket_immortal_create(CRLF, sizeof(CRLF)-1, c->bucket_alloc);
487 #endif
488     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
489     e = apr_bucket_flush_create(c->bucket_alloc);
490     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
491
492     if (send_chunks) {
493         status = ap_pass_brigade(origin->output_filters, header_brigade);
494
495         if (status != APR_SUCCESS) {
496             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
497                          "proxy: request failed to %pI (%s)",
498                          conn->worker->cp->addr, conn->hostname);
499             return status;
500         }
501     }
502
503     /* send the request data, if any. */
504     seen_eos = 0;
505     do {
506         char chunk_hdr[20];  /* must be here due to transient bucket. */
507
508         status = ap_get_brigade(r->input_filters, input_brigade,
509                                 AP_MODE_READBYTES, APR_BLOCK_READ,
510                                 HUGE_STRING_LEN);
511
512         if (status != APR_SUCCESS) {
513             return status;
514         }
515
516         /* If this brigade contain EOS, either stop or remove it. */
517         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
518             seen_eos = 1;
519
520             /* As a shortcut, if this brigade is simply an EOS bucket,
521              * don't send anything down the filter chain.
522              */
523             if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) {
524                 break;
525             }
526
527             /* We can't pass this EOS to the output_filters. */
528             e = APR_BRIGADE_LAST(input_brigade);
529             apr_bucket_delete(e);
530         }
531
532         if (send_chunks) {
533 #define ASCII_CRLF  "\015\012"
534 #define ASCII_ZERO  "\060"
535             apr_size_t hdr_len;
536             apr_off_t bytes;
537
538             apr_brigade_length(input_brigade, 1, &bytes);
539
540             hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
541                                    "%" APR_UINT64_T_HEX_FMT CRLF, 
542                                    (apr_uint64_t)bytes);
543
544             ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
545             e = apr_bucket_transient_create(chunk_hdr, hdr_len,
546                                             body_brigade->bucket_alloc);
547             APR_BRIGADE_INSERT_HEAD(input_brigade, e);
548
549             /*
550              * Append the end-of-chunk CRLF
551              */
552             e = apr_bucket_immortal_create(ASCII_CRLF, 2, c->bucket_alloc);
553             APR_BRIGADE_INSERT_TAIL(input_brigade, e);
554         }
555         else {
556             /* The send_chunks case does not need to be setaside, but this
557              * case does because we may have transient buckets that may get
558              * overwritten in the next iteration of the loop.
559              */
560             e = APR_BRIGADE_FIRST(input_brigade);
561             while (e != APR_BRIGADE_SENTINEL(input_brigade)) {
562                 apr_bucket_setaside(e, p);
563                 e = APR_BUCKET_NEXT(e);
564             }
565         }
566
567         e = apr_bucket_flush_create(c->bucket_alloc);
568         APR_BRIGADE_INSERT_TAIL(input_brigade, e);
569
570         APR_BRIGADE_CONCAT(body_brigade, input_brigade);
571
572         if (send_chunks) {
573             status = ap_pass_brigade(origin->output_filters, body_brigade);
574
575             if (status != APR_SUCCESS) {
576                 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
577                              "proxy: pass request data failed to %pI (%s)",
578                          conn->worker->cp->addr, conn->hostname);
579                 return status;
580             }
581
582             apr_brigade_cleanup(body_brigade);
583         }
584
585     } while (!seen_eos);
586
587     if (send_chunks) {
588         e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
589                                        /* <trailers> */
590                                        ASCII_CRLF, 5, c->bucket_alloc);
591         APR_BRIGADE_INSERT_TAIL(body_brigade, e);
592     }
593
594     if (!send_chunks) {
595         apr_off_t bytes;
596
597         apr_brigade_length(body_brigade, 1, &bytes);
598
599         if (bytes) {
600             const char *cl_hdr = "Content-Length", *cl_val;
601             cl_val = apr_off_t_toa(p, bytes);
602             buf = apr_pstrcat(p, cl_hdr, ": ", cl_val, CRLF, NULL);
603             ap_xlate_proto_to_ascii(buf, strlen(buf));
604             e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
605             APR_BUCKET_INSERT_AFTER(last_header_bucket, e);
606         }
607         else {
608             /* A client might really have sent a C-L of 0.  Pass it on. */
609             const char *cl_hdr = "Content-Length", *cl_val;
610
611             cl_val = apr_table_get(r->headers_in, cl_hdr);
612             if (cl_val && cl_val[0] == '0' && cl_val[1] == 0) {
613                 buf = apr_pstrcat(p, cl_hdr, ": ", cl_val, CRLF, NULL);
614                 ap_xlate_proto_to_ascii(buf, strlen(buf));
615                 e = apr_bucket_pool_create(buf, strlen(buf), p,
616                                            c->bucket_alloc);
617                 APR_BUCKET_INSERT_AFTER(last_header_bucket, e);
618             }
619         }
620         status = ap_pass_brigade(origin->output_filters, header_brigade);
621         if (status != APR_SUCCESS) {
622             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
623                          "proxy: pass request data failed to %pI (%s)",
624                          conn->worker->cp->addr, conn->hostname);
625             return status;
626         }
627
628         apr_brigade_cleanup(header_brigade);
629     }
630
631     status = ap_pass_brigade(origin->output_filters, body_brigade);
632
633     if (status != APR_SUCCESS) {
634         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
635                      "proxy: pass request data failed to %pI (%s)",
636                       conn->worker->cp->addr, conn->hostname);
637         return status;
638     }
639  
640     apr_brigade_cleanup(body_brigade);
641
642     return APR_SUCCESS;
643 }
644 static void process_proxy_header(request_rec* r, proxy_server_conf* c,
645                       const char* key, const char* value)
646 {
647     static const char* date_hdrs[]
648         = { "Date", "Expires", "Last-Modified", NULL } ;
649     static const struct {
650         const char* name ;
651         const char* (*func)(request_rec*, proxy_server_conf*, const char*) ;
652     } transform_hdrs[] = {
653         { "Location", ap_proxy_location_reverse_map } ,
654         { "Content-Location", ap_proxy_location_reverse_map } ,
655         { "URI", ap_proxy_location_reverse_map } ,
656         { "Set-Cookie", proxy_cookie_reverse_map } ,
657         { NULL, NULL }
658     } ;
659     int i ;
660     for ( i = 0 ; date_hdrs[i] ; ++i ) {
661         if ( !strcasecmp(date_hdrs[i], key) ) {
662             apr_table_add(r->headers_out, key,
663                 ap_proxy_date_canon(r->pool, value)) ;
664             return ;
665         }
666     }
667     for ( i = 0 ; transform_hdrs[i].name ; ++i ) {
668         if ( !strcasecmp(transform_hdrs[i].name, key) ) {
669             apr_table_add(r->headers_out, key,
670                 (*transform_hdrs[i].func)(r, c, value)) ;
671             return ;
672        }
673     }
674     apr_table_add(r->headers_out, key, value) ;
675     return ;
676 }
677
678 static void ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c)
679 {
680     int len;
681     char *value, *end;
682     char field[MAX_STRING_LEN];
683     int saw_headers = 0;
684     void *sconf = r->server->module_config;
685     proxy_server_conf *psc;
686
687     psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
688
689     r->headers_out = apr_table_make(r->pool, 20);
690
691     /*
692      * Read header lines until we get the empty separator line, a read error,
693      * the connection closes (EOF), or we timeout.
694      */
695     while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
696
697         if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
698
699             /* We may encounter invalid headers, usually from buggy
700              * MS IIS servers, so we need to determine just how to handle
701              * them. We can either ignore them, assume that they mark the
702              * start-of-body (eg: a missing CRLF) or (the default) mark
703              * the headers as totally bogus and return a 500. The sole
704              * exception is an extra "HTTP/1.0 200, OK" line sprinkled
705              * in between the usual MIME headers, which is a favorite
706              * IIS bug.
707              */
708              /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
709
710             if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
711                 if (psc->badopt == bad_error) {
712                     /* Nope, it wasn't even an extra HTTP header. Give up. */
713                     return ;
714                 }
715                 else if (psc->badopt == bad_body) {
716                     /* if we've already started loading headers_out, then
717                      * return what we've accumulated so far, in the hopes
718                      * that they are useful. Otherwise, we completely bail.
719                      */
720                     /* FIXME: We've already scarfed the supposed 1st line of
721                      * the body, so the actual content may end up being bogus
722                      * as well. If the content is HTML, we may be lucky.
723                      */
724                     if (saw_headers) {
725                         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
726                          "proxy: Starting body due to bogus non-header in headers "
727                          "returned by %s (%s)", r->uri, r->method);
728                         return ;
729                     } else {
730                          ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
731                          "proxy: No HTTP headers "
732                          "returned by %s (%s)", r->uri, r->method);
733                         return ;
734                     }
735                 }
736             }
737             /* this is the psc->badopt == bad_ignore case */
738             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
739                          "proxy: Ignoring bogus HTTP header "
740                          "returned by %s (%s)", r->uri, r->method);
741             continue;
742         }
743
744         *value = '\0';
745         ++value;
746         /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
747          * wrong... and so are many others probably.
748          */
749         while (apr_isspace(*value))
750             ++value;            /* Skip to start of value   */
751
752         /* should strip trailing whitespace as well */
753         for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --
754 end)
755             *end = '\0';
756
757         /* make sure we add so as not to destroy duplicated headers
758          * Modify headers requiring canonicalisation and/or affected
759          * by ProxyPassReverse and family with process_proxy_header
760          */
761         process_proxy_header(r, psc, buffer, value) ;
762         saw_headers = 1;
763
764         /* the header was too long; at the least we should skip extra data */
765         if (len >= size - 1) {
766             while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
767                     >= MAX_STRING_LEN - 1) {
768                 /* soak up the extra data */
769             }
770             if (len == 0) /* time to exit the larger loop as well */
771                 break;
772         }
773     }
774 }
775
776
777
778 static int addit_dammit(void *v, const char *key, const char *val)
779 {
780     apr_table_addn(v, key, val);
781     return 1;
782 }
783
784 static
785 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
786                                             proxy_conn_rec *backend,
787                                             conn_rec *origin,
788                                             proxy_server_conf *conf,
789                                             char *server_portstr) {
790     conn_rec *c = r->connection;
791     char buffer[HUGE_STRING_LEN];
792     char keepchar;
793     request_rec *rp;
794     apr_bucket *e;
795     apr_bucket_brigade *bb;
796     int len, backasswards;
797     int interim_response; /* non-zero whilst interim 1xx responses
798                            * are being read. */
799     apr_table_t *save_table;
800
801     bb = apr_brigade_create(p, c->bucket_alloc);
802
803     /* Get response from the remote server, and pass it up the
804      * filter chain
805      */
806
807     rp = ap_proxy_make_fake_req(origin, r);
808     /* In case anyone needs to know, this is a fake request that is really a
809      * response.
810      */
811     rp->proxyreq = PROXYREQ_RESPONSE;
812
813     do {
814         apr_brigade_cleanup(bb);
815
816         len = ap_getline(buffer, sizeof(buffer), rp, 0);
817         if (len == 0) {
818             /* handle one potential stray CRLF */
819             len = ap_getline(buffer, sizeof(buffer), rp, 0);
820         }
821         if (len <= 0) {
822             ap_proxy_http_cleanup(NULL, r, backend);
823             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
824                           "proxy: error reading status line from remote "
825                           "server %s", backend->hostname);
826             return ap_proxyerror(r, HTTP_BAD_GATEWAY,
827                                  "Error reading from remote server");
828         }
829
830        /* Is it an HTTP/1 response?
831         * This is buggy if we ever see an HTTP/1.10
832         */
833         if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
834             int major, minor;
835
836             if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
837                 major = 1;
838                 minor = 1;
839             }
840             /* If not an HTTP/1 message or
841              * if the status line was > 8192 bytes
842              */
843             else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) {
844                 ap_proxy_http_cleanup(NULL, r, backend);
845                 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
846                 apr_pstrcat(p, "Corrupt status line returned by remote "
847                             "server: ", buffer, NULL));
848             }
849             backasswards = 0;
850
851             keepchar = buffer[12];
852             buffer[12] = '\0';
853             r->status = atoi(&buffer[9]);
854
855             if (keepchar != '\0') {
856                 buffer[12] = keepchar;
857             } else {
858                 /* 2616 requires the space in Status-Line; the origin
859                  * server may have sent one but ap_rgetline_core will
860                  * have stripped it. */
861                 buffer[12] = ' ';
862                 buffer[13] = '\0';
863             }
864             r->status_line = apr_pstrdup(p, &buffer[9]);
865             
866
867             /* read the headers. */
868             /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
869             /* Also, take care with headers with multiple occurences. */
870
871             /* First, tuck away all already existing cookies */
872             save_table = apr_table_make(r->pool, 2);
873             apr_table_do(addit_dammit, save_table, r->headers_out,
874                          "Set-Cookie", NULL);
875
876         /* shove the headers direct into r->headers_out */
877             ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin);
878
879             if (r->headers_out == NULL) {
880                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
881                              r->server, "proxy: bad HTTP/%d.%d header "
882                              "returned by %s (%s)", major, minor, r->uri,
883                              r->method);
884                 backend->close += 1;
885                 /*
886                  * ap_send_error relies on a headers_out to be present. we
887                  * are in a bad position here.. so force everything we send out
888                  * to have nothing to do with the incoming packet
889                  */
890                 r->headers_out = apr_table_make(r->pool,1);
891                 r->status = HTTP_BAD_GATEWAY;
892                 r->status_line = "bad gateway";
893                 return r->status;
894
895             } else {
896                 const char *buf;
897
898                 /* Now, add in the just read cookies */
899                 apr_table_do(addit_dammit, save_table, r->headers_out,
900                          "Set-Cookie", NULL);
901
902                 /* and now load 'em all in */
903                 if (!apr_is_empty_table(save_table)) {
904                     apr_table_unset(r->headers_out, "Set-Cookie");
905                     r->headers_out = apr_table_overlay(r->pool,
906                                                        r->headers_out,
907                                                        save_table);
908                 }
909                 
910                 /* strip connection listed hop-by-hop headers from response */
911                 backend->close += ap_proxy_liststr(apr_table_get(r->headers_out,
912                                                                  "Connection"),
913                                                   "close");
914                 ap_proxy_clear_connection(p, r->headers_out);
915                 if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
916                     ap_set_content_type(r, apr_pstrdup(p, buf));
917                 }            
918                 ap_proxy_pre_http_request(origin,rp);
919             }
920
921             /* handle Via header in response */
922             if (conf->viaopt != via_off && conf->viaopt != via_block) {
923                 const char *server_name = ap_get_server_name(r);
924                 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
925                  * then the server name returned by ap_get_server_name() is the
926                  * origin server name (which does make too much sense with Via: headers)
927                  * so we use the proxy vhost's name instead.
928                  */
929                 if (server_name == r->hostname)
930                     server_name = r->server->server_hostname;
931                 /* create a "Via:" response header entry and merge it */
932                 apr_table_mergen(r->headers_out, "Via",
933                                  (conf->viaopt == via_full)
934                                      ? apr_psprintf(p, "%d.%d %s%s (%s)",
935                                            HTTP_VERSION_MAJOR(r->proto_num),
936                                            HTTP_VERSION_MINOR(r->proto_num),
937                                            server_name,
938                                            server_portstr,
939                                            AP_SERVER_BASEVERSION)
940                                      : apr_psprintf(p, "%d.%d %s%s",
941                                            HTTP_VERSION_MAJOR(r->proto_num),
942                                            HTTP_VERSION_MINOR(r->proto_num),
943                                            server_name,
944                                            server_portstr)
945                 );
946             }
947
948             /* cancel keepalive if HTTP/1.0 or less */
949             if ((major < 1) || (minor < 1)) {
950                 backend->close += 1;
951                 origin->keepalive = AP_CONN_CLOSE;
952             }
953         } else {
954             /* an http/0.9 response */
955             backasswards = 1;
956             r->status = 200;
957             r->status_line = "200 OK";
958             backend->close += 1;
959         }
960
961         interim_response = ap_is_HTTP_INFO(r->status);
962         if (interim_response) {
963             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
964                          "proxy: HTTP: received interim %d response",
965                          r->status);
966         }
967         /* Moved the fixups of Date headers and those affected by
968          * ProxyPassReverse/etc from here to ap_proxy_read_headers
969          */
970
971         if ((r->status == 401) && (conf->error_override != 0)) {
972             const char *buf;
973             const char *wa = "WWW-Authenticate";
974             if ((buf = apr_table_get(r->headers_out, wa))) {
975                 apr_table_set(r->err_headers_out, wa, buf);
976             } else {
977                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
978                              "proxy: origin server sent 401 without WWW-Authenticate header");
979             }
980         }
981
982         r->sent_bodyct = 1;
983         /* Is it an HTTP/0.9 response? If so, send the extra data */
984         if (backasswards) {
985             apr_ssize_t cntr = len;
986             /*@@@FIXME:
987              * At this point in response processing of a 0.9 response,
988              * we don't know yet whether data is binary or not.
989              * mod_charset_lite will get control later on, so it cannot
990              * decide on the conversion of this buffer full of data.
991              * However, chances are that we are not really talking to an
992              * HTTP/0.9 server, but to some different protocol, therefore
993              * the best guess IMHO is to always treat the buffer as "text/x":
994              */
995             ap_xlate_proto_to_ascii(buffer, len);
996             e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
997             APR_BRIGADE_INSERT_TAIL(bb, e);
998         }
999
1000         /* send body - but only if a body is expected */
1001         if ((!r->header_only) &&                   /* not HEAD request */
1002             !interim_response &&                   /* not any 1xx response */
1003             (r->status != HTTP_NO_CONTENT) &&      /* not 204 */
1004             (r->status != HTTP_RESET_CONTENT) &&   /* not 205 */
1005             (r->status != HTTP_NOT_MODIFIED)) {    /* not 304 */
1006
1007             /* We need to copy the output headers and treat them as input
1008              * headers as well.  BUT, we need to do this before we remove
1009              * TE, so that they are preserved accordingly for
1010              * ap_http_filter to know where to end.
1011              */
1012             rp->headers_in = apr_table_copy(r->pool, r->headers_out);
1013
1014             apr_table_unset(r->headers_out,"Transfer-Encoding");
1015
1016             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1017                          "proxy: start body send");
1018              
1019             /*
1020              * if we are overriding the errors, we can't put the content
1021              * of the page into the brigade
1022              */
1023             if (conf->error_override == 0 || ap_is_HTTP_SUCCESS(r->status)) {
1024
1025                 /* read the body, pass it to the output filters */
1026                 int finish = FALSE;
1027                 while (ap_get_brigade(rp->input_filters, 
1028                                       bb, 
1029                                       AP_MODE_READBYTES, 
1030                                       APR_BLOCK_READ, 
1031                                       conf->io_buffer_size) == APR_SUCCESS) {
1032 #if DEBUGGING
1033                     {
1034                     apr_off_t readbytes;
1035                     apr_brigade_length(bb, 0, &readbytes);
1036                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1037                                  r->server, "proxy (PID %d): readbytes: %#x",
1038                                  getpid(), readbytes);
1039                     }
1040 #endif
1041                     /* sanity check */
1042                     if (APR_BRIGADE_EMPTY(bb)) {
1043                         apr_brigade_cleanup(bb);
1044                         break;
1045                     }
1046
1047                     /* found the last brigade? */
1048                     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
1049                         /* if this is the last brigade, cleanup the
1050                          * backend connection first to prevent the
1051                          * backend server from hanging around waiting
1052                          * for a slow client to eat these bytes
1053                          */
1054                         backend->close = 1;
1055                         /* signal that we must leave */
1056                         finish = TRUE;
1057                     }
1058
1059                     /* try send what we read */
1060                     if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) {
1061                         /* Ack! Phbtt! Die! User aborted! */
1062                         backend->close = 1;  /* this causes socket close below */
1063                         finish = TRUE;
1064                     }
1065
1066                     /* make sure we always clean up after ourselves */
1067                     apr_brigade_cleanup(bb);
1068
1069                     /* if we are done, leave */
1070                     if (TRUE == finish) {
1071                         break;
1072                     }
1073                 }
1074             }
1075             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1076                          "proxy: end body send");
1077         } else {
1078             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1079                          "proxy: header only");
1080         }
1081     } while (interim_response);
1082
1083     if (conf->error_override) {
1084         /* the code above this checks for 'OK' which is what the hook expects */
1085         if (ap_is_HTTP_SUCCESS(r->status))
1086             return OK;
1087         else {
1088             /* clear r->status for override error, otherwise ErrorDocument
1089              * thinks that this is a recursive error, and doesn't find the
1090              * custom error page
1091              */
1092             int status = r->status;
1093             r->status = HTTP_OK;
1094             /* Discard body, if one is expected */
1095             if ((status != HTTP_NO_CONTENT) && /* not 204 */
1096                 (status != HTTP_RESET_CONTENT) && /* not 205 */
1097                 (status != HTTP_NOT_MODIFIED)) { /* not 304 */
1098                ap_discard_request_body(rp);
1099            }
1100             return status;
1101         }
1102     } else 
1103         return OK;
1104 }
1105
1106 static
1107 apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r,
1108                                    proxy_conn_rec *backend)
1109 {
1110     /* If there are no KeepAlives, or if the connection has been signalled
1111      * to close, close the socket and clean up
1112      */
1113
1114     /* if the connection is < HTTP/1.1, or Connection: close,
1115      * we close the socket, otherwise we leave it open for KeepAlive support
1116      */
1117     if (backend->close || (r->proto_num < HTTP_VERSION(1,1))) {
1118         backend->close_on_recycle = 1;
1119         ap_set_module_config(r->connection->conn_config, &proxy_http_module, NULL);
1120         ap_proxy_release_connection(scheme, backend, r->server);    
1121     }
1122     return OK;
1123 }
1124
1125 /*
1126  * This handles http:// URLs, and other URLs using a remote proxy over http
1127  * If proxyhost is NULL, then contact the server directly, otherwise
1128  * go via the proxy.
1129  * Note that if a proxy is used, then URLs other than http: can be accessed,
1130  * also, if we have trouble which is clearly specific to the proxy, then
1131  * we return DECLINED so that we can try another proxy. (Or the direct
1132  * route.)
1133  */
1134 int ap_proxy_http_handler(request_rec *r, proxy_worker *worker,
1135                           proxy_server_conf *conf,
1136                           char *url, const char *proxyname, 
1137                           apr_port_t proxyport)
1138 {
1139     int status;
1140     char server_portstr[32];
1141     char *scheme;
1142     const char *proxy_function;
1143     const char *u;
1144     proxy_conn_rec *backend = NULL;
1145     int is_ssl = 0;
1146
1147     /* Note: Memory pool allocation.
1148      * A downstream keepalive connection is always connected to the existence
1149      * (or not) of an upstream keepalive connection. If this is not done then
1150      * load balancing against multiple backend servers breaks (one backend
1151      * server ends up taking 100% of the load), and the risk is run of
1152      * downstream keepalive connections being kept open unnecessarily. This
1153      * keeps webservers busy and ties up resources.
1154      *
1155      * As a result, we allocate all sockets out of the upstream connection
1156      * pool, and when we want to reuse a socket, we check first whether the
1157      * connection ID of the current upstream connection is the same as that
1158      * of the connection when the socket was opened.
1159      */
1160     apr_pool_t *p = r->connection->pool;
1161     conn_rec *c = r->connection;
1162     apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
1163
1164     /* find the scheme */
1165     u = strchr(url, ':');
1166     if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
1167        return DECLINED;
1168     if ((u - url) > 14)
1169         return HTTP_BAD_REQUEST;
1170     scheme = apr_pstrndup(c->pool, url, u - url);
1171     /* scheme is lowercase */
1172     ap_str_tolower(scheme);
1173     /* is it for us? */
1174     if (strcmp(scheme, "https") == 0) {
1175         if (!ap_proxy_ssl_enable(NULL)) {
1176             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1177                          "proxy: HTTPS: declining URL %s"
1178                          " (mod_ssl not configured?)", url);
1179             return DECLINED;
1180         }
1181         is_ssl = 1;
1182         proxy_function = "HTTPS";
1183     }
1184     else if (!(strcmp(scheme, "http") == 0 || (strcmp(scheme, "ftp") == 0 && proxyname))) {
1185         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1186                      "proxy: HTTP: declining URL %s", url);
1187         return DECLINED; /* only interested in HTTP, or FTP via proxy */
1188     }
1189     else {
1190         if (*scheme == 'h')
1191             proxy_function = "HTTP";
1192         else
1193             proxy_function = "FTP";
1194     }
1195     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1196              "proxy: HTTP: serving URL %s", url);
1197     
1198     
1199     /* only use stored info for top-level pages. Sub requests don't share 
1200      * in keepalives
1201      */
1202     if (!r->main) {
1203         backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
1204                                                       &proxy_http_module);
1205     }
1206     /* create space for state information */
1207     if (!backend) {
1208         status = ap_proxy_acquire_connection(proxy_function, &backend, worker, r->server);
1209         if (status != OK) {
1210             if (backend) {
1211                 backend->close_on_recycle = 1;
1212                 ap_proxy_release_connection(proxy_function, backend, r->server);
1213             }
1214             return status;
1215         }
1216         if (!r->main) {
1217             ap_set_module_config(c->conn_config, &proxy_http_module, backend);
1218         }
1219     }
1220
1221     backend->is_ssl = is_ssl;
1222     backend->close_on_recycle = 1;
1223
1224     /* Step One: Determine Who To Connect To */
1225     status = ap_proxy_determine_connection(p, r, conf, worker, backend, c->pool,
1226                                            uri, &url, proxyname, proxyport,
1227                                            server_portstr,
1228                                            sizeof(server_portstr));
1229
1230     if ( status != OK ) {
1231         return status;
1232     }
1233
1234     /* Step Two: Make the Connection */
1235     if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
1236         return HTTP_SERVICE_UNAVAILABLE;
1237     }
1238
1239     /* Step Three: Create conn_rec */
1240     if (!backend->connection) {
1241         status = ap_proxy_connection_create(proxy_function, backend, c, r->server);
1242         if (status != OK)
1243             return status;
1244     }
1245    
1246     /* Step Four: Send the Request */
1247     status = ap_proxy_http_request(p, r, backend, backend->connection, conf, uri, url,
1248                                    server_portstr);
1249     if ( status != OK ) {
1250         return status;
1251     }
1252
1253     /* Step Five: Receive the Response */
1254     status = ap_proxy_http_process_response(p, r, backend, backend->connection, conf,
1255                                             server_portstr);
1256     if (status != OK) {
1257         /* clean up even if there is an error */
1258         ap_proxy_http_cleanup(proxy_function, r, backend);
1259         return status;
1260     }
1261
1262     /* Step Six: Clean Up */
1263     status = ap_proxy_http_cleanup(proxy_function, r, backend);
1264     if ( status != OK ) {
1265         return status;
1266     }
1267
1268     return OK;
1269 }
1270
1271 static void ap_proxy_http_register_hook(apr_pool_t *p)
1272 {
1273     proxy_hook_scheme_handler(ap_proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
1274     proxy_hook_canon_handler(ap_proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
1275 }
1276
1277 module AP_MODULE_DECLARE_DATA proxy_http_module = {
1278     STANDARD20_MODULE_STUFF,
1279     NULL,              /* create per-directory config structure */
1280     NULL,              /* merge per-directory config structures */
1281     NULL,              /* create per-server config structure */
1282     NULL,              /* merge per-server config structures */
1283     NULL,              /* command apr_table_t */
1284     ap_proxy_http_register_hook/* register hooks */
1285 };
1286