mod_proxy_http.c revision 71f17ea73e42279193c077e6a42d0e4112ee4b2a
2d2eda71267231c2526be701fe655db125852c1ffielding/* Licensed to the Apache Software Foundation (ASF) under one or more
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * contributor license agreements. See the NOTICE file distributed with
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * this work for additional information regarding copyright ownership.
b99dbaab171d91e1b664397cc40e039d0c087c65fielding * The ASF licenses this file to You under the Apache License, Version 2.0
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * (the "License"); you may not use this file except in compliance with
2d2eda71267231c2526be701fe655db125852c1ffielding * the License. You may obtain a copy of the License at
2d2eda71267231c2526be701fe655db125852c1ffielding * Unless required by applicable law or agreed to in writing, software
2d2eda71267231c2526be701fe655db125852c1ffielding * distributed under the License is distributed on an "AS IS" BASIS,
2d2eda71267231c2526be701fe655db125852c1ffielding * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2d2eda71267231c2526be701fe655db125852c1ffielding * See the License for the specific language governing permissions and
2d2eda71267231c2526be701fe655db125852c1ffielding * limitations under the License.
2d2eda71267231c2526be701fe655db125852c1ffielding/* HTTP routines for Apache proxy */
f062ed7bd262a37a909dd77ce5fc23b446818823fieldingstatic apr_status_t ap_proxy_http_cleanup(const char *scheme,
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * Canonicalise http-like URLs.
2d2eda71267231c2526be701fe655db125852c1ffielding * scheme is the scheme for the URL
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * url is the URL starting with the first '/'
f062ed7bd262a37a909dd77ce5fc23b446818823fielding * def_port is the default port for this scheme.
2d2eda71267231c2526be701fe655db125852c1ffieldingstatic int proxy_http_canon(request_rec *r, char *url)
f062ed7bd262a37a909dd77ce5fc23b446818823fielding const char *err;
2d2eda71267231c2526be701fe655db125852c1ffielding const char *scheme;
f062ed7bd262a37a909dd77ce5fc23b446818823fielding /* ap_port_of_scheme() */
f062ed7bd262a37a909dd77ce5fc23b446818823fielding ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, r->server,
2d2eda71267231c2526be701fe655db125852c1ffielding /* do syntatic check.
2d2eda71267231c2526be701fe655db125852c1ffielding * We break the URL into host, port, path, search
b980ad7fdc218b4855cde9f75a747527f50c554dwrowe err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb "error parsing URL %s: %s",
2d2eda71267231c2526be701fe655db125852c1ffielding * now parse path/search args, according to rfc1738:
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * process the path.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * In a reverse proxy, our URL has been processed, so canonicalise
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * unless proxy-nocanon is set to say it's raw
fd492f9543f14fb5bae78e04b135c3448eb9cc56rbb * In a forward proxy, we have and MUST NOT MANGLE the original.
fd492f9543f14fb5bae78e04b135c3448eb9cc56rbb switch (r->proxyreq) {
fd492f9543f14fb5bae78e04b135c3448eb9cc56rbb default: /* wtf are we doing here? */
2d2eda71267231c2526be701fe655db125852c1ffielding path = ap_proxy_canonenc(r->pool, url, strlen(url),
61fd0cab072a05b855cbef9c585702401ac5ae29rbb if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
61fd0cab072a05b855cbef9c585702401ac5ae29rbb "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb/* Clear all connection-based headers from the incoming headers table */
2d2eda71267231c2526be701fe655db125852c1ffieldingtypedef struct header_dptr {
000b67449410515eac43e76ef6667915bfd4d2abgsteinstatic int clean_warning_headers(void *data, const char *key, const char *val)
2d2eda71267231c2526be701fe655db125852c1ffielding apr_table_t *headers = ((header_dptr*)data)->table;
7bdef86e15d47d16dcbe7a5611683191774bd5fbgstein ((header_dptr*)data)->table = headers = apr_table_make(pool, 2);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * Parse this, suckers!
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * Warning = "Warning" ":" 1#warning-value
3d96ee83babeec32482c9082c9426340cee8c44dwrowe * warning-value = warn-code SP warn-agent SP warn-text
7bdef86e15d47d16dcbe7a5611683191774bd5fbgstein * [SP warn-date]
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * warn-code = 3DIGIT
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * warn-agent = ( host [ ":" port ] ) | pseudonym
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * ; the name or pseudonym of the server adding
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * ; the Warning header, for use in debugging
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * warn-text = quoted-string
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * warn-date = <"> HTTP-date <">
3d96ee83babeec32482c9082c9426340cee8c44dwrowe * Buggrit, use a bloomin' regexp!
7bdef86e15d47d16dcbe7a5611683191774bd5fbgstein * (\d{3}\s+\S+\s+\".*?\"(\s+\"(.*?)\")?) --> whole in $1, date in $3
c9a95767fbf0f5fb0976a06b97a256033925e433rbb /* OK, we have a date here */
c9a95767fbf0f5fb0976a06b97a256033925e433rbb if (!warn_time || (warn_time == ((header_dptr*)data)->time)) {
61fd0cab072a05b855cbef9c585702401ac5ae29rbbstatic apr_table_t *ap_proxy_clean_warnings(apr_pool_t *p, apr_table_t *headers)
7bdef86e15d47d16dcbe7a5611683191774bd5fbgstein x.time = apr_date_parse_http(apr_table_get(headers, "Date"));
61fd0cab072a05b855cbef9c585702401ac5ae29rbb apr_table_do(clean_warning_headers, &x, headers, "Warning", NULL);
61fd0cab072a05b855cbef9c585702401ac5ae29rbbstatic int clear_conn_headers(void *data, const char *key, const char *val)
7bdef86e15d47d16dcbe7a5611683191774bd5fbgstein const char *name;
61fd0cab072a05b855cbef9c585702401ac5ae29rbb while (*next) {
61fd0cab072a05b855cbef9c585702401ac5ae29rbb while (*next && !apr_isspace(*next) && (*next != ',')) {
61fd0cab072a05b855cbef9c585702401ac5ae29rbb while (*next && (apr_isspace(*next) || (*next == ','))) {
2d2eda71267231c2526be701fe655db125852c1ffieldingstatic void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
2d2eda71267231c2526be701fe655db125852c1ffielding apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb const char te_hdr[] = "Transfer-Encoding: chunked" CRLF;
7bdef86e15d47d16dcbe7a5611683191774bd5fbgstein e = apr_bucket_pool_create(buf, sizeof(te_hdr)-1, p, bucket_alloc);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb const char *cl_val)
62db15de4c1f335a64d45821796ae197cff94ef8rbb e = apr_bucket_pool_create(buf, strlen(buf), p, bucket_alloc);
62db15de4c1f335a64d45821796ae197cff94ef8rbbstatic void terminate_headers(apr_bucket_alloc_t *bucket_alloc,
62db15de4c1f335a64d45821796ae197cff94ef8rbb /* add empty line at the end of the headers */
62db15de4c1f335a64d45821796ae197cff94ef8rbb e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
48d2edbfb84e5559b5da0f8d614ccab805cc67a8rbbstatic int pass_brigade(apr_bucket_alloc_t *bucket_alloc,
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar "proxy: pass request body failed to %pI (%s)",
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY;
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar char chunk_hdr[20]; /* must be here due to transient bucket. */
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar /* If this brigade contains EOS, either stop or remove it. */
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar /* We can't pass this EOS to the output_filters. */
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar * Append the end-of-chunk CRLF
91f0d8da77152d24e4bbb31ce199282b3fd6e3b2coar e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
2d2eda71267231c2526be701fe655db125852c1ffielding /* we never sent the header brigade, so go ahead and
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * take care of that now
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * Save input_brigade in bb brigade. (At least) in the SSL case
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * input_brigade contains transient buckets whose data would get
3d96ee83babeec32482c9082c9426340cee8c44dwrowe * overwritten during the next call of ap_get_brigade in the loop.
a6b9ed64fdf548c61de9714e2cfb999ec59d149cgstein * ap_save_brigade ensures these buckets to be set aside.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * Calling ap_save_brigade with NULL as filter is OK, because
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * bb brigade already has been created and does not need to get
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * created by ap_save_brigade.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* The request is flushed below this loop with chunk EOS header */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb status = ap_get_brigade(r->input_filters, input_brigade,
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* we never sent the header brigade because there was no request body;
3d96ee83babeec32482c9082c9426340cee8c44dwrowe * send it now
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* input brigade still has an EOS which we can't pass to the output_filters. */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* <trailers> */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
2d2eda71267231c2526be701fe655db125852c1ffielding e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* Now we have headers-only, or the chunk EOS mark; flush it */
2d2eda71267231c2526be701fe655db125852c1ffielding rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
d839a9822ee53ce00da24c15f2d9fe054233d342gstein const char *old_cl_val)
2d2eda71267231c2526be701fe655db125852c1ffielding apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
3d96ee83babeec32482c9082c9426340cee8c44dwrowe if (status || *endstr || endstr == old_cl_val || cl_val < 0) {
61fd0cab072a05b855cbef9c585702401ac5ae29rbb "proxy: could not parse request Content-Length (%s)",
61fd0cab072a05b855cbef9c585702401ac5ae29rbb while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* If this brigade contains EOS, either stop or remove it. */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* We can't pass this EOS to the output_filters. */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) {
11a7b0dff22d26770b532c174d1cf2e7b56ec244wrowe e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* C-L < bytes streamed?!?
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * We will error out after the body is completely
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * consumed, but we can't stream more bytes at the
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * back end since they would in part be interpreted
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * as another request! If nothing is sent, then
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * just send nothing.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * Prevents HTTP Response Splitting.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb "proxy: read more bytes of request body than expected "
61fd0cab072a05b855cbef9c585702401ac5ae29rbb "(got %" APR_OFF_T_FMT ", expected %" APR_OFF_T_FMT ")",
3d96ee83babeec32482c9082c9426340cee8c44dwrowe /* we never sent the header brigade, so go ahead and
a6b9ed64fdf548c61de9714e2cfb999ec59d149cgstein * take care of that now
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * Save input_brigade in bb brigade. (At least) in the SSL case
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * input_brigade contains transient buckets whose data would get
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * overwritten during the next call of ap_get_brigade in the loop.
3d96ee83babeec32482c9082c9426340cee8c44dwrowe * ap_save_brigade ensures these buckets to be set aside.
a6b9ed64fdf548c61de9714e2cfb999ec59d149cgstein * Calling ap_save_brigade with NULL as filter is OK, because
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * bb brigade already has been created and does not need to get
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * created by ap_save_brigade.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb /* Once we hit EOS, we are ready to flush. */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos);
61fd0cab072a05b855cbef9c585702401ac5ae29rbb status = ap_get_brigade(r->input_filters, input_brigade,
3d96ee83babeec32482c9082c9426340cee8c44dwrowe "proxy: client %s given Content-Length did not match"
2d2eda71267231c2526be701fe655db125852c1ffielding " number of body bytes read", r->connection->remote_ip);
fd8b91502bc200ed4cca3810560a2a570522b3debrianp /* we never sent the header brigade since there was no request
fd8b91502bc200ed4cca3810560a2a570522b3debrianp * body; send it now with the flush flag
fd8b91502bc200ed4cca3810560a2a570522b3debrianp return(pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1));
fd8b91502bc200ed4cca3810560a2a570522b3debrianp apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
3d96ee83babeec32482c9082c9426340cee8c44dwrowe while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
344f3bc38dfccf6261d5bb8d689794cde113b3d6coar /* If this brigade contains EOS, either stop or remove it. */
344f3bc38dfccf6261d5bb8d689794cde113b3d6coar if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
344f3bc38dfccf6261d5bb8d689794cde113b3d6coar /* We can't pass this EOS to the output_filters. */
fd0edaa8e3d4dd67d0604ccef2e96b071db96643fielding * LimitRequestBody does not affect Proxy requests (Should it?).
fd0edaa8e3d4dd67d0604ccef2e96b071db96643fielding * Let it take effect if we decide to store the body in a
61fd0cab072a05b855cbef9c585702401ac5ae29rbb * temporary file on disk.
61fd0cab072a05b855cbef9c585702401ac5ae29rbb "proxy: Request body is larger than the"
a6b9ed64fdf548c61de9714e2cfb999ec59d149cgstein /* can't spool any more in memory; write latest brigade to disk */
61fd0cab072a05b855cbef9c585702401ac5ae29rbb const char *temp_dir;
42ce672c516baf6e4eaed18ccc1647de2d456d8edougm "proxy: search for temporary directory failed");
42ce672c516baf6e4eaed18ccc1647de2d456d8edougm "proxy: creation of temporary file in directory %s failed",
42ce672c516baf6e4eaed18ccc1647de2d456d8edougm const char *data;
a6b9ed64fdf548c61de9714e2cfb999ec59d149cgstein apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ);
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written);
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb const char *tmpfile_name;
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) {
8b7047e519340545e6807c9749576a40a76b6d3frbb "proxy: write to temporary file %s failed",
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb * Save input_brigade in body_brigade. (At least) in the SSL case
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb * input_brigade contains transient buckets whose data would get
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb * overwritten during the next call of ap_get_brigade in the loop.
561e5f16a2f9fb397aac4c283aaa87a752520a4ddougm * ap_save_brigade ensures these buckets to be set aside.
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb * Calling ap_save_brigade with NULL as filter is OK, because
561e5f16a2f9fb397aac4c283aaa87a752520a4ddougm * body_brigade already has been created and does not need to get
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb * created by ap_save_brigade.
fcc25eda7b150e226d3c1cdaea66a943d3fdee4erbb status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
c9a95767fbf0f5fb0976a06b97a256033925e433rbb status = ap_get_brigade(r->input_filters, input_brigade,
c9a95767fbf0f5fb0976a06b97a256033925e433rbb add_cl(p, bucket_alloc, header_brigade, apr_off_t_toa(p, bytes_spooled));
2d2eda71267231c2526be701fe655db125852c1ffielding apr_brigade_insert_file(header_brigade, tmpfile, 0, fsize, p);
apr_bucket *e;
char *buf;
int counter;
int do_100_continue;
&& ap_request_has_body(r)
if (r->expecting_100) {
return HTTP_EXPECTATION_FAILED;
force10 = 0;
if (!hostname) {
r->uri );
if (do_100_continue) {
const char *buf;
c->remote_ip);
proxy_run_fixups(r);
if (r->main) {
NULL);
if (old_cl_val) {
if (old_te_val) {
goto skip_body;
return HTTP_INTERNAL_SERVER_ERROR;
* This helps us avoid any election of C-L v.s. T-E
return HTTP_BAD_REQUEST;
return HTTP_INTERNAL_SERVER_ERROR;
else if (old_te_val) {
if (force10
else if (old_cl_val) {
else if (!force10
if (!force10) {
switch(rb_method) {
case RB_STREAM_CHUNKED:
case RB_STREAM_CL:
case RB_SPOOL_CL:
|| (bytes_read > 0));
c->remote_ip,
return rv;
return OK;
static const char *date_hdrs[]
const char *name;
} transform_hdrs[] = {
for (i = 0; date_hdrs[i]; ++i) {
int len;
int saw_headers = 0;
*pread_len = 0;
if (saw_headers) {
++value;
end)
char *tmp_s = s;
*writen = n;
return rv;
#ifndef AP_MAX_INTERIM_RESPONSES
char *server_portstr) {
const char *buf;
char keepchar;
apr_bucket *e;
int pread_len = 0;
int backend_broke = 0;
static const char *hop_by_hop_hdrs[] =
int do_100_continue;
&& ap_request_has_body(r)
if (do_100_continue) {
if (len == 0) {
if (len <= 0) {
if (do_100_continue) {
return OK;
else if (!c->keepalives) {
backasswards = 0;
&pread_len);
r->method);
return r->status;
r->headers_out,
for (i=0; hop_by_hop_hdrs[i]; ++i) {
if (do_100_continue
interim_response = 0;
if (interim_response) {
r->status);
* ProxyPassReverse/etc from here to ap_proxy_read_headers
const char *buf;
if (backasswards) {
return proxy_status;
|| c->aborted) {
#if DEBUGGING
= APR_BUCKET_NEXT(e)) {
|| c->aborted) {
if (backend_ptr) {
} while (!finish);
else if (!interim_response) {
apr_psprintf(p,
return DONE;
return OK;
return OK;
int status;
char *scheme;
const char *proxy_function;
int is_ssl = 0;
int retry = 0;
return DECLINED;
return HTTP_BAD_REQUEST;
return DECLINED;
goto cleanup;
if (is_ssl) {
if (is_ssl) {
retry++;
if (backend) {
return status;