/* Create a new table consisting of those elements from an input * headers table that are allowed to be stored in a cache. */ CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool, apr_table_t *t, server_rec *s) { cache_server_conf *conf; char **header; int i; /* Make a copy of the headers, and remove from * the copy any hop-by-hop headers, as defined in Section * 13.5.1 of RFC 2616 */ apr_table_t *headers_out; headers_out = apr_table_copy(pool, t); apr_table_unset(headers_out, "Connection"); apr_table_unset(headers_out, "Keep-Alive"); apr_table_unset(headers_out, "Proxy-Authenticate"); apr_table_unset(headers_out, "Proxy-Authorization"); apr_table_unset(headers_out, "TE"); apr_table_unset(headers_out, "Trailers"); apr_table_unset(headers_out, "Transfer-Encoding"); apr_table_unset(headers_out, "Upgrade"); conf = (cache_server_conf *)ap_get_module_config(s->module_config, &cache_module); /* Remove the user defined headers set with CacheIgnoreHeaders. * This may break RFC 2616 compliance on behalf of the administrator. */ header = (char **)conf->ignore_headers->elts; for (i = 0; i < conf->ignore_headers->nelts; i++) { apr_table_unset(headers_out, header[i]); } return headers_out; }
static void *merge_env_dir_configs(apr_pool_t *p, void *basev, void *addv) { env_dir_config_rec *base = basev; env_dir_config_rec *add = addv; env_dir_config_rec *res = apr_palloc(p, sizeof(*res)); const apr_table_entry_t *elts; const apr_array_header_t *arr; int i; /* * res->vars = copy_table( p, base->vars ); * foreach $unsetenv ( @add->unsetenv ) * table_unset( res->vars, $unsetenv ); * foreach $element ( @add->vars ) * table_set( res->vars, $element.key, $element.val ); * * add->unsetenv already removed the vars from add->vars, * if they preceded the UnsetEnv directive. */ res->vars = apr_table_copy(p, base->vars); res->unsetenv = NULL; arr = apr_table_elts(add->unsetenv); if (arr) { elts = (const apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; ++i) { apr_table_unset(res->vars, elts[i].key); } } arr = apr_table_elts(add->vars); if (arr) { elts = (const apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; ++i) { apr_table_setn(res->vars, elts[i].key, elts[i].val); } } return res; }
/** * Take two sets of headers, sandwich them together, and apply the result to * r->headers_out. * * To complicate this, a header may be duplicated in either table. Should a * header exist in the top table, all matching headers will be removed from * the bottom table before the headers are combined. The Warning headers are * handled specially. Warnings are added rather than being replaced, while * in the case of revalidation 1xx Warnings are stripped. * * The Content-Type and Last-Modified headers are then re-parsed and inserted * into the request. */ void cache_accept_headers(cache_handle_t *h, request_rec *r, apr_table_t *top, apr_table_t *bottom, int revalidation) { const char *v; if (revalidation) { r->headers_out = apr_table_make(r->pool, 10); apr_table_do(filter_header_do, r->headers_out, bottom, NULL); } else if (r->headers_out != bottom) { r->headers_out = apr_table_copy(r->pool, bottom); } apr_table_do(remove_header_do, r->headers_out, top, NULL); apr_table_do(add_header_do, r->headers_out, top, NULL); v = apr_table_get(r->headers_out, "Content-Type"); if (v) { ap_set_content_type(r, v); /* * Also unset possible Content-Type headers in r->headers_out and * r->err_headers_out as they may be different to what we have received * from the cache. * Actually they are not needed as r->content_type set by * ap_set_content_type above will be used in the store_headers functions * of the storage providers as a fallback and the HTTP_HEADER filter * does overwrite the Content-Type header with r->content_type anyway. */ apr_table_unset(r->headers_out, "Content-Type"); apr_table_unset(r->err_headers_out, "Content-Type"); } /* If the cache gave us a Last-Modified header, we can't just * pass it on blindly because of restrictions on future values. */ v = apr_table_get(r->headers_out, "Last-Modified"); if (v) { ap_update_mtime(r, apr_date_parse_http(v)); ap_set_last_modified(r); } }
static request_rec *h2_task_create_request(h2_task_env *env) { conn_rec *conn = &env->c; request_rec *r; apr_pool_t *p; int access_status = HTTP_OK; apr_pool_create(&p, conn->pool); apr_pool_tag(p, "request"); r = apr_pcalloc(p, sizeof(request_rec)); AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn); r->pool = p; r->connection = conn; r->server = conn->base_server; r->user = NULL; r->ap_auth_type = NULL; r->allowed_methods = ap_make_method_list(p, 2); r->headers_in = apr_table_copy(r->pool, env->headers); r->trailers_in = apr_table_make(r->pool, 5); r->subprocess_env = apr_table_make(r->pool, 25); r->headers_out = apr_table_make(r->pool, 12); r->err_headers_out = apr_table_make(r->pool, 5); r->trailers_out = apr_table_make(r->pool, 5); r->notes = apr_table_make(r->pool, 5); r->request_config = ap_create_request_config(r->pool); /* Must be set before we run create request hook */ r->proto_output_filters = conn->output_filters; r->output_filters = r->proto_output_filters; r->proto_input_filters = conn->input_filters; r->input_filters = r->proto_input_filters; ap_run_create_request(r); r->per_dir_config = r->server->lookup_defaults; r->sent_bodyct = 0; /* bytect isn't for body */ r->read_length = 0; r->read_body = REQUEST_NO_BODY; r->status = HTTP_OK; /* Until further notice */ r->header_only = 0; r->the_request = NULL; /* Begin by presuming any module can make its own path_info assumptions, * until some module interjects and changes the value. */ r->used_path_info = AP_REQ_DEFAULT_PATH_INFO; r->useragent_addr = conn->client_addr; r->useragent_ip = conn->client_ip; ap_run_pre_read_request(r, conn); /* Time to populate r with the data we have. */ r->request_time = apr_time_now(); r->the_request = apr_psprintf(r->pool, "%s %s HTTP/1.1", env->method, env->path); r->method = env->method; /* Provide quick information about the request method as soon as known */ r->method_number = ap_method_number_of(r->method); if (r->method_number == M_GET && r->method[0] == 'H') { r->header_only = 1; } ap_parse_uri(r, env->path); r->protocol = (char*)"HTTP/1.1"; r->proto_num = HTTP_VERSION(1, 1); r->hostname = env->authority; /* update what we think the virtual host is based on the headers we've * now read. may update status. */ ap_update_vhost_from_headers(r); /* we may have switched to another server */ r->per_dir_config = r->server->lookup_defaults; /* * Add the HTTP_IN filter here to ensure that ap_discard_request_body * called by ap_die and by ap_send_error_response works correctly on * status codes that do not cause the connection to be dropped and * in situations where the connection should be kept alive. */ ap_add_input_filter_handle(ap_http_input_filter_handle, NULL, r, r->connection); if (access_status != HTTP_OK || (access_status = ap_run_post_read_request(r))) { /* Request check post hooks failed. An example of this would be a * request for a vhost where h2 is disabled --> 421. */ h2_task_die(env, access_status, r); ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); ap_run_log_transaction(r); r = NULL; goto traceout; } AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status); return r; traceout: AP_READ_REQUEST_FAILURE((uintptr_t)r); return r; }
/* * select a specific URL entity in the cache * * It is possible to store more than one entity per URL. Content * negotiation is used to select an entity. Once an entity is * selected, details of it are stored in the per request * config to save time when serving the request later. * * This function returns OK if successful, DECLINED if no * cached entity fits the bill. */ int cache_select_url(request_rec *r, char *url) { cache_provider_list *list; apr_status_t rv; cache_handle_t *h; char *key; cache_request_rec *cache = (cache_request_rec *) ap_get_module_config(r->request_config, &cache_module); rv = cache_generate_key(r, r->pool, &key); if (rv != APR_SUCCESS) { return rv; } /* go through the cache types till we get a match */ h = apr_palloc(r->pool, sizeof(cache_handle_t)); list = cache->providers; while (list) { switch ((rv = list->provider->open_entity(h, r, key))) { case OK: { char *vary = NULL; const char *varyhdr = NULL; int fresh; if (list->provider->recall_headers(h, r) != APR_SUCCESS) { /* TODO: Handle this error */ return DECLINED; } /* * Check Content-Negotiation - Vary * * At this point we need to make sure that the object we found in * the cache is the same object that would be delivered to the * client, when the effects of content negotiation are taken into * effect. * * In plain english, we want to make sure that a language-negotiated * document in one language is not given to a client asking for a * language negotiated document in a different language by mistake. * * This code makes the assumption that the storage manager will * cache the req_hdrs if the response contains a Vary * header. * * RFC2616 13.6 and 14.44 describe the Vary mechanism. */ if ((varyhdr = apr_table_get(h->resp_err_hdrs, "Vary")) == NULL) { varyhdr = apr_table_get(h->resp_hdrs, "Vary"); } vary = apr_pstrdup(r->pool, varyhdr); while (vary && *vary) { char *name = vary; const char *h1, *h2; /* isolate header name */ while (*vary && !apr_isspace(*vary) && (*vary != ',')) ++vary; while (*vary && (apr_isspace(*vary) || (*vary == ','))) { *vary = '\0'; ++vary; } /* * is this header in the request and the header in the cached * request identical? If not, we give up and do a straight get */ h1 = apr_table_get(r->headers_in, name); h2 = apr_table_get(h->req_hdrs, name); if (h1 == h2) { /* both headers NULL, so a match - do nothing */ } else if (h1 && h2 && !strcmp(h1, h2)) { /* both headers exist and are equal - do nothing */ } else { /* headers do not match, so Vary failed */ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server, "cache_select_url(): Vary header mismatch."); return DECLINED; } } cache->provider = list->provider; cache->provider_name = list->provider_name; /* Is our cached response fresh enough? */ fresh = ap_cache_check_freshness(h, r); if (!fresh) { cache_info *info = &(h->cache_obj->info); /* Make response into a conditional */ /* FIXME: What if the request is already conditional? */ if (info && info->etag) { /* if we have a cached etag */ cache->stale_headers = apr_table_copy(r->pool, r->headers_in); apr_table_set(r->headers_in, "If-None-Match", info->etag); cache->stale_handle = h; } else if (info && info->lastmods) { /* if we have a cached Last-Modified header */ cache->stale_headers = apr_table_copy(r->pool, r->headers_in); apr_table_set(r->headers_in, "If-Modified-Since", info->lastmods); cache->stale_handle = h; } return DECLINED; } /* Okay, this response looks okay. Merge in our stuff and go. */ apr_table_setn(r->headers_out, "Content-Type", ap_make_content_type(r, h->content_type)); r->filename = apr_pstrdup(r->pool, h->cache_obj->info.filename); accept_headers(h, r); cache->handle = h; return OK; } case DECLINED: { /* try again with next cache type */ list = list->next; continue; } default: { /* oo-er! an error */ return rv; } } } return DECLINED; }
/* * select a specific URL entity in the cache * * It is possible to store more than one entity per URL. Content * negotiation is used to select an entity. Once an entity is * selected, details of it are stored in the per request * config to save time when serving the request later. * * This function returns OK if successful, DECLINED if no * cached entity fits the bill. */ int cache_select(cache_request_rec *cache, request_rec *r) { cache_provider_list *list; apr_status_t rv; cache_handle_t *h; if (!cache) { /* This should never happen */ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00693) "cache: No cache request information available for key" " generation"); return DECLINED; } /* if no-cache, we can't serve from the cache, but we may store to the * cache. */ if (!ap_cache_check_no_cache(cache, r)) { return DECLINED; } if (!cache->key) { rv = cache_generate_key(r, r->pool, &cache->key); if (rv != APR_SUCCESS) { return DECLINED; } } /* go through the cache types till we get a match */ h = apr_palloc(r->pool, sizeof(cache_handle_t)); list = cache->providers; while (list) { switch ((rv = list->provider->open_entity(h, r, cache->key))) { case OK: { char *vary = NULL; int mismatch = 0; char *last = NULL; if (list->provider->recall_headers(h, r) != APR_SUCCESS) { /* try again with next cache type */ list = list->next; continue; } /* * Check Content-Negotiation - Vary * * At this point we need to make sure that the object we found in * the cache is the same object that would be delivered to the * client, when the effects of content negotiation are taken into * effect. * * In plain english, we want to make sure that a language-negotiated * document in one language is not given to a client asking for a * language negotiated document in a different language by mistake. * * This code makes the assumption that the storage manager will * cache the req_hdrs if the response contains a Vary * header. * * RFC2616 13.6 and 14.44 describe the Vary mechanism. */ vary = cache_strqtok( apr_pstrdup(r->pool, cache_table_getm(r->pool, h->resp_hdrs, "Vary")), CACHE_SEPARATOR, &last); while (vary) { const char *h1, *h2; /* * is this header in the request and the header in the cached * request identical? If not, we give up and do a straight get */ h1 = cache_table_getm(r->pool, r->headers_in, vary); h2 = cache_table_getm(r->pool, h->req_hdrs, vary); if (h1 == h2) { /* both headers NULL, so a match - do nothing */ } else if (h1 && h2 && !strcmp(h1, h2)) { /* both headers exist and are equal - do nothing */ } else { /* headers do not match, so Vary failed */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00694) "cache_select(): Vary header mismatch."); mismatch = 1; break; } vary = cache_strqtok(NULL, CACHE_SEPARATOR, &last); } /* no vary match, try next provider */ if (mismatch) { /* try again with next cache type */ list = list->next; continue; } cache->provider = list->provider; cache->provider_name = list->provider_name; /* * RFC2616 13.3.4 Rules for When to Use Entity Tags and Last-Modified * Dates: An HTTP/1.1 caching proxy, upon receiving a conditional request * that includes both a Last-Modified date and one or more entity tags as * cache validators, MUST NOT return a locally cached response to the * client unless that cached response is consistent with all of the * conditional header fields in the request. */ if (ap_condition_if_match(r, h->resp_hdrs) == AP_CONDITION_NOMATCH || ap_condition_if_unmodified_since(r, h->resp_hdrs) == AP_CONDITION_NOMATCH || ap_condition_if_none_match(r, h->resp_hdrs) == AP_CONDITION_NOMATCH || ap_condition_if_modified_since(r, h->resp_hdrs) == AP_CONDITION_NOMATCH || ap_condition_if_range(r, h->resp_hdrs) == AP_CONDITION_NOMATCH) { mismatch = 1; } /* Is our cached response fresh enough? */ if (mismatch || !cache_check_freshness(h, cache, r)) { const char *etag, *lastmod; /* Cache-Control: only-if-cached and revalidation required, try * the next provider */ if (cache->control_in.only_if_cached) { /* try again with next cache type */ list = list->next; continue; } /* set aside the stale entry for accessing later */ cache->stale_headers = apr_table_copy(r->pool, r->headers_in); cache->stale_handle = h; /* if no existing conditionals, use conditionals of our own */ if (!mismatch) { ap_log_rerror( APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00695) "Cached response for %s isn't fresh. Adding " "conditional request headers.", r->uri); /* Remove existing conditionals that might conflict with ours */ apr_table_unset(r->headers_in, "If-Match"); apr_table_unset(r->headers_in, "If-Modified-Since"); apr_table_unset(r->headers_in, "If-None-Match"); apr_table_unset(r->headers_in, "If-Range"); apr_table_unset(r->headers_in, "If-Unmodified-Since"); etag = apr_table_get(h->resp_hdrs, "ETag"); lastmod = apr_table_get(h->resp_hdrs, "Last-Modified"); if (etag || lastmod) { /* If we have a cached etag and/or Last-Modified add in * our own conditionals. */ if (etag) { apr_table_set(r->headers_in, "If-None-Match", etag); } if (lastmod) { apr_table_set(r->headers_in, "If-Modified-Since", lastmod); } /* * Do not do Range requests with our own conditionals: If * we get 304 the Range does not matter and otherwise the * entity changed and we want to have the complete entity */ apr_table_unset(r->headers_in, "Range"); } } /* ready to revalidate, pretend we were never here */ return DECLINED; } /* Okay, this response looks okay. Merge in our stuff and go. */ cache_accept_headers(h, r, h->resp_hdrs, r->headers_out, 0); cache->handle = h; return OK; } case DECLINED: { /* try again with next cache type */ list = list->next; continue; } default: { /* oo-er! an error */ return rv; } } } /* if Cache-Control: only-if-cached, and not cached, return 504 */ if (cache->control_in.only_if_cached) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00696) "cache: 'only-if-cached' requested and no cached entity, " "returning 504 Gateway Timeout for: %s", r->uri); return HTTP_GATEWAY_TIME_OUT; } return DECLINED; }
char * default_chxj_serf_post(request_rec *r, apr_pool_t *ppool, const char *url_path, char *post_data, apr_size_t post_data_len, int set_headers_flag, apr_size_t *response_len, int *response_code) { apr_pool_t *pool; apr_uri_t url; apr_status_t rv; apr_sockaddr_t *address = NULL; serf_context_t *context; serf_connection_t *connection; app_ctx_t app_ctx; handler_ctx_t handler_ctx; char *ret; DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__); s_init(ppool, &pool); apr_uri_parse(pool, url_path, &url); if (!url.port) { url.port = apr_uri_port_of_scheme(url.scheme); } if (!url.port) { url.port = 80; } if (!url.path) { url.path = "/"; } if (!url.hostname) { url.hostname = "localhost"; } if (url.query) { url.path = apr_psprintf(pool, "%s?%s", url.path, url.query); } rv = apr_sockaddr_info_get(&address, url.hostname, APR_UNSPEC, url.port, 0, pool); if (rv != APR_SUCCESS) { char buf[256]; ERR(r, "apr_sockaddr_info_get() failed: rv:[%d|%s]", rv, apr_strerror(rv, buf, 256)); return NULL; } memset(&app_ctx, 0, sizeof(app_ctx_t)); app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL); if (strcasecmp(url.scheme, "https") == 0) { app_ctx.ssl_flag = 1; } context = serf_context_create(pool); connection = serf_connection_create(context, address, s_connection_setup, &app_ctx, s_connection_closed, &app_ctx, pool); memset(&handler_ctx, 0, sizeof(handler_ctx_t)); handler_ctx.requests_outstanding = 0; handler_ctx.host = url.hostinfo; handler_ctx.method = "POST"; handler_ctx.path = url.path; handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT); if (! handler_ctx.user_agent) { handler_ctx.user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT); } handler_ctx.post_data = post_data; handler_ctx.post_data_len = post_data_len; handler_ctx.acceptor = s_accept_response; handler_ctx.acceptor_ctx = &app_ctx; handler_ctx.handler = s_handle_response; handler_ctx.pool = pool; handler_ctx.r = r; handler_ctx.response_len = 0; handler_ctx.response = NULL; serf_connection_request_create(connection, s_setup_request, &handler_ctx); while (1) { rv = serf_context_run(context, SERF_DURATION_FOREVER, pool); if (APR_STATUS_IS_TIMEUP(rv)) continue; if (rv) { char buf[200]; ERR(r, "Error running context: (%d) %s\n", rv, apr_strerror(rv, buf, sizeof(buf))); break; } if (!apr_atomic_read32(&handler_ctx.requests_outstanding)) { if (handler_ctx.rv != APR_SUCCESS) { char buf[200]; ERR(r, "Error running context: (%d) %s\n", handler_ctx.rv, apr_strerror(handler_ctx.rv, buf, sizeof(buf))); } break; } } DBG(r,"REQ[%X] end of serf request",TO_ADDR(r)); DBG(r,"REQ[%X] response_code:[%d]", TO_ADDR(r),handler_ctx.response_code); DBG(r,"REQ[%X] response:[%s][%" APR_SIZE_T_FMT "]", TO_ADDR(r),handler_ctx.response, handler_ctx.response_len); serf_connection_close(connection); if (handler_ctx.response) { ret = apr_palloc(ppool, handler_ctx.response_len + 1); memset(ret, 0, handler_ctx.response_len + 1); memcpy(ret, handler_ctx.response, handler_ctx.response_len); } else { ret = apr_pstrdup(ppool, ""); } if (set_headers_flag && !rv) { r->headers_out = apr_table_copy(pool, handler_ctx.headers_out); *response_len = handler_ctx.response_len; char *contentType = (char *)apr_table_get(handler_ctx.headers_out, "Content-Type"); if (contentType) { DBG(r,"REQ[%X] response content type[%s]", TO_ADDR(r),contentType); chxj_set_content_type(r, apr_pstrdup(r->pool, contentType)); } } if (rv) { *response_len = 0; } *response_code = handler_ctx.response_code; DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__); return ret; }
PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, apr_bucket_brigade *header_brigade, request_rec *r, proxy_conn_rec *p_conn, proxy_worker *worker, proxy_server_conf *conf, apr_uri_t *uri, char *url, char *server_portstr, char **old_cl_val, char **old_te_val) { conn_rec *c = r->connection; int counter; char *buf; const apr_array_header_t *headers_in_array; const apr_table_entry_t *headers_in; apr_table_t *headers_in_copy; apr_bucket *e; int do_100_continue; conn_rec *origin = p_conn->connection; proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module); /* * 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->ping_timeout_set && !r->header_only && (apr_table_get(r->headers_in, "Content-Length") || apr_table_get(r->headers_in, "Transfer-Encoding")) && (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); p_conn->close = 1; } else { buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); } 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 (conf->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, "AH01092: " "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 (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", c->remote_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); 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); } return OK; }