1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* HTTP routines for Apache proxy */
19 #include "mod_proxy.h"
21 module AP_MODULE_DECLARE_DATA proxy_http_module;
23 int ap_proxy_http_canon(request_rec *r, char *url);
24 int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf,
25 char *url, const char *proxyname,
26 apr_port_t proxyport);
36 static apr_status_t ap_proxy_http_cleanup(request_rec *r,
37 proxy_http_conn_t *p_conn,
38 proxy_conn_rec *backend);
41 * Canonicalise http-like URLs.
42 * scheme is the scheme for the URL
43 * url is the URL starting with the first '/'
44 * def_port is the default port for this scheme.
46 int ap_proxy_http_canon(request_rec *r, char *url)
48 char *host, *path, *search, sport[7];
51 apr_port_t port, def_port;
53 /* ap_port_of_scheme() */
54 if (strncasecmp(url, "http:", 5) == 0) {
58 else if (strncasecmp(url, "https:", 6) == 0) {
65 def_port = apr_uri_port_of_scheme(scheme);
67 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
68 "proxy: HTTP: canonicalising URL %s", url);
71 * We break the URL into host, port, path, search
74 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
76 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
77 "error parsing URL %s: %s",
79 return HTTP_BAD_REQUEST;
82 /* now parse path/search args, according to rfc1738 */
83 /* N.B. if this isn't a true proxy request, then the URL _path_
84 * has already been decoded. True proxy requests have r->uri
85 * == r->unparsed_uri, and no others have that property.
87 if (r->uri == r->unparsed_uri) {
88 search = strchr(url, '?');
96 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
98 return HTTP_BAD_REQUEST;
100 if (port != def_port)
101 apr_snprintf(sport, sizeof(sport), ":%d", port);
105 if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
106 host = apr_pstrcat(r->pool, "[", host, "]", NULL);
108 r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
109 "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
113 static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
115 struct proxy_alias *ent;
119 /* XXX FIXME: Make sure this handled the ambiguous case of the :80
120 * after the hostname */
123 ent = (struct proxy_alias *)conf->raliases->elts;
124 for (i = 0; i < conf->raliases->nelts; i++) {
125 l2 = strlen(ent[i].real);
126 if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) {
127 u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
128 return ap_construct_url(r->pool, u, r);
134 /* Clear all connection-based headers from the incoming headers table */
135 static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
138 char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
140 apr_table_unset(headers, "Proxy-Connection");
146 while (*next && !apr_isspace(*next) && (*next != ',')) {
149 while (*next && (apr_isspace(*next) || (*next == ','))) {
153 apr_table_unset(headers, name);
155 apr_table_unset(headers, "Connection");
159 apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r,
160 proxy_http_conn_t *p_conn,
162 proxy_server_conf *conf,
165 const char *proxyname,
166 apr_port_t proxyport,
167 char *server_portstr,
168 int server_portstr_size) {
171 apr_sockaddr_t *uri_addr;
173 * Break up the URL to determine the host to connect to
176 /* we break the URL into host, port, uri */
177 if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
178 return ap_proxyerror(r, HTTP_BAD_REQUEST,
179 apr_pstrcat(p,"URI cannot be parsed: ", *url,
183 uri->port = apr_uri_port_of_scheme(uri->scheme);
186 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
187 "proxy: HTTP connecting %s to %s:%d", *url, uri->hostname,
190 /* do a DNS lookup for the destination host */
191 /* see memory note above */
192 err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri->hostname),
193 APR_UNSPEC, uri->port, 0, c->pool);
195 /* allocate these out of the connection pool - the check on
196 * r->connection->id makes sure that this string does not get accessed
197 * past the connection lifetime */
198 /* are we connecting directly, or via a proxy? */
200 p_conn->name = apr_pstrdup(c->pool, proxyname);
201 p_conn->port = proxyport;
202 /* see memory note above */
203 err = apr_sockaddr_info_get(&p_conn->addr, p_conn->name, APR_UNSPEC,
204 p_conn->port, 0, c->pool);
206 p_conn->name = apr_pstrdup(c->pool, uri->hostname);
207 p_conn->port = uri->port;
208 p_conn->addr = uri_addr;
209 *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
210 uri->query ? uri->query : "",
211 uri->fragment ? "#" : "",
212 uri->fragment ? uri->fragment : "", NULL);
215 if (err != APR_SUCCESS) {
216 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
217 apr_pstrcat(p, "DNS lookup failure for: ",
218 p_conn->name, NULL));
221 /* Get the server port for the Via headers */
223 server_port = ap_get_server_port(r);
224 if (ap_is_default_port(server_port, r)) {
225 strcpy(server_portstr,"");
227 apr_snprintf(server_portstr, server_portstr_size, ":%d",
232 /* check if ProxyBlock directive on this host */
233 if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) {
234 return ap_proxyerror(r, HTTP_FORBIDDEN,
235 "Connect to remote machine blocked");
241 apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r,
242 proxy_http_conn_t *p_conn,
243 conn_rec *c, conn_rec **origin,
244 proxy_conn_rec *backend,
245 proxy_server_conf *conf,
246 const char *proxyname) {
248 apr_socket_t *client_socket = NULL;
250 /* We have determined who to connect to. Now make the connection, supporting
251 * a KeepAlive connection.
254 /* get all the possible IP addresses for the destname and loop through them
255 * until we get a successful connection
258 /* if a keepalive socket is already open, check whether it must stay
259 * open, or whether it should be closed and a new socket created.
261 /* see memory note above */
262 if (backend->connection) {
263 client_socket = ap_get_module_config(backend->connection->conn_config, &core_module);
264 if ((backend->connection->id == c->id) &&
265 (backend->port == p_conn->port) &&
266 (backend->hostname) &&
267 (!apr_strnatcasecmp(backend->hostname, p_conn->name))) {
268 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
269 "proxy: keepalive address match (keep original socket)");
271 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
272 "proxy: keepalive address mismatch / connection has"
273 " changed (close old socket (%s/%s, %d/%d))",
274 p_conn->name, backend->hostname, p_conn->port,
276 apr_socket_close(client_socket);
277 backend->connection = NULL;
281 /* get a socket - either a keepalive one, or a new one */
283 if ((backend->connection) && (backend->connection->id == c->id)) {
284 apr_size_t buffer_len = 1;
286 apr_status_t socket_status;
287 apr_interval_time_t current_timeout;
289 /* use previous keepalive socket */
290 *origin = backend->connection;
291 p_conn->sock = client_socket;
295 apr_socket_timeout_get(p_conn->sock, ¤t_timeout);
297 apr_socket_timeout_set(p_conn->sock, 0);
298 socket_status = apr_recv(p_conn->sock, test_buffer, &buffer_len);
299 /* put back old timeout */
300 apr_socket_timeout_set(p_conn->sock, current_timeout);
301 if ( APR_STATUS_IS_EOF(socket_status) ) {
302 ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
303 "proxy: previous connection is closed, creating a new connection.");
310 /* create a new socket */
311 backend->connection = NULL;
314 * At this point we have a list of one or more IP addresses of
315 * the machine to connect to. If configured, reorder this
316 * list so that the "best candidate" is first try. "best
317 * candidate" could mean the least loaded server, the fastest
318 * responding server, whatever.
320 * For now we do nothing, ie we get DNS round robin.
323 failed = ap_proxy_connect_to_backend(&p_conn->sock, "HTTP",
324 p_conn->addr, p_conn->name,
325 conf, r->server, c->pool);
327 /* handle a permanent error on the connect */
332 return HTTP_BAD_GATEWAY;
336 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
337 "proxy: socket is connected");
339 /* the socket is now open, create a new backend server connection */
340 *origin = ap_run_create_connection(c->pool, r->server, p_conn->sock,
342 r->connection->sbh, c->bucket_alloc);
344 /* the peer reset the connection already; ap_run_create_connection()
347 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
348 r->server, "proxy: an error occurred creating a "
349 "new connection to %pI (%s)", p_conn->addr,
351 apr_socket_close(p_conn->sock);
352 return HTTP_INTERNAL_SERVER_ERROR;
354 backend->connection = *origin;
355 backend->hostname = apr_pstrdup(c->pool, p_conn->name);
356 backend->port = p_conn->port;
358 if (backend->is_ssl) {
359 if (!ap_proxy_ssl_enable(backend->connection)) {
360 ap_log_error(APLOG_MARK, APLOG_ERR, 0,
361 r->server, "proxy: failed to enable ssl support "
362 "for %pI (%s)", p_conn->addr, p_conn->name);
363 return HTTP_INTERNAL_SERVER_ERROR;
367 ap_proxy_ssl_disable(backend->connection);
370 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
371 "proxy: connection complete to %pI (%s)",
372 p_conn->addr, p_conn->name);
374 /* set up the connection filters */
375 rc = ap_run_pre_connection(*origin, p_conn->sock);
376 if (rc != OK && rc != DONE) {
377 (*origin)->aborted = 1;
378 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
379 "proxy: HTTP: pre_connection setup failed (%d)",
387 static void add_te_chunked(apr_pool_t *p,
388 apr_bucket_alloc_t *bucket_alloc,
389 apr_bucket_brigade *header_brigade)
393 const char te_hdr[] = "Transfer-Encoding: chunked" CRLF;
395 buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1);
396 ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1);
398 e = apr_bucket_pool_create(buf, sizeof(te_hdr)-1, p, bucket_alloc);
399 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
402 static void add_cl(apr_pool_t *p,
403 apr_bucket_alloc_t *bucket_alloc,
404 apr_bucket_brigade *header_brigade,
410 buf = apr_pstrcat(p, "Content-Length: ",
414 ap_xlate_proto_to_ascii(buf, strlen(buf));
415 e = apr_bucket_pool_create(buf, strlen(buf), p, bucket_alloc);
416 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
419 #define ASCII_CRLF "\015\012"
420 #define ASCII_ZERO "\060"
422 static void terminate_headers(apr_bucket_alloc_t *bucket_alloc,
423 apr_bucket_brigade *header_brigade)
427 /* add empty line at the end of the headers */
428 e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
429 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
432 static apr_status_t pass_brigade(apr_bucket_alloc_t *bucket_alloc,
433 request_rec *r, proxy_http_conn_t *p_conn,
434 conn_rec *origin, apr_bucket_brigade *bb,
440 apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
441 APR_BRIGADE_INSERT_TAIL(bb, e);
443 status = ap_pass_brigade(origin->output_filters, bb);
444 if (status != APR_SUCCESS) {
445 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
446 "proxy: pass request body failed to %pI (%s)",
447 p_conn->addr, p_conn->name);
450 apr_brigade_cleanup(bb);
454 static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
456 proxy_http_conn_t *p_conn,
458 apr_bucket_brigade *header_brigade,
459 apr_bucket_brigade *input_brigade)
465 apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
466 apr_bucket_brigade *bb;
469 add_te_chunked(p, bucket_alloc, header_brigade);
470 terminate_headers(bucket_alloc, header_brigade);
472 while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
474 char chunk_hdr[20]; /* must be here due to transient bucket. */
476 /* If this brigade contains EOS, either stop or remove it. */
477 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
480 /* We can't pass this EOS to the output_filters. */
481 e = APR_BRIGADE_LAST(input_brigade);
482 apr_bucket_delete(e);
485 apr_brigade_length(input_brigade, 1, &bytes);
487 hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
488 "%" APR_UINT64_T_HEX_FMT CRLF,
489 (apr_uint64_t)bytes);
491 ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
492 e = apr_bucket_transient_create(chunk_hdr, hdr_len,
494 APR_BRIGADE_INSERT_HEAD(input_brigade, e);
497 * Append the end-of-chunk CRLF
499 e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
500 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
502 if (header_brigade) {
503 /* we never sent the header brigade, so go ahead and
504 * take care of that now
509 * Save input_brigade in bb brigade. (At least) in the SSL case
510 * input_brigade contains transient buckets whose data would get
511 * overwritten during the next call of ap_get_brigade in the loop.
512 * ap_save_brigade ensures these buckets to be set aside.
513 * Calling ap_save_brigade with NULL as filter is OK, because
514 * bb brigade already has been created and does not need to get
515 * created by ap_save_brigade.
517 status = ap_save_brigade(NULL, &bb, &input_brigade, p);
518 if (status != APR_SUCCESS) {
522 header_brigade = NULL;
528 /* The request is flushed below this loop with chunk EOS header */
529 status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0);
530 if (status != APR_SUCCESS) {
538 status = ap_get_brigade(r->input_filters, input_brigade,
539 AP_MODE_READBYTES, APR_BLOCK_READ,
542 if (status != APR_SUCCESS) {
547 if (header_brigade) {
548 /* we never sent the header brigade because there was no request body;
554 if (!APR_BRIGADE_EMPTY(input_brigade)) {
555 /* input brigade still has an EOS which we can't pass to the output_filters. */
556 e = APR_BRIGADE_LAST(input_brigade);
557 AP_DEBUG_ASSERT(APR_BUCKET_IS_EOS(e));
558 apr_bucket_delete(e);
563 e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
567 APR_BRIGADE_INSERT_TAIL(bb, e);
569 /* Now we have headers-only, or the chunk EOS mark; flush it */
570 status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
574 static apr_status_t stream_reqbody_cl(apr_pool_t *p,
576 proxy_http_conn_t *p_conn,
578 apr_bucket_brigade *header_brigade,
579 apr_bucket_brigade *input_brigade,
580 const char *old_cl_val)
583 apr_status_t status = APR_SUCCESS;
584 apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
585 apr_bucket_brigade *bb;
587 apr_off_t cl_val = 0;
589 apr_off_t bytes_streamed = 0;
592 add_cl(p, bucket_alloc, header_brigade, old_cl_val);
593 cl_val = atol(old_cl_val);
595 terminate_headers(bucket_alloc, header_brigade);
597 while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
599 apr_brigade_length(input_brigade, 1, &bytes);
600 bytes_streamed += bytes;
602 /* If this brigade contains EOS, either stop or remove it. */
603 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
606 /* We can't pass this EOS to the output_filters. */
607 e = APR_BRIGADE_LAST(input_brigade);
608 apr_bucket_delete(e);
611 /* C-L < bytes streamed?!?
612 * We will error out after the body is completely
613 * consumed, but we can't stream more bytes at the
614 * back end since they would in part be interpreted
615 * as another request! If nothing is sent, then
618 * Prevents HTTP Response Splitting.
620 if (bytes_streamed > cl_val)
623 if (header_brigade) {
624 /* we never sent the header brigade, so go ahead and
625 * take care of that now
630 * Save input_brigade in bb brigade. (At least) in the SSL case
631 * input_brigade contains transient buckets whose data would get
632 * overwritten during the next call of ap_get_brigade in the loop.
633 * ap_save_brigade ensures these buckets to be set aside.
634 * Calling ap_save_brigade with NULL as filter is OK, because
635 * bb brigade already has been created and does not need to get
636 * created by ap_save_brigade.
638 status = ap_save_brigade(NULL, &bb, &input_brigade, p);
639 if (status != APR_SUCCESS) {
643 header_brigade = NULL;
649 /* Once we hit EOS, we are ready to flush. */
650 status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos);
651 if (status != APR_SUCCESS) {
659 status = ap_get_brigade(r->input_filters, input_brigade,
660 AP_MODE_READBYTES, APR_BLOCK_READ,
663 if (status != APR_SUCCESS) {
668 if (bytes_streamed != cl_val) {
669 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
670 "proxy: client %s given Content-Length did not match"
671 " number of body bytes read", r->connection->remote_ip);
675 if (header_brigade) {
676 /* we never sent the header brigade since there was no request
677 * body; send it now with the flush flag
680 status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
685 #define MAX_MEM_SPOOL 16384
687 static apr_status_t spool_reqbody_cl(apr_pool_t *p,
689 proxy_http_conn_t *p_conn,
691 apr_bucket_brigade *header_brigade,
692 apr_bucket_brigade *input_brigade,
697 apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
698 apr_bucket_brigade *body_brigade;
700 apr_off_t bytes, bytes_spooled = 0, fsize = 0;
701 apr_file_t *tmpfile = NULL;
703 body_brigade = apr_brigade_create(p, bucket_alloc);
705 while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
707 /* If this brigade contains EOS, either stop or remove it. */
708 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
711 /* We can't pass this EOS to the output_filters. */
712 e = APR_BRIGADE_LAST(input_brigade);
713 apr_bucket_delete(e);
716 apr_brigade_length(input_brigade, 1, &bytes);
718 if (bytes_spooled + bytes > MAX_MEM_SPOOL) {
719 /* can't spool any more in memory; write latest brigade to disk */
720 if (tmpfile == NULL) {
721 const char *temp_dir;
724 status = apr_temp_dir_get(&temp_dir, p);
725 if (status != APR_SUCCESS) {
726 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
727 "proxy: search for temporary directory failed");
730 apr_filepath_merge(&template, temp_dir,
731 "modproxy.tmp.XXXXXX",
732 APR_FILEPATH_NATIVE, p);
733 status = apr_file_mktemp(&tmpfile, template, 0, p);
734 if (status != APR_SUCCESS) {
735 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
736 "proxy: creation of temporary file in directory %s failed",
741 for (e = APR_BRIGADE_FIRST(input_brigade);
742 e != APR_BRIGADE_SENTINEL(input_brigade);
743 e = APR_BUCKET_NEXT(e)) {
745 apr_size_t bytes_read, bytes_written;
747 apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
748 status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
749 if (status != APR_SUCCESS) {
750 const char *tmpfile_name;
752 if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
753 tmpfile_name = "(unknown)";
755 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
756 "proxy: write to temporary file %s failed",
760 AP_DEBUG_ASSERT(bytes_read == bytes_written);
761 fsize += bytes_written;
763 apr_brigade_cleanup(input_brigade);
768 * Save input_brigade in body_brigade. (At least) in the SSL case
769 * input_brigade contains transient buckets whose data would get
770 * overwritten during the next call of ap_get_brigade in the loop.
771 * ap_save_brigade ensures these buckets to be set aside.
772 * Calling ap_save_brigade with NULL as filter is OK, because
773 * body_brigade already has been created and does not need to get
774 * created by ap_save_brigade.
776 status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
777 if (status != APR_SUCCESS) {
783 bytes_spooled += bytes;
789 status = ap_get_brigade(r->input_filters, input_brigade,
790 AP_MODE_READBYTES, APR_BLOCK_READ,
793 if (status != APR_SUCCESS) {
798 if (bytes_spooled || force_cl) {
799 add_cl(p, bucket_alloc, header_brigade, apr_off_t_toa(p, bytes_spooled));
801 terminate_headers(bucket_alloc, header_brigade);
802 APR_BRIGADE_CONCAT(header_brigade, body_brigade);
804 /* For platforms where the size of the file may be larger than
805 * that which can be stored in a single bucket (where the
806 * length field is an apr_size_t), split it into several
808 if (sizeof(apr_off_t) > sizeof(apr_size_t)
809 && fsize > AP_MAX_SENDFILE) {
810 e = apr_bucket_file_create(tmpfile, 0, AP_MAX_SENDFILE, p,
812 while (fsize > AP_MAX_SENDFILE) {
814 apr_bucket_copy(e, &ce);
815 APR_BRIGADE_INSERT_TAIL(header_brigade, ce);
816 e->start += AP_MAX_SENDFILE;
817 fsize -= AP_MAX_SENDFILE;
819 e->length = (apr_size_t)fsize; /* Resize just the last bucket */
822 e = apr_bucket_file_create(tmpfile, 0, (apr_size_t)fsize, p,
825 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
827 /* This is all a single brigade, pass with flush flagged */
828 status = pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1);
833 apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
834 proxy_http_conn_t *p_conn, conn_rec *origin,
835 proxy_server_conf *conf,
838 apr_bucket_brigade *header_brigade,
839 char *server_portstr)
841 conn_rec *c = r->connection;
842 apr_bucket_alloc_t *bucket_alloc = c->bucket_alloc;
843 apr_bucket_brigade *input_brigade;
844 apr_bucket_brigade *temp_brigade;
847 const apr_array_header_t *headers_in_array;
848 const apr_table_entry_t *headers_in;
851 enum rb_methods {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED, RB_SPOOL_CL};
852 enum rb_methods rb_method = RB_INIT;
853 const char *old_cl_val = NULL;
854 const char *old_te_val = NULL;
855 apr_off_t bytes_read = 0;
860 * Send the HTTP/1.1 request to the remote server
863 /* strip connection listed hop-by-hop headers from the request */
864 /* even though in theory a connection: close coming from the client
865 * should not affect the connection to the server, it's unlikely
866 * that subsequent client requests will hit this thread/process,
867 * so we cancel server keepalive if the client does.
869 if (ap_proxy_liststr(apr_table_get(r->headers_in,
870 "Connection"), "close")) {
872 /* XXX: we are abusing r->headers_in rather than a copy,
873 * give the core output handler a clue the client would
876 c->keepalive = AP_CONN_CLOSE;
878 ap_proxy_clear_connection(p, r->headers_in);
880 if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
881 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
885 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
888 if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
889 origin->keepalive = AP_CONN_CLOSE;
892 ap_xlate_proto_to_ascii(buf, strlen(buf));
893 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
894 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
895 if (conf->preserve_host == 0) {
896 if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
897 buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str,
900 buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
904 /* don't want to use r->hostname, as the incoming header might have a
907 const char* hostname = apr_table_get(r->headers_in,"Host");
909 hostname = r->server->server_hostname;
910 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
911 "proxy: no HTTP 0.9 request (with no host line) "
912 "on incoming request and preserve host set "
913 "forcing hostname to be %s for uri %s",
917 buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
919 ap_xlate_proto_to_ascii(buf, strlen(buf));
920 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
921 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
924 if (conf->viaopt == via_block) {
925 /* Block all outgoing Via: headers */
926 apr_table_unset(r->headers_in, "Via");
927 } else if (conf->viaopt != via_off) {
928 const char *server_name = ap_get_server_name(r);
929 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
930 * then the server name returned by ap_get_server_name() is the
931 * origin server name (which does make too much sense with Via: headers)
932 * so we use the proxy vhost's name instead.
934 if (server_name == r->hostname)
935 server_name = r->server->server_hostname;
936 /* Create a "Via:" request header entry and merge it */
937 /* Generate outgoing Via: header with/without server comment: */
938 apr_table_mergen(r->headers_in, "Via",
939 (conf->viaopt == via_full)
940 ? apr_psprintf(p, "%d.%d %s%s (%s)",
941 HTTP_VERSION_MAJOR(r->proto_num),
942 HTTP_VERSION_MINOR(r->proto_num),
943 server_name, server_portstr,
944 AP_SERVER_BASEVERSION)
945 : apr_psprintf(p, "%d.%d %s%s",
946 HTTP_VERSION_MAJOR(r->proto_num),
947 HTTP_VERSION_MINOR(r->proto_num),
948 server_name, server_portstr)
952 /* X-Forwarded-*: handling
957 * These request headers are only really useful when the mod_proxy
958 * is used in a reverse proxy configuration, so that useful info
959 * about the client can be passed through the reverse proxy and on
960 * to the backend server, which may require the information to
963 * In a forward proxy situation, these options are a potential
964 * privacy violation, as information about clients behind the proxy
965 * are revealed to arbitrary servers out there on the internet.
967 * The HTTP/1.1 Via: header is designed for passing client
968 * information through proxies to a server, and should be used in
969 * a forward proxy configuation instead of X-Forwarded-*. See the
970 * ProxyVia option for details.
973 if (PROXYREQ_REVERSE == r->proxyreq) {
976 /* Add X-Forwarded-For: so that the upstream has a chance to
977 * determine, where the original request came from.
979 apr_table_mergen(r->headers_in, "X-Forwarded-For",
980 r->connection->remote_ip);
982 /* Add X-Forwarded-Host: so that upstream knows what the
983 * original request hostname was.
985 if ((buf = apr_table_get(r->headers_in, "Host"))) {
986 apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
989 /* Add X-Forwarded-Server: so that upstream knows what the
990 * name of this proxy server is (if there are more than one)
991 * XXX: This duplicates Via: - do we strictly need it?
993 apr_table_mergen(r->headers_in, "X-Forwarded-Server",
994 r->server->server_hostname);
997 /* send request headers */
999 headers_in_array = apr_table_elts(r->headers_in);
1000 headers_in = (const apr_table_entry_t *) headers_in_array->elts;
1001 for (counter = 0; counter < headers_in_array->nelts; counter++) {
1002 if (headers_in[counter].key == NULL
1003 || headers_in[counter].val == NULL
1006 || !strcasecmp(headers_in[counter].key, "Host")
1008 /* Clear out hop-by-hop request headers not to send
1009 * RFC2616 13.5.1 says we should strip these headers
1011 || !strcasecmp(headers_in[counter].key, "Keep-Alive")
1012 || !strcasecmp(headers_in[counter].key, "TE")
1013 || !strcasecmp(headers_in[counter].key, "Trailer")
1014 || !strcasecmp(headers_in[counter].key, "Upgrade")
1016 /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be
1017 * suppressed if THIS server requested the authentication,
1018 * not when a frontend proxy requested it!
1020 * The solution to this problem is probably to strip out
1021 * the Proxy-Authorisation header in the authorisation
1022 * code itself, not here. This saves us having to signal
1023 * somehow whether this request was authenticated or not.
1025 || !strcasecmp(headers_in[counter].key,"Proxy-Authorization")
1026 || !strcasecmp(headers_in[counter].key,"Proxy-Authenticate")) {
1030 /* Skip Transfer-Encoding and Content-Length for now.
1032 if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) {
1033 old_te_val = headers_in[counter].val;
1036 if (!strcasecmp(headers_in[counter].key, "Content-Length")) {
1037 old_cl_val = headers_in[counter].val;
1041 /* for sub-requests, ignore freshness/expiry headers */
1043 if ( !strcasecmp(headers_in[counter].key, "If-Match")
1044 || !strcasecmp(headers_in[counter].key, "If-Modified-Since")
1045 || !strcasecmp(headers_in[counter].key, "If-Range")
1046 || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since")
1047 || !strcasecmp(headers_in[counter].key, "If-None-Match")) {
1052 buf = apr_pstrcat(p, headers_in[counter].key, ": ",
1053 headers_in[counter].val, CRLF,
1055 ap_xlate_proto_to_ascii(buf, strlen(buf));
1056 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
1057 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
1060 /* We have headers, let's figure out our request body... */
1061 input_brigade = apr_brigade_create(p, bucket_alloc);
1063 /* sub-requests never use keepalives, and mustn't pass request bodies.
1064 * Because the new logic looks at input_brigade, we will self-terminate
1065 * input_brigade and jump past all of the request body logic...
1066 * Reading anything with ap_get_brigade is likely to consume the
1067 * main request's body or read beyond EOS - which would be unplesant.
1073 apr_table_unset(r->headers_in, "Content-Length");
1077 apr_table_unset(r->headers_in, "Transfer-Encoding");
1079 rb_method = RB_STREAM_CL;
1080 e = apr_bucket_eos_create(input_brigade->bucket_alloc);
1081 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
1085 /* WE only understand chunked. Other modules might inject
1086 * (and therefore, decode) other flavors but we don't know
1087 * that the can and have done so unless they they remove
1088 * their decoding from the headers_in T-E list.
1089 * XXX: Make this extensible, but in doing so, presume the
1090 * encoding has been done by the extensions' handler, and
1091 * do not modify add_te_chunked's logic
1093 if (old_te_val && strcmp(old_te_val, "chunked") != 0) {
1094 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1095 "proxy: %s Transfer-Encoding is not supported",
1100 if (old_cl_val && old_te_val) {
1101 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_ENOTIMPL, r->server,
1102 "proxy: client %s (%s) requested Transfer-Encoding body"
1103 " with Content-Length (C-L ignored)",
1104 c->remote_ip, c->remote_host ? c->remote_host: "");
1105 apr_table_unset(r->headers_in, "Content-Length");
1107 origin->keepalive = AP_CONN_CLOSE;
1111 /* Prefetch MAX_MEM_SPOOL bytes
1113 * This helps us avoid any election of C-L v.s. T-E
1114 * request bodies, since we are willing to keep in
1115 * memory this much data, in any case. This gives
1116 * us an instant C-L election if the body is of some
1119 temp_brigade = apr_brigade_create(p, bucket_alloc);
1121 status = ap_get_brigade(r->input_filters, temp_brigade,
1122 AP_MODE_READBYTES, APR_BLOCK_READ,
1123 MAX_MEM_SPOOL - bytes_read);
1124 if (status != APR_SUCCESS) {
1125 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
1126 "proxy: prefetch request body failed to %s"
1128 p_conn->name ? p_conn->name: "",
1129 c->remote_ip, c->remote_host ? c->remote_host: "");
1133 apr_brigade_length(temp_brigade, 1, &bytes);
1134 bytes_read += bytes;
1137 * Save temp_brigade in input_brigade. (At least) in the SSL case
1138 * temp_brigade contains transient buckets whose data would get
1139 * overwritten during the next call of ap_get_brigade in the loop.
1140 * ap_save_brigade ensures these buckets to be set aside.
1141 * Calling ap_save_brigade with NULL as filter is OK, because
1142 * input_brigade already has been created and does not need to get
1143 * created by ap_save_brigade.
1145 status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p);
1146 if (status != APR_SUCCESS) {
1147 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
1148 "proxy: processing prefetched request body failed"
1149 " to %s from %s (%s)",
1150 p_conn->name ? p_conn->name: "",
1151 c->remote_ip, c->remote_host ? c->remote_host: "");
1155 /* Ensure we don't hit a wall where we have a buffer too small
1156 * for ap_get_brigade's filters to fetch us another bucket,
1157 * surrender once we hit 80 bytes less than MAX_MEM_SPOOL
1158 * (an arbitrary value.)
1160 } while ((bytes_read < MAX_MEM_SPOOL - 80)
1161 && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)));
1163 /* Use chunked request body encoding or send a content-length body?
1167 * We have no request body (handled by RB_STREAM_CL)
1169 * We have a request body length <= MAX_MEM_SPOOL
1171 * The administrator has setenv force-proxy-request-1.0
1173 * The client sent a C-L body, and the administrator has
1174 * not setenv proxy-sendchunked or has set setenv proxy-sendcl
1176 * The client sent a T-E body, and the administrator has
1177 * setenv proxy-sendcl, and not setenv proxy-sendchunked
1179 * If both proxy-sendcl and proxy-sendchunked are set, the
1180 * behavior is the same as if neither were set, large bodies
1181 * that can't be read will be forwarded in their original
1182 * form of C-L, or T-E.
1184 * To ensure maximum compatibility, setenv proxy-sendcl
1185 * To reduce server resource use, setenv proxy-sendchunked
1187 * Then address specific servers with conditional setenv
1188 * options to restore the default behavior where desireable.
1190 * We have to compute content length by reading the entire request
1191 * body; if request body is not small, we'll spool the remaining
1192 * input to a temporary file. Chunked is always preferable.
1194 * We can only trust the client-provided C-L if the T-E header
1195 * is absent, and the filters are unchanged (the body won't
1196 * be resized by another content filter).
1198 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
1199 /* The whole thing fit, so our decision is trivial, use
1200 * the filtered bytes read from the client for the request
1201 * body Content-Length.
1203 * If we expected no body, and read no body, do not set
1204 * the Content-Length.
1206 if (old_cl_val || old_te_val || bytes_read) {
1207 old_cl_val = apr_off_t_toa(r->pool, bytes_read);
1209 rb_method = RB_STREAM_CL;
1211 else if (old_te_val) {
1213 || (apr_table_get(r->subprocess_env, "proxy-sendcl")
1214 && !apr_table_get(r->subprocess_env, "proxy-sendchunks")
1215 && !apr_table_get(r->subprocess_env, "proxy-sendchunked"))) {
1216 rb_method = RB_SPOOL_CL;
1219 rb_method = RB_STREAM_CHUNKED;
1222 else if (old_cl_val) {
1223 if (r->input_filters == r->proto_input_filters) {
1224 rb_method = RB_STREAM_CL;
1227 && (apr_table_get(r->subprocess_env, "proxy-sendchunks")
1228 || apr_table_get(r->subprocess_env, "proxy-sendchunked"))
1229 && !apr_table_get(r->subprocess_env, "proxy-sendcl")) {
1230 rb_method = RB_STREAM_CHUNKED;
1233 rb_method = RB_SPOOL_CL;
1237 /* This is an appropriate default; very efficient for no-body
1238 * requests, and has the behavior that it will not add any C-L
1239 * when the old_cl_val is NULL.
1241 rb_method = RB_SPOOL_CL;
1244 /* Yes I hate gotos. This is the subrequest shortcut */
1246 /* Handle Connection: header */
1247 if (!force10 && p_conn->close) {
1248 buf = apr_pstrdup(p, "Connection: close" CRLF);
1249 ap_xlate_proto_to_ascii(buf, strlen(buf));
1250 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
1251 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
1254 /* send the request body, if any. */
1256 case RB_STREAM_CHUNKED:
1257 status = stream_reqbody_chunked(p, r, p_conn, origin, header_brigade,
1261 status = stream_reqbody_cl(p, r, p_conn, origin, header_brigade,
1262 input_brigade, old_cl_val);
1265 status = spool_reqbody_cl(p, r, p_conn, origin, header_brigade,
1266 input_brigade, (old_cl_val != NULL)
1267 || (old_te_val != NULL)
1268 || (bytes_read > 0));
1275 if (status != APR_SUCCESS) {
1276 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
1277 "proxy: pass request body failed to %pI (%s)"
1279 p_conn->addr, p_conn->name ? p_conn->name: "",
1280 c->remote_ip, c->remote_host ? c->remote_host: "");
1287 static int addit_dammit(void *v, const char *key, const char *val)
1289 apr_table_addn(v, key, val);
1294 * Limit the number of interim respones we sent back to the client. Otherwise
1295 * we suffer from a memory build up. Besides there is NO sense in sending back
1296 * an unlimited number of interim responses to the client. Thus if we cross
1297 * this limit send back a 502 (Bad Gateway).
1299 #ifndef AP_MAX_INTERIM_RESPONSES
1300 #define AP_MAX_INTERIM_RESPONSES 10
1304 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
1305 proxy_http_conn_t *p_conn,
1307 proxy_conn_rec *backend,
1308 proxy_server_conf *conf,
1309 apr_bucket_brigade *bb,
1310 char *server_portstr) {
1311 conn_rec *c = r->connection;
1312 char buffer[HUGE_STRING_LEN];
1317 apr_table_t *save_table;
1318 int len, backasswards;
1319 int received_continue = 1; /* flag to indicate if we should
1320 * loop over response parsing logic
1321 * in the case that the origin told us
1325 /* Get response from the remote server, and pass it up the
1329 rp = ap_proxy_make_fake_req(origin, r);
1330 /* In case anyone needs to know, this is a fake request that is really a
1333 rp->proxyreq = PROXYREQ_RESPONSE;
1335 while (received_continue && (received_continue <= AP_MAX_INTERIM_RESPONSES)) {
1336 apr_brigade_cleanup(bb);
1338 len = ap_getline(buffer, sizeof(buffer), rp, 0);
1340 /* handle one potential stray CRLF */
1341 len = ap_getline(buffer, sizeof(buffer), rp, 0);
1344 apr_socket_close(p_conn->sock);
1345 backend->connection = NULL;
1346 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1347 "proxy: error reading status line from remote "
1348 "server %s", p_conn->name);
1349 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1350 "Error reading from remote server");
1353 /* Is it an HTTP/1 response?
1354 * This is buggy if we ever see an HTTP/1.10
1356 if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
1359 if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
1363 /* If not an HTTP/1 message or
1364 * if the status line was > 8192 bytes
1366 else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) {
1367 apr_socket_close(p_conn->sock);
1368 backend->connection = NULL;
1369 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1370 apr_pstrcat(p, "Corrupt status line returned by remote "
1371 "server: ", buffer, NULL));
1375 keepchar = buffer[12];
1377 r->status = atoi(&buffer[9]);
1379 if (keepchar != '\0') {
1380 buffer[12] = keepchar;
1382 /* 2616 requires the space in Status-Line; the origin
1383 * server may have sent one but ap_rgetline_core will
1384 * have stripped it. */
1388 r->status_line = apr_pstrdup(p, &buffer[9]);
1390 /* read the headers. */
1391 /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
1392 /* Also, take care with headers with multiple occurences. */
1394 /* First, tuck away all already existing cookies */
1395 save_table = apr_table_make(r->pool, 2);
1396 apr_table_do(addit_dammit, save_table, r->headers_out,
1397 "Set-Cookie", NULL);
1399 r->headers_out = ap_proxy_read_headers(r, rp, buffer,
1400 sizeof(buffer), origin);
1401 if (r->headers_out == NULL) {
1402 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
1403 r->server, "proxy: bad HTTP/%d.%d header "
1404 "returned by %s (%s)", major, minor, r->uri,
1408 * ap_send_error relies on a headers_out to be present. we
1409 * are in a bad position here.. so force everything we send out
1410 * to have nothing to do with the incoming packet
1412 r->headers_out = apr_table_make(r->pool,1);
1413 r->status = HTTP_BAD_GATEWAY;
1414 r->status_line = "bad gateway";
1418 /* Now, add in the just read cookies */
1419 apr_table_do(addit_dammit, save_table, r->headers_out,
1420 "Set-Cookie", NULL);
1422 /* and now load 'em all in */
1423 if (!apr_is_empty_table(save_table)) {
1424 apr_table_unset(r->headers_out, "Set-Cookie");
1425 r->headers_out = apr_table_overlay(r->pool,
1430 /* can't have both Content-Length and Transfer-Encoding */
1431 if (apr_table_get(r->headers_out, "Transfer-Encoding")
1432 && apr_table_get(r->headers_out, "Content-Length")) {
1433 /* 2616 section 4.4, point 3: "if both Transfer-Encoding
1434 * and Content-Length are received, the latter MUST be
1435 * ignored"; so unset it here to prevent any confusion
1437 apr_table_unset(r->headers_out, "Content-Length");
1438 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1440 "proxy: server %s returned Transfer-Encoding and Content-Length",
1445 /* strip connection listed hop-by-hop headers from response */
1446 p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_out,
1449 ap_proxy_clear_connection(p, r->headers_out);
1450 if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
1451 ap_set_content_type(r, apr_pstrdup(p, buf));
1453 if (!ap_is_HTTP_INFO(r->status)) {
1454 ap_proxy_pre_http_request(origin, rp);
1457 /* handle Via header in response */
1458 if (conf->viaopt != via_off && conf->viaopt != via_block) {
1459 const char *server_name = ap_get_server_name(r);
1460 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
1461 * then the server name returned by ap_get_server_name() is the
1462 * origin server name (which does make too much sense with Via: headers)
1463 * so we use the proxy vhost's name instead.
1465 if (server_name == r->hostname)
1466 server_name = r->server->server_hostname;
1468 /* create a "Via:" response header entry and merge it */
1469 apr_table_mergen(r->headers_out, "Via",
1470 (conf->viaopt == via_full)
1471 ? apr_psprintf(p, "%d.%d %s%s (%s)",
1472 HTTP_VERSION_MAJOR(r->proto_num),
1473 HTTP_VERSION_MINOR(r->proto_num),
1476 AP_SERVER_BASEVERSION)
1477 : apr_psprintf(p, "%d.%d %s%s",
1478 HTTP_VERSION_MAJOR(r->proto_num),
1479 HTTP_VERSION_MINOR(r->proto_num),
1485 /* cancel keepalive if HTTP/1.0 or less */
1486 if ((major < 1) || (minor < 1)) {
1488 origin->keepalive = AP_CONN_CLOSE;
1491 /* an http/0.9 response */
1494 r->status_line = "200 OK";
1498 if ( r->status != HTTP_CONTINUE ) {
1499 received_continue = 0;
1501 received_continue++;
1502 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1503 "proxy: HTTP: received 100 CONTINUE");
1506 /* we must accept 3 kinds of date, but generate only 1 kind of date */
1507 if ((buf = apr_table_get(r->headers_out, "Date")) != NULL) {
1508 apr_table_set(r->headers_out, "Date",
1509 ap_proxy_date_canon(p, buf));
1511 if ((buf = apr_table_get(r->headers_out, "Expires")) != NULL) {
1512 apr_table_set(r->headers_out, "Expires",
1513 ap_proxy_date_canon(p, buf));
1515 if ((buf = apr_table_get(r->headers_out, "Last-Modified")) != NULL) {
1516 apr_table_set(r->headers_out, "Last-Modified",
1517 ap_proxy_date_canon(p, buf));
1520 /* munge the Location and URI response headers according to
1523 if ((buf = apr_table_get(r->headers_out, "Location")) != NULL) {
1524 apr_table_set(r->headers_out, "Location",
1525 ap_proxy_location_reverse_map(r, conf, buf));
1527 if ((buf = apr_table_get(r->headers_out, "Content-Location")) != NULL) {
1528 apr_table_set(r->headers_out, "Content-Location",
1529 ap_proxy_location_reverse_map(r, conf, buf));
1531 if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) {
1532 apr_table_set(r->headers_out, "URI",
1533 ap_proxy_location_reverse_map(r, conf, buf));
1536 if ((r->status == 401) && (conf->error_override != 0)) {
1537 const char *wa = "WWW-Authenticate";
1538 if ((buf = apr_table_get(r->headers_out, wa))) {
1539 apr_table_set(r->err_headers_out, wa, buf);
1541 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1542 "proxy: origin server sent 401 without WWW-Authenticate header");
1547 /* Is it an HTTP/0.9 response? If so, send the extra data */
1549 apr_ssize_t cntr = len;
1550 e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
1551 APR_BRIGADE_INSERT_TAIL(bb, e);
1554 /* send body - but only if a body is expected */
1555 if ((!r->header_only) && /* not HEAD request */
1556 (r->status > 199) && /* not any 1xx response */
1557 (r->status != HTTP_NO_CONTENT) && /* not 204 */
1558 (r->status != HTTP_RESET_CONTENT) && /* not 205 */
1559 (r->status != HTTP_NOT_MODIFIED)) { /* not 304 */
1561 /* We need to copy the output headers and treat them as input
1562 * headers as well. BUT, we need to do this before we remove
1563 * TE, so that they are preserved accordingly for
1564 * ap_http_filter to know where to end.
1566 rp->headers_in = apr_table_copy(r->pool, r->headers_out);
1568 apr_table_unset(r->headers_out,"Transfer-Encoding");
1570 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1571 "proxy: start body send");
1574 * if we are overriding the errors, we can't put the content
1575 * of the page into the brigade
1577 if ( (conf->error_override ==0) || r->status < 400 ) {
1579 /* read the body, pass it to the output filters */
1581 while (ap_get_brigade(rp->input_filters,
1585 conf->io_buffer_size) == APR_SUCCESS) {
1588 apr_off_t readbytes;
1589 apr_brigade_length(bb, 0, &readbytes);
1590 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1591 r->server, "proxy (PID %d): readbytes: %#x",
1592 getpid(), readbytes);
1596 if (APR_BRIGADE_EMPTY(bb)) {
1597 apr_brigade_cleanup(bb);
1601 /* found the last brigade? */
1602 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
1603 /* if this is the last brigade, cleanup the
1604 * backend connection first to prevent the
1605 * backend server from hanging around waiting
1606 * for a slow client to eat these bytes
1608 ap_proxy_http_cleanup(r, p_conn, backend);
1609 /* signal that we must leave */
1613 /* try send what we read */
1614 if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS
1616 /* Ack! Phbtt! Die! User aborted! */
1617 p_conn->close = 1; /* this causes socket close below */
1621 /* make sure we always clean up after ourselves */
1622 apr_brigade_cleanup(bb);
1624 /* if we are done, leave */
1625 if (TRUE == finish) {
1630 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1631 "proxy: end body send");
1633 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1634 "proxy: header only");
1638 /* See define of AP_MAX_INTERIM_RESPONSES for why */
1639 if (received_continue > AP_MAX_INTERIM_RESPONSES) {
1640 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1642 "Too many (%d) interim responses from origin server",
1643 received_continue));
1646 if ( conf->error_override ) {
1647 /* the code above this checks for 'OK' which is what the hook expects */
1648 if ( r->status == HTTP_OK )
1651 /* clear r->status for override error, otherwise ErrorDocument
1652 * thinks that this is a recursive error, and doesn't find the
1655 int status = r->status;
1656 r->status = HTTP_OK;
1657 /* Discard body, if one is expected */
1658 if ((status > 199) && /* not any 1xx response */
1659 (status != HTTP_NO_CONTENT) && /* not 204 */
1660 (status != HTTP_RESET_CONTENT) && /* not 205 */
1661 (status != HTTP_NOT_MODIFIED)) { /* not 304 */
1662 ap_discard_request_body(rp);
1671 apr_status_t ap_proxy_http_cleanup(request_rec *r, proxy_http_conn_t *p_conn,
1672 proxy_conn_rec *backend) {
1673 /* If there are no KeepAlives, or if the connection has been signalled
1674 * to close, close the socket and clean up
1677 /* if the connection is < HTTP/1.1, or Connection: close,
1678 * we close the socket, otherwise we leave it open for KeepAlive support
1680 if (p_conn->close || (r->proto_num < HTTP_VERSION(1,1))) {
1682 apr_socket_close(p_conn->sock);
1683 p_conn->sock = NULL;
1684 backend->connection = NULL;
1691 * This handles http:// URLs, and other URLs using a remote proxy over http
1692 * If proxyhost is NULL, then contact the server directly, otherwise
1694 * Note that if a proxy is used, then URLs other than http: can be accessed,
1695 * also, if we have trouble which is clearly specific to the proxy, then
1696 * we return DECLINED so that we can try another proxy. (Or the direct
1699 int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf,
1700 char *url, const char *proxyname,
1701 apr_port_t proxyport)
1704 char server_portstr[32];
1705 conn_rec *origin = NULL;
1706 proxy_conn_rec *backend = NULL;
1709 /* Note: Memory pool allocation.
1710 * A downstream keepalive connection is always connected to the existence
1711 * (or not) of an upstream keepalive connection. If this is not done then
1712 * load balancing against multiple backend servers breaks (one backend
1713 * server ends up taking 100% of the load), and the risk is run of
1714 * downstream keepalive connections being kept open unnecessarily. This
1715 * keeps webservers busy and ties up resources.
1717 * As a result, we allocate all sockets out of the upstream connection
1718 * pool, and when we want to reuse a socket, we check first whether the
1719 * connection ID of the current upstream connection is the same as that
1720 * of the connection when the socket was opened.
1722 apr_pool_t *p = r->connection->pool;
1723 conn_rec *c = r->connection;
1724 apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
1725 apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
1726 proxy_http_conn_t *p_conn = apr_pcalloc(r->connection->pool,
1730 if (strncasecmp(url, "https:", 6) == 0) {
1731 if (!ap_proxy_ssl_enable(NULL)) {
1732 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1733 "proxy: HTTPS: declining URL %s"
1734 " (mod_ssl not configured?)", url);
1739 else if (!(strncasecmp(url, "http:", 5)==0 || (strncasecmp(url, "ftp:", 4)==0 && proxyname))) {
1740 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1741 "proxy: HTTP: declining URL %s", url);
1742 return DECLINED; /* only interested in HTTP, or FTP via proxy */
1744 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1745 "proxy: HTTP: serving URL %s", url);
1748 /* only use stored info for top-level pages. Sub requests don't share
1752 backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
1753 &proxy_http_module);
1755 /* create space for state information */
1757 backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec));
1758 backend->connection = NULL;
1759 backend->hostname = NULL;
1762 ap_set_module_config(c->conn_config, &proxy_http_module, backend);
1766 backend->is_ssl = is_ssl;
1768 /* Step One: Determine Who To Connect To */
1769 status = ap_proxy_http_determine_connection(p, r, p_conn, c, conf, uri,
1770 &url, proxyname, proxyport,
1772 sizeof(server_portstr));
1773 if ( status != OK ) {
1777 /* Step Two: Make the Connection */
1778 status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend,
1780 if ( status != OK ) {
1784 /* Step Three: Send the Request */
1785 status = ap_proxy_http_request(p, r, p_conn, origin, conf, uri, url, bb,
1787 if ( status != OK ) {
1791 /* Step Four: Receive the Response */
1792 status = ap_proxy_http_process_response(p, r, p_conn, origin, backend, conf,
1793 bb, server_portstr);
1794 if ( status != OK ) {
1795 /* clean up even if there is an error */
1796 ap_proxy_http_cleanup(r, p_conn, backend);
1800 /* Step Five: Clean Up */
1801 status = ap_proxy_http_cleanup(r, p_conn, backend);
1802 if ( status != OK ) {
1809 static void ap_proxy_http_register_hook(apr_pool_t *p)
1811 proxy_hook_scheme_handler(ap_proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
1812 proxy_hook_canon_handler(ap_proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
1815 module AP_MODULE_DECLARE_DATA proxy_http_module = {
1816 STANDARD20_MODULE_STUFF,
1817 NULL, /* create per-directory config structure */
1818 NULL, /* merge per-directory config structures */
1819 NULL, /* create per-server config structure */
1820 NULL, /* merge per-server config structures */
1821 NULL, /* command apr_table_t */
1822 ap_proxy_http_register_hook/* register hooks */