1 /* Copyright 1999-2004 The Apache Software Foundation
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 /* HTTP routines for Apache proxy */
18 #include "mod_proxy.h"
20 module AP_MODULE_DECLARE_DATA proxy_http_module;
22 static apr_status_t ap_proxy_http_cleanup(const char *scheme,
24 proxy_conn_rec *backend);
27 * Canonicalise http-like URLs.
28 * scheme is the scheme for the URL
29 * url is the URL starting with the first '/'
30 * def_port is the default port for this scheme.
32 static int proxy_http_canon(request_rec *r, char *url)
34 char *host, *path, *search, sport[7];
37 apr_port_t port, def_port;
39 /* ap_port_of_scheme() */
40 if (strncasecmp(url, "http:", 5) == 0) {
44 else if (strncasecmp(url, "https:", 6) == 0) {
51 def_port = apr_uri_port_of_scheme(scheme);
53 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
54 "proxy: HTTP: canonicalising URL %s", url);
57 * We break the URL into host, port, path, search
60 err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
62 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
63 "error parsing URL %s: %s",
65 return HTTP_BAD_REQUEST;
68 /* now parse path/search args, according to rfc1738 */
69 /* N.B. if this isn't a true proxy request, then the URL _path_
70 * has already been decoded. True proxy requests have r->uri
71 * == r->unparsed_uri, and no others have that property.
73 if (r->uri == r->unparsed_uri) {
74 search = strchr(url, '?');
82 path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq);
84 return HTTP_BAD_REQUEST;
87 apr_snprintf(sport, sizeof(sport), ":%d", port);
91 if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
92 host = apr_pstrcat(r->pool, "[", host, "]", NULL);
94 r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
95 "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
99 /* Clear all connection-based headers from the incoming headers table */
100 static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
103 char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
105 apr_table_unset(headers, "Proxy-Connection");
111 while (*next && !apr_isspace(*next) && (*next != ',')) {
114 while (*next && (apr_isspace(*next) || (*next == ','))) {
118 apr_table_unset(headers, name);
120 apr_table_unset(headers, "Connection");
123 static void add_te_chunked(apr_pool_t *p,
124 apr_bucket_alloc_t *bucket_alloc,
125 apr_bucket_brigade *header_brigade)
129 const char te_hdr[] = "Transfer-Encoding: chunked" CRLF;
131 buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1);
132 ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1);
134 e = apr_bucket_pool_create(buf, sizeof(te_hdr)-1, p, bucket_alloc);
135 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
138 static void add_cl(apr_pool_t *p,
139 apr_bucket_alloc_t *bucket_alloc,
140 apr_bucket_brigade *header_brigade,
146 buf = apr_pstrcat(p, "Content-Length: ",
150 ap_xlate_proto_to_ascii(buf, strlen(buf));
151 e = apr_bucket_pool_create(buf, strlen(buf), p, bucket_alloc);
152 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
155 #define ASCII_CRLF "\015\012"
156 #define ASCII_ZERO "\060"
158 static void terminate_headers(apr_bucket_alloc_t *bucket_alloc,
159 apr_bucket_brigade *header_brigade)
163 /* add empty line at the end of the headers */
164 e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
165 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
168 static apr_status_t pass_brigade(apr_bucket_alloc_t *bucket_alloc,
169 request_rec *r, proxy_conn_rec *conn,
170 conn_rec *origin, apr_bucket_brigade *b,
174 apr_off_t transferred;
177 apr_bucket *e = apr_bucket_flush_create(bucket_alloc);
178 APR_BRIGADE_INSERT_TAIL(b, e);
180 apr_brigade_length(b, 0, &transferred);
181 if (transferred != -1)
182 conn->worker->s->transferred += transferred;
183 status = ap_pass_brigade(origin->output_filters, b);
184 if (status != APR_SUCCESS) {
185 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
186 "proxy: pass request data failed to %pI (%s)",
187 conn->addr, conn->hostname);
190 apr_brigade_cleanup(b);
194 static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
196 proxy_conn_rec *conn,
198 apr_bucket_brigade *header_brigade)
204 apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
205 apr_bucket_brigade *b, *input_brigade;
208 input_brigade = apr_brigade_create(p, bucket_alloc);
211 char chunk_hdr[20]; /* must be here due to transient bucket. */
213 status = ap_get_brigade(r->input_filters, input_brigade,
214 AP_MODE_READBYTES, APR_BLOCK_READ,
217 if (status != APR_SUCCESS) {
221 /* If this brigade contains EOS, either stop or remove it. */
222 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
225 /* As a shortcut, if this brigade is simply an EOS bucket,
226 * don't send anything down the filter chain.
228 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) {
232 /* We can't pass this EOS to the output_filters. */
233 e = APR_BRIGADE_LAST(input_brigade);
234 apr_bucket_delete(e);
237 apr_brigade_length(input_brigade, 1, &bytes);
239 hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
240 "%" APR_UINT64_T_HEX_FMT CRLF,
241 (apr_uint64_t)bytes);
243 ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
244 e = apr_bucket_transient_create(chunk_hdr, hdr_len,
246 APR_BRIGADE_INSERT_HEAD(input_brigade, e);
249 * Append the end-of-chunk CRLF
251 e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
252 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
254 if (header_brigade) {
255 /* we never sent the header brigade, so go ahead and
256 * take care of that now
258 add_te_chunked(p, bucket_alloc, header_brigade);
259 terminate_headers(bucket_alloc, header_brigade);
261 APR_BRIGADE_CONCAT(b, input_brigade);
262 header_brigade = NULL;
268 status = pass_brigade(bucket_alloc, r, conn, origin, b, 0);
269 if (status != APR_SUCCESS) {
274 if (header_brigade) {
275 /* we never sent the header brigade because there was no request body;
276 * send it now without T-E
278 terminate_headers(bucket_alloc, header_brigade);
282 if (!APR_BRIGADE_EMPTY(input_brigade)) {
283 /* input brigade still has an EOS which we can't pass to the output_filters. */
284 e = APR_BRIGADE_LAST(input_brigade);
285 AP_DEBUG_ASSERT(APR_BUCKET_IS_EOS(e));
286 apr_bucket_delete(e);
288 e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
292 APR_BRIGADE_INSERT_TAIL(input_brigade, e);
296 status = pass_brigade(bucket_alloc, r, conn, origin, b, 1);
300 static apr_status_t stream_reqbody_cl(apr_pool_t *p,
302 proxy_conn_rec *conn,
304 apr_bucket_brigade *header_brigade,
305 const char *old_cl_val)
309 apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
310 apr_bucket_brigade *b, *input_brigade;
313 input_brigade = apr_brigade_create(p, bucket_alloc);
316 status = ap_get_brigade(r->input_filters, input_brigade,
317 AP_MODE_READBYTES, APR_BLOCK_READ,
320 if (status != APR_SUCCESS) {
324 /* If this brigade contains EOS, either stop or remove it. */
325 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
328 /* As a shortcut, if this brigade is simply an EOS bucket,
329 * don't send anything down the filter chain.
331 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) {
335 /* We can't pass this EOS to the output_filters. */
336 e = APR_BRIGADE_LAST(input_brigade);
337 apr_bucket_delete(e);
340 if (header_brigade) {
341 /* we never sent the header brigade, so go ahead and
342 * take care of that now
344 add_cl(p, bucket_alloc, header_brigade, old_cl_val);
345 terminate_headers(bucket_alloc, header_brigade);
347 APR_BRIGADE_CONCAT(b, input_brigade);
348 header_brigade = NULL;
354 status = pass_brigade(bucket_alloc, r, conn, origin, b, 0);
355 if (status != APR_SUCCESS) {
360 if (header_brigade) {
361 /* we never sent the header brigade since there was no request
362 * body; send it now, and only specify C-L if client specified
365 if (!strcmp(old_cl_val, "0")) {
366 add_cl(p, bucket_alloc, header_brigade, old_cl_val);
368 terminate_headers(bucket_alloc, header_brigade);
372 /* need to flush any pending data */
373 b = input_brigade; /* empty now; pass_brigade() will add flush */
375 status = pass_brigade(bucket_alloc, r, conn, origin, b, 1);
379 #define MAX_MEM_SPOOL 16384
381 static apr_status_t spool_reqbody_cl(apr_pool_t *p,
383 proxy_conn_rec *conn,
385 apr_bucket_brigade *header_brigade)
389 apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
390 apr_bucket_brigade *body_brigade, *input_brigade;
392 apr_off_t bytes, bytes_spooled = 0, fsize = 0;
393 apr_file_t *tmpfile = NULL;
395 body_brigade = apr_brigade_create(p, bucket_alloc);
396 input_brigade = apr_brigade_create(p, bucket_alloc);
399 status = ap_get_brigade(r->input_filters, input_brigade,
400 AP_MODE_READBYTES, APR_BLOCK_READ,
403 if (status != APR_SUCCESS) {
407 /* If this brigade contains EOS, either stop or remove it. */
408 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
411 /* As a shortcut, if this brigade is simply an EOS bucket,
412 * don't send anything down the filter chain.
414 if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) {
418 /* We can't pass this EOS to the output_filters. */
419 e = APR_BRIGADE_LAST(input_brigade);
420 apr_bucket_delete(e);
423 apr_brigade_length(input_brigade, 1, &bytes);
425 if (bytes_spooled + bytes > MAX_MEM_SPOOL) {
426 /* can't spool any more in memory; write latest brigade to disk;
427 * what we read into memory before reaching our threshold will
428 * remain there; we just write this and any subsequent data to disk
430 if (tmpfile == NULL) {
431 const char *temp_dir;
434 status = apr_temp_dir_get(&temp_dir, p);
435 if (status != APR_SUCCESS) {
436 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
437 "proxy: search for temporary directory failed");
440 apr_filepath_merge(&template, temp_dir,
441 "modproxy.tmp.XXXXXX",
442 APR_FILEPATH_NATIVE, p);
443 status = apr_file_mktemp(&tmpfile, template, 0, p);
444 if (status != APR_SUCCESS) {
445 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
446 "proxy: creation of temporary file in directory %s failed",
451 for (e = APR_BRIGADE_FIRST(input_brigade);
452 e != APR_BRIGADE_SENTINEL(input_brigade);
453 e = APR_BUCKET_NEXT(e)) {
455 apr_size_t bytes_read, bytes_written;
457 apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
458 status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
459 if (status != APR_SUCCESS) {
460 const char *tmpfile_name;
462 if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
463 tmpfile_name = "(unknown)";
465 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
466 "proxy: write to temporary file %s failed",
470 AP_DEBUG_ASSERT(bytes_read == bytes_written);
471 fsize += bytes_written;
473 apr_brigade_cleanup(input_brigade);
476 APR_BRIGADE_CONCAT(body_brigade, input_brigade);
479 bytes_spooled += bytes;
484 add_cl(p, bucket_alloc, header_brigade, apr_off_t_toa(p, bytes_spooled));
486 terminate_headers(bucket_alloc, header_brigade);
487 APR_BRIGADE_CONCAT(header_brigade, body_brigade);
489 /* For platforms where the size of the file may be larger than
490 * that which can be stored in a single bucket (where the
491 * length field is an apr_size_t), split it into several
493 if (sizeof(apr_off_t) > sizeof(apr_size_t)
494 && fsize > AP_MAX_SENDFILE) {
495 e = apr_bucket_file_create(tmpfile, 0, AP_MAX_SENDFILE, p,
497 while (fsize > AP_MAX_SENDFILE) {
499 apr_bucket_copy(e, &ce);
500 APR_BRIGADE_INSERT_TAIL(header_brigade, ce);
501 e->start += AP_MAX_SENDFILE;
502 fsize -= AP_MAX_SENDFILE;
504 e->length = (apr_size_t)fsize; /* Resize just the last bucket */
507 e = apr_bucket_file_create(tmpfile, 0, (apr_size_t)fsize, p,
510 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
512 status = pass_brigade(bucket_alloc, r, conn, origin, header_brigade, 1);
516 static apr_status_t send_request_body(apr_pool_t *p,
518 proxy_conn_rec *conn,
520 apr_bucket_brigade *header_brigade,
523 enum {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED, RB_SPOOL_CL} rb_method = RB_INIT;
524 const char *old_cl_val, *te_val;
525 int cl_zero; /* client sent "Content-Length: 0", which we forward on to server */
528 /* send CL or use chunked encoding?
530 * . CL is the most friendly to the origin server since it is the
531 * most widely supported
532 * . CL stinks if we don't know the length since we have to buffer
533 * the data in memory or on disk until we get the entire data
535 * special cases to check for:
536 * . if we're using HTTP/1.0 to origin server, then we must send CL
537 * . if client sent C-L and there are no input resource filters, the
538 * the body size can't change so we send the same CL and stream the
540 * . if client used chunked or proxy-sendchunks is set, we'll use
544 * we have to compute content length by reading the entire request
545 * body; if request body is not small, we'll spool the remaining input
546 * to a temporary file
548 * special envvars to override the normal decision:
550 * use chunked encoding; not compatible with force-proxy-request-1.0
552 * spool the request body to compute C-L
553 * . proxy-sendunchangedcl
554 * use C-L from client and spool the request body
556 old_cl_val = apr_table_get(r->headers_in, "Content-Length");
557 cl_zero = old_cl_val && !strcmp(old_cl_val, "0");
561 && apr_table_get(r->subprocess_env, "proxy-sendchunks")) {
562 rb_method = RB_STREAM_CHUNKED;
565 && apr_table_get(r->subprocess_env, "proxy-sendcl")) {
566 rb_method = RB_SPOOL_CL;
570 (r->input_filters == r->proto_input_filters
572 || apr_table_get(r->subprocess_env, "proxy-sendunchangedcl"))) {
573 rb_method = RB_STREAM_CL;
576 rb_method = RB_SPOOL_CL;
578 else if ((te_val = apr_table_get(r->headers_in, "Transfer-Encoding"))
579 && !strcasecmp(te_val, "chunked")) {
580 rb_method = RB_STREAM_CHUNKED;
583 rb_method = RB_SPOOL_CL;
588 case RB_STREAM_CHUNKED:
589 status = stream_reqbody_chunked(p, r, conn, origin, header_brigade);
592 status = stream_reqbody_cl(p, r, conn, origin, header_brigade, old_cl_val);
595 status = spool_reqbody_cl(p, r, conn, origin, header_brigade);
605 apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
606 proxy_conn_rec *conn, conn_rec *origin,
607 proxy_server_conf *conf,
609 char *url, char *server_portstr)
611 conn_rec *c = r->connection;
614 const apr_array_header_t *headers_in_array;
615 const apr_table_entry_t *headers_in;
618 apr_bucket_brigade *header_brigade;
621 header_brigade = apr_brigade_create(p, origin->bucket_alloc);
624 * Send the HTTP/1.1 request to the remote server
627 /* strip connection listed hop-by-hop headers from the request */
628 /* even though in theory a connection: close coming from the client
629 * should not affect the connection to the server, it's unlikely
630 * that subsequent client requests will hit this thread/process, so
631 * we cancel server keepalive if the client does.
633 conn->close += ap_proxy_liststr(apr_table_get(r->headers_in,
634 "Connection"), "close");
636 /* sub-requests never use keepalives */
641 ap_proxy_clear_connection(p, r->headers_in);
643 apr_table_setn(r->headers_in, "Connection", "close");
644 origin->keepalive = AP_CONN_CLOSE;
647 /* By default, we can not send chunks. That means we must buffer
648 * the entire request before sending it along to ensure we have
649 * the correct Content-Length attached. A special case is when
650 * the client specifies Content-Length and there are no filters
651 * which muck with the request body. In that situation, we don't
652 * have to buffer the entire request and can still send the
653 * Content-Length. Another special case is when the client
654 * specifies a C-L of 0. Pass that through as well.
657 if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
658 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
661 buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
664 if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
665 apr_table_unset(r->headers_in, "Connection");
666 origin->keepalive = AP_CONN_CLOSE;
668 ap_xlate_proto_to_ascii(buf, strlen(buf));
669 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
670 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
671 if (conf->preserve_host == 0) {
672 if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
673 buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str,
676 buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
680 /* don't want to use r->hostname, as the incoming header might have a
683 const char* hostname = apr_table_get(r->headers_in,"Host");
685 hostname = r->server->server_hostname;
686 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
687 "proxy: no HTTP 0.9 request (with no host line) "
688 "on incoming request and preserve host set "
689 "forcing hostname to be %s for uri %s",
693 buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
695 ap_xlate_proto_to_ascii(buf, strlen(buf));
696 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
697 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
700 if (conf->viaopt == via_block) {
701 /* Block all outgoing Via: headers */
702 apr_table_unset(r->headers_in, "Via");
703 } else if (conf->viaopt != via_off) {
704 const char *server_name = ap_get_server_name(r);
705 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
706 * then the server name returned by ap_get_server_name() is the
707 * origin server name (which does make too much sense with Via: headers)
708 * so we use the proxy vhost's name instead.
710 if (server_name == r->hostname)
711 server_name = r->server->server_hostname;
712 /* Create a "Via:" request header entry and merge it */
713 /* Generate outgoing Via: header with/without server comment: */
714 apr_table_mergen(r->headers_in, "Via",
715 (conf->viaopt == via_full)
716 ? apr_psprintf(p, "%d.%d %s%s (%s)",
717 HTTP_VERSION_MAJOR(r->proto_num),
718 HTTP_VERSION_MINOR(r->proto_num),
719 server_name, server_portstr,
720 AP_SERVER_BASEVERSION)
721 : apr_psprintf(p, "%d.%d %s%s",
722 HTTP_VERSION_MAJOR(r->proto_num),
723 HTTP_VERSION_MINOR(r->proto_num),
724 server_name, server_portstr)
728 /* X-Forwarded-*: handling
733 * These request headers are only really useful when the mod_proxy
734 * is used in a reverse proxy configuration, so that useful info
735 * about the client can be passed through the reverse proxy and on
736 * to the backend server, which may require the information to
739 * In a forward proxy situation, these options are a potential
740 * privacy violation, as information about clients behind the proxy
741 * are revealed to arbitrary servers out there on the internet.
743 * The HTTP/1.1 Via: header is designed for passing client
744 * information through proxies to a server, and should be used in
745 * a forward proxy configuation instead of X-Forwarded-*. See the
746 * ProxyVia option for details.
749 if (PROXYREQ_REVERSE == r->proxyreq) {
752 /* Add X-Forwarded-For: so that the upstream has a chance to
753 * determine, where the original request came from.
755 apr_table_mergen(r->headers_in, "X-Forwarded-For",
756 r->connection->remote_ip);
758 /* Add X-Forwarded-Host: so that upstream knows what the
759 * original request hostname was.
761 if ((buf = apr_table_get(r->headers_in, "Host"))) {
762 apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
765 /* Add X-Forwarded-Server: so that upstream knows what the
766 * name of this proxy server is (if there are more than one)
767 * XXX: This duplicates Via: - do we strictly need it?
769 apr_table_mergen(r->headers_in, "X-Forwarded-Server",
770 r->server->server_hostname);
773 /* send request headers */
775 headers_in_array = apr_table_elts(r->headers_in);
776 headers_in = (const apr_table_entry_t *) headers_in_array->elts;
777 for (counter = 0; counter < headers_in_array->nelts; counter++) {
778 if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
780 /* Clear out hop-by-hop request headers not to send
781 * RFC2616 13.5.1 says we should strip these headers
784 || !apr_strnatcasecmp(headers_in[counter].key, "Host")
786 || !apr_strnatcasecmp(headers_in[counter].key, "Keep-Alive")
787 || !apr_strnatcasecmp(headers_in[counter].key, "TE")
788 || !apr_strnatcasecmp(headers_in[counter].key, "Trailer")
789 || !apr_strnatcasecmp(headers_in[counter].key, "Transfer-Encoding")
790 || !apr_strnatcasecmp(headers_in[counter].key, "Upgrade")
792 /* We'll add appropriate Content-Length later, if appropriate.
794 || !apr_strnatcasecmp(headers_in[counter].key, "Content-Length")
795 /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be
796 * suppressed if THIS server requested the authentication,
797 * not when a frontend proxy requested it!
799 * The solution to this problem is probably to strip out
800 * the Proxy-Authorisation header in the authorisation
801 * code itself, not here. This saves us having to signal
802 * somehow whether this request was authenticated or not.
804 || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authorization")
805 || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authenticate")) {
808 /* for sub-requests, ignore freshness/expiry headers */
810 if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
811 || !apr_strnatcasecmp(headers_in[counter].key, "If-Match")
812 || !apr_strnatcasecmp(headers_in[counter].key, "If-Modified-Since")
813 || !apr_strnatcasecmp(headers_in[counter].key, "If-Range")
814 || !apr_strnatcasecmp(headers_in[counter].key, "If-Unmodified-Since")
815 || !apr_strnatcasecmp(headers_in[counter].key, "If-None-Match")) {
821 buf = apr_pstrcat(p, headers_in[counter].key, ": ",
822 headers_in[counter].val, CRLF,
824 ap_xlate_proto_to_ascii(buf, strlen(buf));
825 e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
826 APR_BRIGADE_INSERT_TAIL(header_brigade, e);
829 /* send the request data, if any. */
830 status = send_request_body(p, r, conn, origin, header_brigade, force10);
831 if (status != APR_SUCCESS) {
832 ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
833 "proxy: pass request data failed to %pI (%s)",
834 conn->addr, conn->hostname);
840 static void process_proxy_header(request_rec* r, proxy_server_conf* c,
841 const char* key, const char* value)
843 static const char* date_hdrs[]
844 = { "Date", "Expires", "Last-Modified", NULL } ;
845 static const struct {
847 ap_proxy_header_reverse_map_fn func;
848 } transform_hdrs[] = {
849 { "Location", ap_proxy_location_reverse_map } ,
850 { "Content-Location", ap_proxy_location_reverse_map } ,
851 { "URI", ap_proxy_location_reverse_map } ,
852 { "Set-Cookie", ap_proxy_cookie_reverse_map } ,
856 for ( i = 0 ; date_hdrs[i] ; ++i ) {
857 if ( !strcasecmp(date_hdrs[i], key) ) {
858 apr_table_add(r->headers_out, key,
859 ap_proxy_date_canon(r->pool, value)) ;
863 for ( i = 0 ; transform_hdrs[i].name ; ++i ) {
864 if ( !strcasecmp(transform_hdrs[i].name, key) ) {
865 apr_table_add(r->headers_out, key,
866 (*transform_hdrs[i].func)(r, c, value)) ;
870 apr_table_add(r->headers_out, key, value) ;
874 static void ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c)
878 char field[MAX_STRING_LEN];
880 void *sconf = r->server->module_config;
881 proxy_server_conf *psc;
883 psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
885 r->headers_out = apr_table_make(r->pool, 20);
888 * Read header lines until we get the empty separator line, a read error,
889 * the connection closes (EOF), or we timeout.
891 while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
893 if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */
895 /* We may encounter invalid headers, usually from buggy
896 * MS IIS servers, so we need to determine just how to handle
897 * them. We can either ignore them, assume that they mark the
898 * start-of-body (eg: a missing CRLF) or (the default) mark
899 * the headers as totally bogus and return a 500. The sole
900 * exception is an extra "HTTP/1.0 200, OK" line sprinkled
901 * in between the usual MIME headers, which is a favorite
904 /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
906 if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
907 if (psc->badopt == bad_error) {
908 /* Nope, it wasn't even an extra HTTP header. Give up. */
911 else if (psc->badopt == bad_body) {
912 /* if we've already started loading headers_out, then
913 * return what we've accumulated so far, in the hopes
914 * that they are useful. Otherwise, we completely bail.
916 /* FIXME: We've already scarfed the supposed 1st line of
917 * the body, so the actual content may end up being bogus
918 * as well. If the content is HTML, we may be lucky.
921 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
922 "proxy: Starting body due to bogus non-header in headers "
923 "returned by %s (%s)", r->uri, r->method);
926 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
927 "proxy: No HTTP headers "
928 "returned by %s (%s)", r->uri, r->method);
933 /* this is the psc->badopt == bad_ignore case */
934 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
935 "proxy: Ignoring bogus HTTP header "
936 "returned by %s (%s)", r->uri, r->method);
942 /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
943 * wrong... and so are many others probably.
945 while (apr_isspace(*value))
946 ++value; /* Skip to start of value */
948 /* should strip trailing whitespace as well */
949 for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --
953 /* make sure we add so as not to destroy duplicated headers
954 * Modify headers requiring canonicalisation and/or affected
955 * by ProxyPassReverse and family with process_proxy_header
957 process_proxy_header(r, psc, buffer, value) ;
960 /* the header was too long; at the least we should skip extra data */
961 if (len >= size - 1) {
962 while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
963 >= MAX_STRING_LEN - 1) {
964 /* soak up the extra data */
966 if (len == 0) /* time to exit the larger loop as well */
974 static int addit_dammit(void *v, const char *key, const char *val)
976 apr_table_addn(v, key, val);
981 apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
982 proxy_conn_rec *backend,
984 proxy_server_conf *conf,
985 char *server_portstr) {
986 conn_rec *c = r->connection;
987 char buffer[HUGE_STRING_LEN];
991 apr_bucket_brigade *bb;
992 int len, backasswards;
993 int interim_response; /* non-zero whilst interim 1xx responses
995 apr_table_t *save_table;
997 bb = apr_brigade_create(p, c->bucket_alloc);
999 /* Get response from the remote server, and pass it up the
1003 rp = ap_proxy_make_fake_req(origin, r);
1004 /* In case anyone needs to know, this is a fake request that is really a
1007 rp->proxyreq = PROXYREQ_RESPONSE;
1009 apr_brigade_cleanup(bb);
1011 len = ap_getline(buffer, sizeof(buffer), rp, 0);
1013 /* handle one potential stray CRLF */
1014 len = ap_getline(buffer, sizeof(buffer), rp, 0);
1017 ap_proxy_http_cleanup(NULL, r, backend);
1018 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1019 "proxy: error reading status line from remote "
1020 "server %s", backend->hostname);
1021 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1022 "Error reading from remote server");
1024 /* XXX: Is this a real headers length send from remote? */
1025 backend->worker->s->read += len;
1027 /* Is it an HTTP/1 response?
1028 * This is buggy if we ever see an HTTP/1.10
1030 if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
1033 if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
1037 /* If not an HTTP/1 message or
1038 * if the status line was > 8192 bytes
1040 else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) {
1041 ap_proxy_http_cleanup(NULL, r, backend);
1042 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1043 apr_pstrcat(p, "Corrupt status line returned by remote "
1044 "server: ", buffer, NULL));
1048 keepchar = buffer[12];
1050 r->status = atoi(&buffer[9]);
1052 if (keepchar != '\0') {
1053 buffer[12] = keepchar;
1055 /* 2616 requires the space in Status-Line; the origin
1056 * server may have sent one but ap_rgetline_core will
1057 * have stripped it. */
1061 r->status_line = apr_pstrdup(p, &buffer[9]);
1064 /* read the headers. */
1065 /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
1066 /* Also, take care with headers with multiple occurences. */
1068 /* First, tuck away all already existing cookies */
1069 save_table = apr_table_make(r->pool, 2);
1070 apr_table_do(addit_dammit, save_table, r->headers_out,
1071 "Set-Cookie", NULL);
1073 /* shove the headers direct into r->headers_out */
1074 ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin);
1076 if (r->headers_out == NULL) {
1077 ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
1078 r->server, "proxy: bad HTTP/%d.%d header "
1079 "returned by %s (%s)", major, minor, r->uri,
1081 backend->close += 1;
1083 * ap_send_error relies on a headers_out to be present. we
1084 * are in a bad position here.. so force everything we send out
1085 * to have nothing to do with the incoming packet
1087 r->headers_out = apr_table_make(r->pool,1);
1088 r->status = HTTP_BAD_GATEWAY;
1089 r->status_line = "bad gateway";
1095 /* Now, add in the just read cookies */
1096 apr_table_do(addit_dammit, save_table, r->headers_out,
1097 "Set-Cookie", NULL);
1099 /* and now load 'em all in */
1100 if (!apr_is_empty_table(save_table)) {
1101 apr_table_unset(r->headers_out, "Set-Cookie");
1102 r->headers_out = apr_table_overlay(r->pool,
1107 /* strip connection listed hop-by-hop headers from response */
1108 backend->close += ap_proxy_liststr(apr_table_get(r->headers_out,
1111 ap_proxy_clear_connection(p, r->headers_out);
1112 if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
1113 ap_set_content_type(r, apr_pstrdup(p, buf));
1115 ap_proxy_pre_http_request(origin,rp);
1118 /* handle Via header in response */
1119 if (conf->viaopt != via_off && conf->viaopt != via_block) {
1120 const char *server_name = ap_get_server_name(r);
1121 /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
1122 * then the server name returned by ap_get_server_name() is the
1123 * origin server name (which does make too much sense with Via: headers)
1124 * so we use the proxy vhost's name instead.
1126 if (server_name == r->hostname)
1127 server_name = r->server->server_hostname;
1128 /* create a "Via:" response header entry and merge it */
1129 apr_table_mergen(r->headers_out, "Via",
1130 (conf->viaopt == via_full)
1131 ? apr_psprintf(p, "%d.%d %s%s (%s)",
1132 HTTP_VERSION_MAJOR(r->proto_num),
1133 HTTP_VERSION_MINOR(r->proto_num),
1136 AP_SERVER_BASEVERSION)
1137 : apr_psprintf(p, "%d.%d %s%s",
1138 HTTP_VERSION_MAJOR(r->proto_num),
1139 HTTP_VERSION_MINOR(r->proto_num),
1145 /* cancel keepalive if HTTP/1.0 or less */
1146 if ((major < 1) || (minor < 1)) {
1147 backend->close += 1;
1148 origin->keepalive = AP_CONN_CLOSE;
1151 /* an http/0.9 response */
1154 r->status_line = "200 OK";
1155 backend->close += 1;
1158 interim_response = ap_is_HTTP_INFO(r->status);
1159 if (interim_response) {
1160 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1161 "proxy: HTTP: received interim %d response",
1164 /* Moved the fixups of Date headers and those affected by
1165 * ProxyPassReverse/etc from here to ap_proxy_read_headers
1168 if ((r->status == 401) && (conf->error_override != 0)) {
1170 const char *wa = "WWW-Authenticate";
1171 if ((buf = apr_table_get(r->headers_out, wa))) {
1172 apr_table_set(r->err_headers_out, wa, buf);
1174 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1175 "proxy: origin server sent 401 without WWW-Authenticate header");
1180 /* Is it an HTTP/0.9 response? If so, send the extra data */
1182 apr_ssize_t cntr = len;
1184 * At this point in response processing of a 0.9 response,
1185 * we don't know yet whether data is binary or not.
1186 * mod_charset_lite will get control later on, so it cannot
1187 * decide on the conversion of this buffer full of data.
1188 * However, chances are that we are not really talking to an
1189 * HTTP/0.9 server, but to some different protocol, therefore
1190 * the best guess IMHO is to always treat the buffer as "text/x":
1192 ap_xlate_proto_to_ascii(buffer, len);
1193 e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
1194 APR_BRIGADE_INSERT_TAIL(bb, e);
1197 /* send body - but only if a body is expected */
1198 if ((!r->header_only) && /* not HEAD request */
1199 !interim_response && /* not any 1xx response */
1200 (r->status != HTTP_NO_CONTENT) && /* not 204 */
1201 (r->status != HTTP_NOT_MODIFIED)) { /* not 304 */
1203 /* We need to copy the output headers and treat them as input
1204 * headers as well. BUT, we need to do this before we remove
1205 * TE, so that they are preserved accordingly for
1206 * ap_http_filter to know where to end.
1208 rp->headers_in = apr_table_copy(r->pool, r->headers_out);
1210 apr_table_unset(r->headers_out,"Transfer-Encoding");
1212 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1213 "proxy: start body send");
1216 * if we are overriding the errors, we can't put the content
1217 * of the page into the brigade
1219 if (conf->error_override == 0 || ap_is_HTTP_SUCCESS(r->status)) {
1220 /* read the body, pass it to the output filters */
1221 apr_read_type_e mode = APR_NONBLOCK_READ;
1225 apr_off_t readbytes;
1228 rv = ap_get_brigade(rp->input_filters, bb,
1229 AP_MODE_READBYTES, mode,
1230 conf->io_buffer_size);
1232 /* ap_get_brigade will return success with an empty brigade
1233 * for a non-blocking read which would block: */
1234 if (APR_STATUS_IS_EAGAIN(rv)
1235 || (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))) {
1236 /* flush to the client and switch to blocking mode */
1237 e = apr_bucket_flush_create(c->bucket_alloc);
1238 APR_BRIGADE_INSERT_TAIL(bb, e);
1239 if (ap_pass_brigade(r->output_filters, bb)
1244 apr_brigade_cleanup(bb);
1245 mode = APR_BLOCK_READ;
1248 else if (rv != APR_SUCCESS) {
1249 ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c,
1250 "proxy: error reading response");
1253 /* next time try a non-blocking read */
1254 mode = APR_NONBLOCK_READ;
1256 apr_brigade_length(bb, 0, &readbytes);
1257 backend->worker->s->read += readbytes;
1260 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
1261 r->server, "proxy (PID %d): readbytes: %#x",
1262 getpid(), readbytes);
1266 if (APR_BRIGADE_EMPTY(bb)) {
1267 apr_brigade_cleanup(bb);
1271 /* found the last brigade? */
1272 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
1273 /* if this is the last brigade, cleanup the
1274 * backend connection first to prevent the
1275 * backend server from hanging around waiting
1276 * for a slow client to eat these bytes
1279 /* signal that we must leave */
1283 /* try send what we read */
1284 if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS
1286 /* Ack! Phbtt! Die! User aborted! */
1287 backend->close = 1; /* this causes socket close below */
1291 /* make sure we always clean up after ourselves */
1292 apr_brigade_cleanup(bb);
1296 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1297 "proxy: end body send");
1299 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1300 "proxy: header only");
1302 } while (interim_response);
1304 if (conf->error_override) {
1305 /* the code above this checks for 'OK' which is what the hook expects */
1306 if (ap_is_HTTP_SUCCESS(r->status))
1309 /* clear r->status for override error, otherwise ErrorDocument
1310 * thinks that this is a recursive error, and doesn't find the
1313 int status = r->status;
1314 r->status = HTTP_OK;
1315 /* Discard body, if one is expected */
1316 if ((status != HTTP_NO_CONTENT) && /* not 204 */
1317 (status != HTTP_NOT_MODIFIED)) { /* not 304 */
1318 ap_discard_request_body(rp);
1327 apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r,
1328 proxy_conn_rec *backend)
1330 /* If there are no KeepAlives, or if the connection has been signalled
1331 * to close, close the socket and clean up
1334 /* if the connection is < HTTP/1.1, or Connection: close,
1335 * we close the socket, otherwise we leave it open for KeepAlive support
1337 if (backend->close || (r->proto_num < HTTP_VERSION(1,1))) {
1338 backend->close_on_recycle = 1;
1339 ap_set_module_config(r->connection->conn_config, &proxy_http_module, NULL);
1340 ap_proxy_release_connection(scheme, backend, r->server);
1346 * This handles http:// URLs, and other URLs using a remote proxy over http
1347 * If proxyhost is NULL, then contact the server directly, otherwise
1349 * Note that if a proxy is used, then URLs other than http: can be accessed,
1350 * also, if we have trouble which is clearly specific to the proxy, then
1351 * we return DECLINED so that we can try another proxy. (Or the direct
1354 static int proxy_http_handler(request_rec *r, proxy_worker *worker,
1355 proxy_server_conf *conf,
1356 char *url, const char *proxyname,
1357 apr_port_t proxyport)
1360 char server_portstr[32];
1362 const char *proxy_function;
1364 proxy_conn_rec *backend = NULL;
1367 /* Note: Memory pool allocation.
1368 * A downstream keepalive connection is always connected to the existence
1369 * (or not) of an upstream keepalive connection. If this is not done then
1370 * load balancing against multiple backend servers breaks (one backend
1371 * server ends up taking 100% of the load), and the risk is run of
1372 * downstream keepalive connections being kept open unnecessarily. This
1373 * keeps webservers busy and ties up resources.
1375 * As a result, we allocate all sockets out of the upstream connection
1376 * pool, and when we want to reuse a socket, we check first whether the
1377 * connection ID of the current upstream connection is the same as that
1378 * of the connection when the socket was opened.
1380 apr_pool_t *p = r->connection->pool;
1381 conn_rec *c = r->connection;
1382 apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
1384 /* find the scheme */
1385 u = strchr(url, ':');
1386 if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
1389 return HTTP_BAD_REQUEST;
1390 scheme = apr_pstrndup(c->pool, url, u - url);
1391 /* scheme is lowercase */
1392 ap_str_tolower(scheme);
1394 if (strcmp(scheme, "https") == 0) {
1395 if (!ap_proxy_ssl_enable(NULL)) {
1396 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1397 "proxy: HTTPS: declining URL %s"
1398 " (mod_ssl not configured?)", url);
1402 proxy_function = "HTTPS";
1404 else if (!(strcmp(scheme, "http") == 0 || (strcmp(scheme, "ftp") == 0 && proxyname))) {
1405 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1406 "proxy: HTTP: declining URL %s", url);
1407 return DECLINED; /* only interested in HTTP, or FTP via proxy */
1411 proxy_function = "HTTP";
1413 proxy_function = "FTP";
1415 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1416 "proxy: HTTP: serving URL %s", url);
1419 /* only use stored info for top-level pages. Sub requests don't share
1423 backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
1424 &proxy_http_module);
1426 /* create space for state information */
1428 if ((status = ap_proxy_acquire_connection(proxy_function, &backend,
1429 worker, r->server)) != OK)
1433 ap_set_module_config(c->conn_config, &proxy_http_module, backend);
1437 backend->is_ssl = is_ssl;
1438 backend->close_on_recycle = 1;
1440 /* Step One: Determine Who To Connect To */
1441 if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend,
1442 uri, &url, proxyname,
1443 proxyport, server_portstr,
1444 sizeof(server_portstr))) != OK)
1447 /* Step Two: Make the Connection */
1448 if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
1449 if (r->proxyreq == PROXYREQ_PROXY)
1450 status = HTTP_NOT_FOUND;
1452 status = HTTP_SERVICE_UNAVAILABLE;
1456 /* Step Three: Create conn_rec */
1457 if (!backend->connection) {
1458 if ((status = ap_proxy_connection_create(proxy_function, backend,
1459 c, r->server)) != OK)
1463 /* Step Four: Send the Request */
1464 if ((status = ap_proxy_http_request(p, r, backend, backend->connection,
1465 conf, uri, url, server_portstr)) != OK)
1468 /* Step Five: Receive the Response */
1469 if ((status = ap_proxy_http_process_response(p, r, backend,
1470 backend->connection,
1471 conf, server_portstr)) != OK)
1474 /* Step Six: Clean Up */
1480 backend->close_on_recycle = 1;
1482 ap_proxy_http_cleanup(proxy_function, r, backend);
1487 static void ap_proxy_http_register_hook(apr_pool_t *p)
1489 proxy_hook_scheme_handler(proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
1490 proxy_hook_canon_handler(proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
1493 module AP_MODULE_DECLARE_DATA proxy_http_module = {
1494 STANDARD20_MODULE_STUFF,
1495 NULL, /* create per-directory config structure */
1496 NULL, /* merge per-directory config structures */
1497 NULL, /* create per-server config structure */
1498 NULL, /* merge per-server config structures */
1499 NULL, /* command apr_table_t */
1500 ap_proxy_http_register_hook/* register hooks */