1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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
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.
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.
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.
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
47 * ====================================================================
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/>.
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.
59 /* HTTP routines for Apache proxy */
61 #include "mod_proxy.h"
63 module AP_MODULE_DECLARE_DATA proxy_http_module;
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);
78 static apr_status_t ap_proxy_http_cleanup(request_rec *r,
79 proxy_http_conn_t *p_conn,
80 proxy_conn_rec *backend);
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.
88 int ap_proxy_http_canon(request_rec *r, char *url)
90 char *host, *path, *search, sport[7];
93 apr_port_t port, def_port;
95 /* ap_port_of_scheme() */
96 if (strncasecmp(url, "http:", 5) == 0) {
100 else if (strncasecmp(url, "https:", 6) == 0) {
107 def_port = apr_uri_port_of_scheme(scheme);
109 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
110 "proxy: HTTP: canonicalising URL %s", url);
112 /* do syntatic check.
113 * We break the URL into host, port, path, search
116 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
118 return HTTP_BAD_REQUEST;
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.
125 if (r->uri == r->unparsed_uri) {
126 search = strchr(url, '?');
134 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
136 return HTTP_BAD_REQUEST;
138 if (port != def_port)
139 apr_snprintf(sport, sizeof(sport), ":%d", port);
143 r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
144 "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
148 static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
150 struct proxy_alias *ent;
154 /* XXX FIXME: Make sure this handled the ambiguous case of the :80
155 * after the hostname */
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);
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)
173 char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
175 apr_table_unset(headers, "Proxy-Connection");
181 while (*next && !apr_isspace(*next) && (*next != ',')) {
184 while (*next && (apr_isspace(*next) || (*next == ','))) {
188 apr_table_unset(headers, name);
190 apr_table_unset(headers, "Connection");
194 apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r,
195 proxy_http_conn_t *p_conn,
197 proxy_server_conf *conf,
200 const char *proxyname,
201 apr_port_t proxyport,
202 char *server_portstr,
203 int server_portstr_size) {
206 apr_sockaddr_t *uri_addr;
208 * Break up the URL to determine the host to connect to
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,
218 uri->port = apr_uri_port_of_scheme(uri->scheme);
221 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
222 "proxy: HTTP connecting %s to %s:%d", *url, uri->hostname,
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);
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? */
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);
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);
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));
256 /* Get the server port for the Via headers */
258 server_port = ap_get_server_port(r);
259 if (ap_is_default_port(server_port, r)) {
260 strcpy(server_portstr,"");
262 apr_snprintf(server_portstr, server_portstr_size, ":%d",
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");
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) {
283 apr_socket_t *client_socket = NULL;
285 /* We have determined who to connect to. Now make the connection, supporting
286 * a KeepAlive connection.
289 /* get all the possible IP addresses for the destname and loop through them
290 * until we get a successful connection
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.
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)");
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,
311 apr_socket_close(client_socket);
312 backend->connection = NULL;
316 /* get a socket - either a keepalive one, or a new one */
318 if ((backend->connection) && (backend->connection->id == c->id)) {
319 apr_size_t buffer_len = 1;
321 apr_status_t socket_status;
322 apr_interval_time_t current_timeout;
324 /* use previous keepalive socket */
325 *origin = backend->connection;
326 p_conn->sock = client_socket;
330 apr_socket_timeout_get(p_conn->sock, ¤t_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");
344 /* create a new socket */
345 backend->connection = NULL;
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.
354 * For now we do nothing, ie we get DNS round robin.
357 failed = ap_proxy_connect_to_backend(&p_conn->sock, "HTTP",
358 p_conn->addr, p_conn->name,
359 conf, r->server, c->pool);
361 /* handle a permanent error on the connect */
366 return HTTP_BAD_GATEWAY;
370 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
371 "proxy: socket is connected");
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,
376 r->connection->sbh, c->bucket_alloc);
378 /* the peer reset the connection already; ap_run_create_connection()
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,
385 apr_socket_close(p_conn->sock);
386 return HTTP_INTERNAL_SERVER_ERROR;
388 backend->connection = *origin;
389 backend->hostname = apr_pstrdup(c->pool, p_conn->name);
390 backend->port = p_conn->port;
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;
401 ap_proxy_ssl_disable(backend->connection);
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);
408 /* set up the connection filters */
409 ap_run_pre_connection(*origin, p_conn->sock);
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,
419 char *url, char *server_portstr)
421 conn_rec *c = r->connection;
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;
428 apr_bucket_brigade *header_brigade, *body_brigade, *input_brigade;
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);
435 * Send the HTTP/1.1 request to the remote server
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.
444 p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_in,
445 "Connection"), "close");
447 /* sub-requests never use keepalives */
452 ap_proxy_clear_connection(p, r->headers_in);
454 apr_table_setn(r->headers_in, "Connection", "close");
455 origin->keepalive = AP_CONN_CLOSE;
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.
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);
467 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
468 if (apr_table_get(r->subprocess_env, "proxy-sendchunks")) {
472 if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
473 apr_table_unset(r->headers_in, "Connection");
474 origin->keepalive = AP_CONN_CLOSE;
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,
484 buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
488 /* don't want to use r->hostname, as the incoming header might have a
491 const char* hostname = apr_table_get(r->headers_in,"Host");
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",
501 buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
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);
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)
528 /* X-Forwarded-*: handling
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
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.
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.
549 if (PROXYREQ_REVERSE == r->proxyreq) {
552 /* Add X-Forwarded-For: so that the upstream has a chance to
553 * determine, where the original request came from.
555 apr_table_mergen(r->headers_in, "X-Forwarded-For",
556 r->connection->remote_ip);
558 /* Add X-Forwarded-Host: so that upstream knows what the
559 * original request hostname was.
561 if ((buf = apr_table_get(r->headers_in, "Host"))) {
562 apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
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?
569 apr_table_mergen(r->headers_in, "X-Forwarded-Server",
570 r->server->server_hostname);
573 /* send request headers */
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
580 /* Clear out hop-by-hop request headers not to send
581 * RFC2616 13.5.1 says we should strip these headers
584 || !apr_strnatcasecmp(headers_in[counter].key, "Host")
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")
592 /* We have no way of knowing whether this Content-Length will
593 * be accurate, so we must not include it.
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!
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.
605 || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authorization")
606 || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authenticate")) {
609 /* for sub-requests, ignore freshness/expiry headers */
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")) {
622 buf = apr_pstrcat(p, headers_in[counter].key, ": ",
623 headers_in[counter].val, CRLF,
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);
630 /* If we can send chunks, do so! */
632 const char *te_hdr = "Transfer-Encoding: chunked" CRLF;
634 buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1);
635 ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1);
637 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
638 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
641 last_header_bucket = APR_BRIGADE_LAST(header_brigade);
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);
648 e = apr_bucket_immortal_create(CRLF, sizeof(CRLF)-1, c->bucket_alloc);
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);
655 status = ap_pass_brigade(origin->output_filters, header_brigade);
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);
665 /* send the request data, if any. */
668 char chunk_hdr[20]; /* must be here due to transient bucket. */
670 status = ap_get_brigade(r->input_filters, input_brigade,
671 AP_MODE_READBYTES, APR_BLOCK_READ,
674 if (status != APR_SUCCESS) {
678 /* If this brigade contain EOS, either stop or remove it. */
679 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
682 /* As a shortcut, if this brigade is simply an EOS bucket,
683 * don't send anything down the filter chain.
685 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) {
689 /* We can't pass this EOS to the output_filters. */
690 APR_BUCKET_REMOVE(APR_BRIGADE_LAST(input_brigade));
694 #define ASCII_CRLF "\015\012"
695 #define ASCII_ZERO "\060"
699 apr_brigade_length(input_brigade, 1, &bytes);
701 hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
702 "%qx" CRLF, (apr_uint64_t)bytes);
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);
710 * Append the end-of-chunk CRLF
712 e = apr_bucket_immortal_create(ASCII_CRLF, 2, c->bucket_alloc);
713 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
716 e = apr_bucket_flush_create(c->bucket_alloc);
717 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
719 APR_BRIGADE_CONCAT(body_brigade, input_brigade);
722 status = ap_pass_brigade(origin->output_filters, body_brigade);
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);
731 apr_brigade_cleanup(body_brigade);
737 e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
739 ASCII_CRLF, 5, c->bucket_alloc);
740 APR_BRIGADE_INSERT_TAIL(body_brigade, e);
746 apr_brigade_length(body_brigade, 1, &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);
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);
764 apr_brigade_cleanup(header_brigade);
767 status = ap_pass_brigade(origin->output_filters, body_brigade);
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);
776 apr_brigade_cleanup(body_brigade);
782 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
783 proxy_http_conn_t *p_conn,
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];
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
801 bb = apr_brigade_create(p, c->bucket_alloc);
803 /* Get response from the remote server, and pass it up the
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
811 rp->proxyreq = PROXYREQ_RESPONSE;
813 while (received_continue) {
814 apr_brigade_cleanup(bb);
816 len = ap_getline(buffer, sizeof(buffer), rp, 0);
818 /* handle one potential stray CRLF */
819 len = ap_getline(buffer, sizeof(buffer), rp, 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");
831 /* Is it an HTTP/1 response?
832 * This is buggy if we ever see an HTTP/1.10
834 if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
837 if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
841 /* If not an HTTP/1 message or
842 * if the status line was > 8192 bytes
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));
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,
861 r->status = atoi(&buffer[9]);
863 buffer[12] = keepchar;
864 r->status_line = apr_pstrdup(p, &buffer[9]);
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. */
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,
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
884 r->headers_out = apr_table_make(r->pool,1);
885 r->status = HTTP_BAD_GATEWAY;
886 r->status_line = "bad gateway";
890 /* strip connection listed hop-by-hop headers from response */
892 p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_out,
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));
899 ap_proxy_pre_http_request(origin,rp);
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),
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),
921 /* cancel keepalive if HTTP/1.0 or less */
922 if ((major < 1) || (minor < 1)) {
924 origin->keepalive = AP_CONN_CLOSE;
927 /* an http/0.9 response */
930 r->status_line = "200 OK";
934 if ( r->status != HTTP_CONTINUE ) {
935 received_continue = 0;
937 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
938 "proxy: HTTP: received 100 CONTINUE");
941 /* we must accept 3 kinds of date, but generate only 1 kind of date */
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));
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));
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));
958 /* munge the Location and URI response headers according to
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));
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));
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));
977 if ((r->status == 401) && (conf->error_override != 0)) {
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);
983 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
984 "proxy: origin server sent 401 without w-a header");
989 /* Is it an HTTP/0.9 response? If so, send the extra data */
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);
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 */
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.
1008 rp->headers_in = apr_table_copy(r->pool, r->headers_out);
1010 apr_table_unset(r->headers_out,"Transfer-Encoding");
1012 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1013 "proxy: start body send");
1016 * if we are overriding the errors, we can't put the content
1017 * of the page into the brigade
1019 if ( (conf->error_override ==0) || r->status < 400 ) {
1021 /* read the body, pass it to the output filters */
1023 while (ap_get_brigade(rp->input_filters,
1027 conf->io_buffer_size) == APR_SUCCESS) {
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);
1038 if (APR_BRIGADE_EMPTY(bb)) {
1039 apr_brigade_cleanup(bb);
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
1050 ap_proxy_http_cleanup(r, p_conn, backend);
1051 /* signal that we must leave */
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 */
1062 /* make sure we always clean up after ourselves */
1063 apr_brigade_cleanup(bb);
1065 /* if we are done, leave */
1066 if (TRUE == finish) {
1071 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1072 "proxy: end body send");
1074 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1075 "proxy: header only");
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 )
1084 /* clear r->status for override error, otherwise ErrorDocument
1085 * thinks that this is a recursive error, and doesn't find the
1088 int status = r->status;
1089 r->status = HTTP_OK;
1090 ap_discard_request_body(rp);
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
1104 /* if the connection is < HTTP/1.1, or Connection: close,
1105 * we close the socket, otherwise we leave it open for KeepAlive support
1107 if (p_conn->close || (r->proto_num < HTTP_VERSION(1,1))) {
1109 apr_socket_close(p_conn->sock);
1110 p_conn->sock = NULL;
1111 backend->connection = NULL;
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
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
1126 int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf,
1127 char *url, const char *proxyname,
1128 apr_port_t proxyport)
1131 char server_portstr[32];
1132 conn_rec *origin = NULL;
1133 proxy_conn_rec *backend = NULL;
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.
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.
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,
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);
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 */
1170 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1171 "proxy: HTTP: serving URL %s", url);
1174 /* only use stored info for top-level pages. Sub requests don't share
1178 backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
1179 &proxy_http_module);
1181 /* create space for state information */
1183 backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
1184 backend->connection = NULL;
1185 backend->hostname = NULL;
1188 ap_set_module_config(c->conn_config, &proxy_http_module, backend);
1192 backend->is_ssl = is_ssl;
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,
1198 sizeof(server_portstr));
1199 if ( status != OK ) {
1203 /* Step Two: Make the Connection */
1204 status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend,
1206 if ( status != OK ) {
1210 /* Step Three: Send the Request */
1211 status = ap_proxy_http_request(p, r, p_conn, origin, conf, uri, url,
1213 if ( status != OK ) {
1217 /* Step Four: Receive the Response */
1218 status = ap_proxy_http_process_response(p, r, p_conn, origin, backend, conf,
1220 if ( status != OK ) {
1221 /* clean up even if there is an error */
1222 ap_proxy_http_cleanup(r, p_conn, backend);
1226 /* Step Five: Clean Up */
1227 status = ap_proxy_http_cleanup(r, p_conn, backend);
1228 if ( status != OK ) {
1235 static void ap_proxy_http_register_hook(apr_pool_t *p)
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);
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 */