diff options
Diffstat (limited to 'modules/proxy/mod_proxy_http.c')
-rw-r--r-- | modules/proxy/mod_proxy_http.c | 364 |
1 files changed, 54 insertions, 310 deletions
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index 07b5408d..cffad2e7 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -21,6 +21,9 @@ module AP_MODULE_DECLARE_DATA proxy_http_module; +static int (*ap_proxy_clear_connection_fn)(request_rec *r, apr_table_t *headers) = + NULL; + static apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r, proxy_conn_rec *backend); @@ -178,33 +181,7 @@ static apr_table_t *ap_proxy_clean_warnings(apr_pool_t *p, apr_table_t *headers) return headers; } } -static int clear_conn_headers(void *data, const char *key, const char *val) -{ - apr_table_t *headers = ((header_dptr*)data)->table; - apr_pool_t *pool = ((header_dptr*)data)->pool; - const char *name; - char *next = apr_pstrdup(pool, val); - while (*next) { - name = next; - while (*next && !apr_isspace(*next) && (*next != ',')) { - ++next; - } - while (*next && (apr_isspace(*next) || (*next == ','))) { - *next++ = '\0'; - } - apr_table_unset(headers, name); - } - return 1; -} -static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers) -{ - header_dptr x; - x.pool = p; - x.table = headers; - apr_table_unset(headers, "Proxy-Connection"); - apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL); - apr_table_unset(headers, "Connection"); -} + static void add_te_chunked(apr_pool_t *p, apr_bucket_alloc_t *bucket_alloc, apr_bucket_brigade *header_brigade) @@ -250,44 +227,6 @@ static void terminate_headers(apr_bucket_alloc_t *bucket_alloc, APR_BRIGADE_INSERT_TAIL(header_brigade, e); } -static int pass_brigade(apr_bucket_alloc_t *bucket_alloc, - request_rec *r, proxy_conn_rec *p_conn, - conn_rec *origin, apr_bucket_brigade *bb, - int flush) -{ - apr_status_t status; - apr_off_t transferred; - - if (flush) { - apr_bucket *e = apr_bucket_flush_create(bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, e); - } - apr_brigade_length(bb, 0, &transferred); - if (transferred != -1) - p_conn->worker->s->transferred += transferred; - status = ap_pass_brigade(origin->output_filters, bb); - if (status != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084) - "pass request body failed to %pI (%s)", - p_conn->addr, p_conn->hostname); - if (origin->aborted) { - const char *ssl_note; - - if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv")) - != NULL) && (strcmp(ssl_note, "err") == 0)) { - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, - "Error during SSL Handshake with" - " remote server"); - } - return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY; - } - else { - return HTTP_BAD_REQUEST; - } - } - apr_brigade_cleanup(bb); - return OK; -} #define MAX_MEM_SPOOL 16384 @@ -366,7 +305,7 @@ static int stream_reqbody_chunked(apr_pool_t *p, } /* The request is flushed below this loop with chunk EOS header */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0); + rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0); if (rv != OK) { return rv; } @@ -412,7 +351,7 @@ static int stream_reqbody_chunked(apr_pool_t *p, } /* Now we have headers-only, or the chunk EOS mark; flush it */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1); + rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1); return rv; } @@ -422,7 +361,7 @@ static int stream_reqbody_cl(apr_pool_t *p, conn_rec *origin, apr_bucket_brigade *header_brigade, apr_bucket_brigade *input_brigade, - const char *old_cl_val) + char *old_cl_val) { int seen_eos = 0, rv = 0; apr_status_t status = APR_SUCCESS; @@ -511,7 +450,7 @@ static int stream_reqbody_cl(apr_pool_t *p, } /* Once we hit EOS, we are ready to flush. */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos); + rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos); if (rv != OK) { return rv ; } @@ -541,7 +480,7 @@ static int stream_reqbody_cl(apr_pool_t *p, * body; send it now with the flush flag */ bb = header_brigade; - return(pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1)); + return(ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1)); } return OK; @@ -685,7 +624,7 @@ static int spool_reqbody_cl(apr_pool_t *p, APR_BRIGADE_INSERT_TAIL(header_brigade, e); } /* This is all a single brigade, pass with flush flagged */ - return(pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1)); + return(ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1)); } /* @@ -752,257 +691,31 @@ int ap_proxy_http_request(apr_pool_t *p, request_rec *r, apr_bucket_brigade *temp_brigade; apr_bucket *e; char *buf; - const apr_array_header_t *headers_in_array; - const apr_table_entry_t *headers_in; - int counter; apr_status_t status; enum rb_methods {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED, RB_SPOOL_CL}; enum rb_methods rb_method = RB_INIT; - const char *old_cl_val = NULL; - const char *old_te_val = NULL; + char *old_cl_val = NULL; + char *old_te_val = NULL; apr_off_t bytes_read = 0; apr_off_t bytes; int force10, rv; - apr_table_t *headers_in_copy; - proxy_dir_conf *dconf; conn_rec *origin = p_conn->connection; - int do_100_continue; - - dconf = ap_get_module_config(r->per_dir_config, &proxy_module); - header_brigade = apr_brigade_create(p, origin->bucket_alloc); - - /* - * 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. - */ - do_100_continue = (worker->s->ping_timeout_set - && ap_request_has_body(r) - && (PROXYREQ_REVERSE == r->proxyreq) - && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0"))); if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { - /* - * 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; } - buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL); force10 = 1; - p_conn->close = 1; } else { - buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); force10 = 0; } - if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { - origin->keepalive = AP_CONN_CLOSE; - p_conn->close = 1; - } - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); - if (dconf->preserve_host == 0) { - if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ - if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { - buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:", - uri->port_str, CRLF, NULL); - } else { - buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL); - } - } else { - if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { - buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", - uri->port_str, CRLF, NULL); - } else { - buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL); - } - } - } - else { - /* don't want to use r->hostname, as the incoming header might have a - * port attached - */ - const char* hostname = apr_table_get(r->headers_in,"Host"); - if (!hostname) { - hostname = r->server->server_hostname; - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092) - "no HTTP 0.9 request (with no host line) " - "on incoming request and preserve host set " - "forcing hostname to be %s for uri %s", - hostname, r->uri); - } - buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL); - } - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); - - /* handle Via */ - if (conf->viaopt == via_block) { - /* Block all outgoing Via: headers */ - apr_table_unset(r->headers_in, "Via"); - } else if (conf->viaopt != via_off) { - 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) - server_name = r->server->server_hostname; - /* Create a "Via:" request header entry and merge it */ - /* Generate outgoing Via: header with/without server comment: */ - apr_table_mergen(r->headers_in, "Via", - (conf->viaopt == via_full) - ? apr_psprintf(p, "%d.%d %s%s (%s)", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - server_name, server_portstr, - AP_SERVER_BASEVERSION) - : apr_psprintf(p, "%d.%d %s%s", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - server_name, server_portstr) - ); - } - - /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test - * to backend - */ - if (do_100_continue) { - apr_table_mergen(r->headers_in, "Expect", "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. - */ - apr_table_mergen(r->headers_in, "X-Forwarded-For", - r->useragent_ip); - - /* Add X-Forwarded-Host: so that upstream knows what the - * original request hostname was. - */ - if ((buf = apr_table_get(r->headers_in, "Host"))) { - apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf); - } - - /* 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? - */ - apr_table_mergen(r->headers_in, "X-Forwarded-Server", - 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. - */ - headers_in_copy = apr_table_copy(r->pool, r->headers_in); - ap_proxy_clear_connection(p, headers_in_copy); - /* send request headers */ - headers_in_array = apr_table_elts(headers_in_copy); - headers_in = (const apr_table_entry_t *) headers_in_array->elts; - for (counter = 0; counter < headers_in_array->nelts; counter++) { - if (headers_in[counter].key == NULL - || headers_in[counter].val == NULL - - /* Already sent */ - || !strcasecmp(headers_in[counter].key, "Host") - - /* Clear out hop-by-hop request headers not to send - * RFC2616 13.5.1 says we should strip these headers - */ - || !strcasecmp(headers_in[counter].key, "Keep-Alive") - || !strcasecmp(headers_in[counter].key, "TE") - || !strcasecmp(headers_in[counter].key, "Trailer") - || !strcasecmp(headers_in[counter].key, "Upgrade") - - ) { - 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. - */ - if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) { - if (r->user != NULL) { /* we've authenticated */ - if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { - continue; - } - } - } - - - /* Skip Transfer-Encoding and Content-Length for now. - */ - if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) { - old_te_val = headers_in[counter].val; - continue; - } - if (!strcasecmp(headers_in[counter].key, "Content-Length")) { - old_cl_val = headers_in[counter].val; - continue; - } - - /* for sub-requests, ignore freshness/expiry headers */ - if (r->main) { - if ( !strcasecmp(headers_in[counter].key, "If-Match") - || !strcasecmp(headers_in[counter].key, "If-Modified-Since") - || !strcasecmp(headers_in[counter].key, "If-Range") - || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since") - || !strcasecmp(headers_in[counter].key, "If-None-Match")) { - continue; - } - } - - buf = apr_pstrcat(p, headers_in[counter].key, ": ", - headers_in[counter].val, CRLF, - NULL); - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); + header_brigade = apr_brigade_create(p, origin->bucket_alloc); + rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, p_conn, + worker, conf, uri, url, server_portstr, + &old_cl_val, &old_te_val); + if (rv != OK) { + return rv; } /* We have headers, let's figure out our request body... */ @@ -1576,6 +1289,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, "error reading status line from remote " "server %s:%d", backend->hostname, backend->port); if (APR_STATUS_IS_TIMEUP(rc)) { + apr_table_set(r->notes, "proxy_timedout", "1"); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01103) "read timeout"); if (do_100_continue) { return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, "Timeout on 100-Continue"); @@ -1754,11 +1468,10 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, * ap_http_filter to know where to end. */ te = apr_table_get(r->headers_out, "Transfer-Encoding"); + /* strip connection listed hop-by-hop headers from response */ - if (ap_find_token(p, apr_table_get(r->headers_out, "Connection"), - "close")) - backend->close = 1; - ap_proxy_clear_connection(p, r->headers_out); + backend->close = ap_proxy_clear_connection_fn(r, r->headers_out); + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { ap_set_content_type(r, apr_pstrdup(p, buf)); } @@ -1770,6 +1483,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, for (i=0; hop_by_hop_hdrs[i]; ++i) { apr_table_unset(r->headers_out, hop_by_hop_hdrs[i]); } + /* Delete warnings with wrong date */ r->headers_out = ap_proxy_clean_warnings(p, r->headers_out); @@ -1837,12 +1551,16 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, * behaviour here might break something. * * So let's make it configurable. + * + * We need to set "r->expecting_100 = 1" otherwise origin + * server behaviour will apply. */ const char *policy = apr_table_get(r->subprocess_env, "proxy-interim-response"); ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "HTTP: received interim %d response", r->status); - if (!policy || !strcasecmp(policy, "RFC")) { + if (!policy + || (!strcasecmp(policy, "RFC") && ((r->expecting_100 = 1)))) { ap_send_interim_response(r, 1); } /* FIXME: refine this to be able to specify per-response-status @@ -2294,8 +2012,34 @@ cleanup: } return status; } + +/* post_config hook: */ +static int proxy_http_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + + /* proxy_http_post_config() will be called twice during startup. So, don't + * set up the static data the 1st time through. */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + + if (!ap_proxy_clear_connection_fn) { + ap_proxy_clear_connection_fn = + APR_RETRIEVE_OPTIONAL_FN(ap_proxy_clear_connection); + if (!ap_proxy_clear_connection_fn) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02477) + "mod_proxy must be loaded for mod_proxy_http"); + return !OK; + } + } + + return OK; +} + static void ap_proxy_http_register_hook(apr_pool_t *p) { + ap_hook_post_config(proxy_http_post_config, NULL, NULL, APR_HOOK_MIDDLE); proxy_hook_scheme_handler(proxy_http_handler, NULL, NULL, APR_HOOK_FIRST); proxy_hook_canon_handler(proxy_http_canon, NULL, NULL, APR_HOOK_FIRST); warn_rx = ap_pregcomp(p, "[0-9]{3}[ \t]+[^ \t]+[ \t]+\"[^\"]*\"([ \t]+\"([^\"]+)\")?", 0); |