mod_proxy_http.c revision a221184be5b40f8349982d94cda02b98068ce0d8
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* HTTP routines for Apache proxy */
#include "mod_proxy.h"
#include "ap_regex.h"
request_rec *r,
/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
* url is the URL starting with the first '/'
* def_port is the default port for this scheme.
*/
{
const char *err;
const char *scheme;
/* ap_port_of_scheme() */
url += 5;
scheme = "http";
}
url += 6;
scheme = "https";
}
else {
return DECLINED;
}
"HTTP: canonicalising URL %s", url);
/* do syntatic check.
* We break the URL into host, port, path, search
*/
if (err) {
return HTTP_BAD_REQUEST;
}
/*
* process the path.
*
* In a reverse proxy, our URL has been processed, so canonicalise
* unless proxy-nocanon is set to say it's raw
* In a forward proxy, we have and MUST NOT MANGLE the original.
*/
switch (r->proxyreq) {
default: /* wtf are we doing here? */
case PROXYREQ_REVERSE:
}
else {
}
break;
case PROXYREQ_PROXY:
break;
}
return HTTP_BAD_REQUEST;
else
sport[0] = '\0';
}
return OK;
}
/* Clear all connection-based headers from the incoming headers table */
typedef struct header_dptr {
} header_dptr;
static ap_regex_t *warn_rx;
{
char *warning;
char *date;
const int nmatch = 3;
}
/*
* Parse this, suckers!
*
* Warning = "Warning" ":" 1#warning-value
*
* warning-value = warn-code SP warn-agent SP warn-text
* [SP warn-date]
*
* warn-code = 3DIGIT
* warn-agent = ( host [ ":" port ] ) | pseudonym
* ; the name or pseudonym of the server adding
* ; the Warning header, for use in debugging
* warn-text = quoted-string
* warn-date = <"> HTTP-date <">
*
* Buggrit, use a bloomin' regexp!
* (\d{3}\s+\S+\s+\".*?\"(\s+\"(.*?)\")?) --> whole in $1, date in $3
*/
warn_time = 0;
/* OK, we have a date here */
}
}
}
return 1;
}
{
header_dptr x;
x.pool = p;
}
else {
return headers;
}
}
{
const char *name;
while (*next) {
++next;
}
*next++ = '\0';
}
}
return 1;
}
{
header_dptr x;
x.pool = p;
}
static void add_te_chunked(apr_pool_t *p,
{
apr_bucket *e;
char *buf;
}
static void add_cl(apr_pool_t *p,
const char *cl_val)
{
apr_bucket *e;
char *buf;
CRLF,
NULL);
}
#define ASCII_CRLF "\015\012"
#define ASCII_ZERO "\060"
{
apr_bucket *e;
/* add empty line at the end of the headers */
}
int flush)
{
if (flush) {
}
if (transferred != -1)
if (status != APR_SUCCESS) {
"pass request body failed to %pI (%s)",
const char *ssl_note;
return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR,
"Error during SSL Handshake with"
" remote server");
}
}
else {
return HTTP_BAD_REQUEST;
}
}
return OK;
}
#define MAX_MEM_SPOOL 16384
static int stream_reqbody_chunked(apr_pool_t *p,
request_rec *r,
{
apr_bucket *e;
{
/* If this brigade contains EOS, either stop or remove it. */
seen_eos = 1;
/* We can't pass this EOS to the output_filters. */
}
/*
* Append the end-of-chunk CRLF
*/
if (header_brigade) {
/* we never sent the header brigade, so go ahead and
* take care of that now
*/
bb = header_brigade;
/*
* Save input_brigade in bb brigade. (At least) in the SSL case
* input_brigade contains transient buckets whose data would get
* overwritten during the next call of ap_get_brigade in the loop.
* ap_save_brigade ensures these buckets to be set aside.
* Calling ap_save_brigade with NULL as filter is OK, because
* bb brigade already has been created and does not need to get
* created by ap_save_brigade.
*/
if (status != APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
}
}
else {
bb = input_brigade;
}
/* The request is flushed below this loop with chunk EOS header */
return rv;
}
if (seen_eos) {
break;
}
if (status != APR_SUCCESS) {
return HTTP_BAD_REQUEST;
}
}
if (header_brigade) {
/* we never sent the header brigade because there was no request body;
* send it now
*/
bb = header_brigade;
}
else {
if (!APR_BRIGADE_EMPTY(input_brigade)) {
/* input brigade still has an EOS which we can't pass to the output_filters. */
}
bb = input_brigade;
}
/* <trailers> */
5, bucket_alloc);
}
/* Now we have headers-only, or the chunk EOS mark; flush it */
return rv;
}
static int stream_reqbody_cl(apr_pool_t *p,
request_rec *r,
const char *old_cl_val)
{
apr_bucket *e;
apr_off_t bytes_streamed = 0;
if (old_cl_val) {
char *endstr;
"could not parse request Content-Length (%s)",
return HTTP_BAD_REQUEST;
}
}
{
bytes_streamed += bytes;
/* If this brigade contains EOS, either stop or remove it. */
seen_eos = 1;
/* We can't pass this EOS to the output_filters. */
}
}
/* C-L < bytes streamed?!?
* We will error out after the body is completely
* consumed, but we can't stream more bytes at the
* back end since they would in part be interpreted
* as another request! If nothing is sent, then
* just send nothing.
*
* Prevents HTTP Response Splitting.
*/
if (bytes_streamed > cl_val) {
"read more bytes of request body than expected "
return HTTP_INTERNAL_SERVER_ERROR;
}
if (header_brigade) {
/* we never sent the header brigade, so go ahead and
* take care of that now
*/
bb = header_brigade;
/*
* Save input_brigade in bb brigade. (At least) in the SSL case
* input_brigade contains transient buckets whose data would get
* overwritten during the next call of ap_get_brigade in the loop.
* ap_save_brigade ensures these buckets to be set aside.
* Calling ap_save_brigade with NULL as filter is OK, because
* bb brigade already has been created and does not need to get
* created by ap_save_brigade.
*/
if (status != APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
}
}
else {
bb = input_brigade;
}
/* Once we hit EOS, we are ready to flush. */
return rv ;
}
if (seen_eos) {
break;
}
if (status != APR_SUCCESS) {
return HTTP_BAD_REQUEST;
}
}
if (bytes_streamed != cl_val) {
"client %s given Content-Length did not match"
return HTTP_BAD_REQUEST;
}
if (header_brigade) {
/* we never sent the header brigade since there was no request
* body; send it now with the flush flag
*/
bb = header_brigade;
}
return OK;
}
static int spool_reqbody_cl(apr_pool_t *p,
request_rec *r,
int force_cl)
{
int seen_eos = 0;
apr_bucket *e;
limit = ap_get_limit_req_body(r);
{
/* If this brigade contains EOS, either stop or remove it. */
seen_eos = 1;
/* We can't pass this EOS to the output_filters. */
}
/*
* LimitRequestBody does not affect Proxy requests (Should it?).
* Let it take effect if we decide to store the body in a
* temporary file on disk.
*/
"Request body is larger than the configured "
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
/* can't spool any more in memory; write latest brigade to disk */
const char *temp_dir;
char *template;
if (status != APR_SUCCESS) {
"search for temporary directory failed");
return HTTP_INTERNAL_SERVER_ERROR;
}
"modproxy.tmp.XXXXXX",
APR_FILEPATH_NATIVE, p);
if (status != APR_SUCCESS) {
"creation of temporary file in directory "
"%s failed", temp_dir);
return HTTP_INTERNAL_SERVER_ERROR;
}
}
for (e = APR_BRIGADE_FIRST(input_brigade);
e = APR_BUCKET_NEXT(e)) {
const char *data;
if (status != APR_SUCCESS) {
const char *tmpfile_name;
tmpfile_name = "(unknown)";
}
"write to temporary file %s failed",
return HTTP_INTERNAL_SERVER_ERROR;
}
fsize += bytes_written;
}
}
else {
/*
* Save input_brigade in body_brigade. (At least) in the SSL case
* input_brigade contains transient buckets whose data would get
* overwritten during the next call of ap_get_brigade in the loop.
* ap_save_brigade ensures these buckets to be set aside.
* Calling ap_save_brigade with NULL as filter is OK, because
* body_brigade already has been created and does not need to get
* created by ap_save_brigade.
*/
if (status != APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
}
}
bytes_spooled += bytes;
if (seen_eos) {
break;
}
if (status != APR_SUCCESS) {
return HTTP_BAD_REQUEST;
}
}
if (bytes_spooled || force_cl) {
}
if (tmpfile) {
}
}
/* This is all a single brigade, pass with flush flagged */
}
/*
* Transform buckets from one bucket allocator to another one by creating a
* transient bucket for each data bucket and let it use the data read from
* the old bucket. Metabuckets are transformed by just recreating them.
* Attention: Currently only the following bucket types are handled:
*
* All data buckets
* FLUSH
* EOS
*
* If an other bucket type is found its type is logged as a debug message
* and APR_EGENERAL is returned.
*/
{
apr_bucket *e;
const char *data;
for (e = APR_BRIGADE_FIRST(from);
e != APR_BRIGADE_SENTINEL(from);
e = APR_BUCKET_NEXT(e)) {
if (!APR_BUCKET_IS_METADATA(e)) {
}
else if (APR_BUCKET_IS_FLUSH(e)) {
}
else if (APR_BUCKET_IS_EOS(e)) {
}
else {
"Unhandled bucket type of type %s in"
rv = APR_EGENERAL;
}
}
return rv;
}
static
char *url, char *server_portstr)
{
conn_rec *c = r->connection;
apr_bucket *e;
char *buf;
const apr_array_header_t *headers_in_array;
const apr_table_entry_t *headers_in;
int counter;
const char *old_cl_val = NULL;
const char *old_te_val = NULL;
apr_off_t bytes_read = 0;
int do_100_continue;
/*
* Send the HTTP/1.1 request to the remote server
*/
/*
* To be compliant, we only use 100-Continue for requests with bodies.
* We also make sure we won't be talking HTTP/1.0 as well.
*/
&& ap_request_has_body(r)
&& (PROXYREQ_REVERSE == r->proxyreq)
/*
* According to RFC 2616 8.2.3 we are not allowed to forward an
* Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
* a HTTP_EXPECTATION_FAILED
*/
if (r->expecting_100) {
return HTTP_EXPECTATION_FAILED;
}
force10 = 1;
} else {
force10 = 0;
}
}
if (dconf->preserve_host == 0) {
} else {
}
} else {
} else {
}
}
}
else {
/* don't want to use r->hostname, as the incoming header might have a
* port attached
*/
if (!hostname) {
"no HTTP 0.9 request (with no host line) "
"on incoming request and preserve host set "
"forcing hostname to be %s for uri %s",
}
}
/* handle Via */
/* Block all outgoing Via: headers */
const char *server_name = ap_get_server_name(r);
/* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
* then the server name returned by ap_get_server_name() is the
* origin server name (which does make too much sense with Via: headers)
* so we use the proxy vhost's name instead.
*/
if (server_name == r->hostname)
/* Create a "Via:" request header entry and merge it */
? apr_psprintf(p, "%d.%d %s%s (%s)",
: apr_psprintf(p, "%d.%d %s%s",
);
}
/* Use HTTP/1.1 100-Continue as quick "HTTP ping" test
* to backend
*/
if (do_100_continue) {
r->expecting_100 = 1;
}
/* X-Forwarded-*: handling
*
* XXX Privacy Note:
* -----------------
*
* These request headers are only really useful when the mod_proxy
* is used in a reverse proxy configuration, so that useful info
* about the client can be passed through the reverse proxy and on
* to the backend server, which may require the information to
* function properly.
*
* In a forward proxy situation, these options are a potential
* privacy violation, as information about clients behind the proxy
* are revealed to arbitrary servers out there on the internet.
*
* The HTTP/1.1 Via: header is designed for passing client
* information through proxies to a server, and should be used in
* a forward proxy configuation instead of X-Forwarded-*. See the
* ProxyVia option for details.
*/
if (dconf->add_forwarded_headers) {
if (PROXYREQ_REVERSE == r->proxyreq) {
const char *buf;
/* Add X-Forwarded-For: so that the upstream has a chance to
* determine, where the original request came from.
*/
r->useragent_ip);
/* Add X-Forwarded-Host: so that upstream knows what the
* original request hostname was.
*/
}
/* Add X-Forwarded-Server: so that upstream knows what the
* name of this proxy server is (if there are more than one)
* XXX: This duplicates Via: - do we strictly need it?
*/
r->server->server_hostname);
}
}
proxy_run_fixups(r);
/*
* Make a copy of the headers_in table before clearing the connection
* headers as we need the connection headers later in the http output
* filter to prepare the correct response headers.
*
* Note: We need to take r->pool for apr_table_copy as the key / value
* pairs in r->headers_in have been created out of r->pool and
* p might be (and actually is) a longer living pool.
* This would trigger the bad pool ancestry abort in apr_table_copy if
* apr is compiled with APR_POOL_DEBUG.
*/
/* send request headers */
/* Already sent */
/* Clear out hop-by-hop request headers not to send
* RFC2616 13.5.1 says we should strip these headers
*/
) {
continue;
}
/* Do we want to strip Proxy-Authorization ?
* If we haven't used it, then NO
* If we have used it then MAYBE: RFC2616 says we MAY propagate it.
* So let's make it configurable by env.
*/
continue;
}
}
}
/* Skip Transfer-Encoding and Content-Length for now.
*/
continue;
}
continue;
}
if (r->main) {
continue;
}
}
NULL);
}
/* We have headers, let's figure out our request body... */
/* sub-requests never use keepalives, and mustn't pass request bodies.
* Because the new logic looks at input_brigade, we will self-terminate
* input_brigade and jump past all of the request body logic...
* Reading anything with ap_get_brigade is likely to consume the
* main request's body or read beyond EOS - which would be unplesant.
*
* An exception: when a kept_body is present, then subrequest CAN use
* pass request bodies, and we DONT skip the body.
*/
/* XXX: Why DON'T sub-requests use keepalives? */
if (old_cl_val) {
old_cl_val = NULL;
}
if (old_te_val) {
old_te_val = NULL;
}
goto skip_body;
}
/* WE only understand chunked. Other modules might inject
* (and therefore, decode) other flavors but we don't know
* that the can and have done so unless they they remove
* their decoding from the headers_in T-E list.
* XXX: Make this extensible, but in doing so, presume the
* encoding has been done by the extensions' handler, and
* do not modify add_te_chunked's logic
*/
"%s Transfer-Encoding is not supported", old_te_val);
return HTTP_INTERNAL_SERVER_ERROR;
}
if (old_cl_val && old_te_val) {
"client %s (%s) requested Transfer-Encoding "
"chunked body with Content-Length (C-L ignored)",
old_cl_val = NULL;
}
/* Prefetch MAX_MEM_SPOOL bytes
*
* This helps us avoid any election of C-L v.s. T-E
* request bodies, since we are willing to keep in
* memory this much data, in any case. This gives
* us an instant C-L election if the body is of some
* reasonable size.
*/
do {
if (status != APR_SUCCESS) {
"prefetch request body failed to %pI (%s)"
" from %s (%s)",
return HTTP_BAD_REQUEST;
}
bytes_read += bytes;
/*
* Save temp_brigade in input_brigade. (At least) in the SSL case
* temp_brigade contains transient buckets whose data would get
* overwritten during the next call of ap_get_brigade in the loop.
* ap_save_brigade ensures these buckets to be set aside.
* Calling ap_save_brigade with NULL as filter is OK, because
* input_brigade already has been created and does not need to get
* created by ap_save_brigade.
*/
if (status != APR_SUCCESS) {
"processing prefetched request body failed"
" to %pI (%s) from %s (%s)",
return HTTP_INTERNAL_SERVER_ERROR;
}
/* Ensure we don't hit a wall where we have a buffer too small
* for ap_get_brigade's filters to fetch us another bucket,
* surrender once we hit 80 bytes less than MAX_MEM_SPOOL
* (an arbitrary value.)
*/
/* Use chunked request body encoding or send a content-length body?
*
* Prefer C-L when:
*
* We have no request body (handled by RB_STREAM_CL)
*
* We have a request body length <= MAX_MEM_SPOOL
*
* The administrator has setenv force-proxy-request-1.0
*
* The client sent a C-L body, and the administrator has
* not setenv proxy-sendchunked or has set setenv proxy-sendcl
*
* The client sent a T-E body, and the administrator has
* setenv proxy-sendcl, and not setenv proxy-sendchunked
*
* If both proxy-sendcl and proxy-sendchunked are set, the
* behavior is the same as if neither were set, large bodies
* that can't be read will be forwarded in their original
* form of C-L, or T-E.
*
* To ensure maximum compatibility, setenv proxy-sendcl
* To reduce server resource use, setenv proxy-sendchunked
*
* Then address specific servers with conditional setenv
* options to restore the default behavior where desireable.
*
* We have to compute content length by reading the entire request
* body; if request body is not small, we'll spool the remaining
* input to a temporary file. Chunked is always preferable.
*
* We can only trust the client-provided C-L if the T-E header
* is absent, and the filters are unchanged (the body won't
* be resized by another content filter).
*/
/* The whole thing fit, so our decision is trivial, use
* the filtered bytes read from the client for the request
* body Content-Length.
*
* If we expected no body, and read no body, do not set
* the Content-Length.
*/
}
}
else if (old_te_val) {
if (force10
}
else {
}
}
else if (old_cl_val) {
if (r->input_filters == r->proto_input_filters) {
}
else if (!force10
}
else {
}
}
else {
/* This is an appropriate default; very efficient for no-body
* requests, and has the behavior that it will not add any C-L
* when the old_cl_val is NULL.
*/
}
/* Yes I hate gotos. This is the subrequest shortcut */
/*
* Handle Connection: header if we do HTTP/1.1 request:
* If we plan to close the backend connection sent Connection: close
* otherwise sent Connection: Keep-Alive.
*/
if (!force10) {
}
else {
}
}
/* send the request body, if any. */
switch(rb_method) {
case RB_STREAM_CHUNKED:
break;
case RB_STREAM_CL:
break;
case RB_SPOOL_CL:
|| (old_te_val != NULL)
|| (bytes_read > 0));
break;
default:
/* shouldn't be possible */
break;
}
/* apr_status_t value has been logged in lower level method */
"pass request body failed to %pI (%s) from %s (%s)",
return rv;
}
return OK;
}
/*
* If the date is a valid RFC 850 date or asctime() date, then it
* is converted to the RFC 1123 format.
*/
{
char* ndate;
if (!time) {
return date;
}
if (rv != APR_SUCCESS) {
return date;
}
return ndate;
}
{
rp->connection = c;
proxy_run_create_req(r, rp);
return rp;
}
{
static const char *date_hdrs[]
static const struct {
const char *name;
} transform_hdrs[] = {
{ "Location", ap_proxy_location_reverse_map },
{ "Content-Location", ap_proxy_location_reverse_map },
{ "URI", ap_proxy_location_reverse_map },
{ "Destination", ap_proxy_location_reverse_map },
{ "Set-Cookie", ap_proxy_cookie_reverse_map },
};
int i;
for (i = 0; date_hdrs[i]; ++i) {
return;
}
}
for (i = 0; transform_hdrs[i].name; ++i) {
return;
}
}
return;
}
/*
* Note: pread_len is the length of the response that we've mistakenly
* read (assuming that we don't consider that an error via
* ProxyBadHeader StartBody). This depends on buffer actually being
* local storage to the calling code in order for pread_len to make
* any sense at all, since we depend on buffer still containing
* what was read by ap_getline() upon return.
*/
{
int len;
char field[MAX_STRING_LEN];
int saw_headers = 0;
*pread_len = 0;
/*
* Read header lines until we get the empty separator line, a read error,
* the connection closes (EOF), or we timeout.
*/
"Headers received from backend:");
/* We may encounter invalid headers, usually from buggy
* MS IIS servers, so we need to determine just how to handle
* them. We can either ignore them, assume that they mark the
* start-of-body (eg: a missing CRLF) or (the default) mark
* the headers as totally bogus and return a 500. The sole
* exception is an extra "HTTP/1.0 200, OK" line sprinkled
* in between the usual MIME headers, which is a favorite
* IIS bug.
*/
/* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
/* Nope, it wasn't even an extra HTTP header. Give up. */
r->headers_out = NULL;
return ;
}
/* if we've already started loading headers_out, then
* return what we've accumulated so far, in the hopes
* that they are useful; also note that we likely pre-read
* the first line of the response.
*/
if (saw_headers) {
"Starting body due to bogus non-header "
"in headers returned by %s (%s)",
return ;
} else {
"No HTTP headers returned by %s (%s)",
return ;
}
}
}
/* this is the psc->badopt == bad_ignore case */
"Ignoring bogus HTTP header returned by %s (%s)",
continue;
}
*value = '\0';
++value;
/* XXX: RFC2068 defines only SP and HT as whitespace, this test is
* wrong... and so are many others probably.
*/
while (apr_isspace(*value))
++value; /* Skip to start of value */
/* should strip trailing whitespace as well */
end)
*end = '\0';
/* make sure we add so as not to destroy duplicated headers
* by ProxyPassReverse and family with process_proxy_header
*/
saw_headers = 1;
/* the header was too long; at the least we should skip extra data */
>= MAX_STRING_LEN - 1) {
/* soak up the extra data */
}
if (len == 0) /* time to exit the larger loop as well */
break;
}
}
}
{
return 1;
}
static
{
char *tmp_s = s;
if (rv == APR_SUCCESS) {
} else if (APR_STATUS_IS_ENOSPC(rv)) {
*writen = n;
} else {
*writen = -1;
}
return rv;
}
/*
* Limit the number of interim respones we sent back to the client. Otherwise
* we suffer from a memory build up. Besides there is NO sense in sending back
* an unlimited number of interim responses to the client. Thus if we cross
* this limit send back a 502 (Bad Gateway).
*/
#ifndef AP_MAX_INTERIM_RESPONSES
#define AP_MAX_INTERIM_RESPONSES 10
#endif
static
char *server_portstr) {
conn_rec *c = r->connection;
char buffer[HUGE_STRING_LEN];
const char *buf;
char keepchar;
apr_bucket *e;
int len, backasswards;
int interim_response = 0; /* non-zero whilst interim 1xx responses
* are being read. */
int pread_len = 0;
int backend_broke = 0;
static const char *hop_by_hop_hdrs[] =
int i;
int original_status = r->status;
int proxy_status = OK;
const char *original_status_line = r->status_line;
const char *proxy_status_line = NULL;
int do_100_continue;
&& ap_request_has_body(r)
&& (PROXYREQ_REVERSE == r->proxyreq)
/* Setup for 100-Continue timeout if appropriate */
if (do_100_continue) {
if (rc != APR_SUCCESS) {
"could not set 100-Continue timeout");
}
}
}
/* Get response from the remote server, and pass it up the
* filter chain
*/
/* In case anyone needs to know, this is a fake request that is really a
* response.
*/
do {
if (len == 0) {
/* handle one potential stray CRLF */
}
if (len <= 0) {
"error reading status line from remote "
if (APR_STATUS_IS_TIMEUP(rc)) {
if (do_100_continue) {
}
}
/*
* If we are a reverse proxy request shutdown the connection
* WITHOUT ANY response to trigger a retry by the client
* if allowed (as for idempotent requests).
* BUT currently we should not do this if the request is the
* first request on a keepalive connection as browsers like
* seamonkey only display an empty page in this case and do
* not do a retry. We should also not do this on a
* connection which times out; instead handle as
* we normally would handle timeouts
*/
!APR_STATUS_IS_TIMEUP(rc)) {
"Closing connection to client because"
" reading from backend server %s:%d failed."
ap_proxy_backend_broke(r, bb);
/*
* Add an EOC bucket to signal the ap_http_header_filter
* that it should get out of our way, BUT ensure that the
* EOC bucket is inserted BEFORE an EOS bucket in bb as
* some resource filters like mod_deflate pass everything
* up to the EOS down the chain immediately and sent the
* remainder of the brigade later (or even never). But in
* this case the ap_http_header_filter does not get out of
* our way soon enough.
*/
e = ap_bucket_eoc_create(c->bucket_alloc);
&& !APR_BUCKET_IS_EOS(eos)) {
}
}
else {
}
/* Mark the backend connection for closing */
/* Need to return OK to avoid sending an error message */
return OK;
}
else if (!c->keepalives) {
"NOT Closing connection to client"
" although reading from backend server %s:%d"
" failed.",
}
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server");
}
/* XXX: Is this a real headers length send from remote? */
/* Is it an HTTP/1 response?
* This is buggy if we ever see an HTTP/1.10
*/
/* If not an HTTP/1 message or
* if the status line was > 8192 bytes
*/
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
apr_pstrcat(p, "Corrupt status line returned by remote "
}
backasswards = 0;
if (keepchar != '\0') {
} else {
/* 2616 requires the space in Status-Line; the origin
* server may have sent one but ap_rgetline_core will
* have stripped it. */
}
/* The status out of the front is the same as the status coming in
* from the back, until further notice.
*/
r->status = proxy_status;
r->status_line = proxy_status_line;
"Status from backend: %d", proxy_status);
/* read the headers. */
/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
/* Also, take care with headers with multiple occurences. */
/* First, tuck away all already existing cookies */
"Set-Cookie", NULL);
/* shove the headers direct into r->headers_out */
&pread_len);
if (r->headers_out == NULL) {
"bad HTTP/%d.%d header returned by %s (%s)",
/*
* ap_send_error relies on a headers_out to be present. we
* are in a bad position here.. so force everything we send out
* to have nothing to do with the incoming packet
*/
r->status = HTTP_BAD_GATEWAY;
r->status_line = "bad gateway";
return r->status;
}
/* Now, add in the just read cookies */
"Set-Cookie", NULL);
/* and now load 'em all in */
if (!apr_is_empty_table(save_table)) {
r->headers_out,
}
/* can't have both Content-Length and Transfer-Encoding */
/*
* 2616 section 4.4, point 3: "if both Transfer-Encoding
* and Content-Length are received, the latter MUST be
* ignored";
*
* To help mitigate HTTP Splitting, unset Content-Length
* and shut down the backend server connection
* XXX: We aught to treat such a response as uncachable
*/
"server %s:%d returned Transfer-Encoding"
" and Content-Length",
}
/*
* Save a possible Transfer-Encoding header as we need it later for
* ap_http_filter to know where to end.
*/
/* strip connection listed hop-by-hop headers from response */
ap_proxy_clear_connection(p, r->headers_out);
}
if (!ap_is_HTTP_INFO(proxy_status)) {
}
/* Clear hop-by-hop headers */
for (i=0; hop_by_hop_hdrs[i]; ++i) {
}
/* Delete warnings with wrong date */
/* handle Via header in response */
const char *server_name = ap_get_server_name(r);
/* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
* then the server name returned by ap_get_server_name() is the
* origin server name (which does make too much sense with Via: headers)
* so we use the proxy vhost's name instead.
*/
if (server_name == r->hostname)
/* create a "Via:" response header entry and merge it */
? apr_psprintf(p, "%d.%d %s%s (%s)",
: apr_psprintf(p, "%d.%d %s%s",
);
}
/* cancel keepalive if HTTP/1.0 or less */
}
} else {
/* an http/0.9 response */
backasswards = 1;
r->status = 200;
r->status_line = "200 OK";
}
if (ap_is_HTTP_INFO(proxy_status)) {
/* Reset to old timeout iff we've adjusted it */
if (do_100_continue
&& (r->status == HTTP_CONTINUE)
}
}
else {
interim_response = 0;
}
if (interim_response) {
/* RFC2616 tells us to forward this.
*
* OTOH, an interim response here may mean the backend
* is playing sillybuggers. The Client didn't ask for
* it within the defined HTTP/1.1 mechanisms, and if
* it's an extension, it may also be unsupported by us.
*
* There's also the possibility that changing existing
* behaviour here might break something.
*
* So let's make it configurable.
*/
"proxy-interim-response");
"HTTP: received interim %d response", r->status);
ap_send_interim_response(r, 1);
}
/* FIXME: refine this to be able to specify per-response-status
* policies and maybe also add option to bail out with 502
*/
"undefined proxy interim response policy");
}
}
/* Moved the fixups of Date headers and those affected by
* ProxyPassReverse/etc from here to ap_proxy_read_headers
*/
const char *buf;
const char *wa = "WWW-Authenticate";
} else {
"origin server sent 401 without "
"WWW-Authenticate header");
}
}
r->sent_bodyct = 1;
/*
* Is it an HTTP/0.9 response or did we maybe preread the 1st line of
* the response? If so, load the extra data. These are 2 mutually
* exclusive possibilities, that just happen to require very
* similar behavior.
*/
if (backasswards || pread_len) {
if (backasswards) {
/*@@@FIXME:
* At this point in response processing of a 0.9 response,
* we don't know yet whether data is binary or not.
* mod_charset_lite will get control later on, so it cannot
* decide on the conversion of this buffer full of data.
* However, chances are that we are not really talking to an
* HTTP/0.9 server, but to some different protocol, therefore
* the best guess IMHO is to always treat the buffer as "text/x":
*/
}
}
/* PR 41646: get HEAD right with ProxyErrorOverride */
/* clear r->status for override error, otherwise ErrorDocument
* thinks that this is a recursive error, and doesn't find the
* custom error page
*/
/* Discard body, if one is expected */
if (!r->header_only && /* not HEAD request */
}
return proxy_status;
}
/* send body - but only if a body is expected */
if ((!r->header_only) && /* not HEAD request */
!interim_response && /* not any 1xx response */
/* We need to copy the output headers and treat them as input
* headers as well. BUT, we need to do this before we remove
* TE, so that they are preserved accordingly for
* ap_http_filter to know where to end.
*/
/*
* Restore Transfer-Encoding header from response if we saved
* one before and there is none left. We need it for the
* ap_http_filter. See above.
*/
}
/*
* if we are overriding the errors, we can't put the content
* of the page into the brigade
*/
/* read the body, pass it to the output filters */
/* Handle the case where the error document is itself reverse
* proxied and was successful. We must maintain any previous
* error status so that an underlying error (eg HTTP_NOT_FOUND)
* doesn't become an HTTP_OK.
*/
&& ap_is_HTTP_ERROR(original_status)) {
r->status = original_status;
}
do {
/* ap_get_brigade will return success with an empty brigade
* for a non-blocking read which would block: */
if (APR_STATUS_IS_EAGAIN(rv)
/* flush to the client and switch to blocking mode */
e = apr_bucket_flush_create(c->bucket_alloc);
|| c->aborted) {
break;
}
continue;
}
break;
}
else if (rv != APR_SUCCESS) {
/* In this case, we are in real trouble because
* our backend bailed on us. Pass along a 502 error
* error bucket
*/
"error reading response");
ap_proxy_backend_broke(r, bb);
backend_broke = 1;
break;
}
/* next time try a non-blocking read */
#if DEBUGGING
{
"readbytes: %#x", readbytes);
}
#endif
/* sanity check */
if (APR_BRIGADE_EMPTY(bb)) {
break;
}
/* Switch the allocator lifetime of the buckets */
/* found the last brigade? */
/* signal that we must leave */
/* the brigade may contain transient buckets that contain
* data that lives only as long as the backend connection.
* Force a setaside so these transient buckets become heap
* buckets that live as long as the request.
*/
for (e = APR_BRIGADE_FIRST(pass_bb); e
!= APR_BRIGADE_SENTINEL(pass_bb); e
= APR_BUCKET_NEXT(e)) {
apr_bucket_setaside(e, r->pool);
}
/* finally it is safe to clean up the brigade from the
* connection pool, as we have forced a setaside on all
* buckets.
*/
/* make sure we release the backend connection as soon
* as we know we are done, so that the backend isn't
* left waiting for a slow client to eventually
* acknowledge the data.
*/
/* Ensure that the backend is not reused */
*backend_ptr = NULL;
}
/* try send what we read */
|| c->aborted) {
/* Ack! Phbtt! Die! User aborted! */
/* Only close backend if we haven't got all from the
* backend. Furthermore if *backend_ptr is NULL it is no
* longer safe to fiddle around with backend as it might
* be already in use by another thread.
*/
if (*backend_ptr) {
}
}
/* make sure we always clean up after ourselves */
} while (!finish);
}
}
else if (!interim_response) {
/* make sure we release the backend connection as soon
* as we know we are done, so that the backend isn't
* left waiting for a slow client to eventually
* acknowledge the data.
*/
*backend_ptr = NULL;
/* Pass EOS bucket down the filter chain. */
e = apr_bucket_eos_create(c->bucket_alloc);
}
/* See define of AP_MAX_INTERIM_RESPONSES for why */
if (interim_response >= AP_MAX_INTERIM_RESPONSES) {
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
apr_psprintf(p,
"Too many (%d) interim responses from origin server",
}
/* If our connection with the client is to be aborted, return DONE. */
if (c->aborted || backend_broke) {
return DONE;
}
return OK;
}
static
{
return OK;
}
/*
* This handles http:// URLs, and other URLs using a remote proxy over http
* If proxyhost is NULL, then contact the server directly, otherwise
* go via the proxy.
* Note that if a proxy is used, then URLs other than http: can be accessed,
* also, if we have trouble which is clearly specific to the proxy, then
* we return DECLINED so that we can try another proxy. (Or the direct
* route.)
*/
{
int status;
char server_portstr[32];
char *scheme;
const char *proxy_function;
const char *u;
int is_ssl = 0;
conn_rec *c = r->connection;
int retry = 0;
/*
* Use a shorter-lived pool to reduce memory usage
* and avoid a memory leak
*/
apr_pool_t *p = r->pool;
/* find the scheme */
return DECLINED;
if ((u - url) > 14)
return HTTP_BAD_REQUEST;
/* scheme is lowercase */
/* is it for us? */
if (!ap_proxy_ssl_enable(NULL)) {
"HTTPS: declining URL %s (mod_ssl not configured?)",
url);
return DECLINED;
}
is_ssl = 1;
proxy_function = "HTTPS";
}
url);
return DECLINED; /* only interested in HTTP, or FTP via proxy */
}
else {
if (*scheme == 'h')
proxy_function = "HTTP";
else
proxy_function = "FTP";
}
/* create space for state information */
goto cleanup;
if (is_ssl) {
}
/*
* In the case that we are handling a reverse proxy connection and this
* is not a request that is coming over an already kept alive connection
* with the client, do NOT reuse the connection to the backend, because
* we cannot forward a failure to the client in this case as the client
* does NOT expect this in this situation.
* Yes, this creates a performance penalty.
*/
}
while (retry < 2) {
/* Step One: Determine Who To Connect To */
sizeof(server_portstr))) != OK)
break;
/* Step Two: Make the Connection */
"HTTP: failed to make connection to backend: %s",
break;
}
/* Step Three: Create conn_rec */
if (!backend->connection) {
break;
/*
* On SSL connections set a note on the connection what CN is
* requested, such that mod_ssl can check if it is requested to do
* so.
*/
if (is_ssl) {
}
}
/* Step Four: Send the Request
* On the off-chance that we forced a 100-Continue as a
* kinda HTTP ping test, allow for retries
*/
"HTTP: 100-Continue failed to %pI (%s)",
retry++;
continue;
} else {
break;
}
}
/* Step Five: Receive the Response... Fall thru to cleanup */
break;
}
/* Step Six: Clean Up */
if (backend) {
}
return status;
}
static void ap_proxy_http_register_hook(apr_pool_t *p)
{
}
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
NULL, /* command apr_table_t */
ap_proxy_http_register_hook/* register hooks */
};