]> granicus.if.org Git - apache/blob - modules/proxy/proxy_http.c
Clarify an error message
[apache] / modules / proxy / proxy_http.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /* HTTP routines for Apache proxy */
60
61 #include "mod_proxy.h"
62
63 module AP_MODULE_DECLARE_DATA proxy_http_module;
64
65 int ap_proxy_http_canon(request_rec *r, char *url);
66 int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf,
67                           char *url, const char *proxyname, 
68                           apr_port_t proxyport);
69
70 typedef struct {
71     const char     *name;
72     apr_port_t      port;
73     apr_sockaddr_t *addr;
74     apr_socket_t   *sock;
75     int             close;
76 } proxy_http_conn_t;
77
78 static apr_status_t ap_proxy_http_cleanup(request_rec *r,
79                                           proxy_http_conn_t *p_conn,
80                                           proxy_conn_rec *backend);
81
82 /*
83  * Canonicalise http-like URLs.
84  *  scheme is the scheme for the URL
85  *  url    is the URL starting with the first '/'
86  *  def_port is the default port for this scheme.
87  */
88 int ap_proxy_http_canon(request_rec *r, char *url)
89 {
90     char *host, *path, *search, sport[7];
91     const char *err;
92     const char *scheme;
93     apr_port_t port, def_port;
94
95     /* ap_port_of_scheme() */
96     if (strncasecmp(url, "http:", 5) == 0) {
97         url += 5;
98         scheme = "http";
99     }
100     else if (strncasecmp(url, "https:", 6) == 0) {
101         url += 6;
102         scheme = "https";
103     }
104     else {
105         return DECLINED;
106     }
107     def_port = apr_uri_port_of_scheme(scheme);
108
109     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
110              "proxy: HTTP: canonicalising URL %s", url);
111
112     /* do syntatic check.
113      * We break the URL into host, port, path, search
114      */
115     port = def_port;
116     err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
117     if (err)
118         return HTTP_BAD_REQUEST;
119
120     /* now parse path/search args, according to rfc1738 */
121     /* N.B. if this isn't a true proxy request, then the URL _path_
122      * has already been decoded.  True proxy requests have r->uri
123      * == r->unparsed_uri, and no others have that property.
124      */
125     if (r->uri == r->unparsed_uri) {
126         search = strchr(url, '?');
127         if (search != NULL)
128             *(search++) = '\0';
129     }
130     else
131         search = r->args;
132
133     /* process path */
134     path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
135     if (path == NULL)
136         return HTTP_BAD_REQUEST;
137
138     if (port != def_port)
139         apr_snprintf(sport, sizeof(sport), ":%d", port);
140     else
141         sport[0] = '\0';
142
143     r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, 
144             "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
145     return OK;
146 }
147  
148 static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
149 {
150     struct proxy_alias *ent;
151     int i, l1, l2;
152     char *u;
153
154     /* XXX FIXME: Make sure this handled the ambiguous case of the :80
155      * after the hostname */
156
157     l1 = strlen(url);
158     ent = (struct proxy_alias *)conf->raliases->elts;
159     for (i = 0; i < conf->raliases->nelts; i++) {
160         l2 = strlen(ent[i].real);
161         if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) {
162             u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
163             return ap_construct_url(r->pool, u, r);
164         }
165     }
166     return url;
167 }
168
169 /* Clear all connection-based headers from the incoming headers table */
170 static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
171 {
172     const char *name;
173     char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
174
175     apr_table_unset(headers, "Proxy-Connection");
176     if (!next)
177         return;
178
179     while (*next) {
180         name = next;
181         while (*next && !apr_isspace(*next) && (*next != ',')) {
182             ++next;
183         }
184         while (*next && (apr_isspace(*next) || (*next == ','))) {
185             *next = '\0';
186             ++next;
187         }
188         apr_table_unset(headers, name);
189     }
190     apr_table_unset(headers, "Connection");
191 }
192
193 static
194 apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r,
195                                                 proxy_http_conn_t *p_conn,
196                                                 conn_rec *c,
197                                                 proxy_server_conf *conf,
198                                                 apr_uri_t *uri,
199                                                 char **url,
200                                                 const char *proxyname,
201                                                 apr_port_t proxyport,
202                                                 char *server_portstr,
203                                                 int server_portstr_size) {
204     int server_port;
205     apr_status_t err;
206     apr_sockaddr_t *uri_addr;
207     /*
208      * Break up the URL to determine the host to connect to
209      */
210
211     /* we break the URL into host, port, uri */
212     if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
213         return ap_proxyerror(r, HTTP_BAD_REQUEST,
214                              apr_pstrcat(p,"URI cannot be parsed: ", *url,
215                                          NULL));
216     }
217     if (!uri->port) {
218         uri->port = apr_uri_port_of_scheme(uri->scheme);
219     }
220
221     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
222                  "proxy: HTTP connecting %s to %s:%d", *url, uri->hostname,
223                  uri->port);
224
225     /* do a DNS lookup for the destination host */
226     /* see memory note above */
227     err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri->hostname),
228                                 APR_UNSPEC, uri->port, 0, c->pool);
229
230     /* allocate these out of the connection pool - the check on
231      * r->connection->id makes sure that this string does not get accessed
232      * past the connection lifetime */
233     /* are we connecting directly, or via a proxy? */
234     if (proxyname) {
235         p_conn->name = apr_pstrdup(c->pool, proxyname);
236         p_conn->port = proxyport;
237         /* see memory note above */
238         err = apr_sockaddr_info_get(&p_conn->addr, p_conn->name, APR_UNSPEC,
239                                     p_conn->port, 0, c->pool);
240     } else {
241         p_conn->name = apr_pstrdup(c->pool, uri->hostname);
242         p_conn->port = uri->port;
243         p_conn->addr = uri_addr;
244         *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
245                            uri->query ? uri->query : "",
246                            uri->fragment ? "#" : "",
247                            uri->fragment ? uri->fragment : "", NULL);
248     }
249
250     if (err != APR_SUCCESS) {
251         return ap_proxyerror(r, HTTP_BAD_GATEWAY,
252                              apr_pstrcat(p, "DNS lookup failure for: ",
253                                          p_conn->name, NULL));
254     }
255
256     /* Get the server port for the Via headers */
257     {
258         server_port = ap_get_server_port(r);
259         if (ap_is_default_port(server_port, r)) {
260             strcpy(server_portstr,"");
261         } else {
262             apr_snprintf(server_portstr, server_portstr_size, ":%d",
263                          server_port);
264         }
265     }
266
267     /* check if ProxyBlock directive on this host */
268     if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) {
269         return ap_proxyerror(r, HTTP_FORBIDDEN,
270                              "Connect to remote machine blocked");
271     }
272     return OK;
273 }
274
275 static
276 apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r,
277                                              proxy_http_conn_t *p_conn,
278                                              conn_rec *c, conn_rec **origin,
279                                              proxy_conn_rec *backend,
280                                              proxy_server_conf *conf,
281                                              const char *proxyname) {
282     int failed=0, new=0;
283     apr_socket_t *client_socket = NULL;
284
285     /* We have determined who to connect to. Now make the connection, supporting
286      * a KeepAlive connection.
287      */
288
289     /* get all the possible IP addresses for the destname and loop through them
290      * until we get a successful connection
291      */
292
293     /* if a keepalive socket is already open, check whether it must stay
294      * open, or whether it should be closed and a new socket created.
295      */
296     /* see memory note above */
297     if (backend->connection) {
298         client_socket = ap_get_module_config(backend->connection->conn_config, &core_module);
299         if ((backend->connection->id == c->id) &&
300             (backend->port == p_conn->port) &&
301             (backend->hostname) &&
302             (!apr_strnatcasecmp(backend->hostname, p_conn->name))) {
303             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
304                          "proxy: keepalive address match (keep original socket)");
305         } else {
306             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
307                          "proxy: keepalive address mismatch / connection has"
308                          " changed (close old socket (%s/%s, %d/%d))", 
309                          p_conn->name, backend->hostname, p_conn->port,
310                          backend->port);
311             apr_socket_close(client_socket);
312             backend->connection = NULL;
313         }
314     }
315
316     /* get a socket - either a keepalive one, or a new one */
317     new = 1;
318     if ((backend->connection) && (backend->connection->id == c->id)) {
319         apr_size_t buffer_len = 1;
320         char test_buffer[1]; 
321         apr_status_t socket_status;
322         apr_interval_time_t current_timeout;
323
324         /* use previous keepalive socket */
325         *origin = backend->connection;
326         p_conn->sock = client_socket;
327         new = 0;
328
329         /* save timeout */
330         apr_socket_timeout_get(p_conn->sock, &current_timeout);
331         /* set no timeout */
332         apr_socket_timeout_set(p_conn->sock, 0);
333         socket_status = apr_recv(p_conn->sock, test_buffer, &buffer_len);
334         /* put back old timeout */
335         apr_socket_timeout_set(p_conn->sock, current_timeout);
336         if ( APR_STATUS_IS_EOF(socket_status) ) {
337             ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
338                          "proxy: HTTP: previous connection is closed");
339             new = 1;
340         }
341     }
342     if (new) {
343
344         /* create a new socket */
345         backend->connection = NULL;
346
347         /*
348          * At this point we have a list of one or more IP addresses of
349          * the machine to connect to. If configured, reorder this
350          * list so that the "best candidate" is first try. "best
351          * candidate" could mean the least loaded server, the fastest
352          * responding server, whatever.
353          *
354          * For now we do nothing, ie we get DNS round robin.
355          * XXX FIXME
356          */
357         failed = ap_proxy_connect_to_backend(&p_conn->sock, "HTTP",
358                                              p_conn->addr, p_conn->name,
359                                              conf, r->server, c->pool);
360
361         /* handle a permanent error on the connect */
362         if (failed) {
363             if (proxyname) {
364                 return DECLINED;
365             } else {
366                 return HTTP_BAD_GATEWAY;
367             }
368         }
369
370         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
371                      "proxy: socket is connected");
372
373         /* the socket is now open, create a new backend server connection */
374         *origin = ap_run_create_connection(c->pool, r->server, p_conn->sock,
375                                            r->connection->id,
376                                            r->connection->sbh, c->bucket_alloc);
377         if (!*origin) {
378         /* the peer reset the connection already; ap_run_create_connection() 
379          * closed the socket
380          */
381             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
382                          r->server, "proxy: an error occurred creating a "
383                          "new connection to %pI (%s)", p_conn->addr,
384                          p_conn->name);
385             apr_socket_close(p_conn->sock);
386             return HTTP_INTERNAL_SERVER_ERROR;
387         }
388         backend->connection = *origin;
389         backend->hostname = apr_pstrdup(c->pool, p_conn->name);
390         backend->port = p_conn->port;
391
392         if (backend->is_ssl) {
393             if (!ap_proxy_ssl_enable(backend->connection)) {
394                 ap_log_error(APLOG_MARK, APLOG_ERR, 0,
395                              r->server, "proxy: failed to enable ssl support "
396                              "for %pI (%s)", p_conn->addr, p_conn->name);
397                 return HTTP_INTERNAL_SERVER_ERROR;
398             }
399         }
400         else {
401             ap_proxy_ssl_disable(backend->connection);
402         }
403
404         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
405                      "proxy: connection complete to %pI (%s)",
406                      p_conn->addr, p_conn->name);
407
408         /* set up the connection filters */
409         ap_run_pre_connection(*origin, p_conn->sock);
410     }
411     return OK;
412 }
413
414 static
415 apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
416                                    proxy_http_conn_t *p_conn, conn_rec *origin, 
417                                    proxy_server_conf *conf,
418                                    apr_uri_t *uri,
419                                    char *url, char *server_portstr)
420 {
421     conn_rec *c = r->connection;
422     char *buf;
423     apr_bucket *e, *last_header_bucket;
424     const apr_array_header_t *headers_in_array;
425     const apr_table_entry_t *headers_in;
426     int counter, seen_eos, send_chunks;
427     apr_status_t status;
428     apr_bucket_brigade *header_brigade, *body_brigade, *input_brigade;
429
430     header_brigade = apr_brigade_create(p, origin->bucket_alloc);
431     body_brigade = apr_brigade_create(p, origin->bucket_alloc);
432     input_brigade = apr_brigade_create(p, origin->bucket_alloc);
433
434     /*
435      * Send the HTTP/1.1 request to the remote server
436      */
437
438     /* strip connection listed hop-by-hop headers from the request */
439     /* even though in theory a connection: close coming from the client
440      * should not affect the connection to the server, it's unlikely
441      * that subsequent client requests will hit this thread/process, so
442      * we cancel server keepalive if the client does.
443      */
444     p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_in,
445                                                      "Connection"), "close");
446
447     /* sub-requests never use keepalives */
448     if (r->main) {
449         p_conn->close++;
450     }
451
452     ap_proxy_clear_connection(p, r->headers_in);
453     if (p_conn->close) {
454         apr_table_setn(r->headers_in, "Connection", "close");
455         origin->keepalive = AP_CONN_CLOSE;
456     }
457
458     /* By default, we can not send chunks. That means we must buffer
459      * the entire request before sending it along to ensure we have
460      * the correct Content-Length attached.
461      */
462     send_chunks = 0;
463
464     if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
465         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
466     } else {
467         buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
468         if (apr_table_get(r->subprocess_env, "proxy-sendchunks")) {
469             send_chunks = 1;
470         }
471     }
472     if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
473         apr_table_unset(r->headers_in, "Connection");
474         origin->keepalive = AP_CONN_CLOSE;
475     }
476     ap_xlate_proto_to_ascii(buf, strlen(buf));
477     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
478     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
479     if (conf->preserve_host == 0) {
480         if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
481             buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str,
482                               CRLF, NULL);
483         } else {
484             buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
485         }
486     } 
487     else {
488         /* don't want to use r->hostname, as the incoming header might have a 
489          * port attached 
490          */
491         const char* hostname = apr_table_get(r->headers_in,"Host");        
492         if (!hostname) {
493             hostname =  r->server->server_hostname;
494             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
495                           "proxy: no HTTP 0.9 request (with no host line) "
496                           "on incoming request and preserve host set "
497                           "forcing hostname to be %s for uri %s", 
498                           hostname, 
499                           r->uri );
500         }
501         buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
502     }
503     ap_xlate_proto_to_ascii(buf, strlen(buf));
504     e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);        
505     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
506
507     /* handle Via */
508     if (conf->viaopt == via_block) {
509         /* Block all outgoing Via: headers */
510         apr_table_unset(r->headers_in, "Via");
511     } else if (conf->viaopt != via_off) {
512         /* Create a "Via:" request header entry and merge it */
513         /* Generate outgoing Via: header with/without server comment: */
514         apr_table_mergen(r->headers_in, "Via",
515                          (conf->viaopt == via_full)
516                          ? apr_psprintf(p, "%d.%d %s%s (%s)",
517                                         HTTP_VERSION_MAJOR(r->proto_num),
518                                         HTTP_VERSION_MINOR(r->proto_num),
519                                         ap_get_server_name(r), server_portstr,
520                                         AP_SERVER_BASEVERSION)
521                          : apr_psprintf(p, "%d.%d %s%s",
522                                         HTTP_VERSION_MAJOR(r->proto_num),
523                                         HTTP_VERSION_MINOR(r->proto_num),
524                                         ap_get_server_name(r), server_portstr)
525         );
526     }
527
528     /* X-Forwarded-*: handling
529      *
530      * XXX Privacy Note:
531      * -----------------
532      *
533      * These request headers are only really useful when the mod_proxy
534      * is used in a reverse proxy configuration, so that useful info
535      * about the client can be passed through the reverse proxy and on
536      * to the backend server, which may require the information to
537      * function properly.
538      *
539      * In a forward proxy situation, these options are a potential
540      * privacy violation, as information about clients behind the proxy
541      * are revealed to arbitrary servers out there on the internet.
542      *
543      * The HTTP/1.1 Via: header is designed for passing client
544      * information through proxies to a server, and should be used in
545      * a forward proxy configuation instead of X-Forwarded-*. See the
546      * ProxyVia option for details.
547      */
548
549     if (PROXYREQ_REVERSE == r->proxyreq) {
550         const char *buf;
551
552         /* Add X-Forwarded-For: so that the upstream has a chance to
553          * determine, where the original request came from.
554          */
555         apr_table_mergen(r->headers_in, "X-Forwarded-For",
556                        r->connection->remote_ip);
557
558         /* Add X-Forwarded-Host: so that upstream knows what the
559          * original request hostname was.
560          */
561         if ((buf = apr_table_get(r->headers_in, "Host"))) {
562             apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
563         }
564
565         /* Add X-Forwarded-Server: so that upstream knows what the
566          * name of this proxy server is (if there are more than one)
567          * XXX: This duplicates Via: - do we strictly need it?
568          */
569         apr_table_mergen(r->headers_in, "X-Forwarded-Server",
570                        r->server->server_hostname);
571     }
572
573     /* send request headers */
574     proxy_run_fixups(r);
575     headers_in_array = apr_table_elts(r->headers_in);
576     headers_in = (const apr_table_entry_t *) headers_in_array->elts;
577     for (counter = 0; counter < headers_in_array->nelts; counter++) {
578         if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
579
580         /* Clear out hop-by-hop request headers not to send
581          * RFC2616 13.5.1 says we should strip these headers
582          */
583                 /* Already sent */
584             || !apr_strnatcasecmp(headers_in[counter].key, "Host")
585
586             || !apr_strnatcasecmp(headers_in[counter].key, "Keep-Alive")
587             || !apr_strnatcasecmp(headers_in[counter].key, "TE")
588             || !apr_strnatcasecmp(headers_in[counter].key, "Trailer")
589             || !apr_strnatcasecmp(headers_in[counter].key, "Transfer-Encoding")
590             || !apr_strnatcasecmp(headers_in[counter].key, "Upgrade")
591
592             /* We have no way of knowing whether this Content-Length will
593              * be accurate, so we must not include it.
594              */
595             || !apr_strnatcasecmp(headers_in[counter].key, "Content-Length")
596         /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
597          * suppressed if THIS server requested the authentication,
598          * not when a frontend proxy requested it!
599          *
600          * The solution to this problem is probably to strip out
601          * the Proxy-Authorisation header in the authorisation
602          * code itself, not here. This saves us having to signal
603          * somehow whether this request was authenticated or not.
604          */
605             || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authorization")
606             || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authenticate")) {
607             continue;
608         }
609         /* for sub-requests, ignore freshness/expiry headers */
610         if (r->main) {
611                 if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
612                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Match")
613                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Modified-Since")
614                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Range")
615                      || !apr_strnatcasecmp(headers_in[counter].key, "If-Unmodified-Since")                     
616                      || !apr_strnatcasecmp(headers_in[counter].key, "If-None-Match")) {
617                     continue;
618                 }
619         }
620
621
622         buf = apr_pstrcat(p, headers_in[counter].key, ": ",
623                           headers_in[counter].val, CRLF,
624                           NULL);
625         ap_xlate_proto_to_ascii(buf, strlen(buf));
626         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
627         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
628     }
629
630     /* If we can send chunks, do so! */
631     if (send_chunks) {
632         const char *te_hdr = "Transfer-Encoding: chunked" CRLF;
633
634         buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1);
635         ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1);
636
637         e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
638         APR_BRIGADE_INSERT_TAIL(header_brigade, e);
639     }
640     else {
641         last_header_bucket = APR_BRIGADE_LAST(header_brigade);
642     }
643
644     /* add empty line at the end of the headers */
645 #if APR_CHARSET_EBCDIC
646     e = apr_bucket_immortal_create("\015\012", 2, c->bucket_alloc);
647 #else
648     e = apr_bucket_immortal_create(CRLF, sizeof(CRLF)-1, c->bucket_alloc);
649 #endif
650     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
651     e = apr_bucket_flush_create(c->bucket_alloc);
652     APR_BRIGADE_INSERT_TAIL(header_brigade, e);
653
654     if (send_chunks) {
655         status = ap_pass_brigade(origin->output_filters, header_brigade);
656
657         if (status != APR_SUCCESS) {
658             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
659                          "proxy: request failed to %pI (%s)",
660                          p_conn->addr, p_conn->name);
661             return status;
662         }
663     }
664
665     /* send the request data, if any. */
666     seen_eos = 0;
667     do {
668         char chunk_hdr[20];  /* must be here due to transient bucket. */
669
670         status = ap_get_brigade(r->input_filters, input_brigade,
671                                 AP_MODE_READBYTES, APR_BLOCK_READ,
672                                 HUGE_STRING_LEN);
673
674         if (status != APR_SUCCESS) {
675             return status;
676         }
677
678         /* If this brigade contain EOS, either stop or remove it. */
679         if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
680             seen_eos = 1;
681
682             /* As a shortcut, if this brigade is simply an EOS bucket,
683              * don't send anything down the filter chain.
684              */
685             if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) {
686                 break;
687             }
688
689             /* We can't pass this EOS to the output_filters. */
690             APR_BUCKET_REMOVE(APR_BRIGADE_LAST(input_brigade));
691         }
692
693         if (send_chunks) {
694 #define ASCII_CRLF  "\015\012"
695 #define ASCII_ZERO  "\060"
696             apr_size_t hdr_len;
697             apr_off_t bytes;
698
699             apr_brigade_length(input_brigade, 1, &bytes);
700
701             hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
702                                    "%qx" CRLF, (apr_uint64_t)bytes);
703
704             ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
705             e = apr_bucket_transient_create(chunk_hdr, hdr_len,
706                                             body_brigade->bucket_alloc);
707             APR_BRIGADE_INSERT_HEAD(input_brigade, e);
708
709             /*
710              * Append the end-of-chunk CRLF
711              */
712             e = apr_bucket_immortal_create(ASCII_CRLF, 2, c->bucket_alloc);
713             APR_BRIGADE_INSERT_TAIL(input_brigade, e);
714         }
715
716         e = apr_bucket_flush_create(c->bucket_alloc);
717         APR_BRIGADE_INSERT_TAIL(input_brigade, e);
718
719         APR_BRIGADE_CONCAT(body_brigade, input_brigade);
720
721         if (send_chunks) {
722             status = ap_pass_brigade(origin->output_filters, body_brigade);
723
724             if (status != APR_SUCCESS) {
725                 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
726                              "proxy: pass request data failed to %pI (%s)",
727                          p_conn->addr, p_conn->name);
728                 return status;
729             }
730
731             apr_brigade_cleanup(body_brigade);
732         }
733
734     } while (!seen_eos);
735
736     if (send_chunks) {
737         e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
738                                        /* <trailers> */
739                                        ASCII_CRLF, 5, c->bucket_alloc);
740         APR_BRIGADE_INSERT_TAIL(body_brigade, e);
741     }
742
743     if (!send_chunks) {
744         apr_off_t bytes;
745
746         apr_brigade_length(body_brigade, 1, &bytes);
747
748         if (bytes) {
749             const char *cl_hdr = "Content-Length", *cl_val;
750             cl_val = apr_off_t_toa(c->pool, bytes);
751             buf = apr_pstrcat(p, cl_hdr, ": ", cl_val, CRLF, NULL);
752             ap_xlate_proto_to_ascii(buf, strlen(buf));
753             e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
754             APR_BUCKET_INSERT_AFTER(last_header_bucket, e);
755         }
756         status = ap_pass_brigade(origin->output_filters, header_brigade);
757         if (status != APR_SUCCESS) {
758             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
759                          "proxy: pass request data failed to %pI (%s)",
760                          p_conn->addr, p_conn->name);
761             return status;
762         }
763
764         apr_brigade_cleanup(header_brigade);
765     }
766
767     status = ap_pass_brigade(origin->output_filters, body_brigade);
768
769     if (status != APR_SUCCESS) {
770         ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
771                      "proxy: pass request data failed to %pI (%s)",
772                      p_conn->addr, p_conn->name);
773         return status;
774     }
775  
776     apr_brigade_cleanup(body_brigade);
777
778     return APR_SUCCESS;
779 }
780
781 static
782 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
783                                             proxy_http_conn_t *p_conn,
784                                             conn_rec *origin,
785                                             proxy_conn_rec *backend,
786                                             proxy_server_conf *conf,
787                                             char *server_portstr) {
788     conn_rec *c = r->connection;
789     char buffer[HUGE_STRING_LEN];
790     char keepchar;
791     request_rec *rp;
792     apr_bucket *e;
793     apr_bucket_brigade *bb;
794     int len, backasswards;
795     int received_continue = 1; /* flag to indicate if we should
796                                 * loop over response parsing logic
797                                 * in the case that the origin told us
798                                 * to HTTP_CONTINUE
799                                 */
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     while (received_continue) {
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             apr_socket_close(p_conn->sock);
823             backend->connection = NULL;
824             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
825                           "proxy: error reading status line from remote "
826                           "server %s", p_conn->name);
827             return ap_proxyerror(r, HTTP_BAD_GATEWAY,
828                                  "Error reading from remote server");
829         }
830
831        /* Is it an HTTP/1 response?
832         * This is buggy if we ever see an HTTP/1.10
833         */
834         if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
835             int major, minor;
836
837             if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
838                 major = 1;
839                 minor = 1;
840             }
841             /* If not an HTTP/1 message or
842              * if the status line was > 8192 bytes
843              */
844             else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) {
845                 apr_socket_close(p_conn->sock);
846                 backend->connection = NULL;
847                 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
848                 apr_pstrcat(p, "Corrupt status line returned by remote "
849                             "server: ", buffer, NULL));
850             }
851             backasswards = 0;
852
853             keepchar = buffer[12];
854             if (keepchar == '\0') {
855                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
856                              r->server, "proxy: bad HTTP/%d.%d status line "
857                              "returned by %s (%s)", major, minor, r->uri,
858                              r->method);
859             }
860             buffer[12] = '\0';
861             r->status = atoi(&buffer[9]);
862
863             buffer[12] = keepchar;
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             r->headers_out = ap_proxy_read_headers(r, rp, buffer,
872                                                    sizeof(buffer), origin);
873             if (r->headers_out == NULL) {
874                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
875                              r->server, "proxy: bad HTTP/%d.%d header "
876                              "returned by %s (%s)", major, minor, r->uri,
877                              r->method);
878                 p_conn->close += 1;
879                 /*
880                  * ap_send_error relies on a headers_out to be present. we
881                  * are in a bad position here.. so force everything we send out
882                  * to have nothing to do with the incoming packet
883                  */
884                 r->headers_out = apr_table_make(r->pool,1);
885                 r->status = HTTP_BAD_GATEWAY;
886                 r->status_line = "bad gateway";
887                 return r->status;
888
889             } else {
890                 /* strip connection listed hop-by-hop headers from response */
891                 const char *buf;
892                 p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_out,
893                                                                 "Connection"),
894                                                   "close");
895                 ap_proxy_clear_connection(p, r->headers_out);
896                 if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
897                     ap_set_content_type(r, apr_pstrdup(p, buf));
898                 }            
899                 ap_proxy_pre_http_request(origin,rp);
900             }
901
902             /* handle Via header in response */
903             if (conf->viaopt != via_off && conf->viaopt != via_block) {
904                 /* create a "Via:" response header entry and merge it */
905                 apr_table_mergen(r->headers_out, "Via",
906                                  (conf->viaopt == via_full)
907                                      ? apr_psprintf(p, "%d.%d %s%s (%s)",
908                                            HTTP_VERSION_MAJOR(r->proto_num),
909                                            HTTP_VERSION_MINOR(r->proto_num),
910                                            ap_get_server_name(r),
911                                            server_portstr,
912                                            AP_SERVER_BASEVERSION)
913                                      : apr_psprintf(p, "%d.%d %s%s",
914                                            HTTP_VERSION_MAJOR(r->proto_num),
915                                            HTTP_VERSION_MINOR(r->proto_num),
916                                            ap_get_server_name(r),
917                                            server_portstr)
918                 );
919             }
920
921             /* cancel keepalive if HTTP/1.0 or less */
922             if ((major < 1) || (minor < 1)) {
923                 p_conn->close += 1;
924                 origin->keepalive = AP_CONN_CLOSE;
925             }
926         } else {
927             /* an http/0.9 response */
928             backasswards = 1;
929             r->status = 200;
930             r->status_line = "200 OK";
931             p_conn->close += 1;
932         }
933
934         if ( r->status != HTTP_CONTINUE ) {
935             received_continue = 0;
936         } else {
937             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
938                          "proxy: HTTP: received 100 CONTINUE");
939         }
940
941         /* we must accept 3 kinds of date, but generate only 1 kind of date */
942         {
943             const char *buf;
944             if ((buf = apr_table_get(r->headers_out, "Date")) != NULL) {
945                 apr_table_set(r->headers_out, "Date",
946                               ap_proxy_date_canon(p, buf));
947             }
948             if ((buf = apr_table_get(r->headers_out, "Expires")) != NULL) {
949                 apr_table_set(r->headers_out, "Expires",
950                               ap_proxy_date_canon(p, buf));
951             }
952             if ((buf = apr_table_get(r->headers_out, "Last-Modified")) != NULL) {
953                 apr_table_set(r->headers_out, "Last-Modified",
954                               ap_proxy_date_canon(p, buf));
955             }
956         }
957
958         /* munge the Location and URI response headers according to
959          * ProxyPassReverse
960          */
961         {
962             const char *buf;
963             if ((buf = apr_table_get(r->headers_out, "Location")) != NULL) {
964                 apr_table_set(r->headers_out, "Location",
965                               ap_proxy_location_reverse_map(r, conf, buf));
966             }
967             if ((buf = apr_table_get(r->headers_out, "Content-Location")) != NULL) {
968                 apr_table_set(r->headers_out, "Content-Location",
969                               ap_proxy_location_reverse_map(r, conf, buf));
970             }
971             if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) {
972                 apr_table_set(r->headers_out, "URI",
973                               ap_proxy_location_reverse_map(r, conf, buf));
974             }
975         }
976
977         if ((r->status == 401) && (conf->error_override != 0)) {
978             const char *buf;
979             const char *wa = "WWW-Authenticate";
980             if ((buf = apr_table_get(r->headers_out, wa))) {
981                 apr_table_set(r->err_headers_out, wa, buf);
982             } else {
983                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
984                              "proxy: origin server sent 401 without WWW-Authenticate header");
985             }
986         }
987
988         r->sent_bodyct = 1;
989         /* Is it an HTTP/0.9 response? If so, send the extra data */
990         if (backasswards) {
991             apr_ssize_t cntr = len;
992             e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
993             APR_BRIGADE_INSERT_TAIL(bb, e);
994         }
995
996         /* send body - but only if a body is expected */
997         if ((!r->header_only) &&                   /* not HEAD request */
998             (r->status > 199) &&                   /* not any 1xx response */
999             (r->status != HTTP_NO_CONTENT) &&      /* not 204 */
1000             (r->status != HTTP_RESET_CONTENT) &&   /* not 205 */
1001             (r->status != HTTP_NOT_MODIFIED)) {    /* not 304 */
1002
1003             /* We need to copy the output headers and treat them as input
1004              * headers as well.  BUT, we need to do this before we remove
1005              * TE, so that they are preserved accordingly for
1006              * ap_http_filter to know where to end.
1007              */
1008             rp->headers_in = apr_table_copy(r->pool, r->headers_out);
1009
1010             apr_table_unset(r->headers_out,"Transfer-Encoding");
1011
1012             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1013                          "proxy: start body send");
1014              
1015             /*
1016              * if we are overriding the errors, we can't put the content
1017              * of the page into the brigade
1018              */
1019             if ( (conf->error_override ==0) || r->status < 400 ) {
1020
1021                 /* read the body, pass it to the output filters */
1022                 int finish = FALSE;
1023                 while (ap_get_brigade(rp->input_filters, 
1024                                       bb, 
1025                                       AP_MODE_READBYTES, 
1026                                       APR_BLOCK_READ, 
1027                                       conf->io_buffer_size) == APR_SUCCESS) {
1028 #if DEBUGGING
1029                     {
1030                     apr_off_t readbytes;
1031                     apr_brigade_length(bb, 0, &readbytes);
1032                     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1033                                  r->server, "proxy (PID %d): readbytes: %#x",
1034                                  getpid(), readbytes);
1035                     }
1036 #endif
1037                     /* sanity check */
1038                     if (APR_BRIGADE_EMPTY(bb)) {
1039                         apr_brigade_cleanup(bb);
1040                         break;
1041                     }
1042
1043                     /* found the last brigade? */
1044                     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
1045                         /* if this is the last brigade, cleanup the
1046                          * backend connection first to prevent the
1047                          * backend server from hanging around waiting
1048                          * for a slow client to eat these bytes
1049                          */
1050                         ap_proxy_http_cleanup(r, p_conn, backend);
1051                         /* signal that we must leave */
1052                         finish = TRUE;
1053                     }
1054
1055                     /* try send what we read */
1056                     if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) {
1057                         /* Ack! Phbtt! Die! User aborted! */
1058                         p_conn->close = 1;  /* this causes socket close below */
1059                         finish = TRUE;
1060                     }
1061
1062                     /* make sure we always clean up after ourselves */
1063                     apr_brigade_cleanup(bb);
1064
1065                     /* if we are done, leave */
1066                     if (TRUE == finish) {
1067                         break;
1068                     }
1069                 }
1070             }
1071             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1072                          "proxy: end body send");
1073         } else {
1074             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1075                          "proxy: header only");
1076         }
1077     }
1078
1079     if ( conf->error_override ) {
1080         /* the code above this checks for 'OK' which is what the hook expects */
1081         if ( r->status == HTTP_OK )
1082             return OK;
1083         else  {
1084             /* clear r->status for override error, otherwise ErrorDocument
1085              * thinks that this is a recursive error, and doesn't find the
1086              * custom error page
1087              */
1088             int status = r->status;
1089             r->status = HTTP_OK;
1090             ap_discard_request_body(rp);
1091             return status;
1092         }
1093     } else 
1094         return OK;
1095 }
1096
1097 static
1098 apr_status_t ap_proxy_http_cleanup(request_rec *r, proxy_http_conn_t *p_conn,
1099                                    proxy_conn_rec *backend) {
1100     /* If there are no KeepAlives, or if the connection has been signalled
1101      * to close, close the socket and clean up
1102      */
1103
1104     /* if the connection is < HTTP/1.1, or Connection: close,
1105      * we close the socket, otherwise we leave it open for KeepAlive support
1106      */
1107     if (p_conn->close || (r->proto_num < HTTP_VERSION(1,1))) {
1108         if (p_conn->sock) {
1109             apr_socket_close(p_conn->sock);
1110             p_conn->sock = NULL;
1111             backend->connection = NULL;
1112         }
1113     }
1114     return OK;
1115 }
1116
1117 /*
1118  * This handles http:// URLs, and other URLs using a remote proxy over http
1119  * If proxyhost is NULL, then contact the server directly, otherwise
1120  * go via the proxy.
1121  * Note that if a proxy is used, then URLs other than http: can be accessed,
1122  * also, if we have trouble which is clearly specific to the proxy, then
1123  * we return DECLINED so that we can try another proxy. (Or the direct
1124  * route.)
1125  */
1126 int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf,
1127                           char *url, const char *proxyname, 
1128                           apr_port_t proxyport)
1129 {
1130     int status;
1131     char server_portstr[32];
1132     conn_rec *origin = NULL;
1133     proxy_conn_rec *backend = NULL;
1134     int is_ssl = 0;
1135
1136     /* Note: Memory pool allocation.
1137      * A downstream keepalive connection is always connected to the existence
1138      * (or not) of an upstream keepalive connection. If this is not done then
1139      * load balancing against multiple backend servers breaks (one backend
1140      * server ends up taking 100% of the load), and the risk is run of
1141      * downstream keepalive connections being kept open unnecessarily. This
1142      * keeps webservers busy and ties up resources.
1143      *
1144      * As a result, we allocate all sockets out of the upstream connection
1145      * pool, and when we want to reuse a socket, we check first whether the
1146      * connection ID of the current upstream connection is the same as that
1147      * of the connection when the socket was opened.
1148      */
1149     apr_pool_t *p = r->connection->pool;
1150     conn_rec *c = r->connection;
1151     apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
1152     proxy_http_conn_t *p_conn = apr_pcalloc(r->connection->pool,
1153                                            sizeof(*p_conn));
1154
1155     /* is it for us? */
1156     if (strncasecmp(url, "https:", 6) == 0) {
1157         if (!ap_proxy_ssl_enable(NULL)) {
1158             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1159                          "proxy: HTTPS: declining URL %s"
1160                          " (mod_ssl not configured?)", url);
1161             return DECLINED;
1162         }
1163         is_ssl = 1;
1164     }
1165     else if (!(strncasecmp(url, "http:", 5)==0 || (strncasecmp(url, "ftp:", 4)==0 && proxyname))) {
1166         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1167                      "proxy: HTTP: declining URL %s", url);
1168         return DECLINED; /* only interested in HTTP, or FTP via proxy */
1169     }
1170     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1171              "proxy: HTTP: serving URL %s", url);
1172     
1173     
1174     /* only use stored info for top-level pages. Sub requests don't share 
1175      * in keepalives
1176      */
1177     if (!r->main) {
1178         backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
1179                                                       &proxy_http_module);
1180     }
1181     /* create space for state information */
1182     if (!backend) {
1183         backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
1184         backend->connection = NULL;
1185         backend->hostname = NULL;
1186         backend->port = 0;
1187         if (!r->main) {
1188             ap_set_module_config(c->conn_config, &proxy_http_module, backend);
1189         }
1190     }
1191
1192     backend->is_ssl = is_ssl;
1193
1194     /* Step One: Determine Who To Connect To */
1195     status = ap_proxy_http_determine_connection(p, r, p_conn, c, conf, uri,
1196                                                 &url, proxyname, proxyport,
1197                                                 server_portstr,
1198                                                 sizeof(server_portstr));
1199     if ( status != OK ) {
1200         return status;
1201     }
1202
1203     /* Step Two: Make the Connection */
1204     status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend,
1205                                              conf, proxyname);
1206     if ( status != OK ) {
1207         return status;
1208     }
1209    
1210     /* Step Three: Send the Request */
1211     status = ap_proxy_http_request(p, r, p_conn, origin, conf, uri, url,
1212                                    server_portstr);
1213     if ( status != OK ) {
1214         return status;
1215     }
1216
1217     /* Step Four: Receive the Response */
1218     status = ap_proxy_http_process_response(p, r, p_conn, origin, backend, conf,
1219                                             server_portstr);
1220     if ( status != OK ) {
1221         /* clean up even if there is an error */
1222         ap_proxy_http_cleanup(r, p_conn, backend);
1223         return status;
1224     }
1225
1226     /* Step Five: Clean Up */
1227     status = ap_proxy_http_cleanup(r, p_conn, backend);
1228     if ( status != OK ) {
1229         return status;
1230     }
1231
1232     return OK;
1233 }
1234
1235 static void ap_proxy_http_register_hook(apr_pool_t *p)
1236 {
1237     proxy_hook_scheme_handler(ap_proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
1238     proxy_hook_canon_handler(ap_proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
1239 }
1240
1241 module AP_MODULE_DECLARE_DATA proxy_http_module = {
1242     STANDARD20_MODULE_STUFF,
1243     NULL,              /* create per-directory config structure */
1244     NULL,              /* merge per-directory config structures */
1245     NULL,              /* create per-server config structure */
1246     NULL,              /* merge per-server config structures */
1247     NULL,              /* command apr_table_t */
1248     ap_proxy_http_register_hook/* register hooks */
1249 };
1250