From 981021f917cbe08b1e6568363a945249cfe414b4 Mon Sep 17 00:00:00 2001 From: Ryan Bloom Date: Tue, 14 Aug 2001 18:04:58 +0000 Subject: [PATCH] Break http_proxy_handler into multiple smaller functions. Submitted by: John Barbee git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@90159 13f79535-47bb-0310-9956-ffa450edef68 --- modules/proxy/CHANGES | 3 + modules/proxy/proxy_http.c | 845 +++++++++++++++++++++---------------- 2 files changed, 482 insertions(+), 366 deletions(-) diff --git a/modules/proxy/CHANGES b/modules/proxy/CHANGES index adc02d573c..0059dce2aa 100644 --- a/modules/proxy/CHANGES +++ b/modules/proxy/CHANGES @@ -1,5 +1,8 @@ mod_proxy changes for httpd 2.0.23-dev + *) break the proxy_http_handler into multiple smaller functions. + [John Barbee ] + *) Fix the proxy when the origin server sends back a 100 Continue response. [John Barbee ] diff --git a/modules/proxy/proxy_http.c b/modules/proxy/proxy_http.c index 9d461ed4ca..5f3c2a9a54 100644 --- a/modules/proxy/proxy_http.c +++ b/modules/proxy/proxy_http.c @@ -67,6 +67,13 @@ int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, char *url, const char *proxyname, apr_port_t proxyport); +typedef struct { + const char *name; + int port; + apr_sockaddr_t *addr; + apr_socket_t *sock; + int close; +} proxy_http_conn_t; /* * Canonicalise http-like URLs. @@ -178,339 +185,308 @@ static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers) apr_table_unset(headers, "Connection"); } -/* - * 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 ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, - char *url, const char *proxyname, - apr_port_t proxyport) -{ - request_rec *rp; - const char *connectname; - int connectport = 0; +static +apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *c, + proxy_server_conf *conf, + apr_uri_components *uri, + char *url, + const char *proxyname, + apr_port_t proxyport, + char *server_portstr) { + int server_port; + apr_status_t err; apr_sockaddr_t *uri_addr; - apr_sockaddr_t *connect_addr; - char server_portstr[32]; - apr_socket_t *sock; - int i, len, backasswards, eos, close=0, failed=0, new=0; - apr_status_t err, rv; - apr_array_header_t *headers_in_array; - apr_table_entry_t *headers_in; - char buffer[HUGE_STRING_LEN]; - char *buf; - conn_rec *origin = NULL; - apr_uri_components uri; - proxy_conn_rec *backend; - int received_continue = 1; /* flag to indicate if we should - * loop over response parsing logic - * in the case that the origin told us - * to HTTP_CONTINUE - */ - - /* Note: Memory pool allocation. - * A downstream keepalive connection is always connected to the existence - * (or not) of an upstream keepalive connection. If this is not done then - * load balancing against multiple backend servers breaks (one backend - * server ends up taking 100% of the load), and the risk is run of - * downstream keepalive connections being kept open unnecessarily. This - * keeps webservers busy and ties up resources. - * - * As a result, we allocate all sockets out of the upstream connection - * pool, and when we want to reuse a socket, we check first whether the - * connection ID of the current upstream connection is the same as that - * of the connection when the socket was opened. - */ - apr_pool_t *p = r->connection->pool; - conn_rec *c = r->connection; - apr_bucket *e; - apr_bucket_brigade *bb = apr_brigade_create(p); - - /* is it for us? */ - if (strncasecmp(url, "http:", 5)) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: HTTP: rejecting URL %s", url); - return DECLINED; /* only interested in HTTP */ - } - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: HTTP: serving URL %s", url); - - /* create space for state information */ - backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, &proxy_http_module); - if (!backend) { - backend = ap_pcalloc(c->pool, sizeof(proxy_conn_rec)); - backend->connection = NULL; - backend->hostname = NULL; - backend->port = 0; - ap_set_module_config(c->conn_config, &proxy_http_module, backend); - } - /* - * Step One: Determine Who To Connect To - * * Break up the URL to determine the host to connect to */ /* we break the URL into host, port, uri */ - if (APR_SUCCESS != apr_uri_parse_components(p, url, &uri)) { - return ap_proxyerror(r, HTTP_BAD_REQUEST, - apr_pstrcat(p,"URI cannot be parsed: ", url, NULL)); + if (APR_SUCCESS != apr_uri_parse_components(p, url, uri)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_pstrcat(p,"URI cannot be parsed: ", url, + NULL)); } - if (!uri.port) { - uri.port = apr_uri_default_port_for_scheme(uri.scheme); + if (!uri->port) { + uri->port = apr_uri_default_port_for_scheme(uri->scheme); } ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: HTTP connecting %s to %s:%d", url, uri.hostname, uri.port); + "proxy: HTTP connecting %s to %s:%d", url, uri->hostname, + uri->port); /* do a DNS lookup for the destination host */ /* see memory note above */ - err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri.hostname), APR_UNSPEC, uri.port, 0, c->pool); + err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri->hostname), + APR_UNSPEC, uri->port, 0, c->pool); - /* allocate these out of the connection pool - the check on r->connection->id makes - * sure that this string does not get accessed past the connection lifetime */ + /* allocate these out of the connection pool - the check on + * r->connection->id makes sure that this string does not get accessed + * past the connection lifetime */ /* are we connecting directly, or via a proxy? */ if (proxyname) { - connectname = apr_pstrdup(c->pool,proxyname); - connectport = proxyport; - /* see memory note above */ - err = apr_sockaddr_info_get(&connect_addr, connectname, APR_UNSPEC, connectport, 0, c->pool); - } - else { - connectname = apr_pstrdup(c->pool, uri.hostname); - connectport = uri.port; - connect_addr = uri_addr; - url = apr_pstrcat(p, uri.path, uri.query ? "?" : "", - uri.query ? uri.query : "", uri.fragment ? "#" : "", - uri.fragment ? uri.fragment : "", NULL); + p_conn->name = apr_pstrdup(c->pool, proxyname); + p_conn->port = proxyport; + /* see memory note above */ + err = apr_sockaddr_info_get(&p_conn->addr, p_conn->name, APR_UNSPEC, + p_conn->port, 0, c->pool); + } else { + p_conn->name = apr_pstrdup(c->pool, uri->hostname); + p_conn->port = uri->port; + p_conn->addr = uri_addr; + url = apr_pstrcat(p, uri->path, uri->query ? "?" : "", + uri->query ? uri->query : "", + uri->fragment ? "#" : "", + uri->fragment ? uri->fragment : "", NULL); } + if (err != APR_SUCCESS) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "DNS lookup failure for: ", + p_conn->name, NULL)); + } /* Get the server port for the Via headers */ { - i = ap_get_server_port(r); - if (ap_is_default_port(i,r)) { - strcpy(server_portstr,""); - } else { - apr_snprintf(server_portstr, sizeof server_portstr, ":%d", i); - } + server_port = ap_get_server_port(r); + if (ap_is_default_port(server_port, r)) { + strcpy(server_portstr,""); + } else { + apr_snprintf(server_portstr, sizeof(server_portstr), ":%d", + server_port); + } } /* check if ProxyBlock directive on this host */ if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) { - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked"); + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); } + return OK; +} - - /* - * Step Two: Make the Connection - * - * We have determined who to connect to. Now make the connection, supporting +static +apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *c, conn_rec **origin, + proxy_conn_rec *backend, + proxy_server_conf *conf, + const char *proxyname) { + int failed=0, new=0; + apr_status_t rv; + + /* We have determined who to connect to. Now make the connection, supporting * a KeepAlive connection. */ /* get all the possible IP addresses for the destname and loop through them * until we get a successful connection */ - if (APR_SUCCESS != err) { - return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, - "DNS lookup failure for: ", - connectname, NULL)); - } /* if a keepalive socket is already open, check whether it must stay * open, or whether it should be closed and a new socket created. */ /* see memory note above */ if (backend->connection) { - if ((backend->connection->id == c->id) && - (backend->port == connectport) && - (backend->hostname) && - (!apr_strnatcasecmp(backend->hostname,connectname))) { - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: keepalive address match (keep original socket)"); - } - else { + if ((backend->connection->id == c->id) && + (backend->port == p_conn->port) && + (backend->hostname) && + (!apr_strnatcasecmp(backend->hostname, p_conn->name))) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: keepalive address match (keep original socket)"); + } else { ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: keepalive address mismatch / connection has" - " changed (close old socket (%s/%s, %d/%d))", - connectname, backend->hostname, - connectport, backend->port); + "proxy: keepalive address mismatch / connection has" + " changed (close old socket (%s/%s, %d/%d))", + p_conn->name, backend->hostname, p_conn->port, + backend->port); apr_socket_close(backend->connection->client_socket); backend->connection = NULL; - } + } } /* get a socket - either a keepalive one, or a new one */ new = 1; if ((backend->connection) && (backend->connection->id == c->id)) { - /* use previous keepalive socket */ - origin = backend->connection; - sock = origin->client_socket; - new = 0; + /* use previous keepalive socket */ + *origin = backend->connection; + p_conn->sock = (*origin)->client_socket; + new = 0; - /* reset the connection filters */ - ap_proxy_reset_output_filters(origin); + /* reset the connection filters */ + ap_proxy_reset_output_filters(*origin); - /* XXX FIXME: If the socket has since closed, change new to 1 so - * a new socket is opened */ + /* XXX FIXME: If the socket has since closed, change new to 1 so + * a new socket is opened */ } if (new) { - /* create a new socket */ - backend->connection = NULL; + /* create a new socket */ + backend->connection = NULL; - /* see memory note above */ - if ((rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, c->pool)) != APR_SUCCESS) { + /* see memory note above */ + if ((rv = apr_socket_create(&p_conn->sock, APR_INET, SOCK_STREAM, + c->pool)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "proxy: error creating socket"); - return HTTP_INTERNAL_SERVER_ERROR; - } + "proxy: error creating socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } #if !defined(TPF) && !defined(BEOS) - if (conf->recv_buffer_size > 0 && (rv = apr_setsocketopt(sock, APR_SO_RCVBUF, - conf->recv_buffer_size))) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); - } + if (conf->recv_buffer_size > 0 && + (rv = apr_setsocketopt(p_conn->sock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "setsockopt(SO_RCVBUF): Failed to set " + "ProxyReceiveBufferSize, using default"); + } #endif /* Set a timeout on the socket */ - apr_setsocketopt(sock, APR_SO_TIMEOUT, (int)(r->server->timeout * APR_USEC_PER_SEC)); + apr_setsocketopt(p_conn->sock, APR_SO_TIMEOUT, + (int)(r->server->timeout * APR_USEC_PER_SEC)); - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: socket has been created"); - - /* - * At this point we have a list of one or more IP addresses of - * the machine to connect to. If configured, reorder this - * list so that the "best candidate" is first try. "best - * candidate" could mean the least loaded server, the fastest - * responding server, whatever. + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: socket has been created"); + + /* + * At this point we have a list of one or more IP addresses of + * the machine to connect to. If configured, reorder this + * list so that the "best candidate" is first try. "best + * candidate" could mean the least loaded server, the fastest + * responding server, whatever. * * For now we do nothing, ie we get DNS round robin. - * XXX FIXME - */ - + * XXX FIXME + */ - /* try each IP address until we connect successfully */ - failed = 1; - while (connect_addr) { + /* try each IP address until we connect successfully */ + failed = 1; + while (p_conn->addr) { - /* make the connection out of the socket */ - rv = apr_connect(sock, connect_addr); + /* make the connection out of the socket */ + rv = apr_connect(p_conn->sock, p_conn->addr); - /* if an error occurred, loop round and try again */ + /* if an error occurred, loop round and try again */ if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, - "proxy: attempt to connect to %pI (%s) failed", - connect_addr, connectname); - connect_addr = connect_addr->next; - continue; + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: attempt to connect to %pI (%s) failed", + p_conn->addr, p_conn->name); + p_conn->addr = p_conn->addr->next; + continue; } - /* if we get here, all is well */ - failed = 0; - break; - } - - /* handle a permanent error from the above loop */ - if (failed) { - apr_socket_close(sock); - if (proxyname) { - return DECLINED; - } - else { - return HTTP_BAD_GATEWAY; - } - } + /* if we get here, all is well */ + failed = 0; + break; + } - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: socket is connected"); - - /* the socket is now open, create a new backend server connection */ - origin = ap_new_connection(c->pool, r->server, sock, r->connection->id); - if (!origin) { - /* the peer reset the connection already; ap_new_connection() - * closed the socket */ - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: an error occurred creating a new connection to %pI (%s)", connect_addr, connectname); - apr_socket_close(sock); - return HTTP_INTERNAL_SERVER_ERROR; - } - backend->connection = origin; - backend->hostname = apr_pstrdup(c->pool, connectname); - backend->port = connectport; + /* handle a permanent error from the above loop */ + if (failed) { + apr_socket_close(p_conn->sock); + if (proxyname) { + return DECLINED; + } else { + return HTTP_BAD_GATEWAY; + } + } - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: connection complete"); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: socket is connected"); + + /* the socket is now open, create a new backend server connection */ + *origin = ap_new_connection(c->pool, r->server, p_conn->sock, + r->connection->id); + if (!origin) { + /* the peer reset the connection already; ap_new_connection() + * closed the socket + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + r->server, "proxy: an error occurred creating a " + "new connection to %pI (%s)", p_conn->addr, + p_conn->name); + apr_socket_close(p_conn->sock); + return HTTP_INTERNAL_SERVER_ERROR; + } + backend->connection = *origin; + backend->hostname = apr_pstrdup(c->pool, p_conn->name); + backend->port = p_conn->port; - /* set up the connection filters */ - ap_proxy_pre_http_connection(origin, NULL); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: connection complete"); + /* set up the connection filters */ + ap_proxy_pre_http_connection(*origin, NULL); } + return OK; +} - +static +apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r, + proxy_http_conn_t *p_conn, conn_rec *origin, + proxy_server_conf *conf, + apr_uri_components *uri, + char *url, apr_bucket_brigade *bb, + char *server_portstr) { + char buffer[HUGE_STRING_LEN]; + char *buf; + apr_bucket *e; + apr_array_header_t *headers_in_array; + apr_table_entry_t *headers_in; + int counter; /* - * Step Three: Send the Request - * * Send the HTTP/1.1 request to the remote server */ - /* strip connection listed hop-by-hop headers from the request */ /* even though in theory a connection: close coming from the client * should not affect the connection to the server, it's unlikely * that subsequent client requests will hit this thread/process, so * we cancel server keepalive if the client does. */ - close += ap_proxy_liststr(apr_table_get(r->headers_in, "Connection"), "close"); + p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_in, + "Connection"), "close"); ap_proxy_clear_connection(p, r->headers_in); - if (close) { - apr_table_setn(r->headers_in, "Connection", "close"); - origin->keepalive = 0; + if (p_conn->close) { + apr_table_setn(r->headers_in, "Connection", "close"); + origin->keepalive = 0; } buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); - if (uri.port_str && uri.port != DEFAULT_HTTP_PORT) { - buf = apr_pstrcat(p, "Host: ", uri.hostname, ":", uri.port_str, CRLF, NULL); + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { + buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str, CRLF, + NULL); e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); - } - else { - buf = apr_pstrcat(p, "Host: ", uri.hostname, CRLF, NULL); + } else { + buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL); e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); } /* handle Via */ if (conf->viaopt == via_block) { - /* Block all outgoing Via: headers */ - apr_table_unset(r->headers_in, "Via"); + /* Block all outgoing Via: headers */ + apr_table_unset(r->headers_in, "Via"); } else if (conf->viaopt != via_off) { - /* 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), - ap_get_server_name(r), server_portstr, - AP_SERVER_BASEVERSION) - : apr_psprintf(p, "%d.%d %s%s", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - ap_get_server_name(r), server_portstr) - ); + /* 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), + ap_get_server_name(r), server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), server_portstr) + ); } /* X-Forwarded-*: handling @@ -540,7 +516,8 @@ int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, /* Add X-Forwarded-For: so that the upstream has a chance to * determine, where the original request came from. */ - apr_table_setn(r->headers_in, "X-Forwarded-For", r->connection->remote_ip); + apr_table_setn(r->headers_in, "X-Forwarded-For", + r->connection->remote_ip); /* Add X-Forwarded-Host: so that upstream knows what the * original request hostname was. @@ -554,42 +531,46 @@ int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, * XXX: This duplicates Via: - do we strictly need it? */ apr_table_setn(r->headers_in, "X-Forwarded-Server", - r->server->server_hostname); + r->server->server_hostname); } /* send request headers */ headers_in_array = apr_table_elts(r->headers_in); headers_in = (apr_table_entry_t *) headers_in_array->elts; - for (i = 0; i < headers_in_array->nelts; i++) { - if (headers_in[i].key == NULL || headers_in[i].val == NULL - - /* Clear out hop-by-hop request headers not to send - * RFC2616 13.5.1 says we should strip these headers - */ - || !apr_strnatcasecmp(headers_in[i].key, "Host") /* Already sent */ - || !apr_strnatcasecmp(headers_in[i].key, "Keep-Alive") - || !apr_strnatcasecmp(headers_in[i].key, "TE") - || !apr_strnatcasecmp(headers_in[i].key, "Trailer") - || !apr_strnatcasecmp(headers_in[i].key, "Transfer-Encoding") - || !apr_strnatcasecmp(headers_in[i].key, "Upgrade") + for (counter = 0; counter < headers_in_array->nelts; counter++) { + if (headers_in[counter].key == NULL || headers_in[counter].val == NULL + + /* Clear out hop-by-hop request headers not to send + * RFC2616 13.5.1 says we should strip these headers + */ + /* Already sent */ + || !apr_strnatcasecmp(headers_in[counter].key, "Host") + + || !apr_strnatcasecmp(headers_in[counter].key, "Keep-Alive") + || !apr_strnatcasecmp(headers_in[counter].key, "TE") + || !apr_strnatcasecmp(headers_in[counter].key, "Trailer") + || !apr_strnatcasecmp(headers_in[counter].key, "Transfer-Encoding") + || !apr_strnatcasecmp(headers_in[counter].key, "Upgrade") /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be * suppressed if THIS server requested the authentication, * not when a frontend proxy requested it! - * - * The solution to this problem is probably to strip out - * the Proxy-Authorisation header in the authorisation - * code itself, not here. This saves us having to signal - * somehow whether this request was authenticated or not. + * + * The solution to this problem is probably to strip out + * the Proxy-Authorisation header in the authorisation + * code itself, not here. This saves us having to signal + * somehow whether this request was authenticated or not. */ - || !apr_strnatcasecmp(headers_in[i].key, "Proxy-Authorization") - || !apr_strnatcasecmp(headers_in[i].key, "Proxy-Authenticate")) - continue; + || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authorization") + || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authenticate")) { + continue; + } - buf = apr_pstrcat(p, headers_in[i].key, ": ", headers_in[i].val, CRLF, NULL); + buf = apr_pstrcat(p, headers_in[counter].key, ": ", + headers_in[counter].val, CRLF, + NULL); e = apr_bucket_pool_create(buf, strlen(buf), p); APR_BRIGADE_INSERT_TAIL(bb, e); - } /* add empty line at the end of the headers */ @@ -602,152 +583,185 @@ int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, /* send the request data, if any. */ if (ap_should_client_block(r)) { - while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) { - e = apr_bucket_pool_create(buffer, i, p); + while ((counter = ap_get_client_block(r, buffer, sizeof(buffer))) > 0) { + e = apr_bucket_pool_create(buffer, counter, p); + APR_BRIGADE_INSERT_TAIL(bb, e); + e = apr_bucket_flush_create(); APR_BRIGADE_INSERT_TAIL(bb, e); - e = apr_bucket_flush_create(); - APR_BRIGADE_INSERT_TAIL(bb, e); - ap_pass_brigade(origin->output_filters, bb); - apr_brigade_cleanup(bb); + ap_pass_brigade(origin->output_filters, bb); + apr_brigade_cleanup(bb); } } + return OK; +} +static +apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *origin, + proxy_conn_rec *backend, + proxy_server_conf *conf, + apr_bucket_brigade *bb, + char *server_portstr) { + char buffer[HUGE_STRING_LEN]; + request_rec *rp; + apr_bucket *e; + apr_status_t rv; + int eos, len, backasswards; + int received_continue = 1; /* flag to indicate if we should + * loop over response parsing logic + * in the case that the origin told us + * to HTTP_CONTINUE + */ - /* - * Step Four: Receive the Response - * - * Get response from the remote server, and pass it up the + /* Get response from the remote server, and pass it up the * filter chain */ rp = ap_proxy_make_fake_req(origin, r); - apr_brigade_cleanup(bb); - while (received_continue) { + apr_brigade_cleanup(bb); + if (APR_SUCCESS != (rv = ap_proxy_string_read(origin, bb, buffer, sizeof(buffer), &eos))) { - apr_socket_close(sock); + apr_socket_close(p_conn->sock); backend->connection = NULL; ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - "proxy: error reading status line from remote server %s", - connectname); - return ap_proxyerror(r, HTTP_BAD_GATEWAY, - "Error reading from remote server"); + "proxy: error reading status line from remote " + "server %s", p_conn->name); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); } len = strlen(buffer); - - /* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */ + + /* Is it an HTTP/1 response? + * This is buggy if we ever see an HTTP/1.10 + */ if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { int major, minor; - + if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) { major = 1; minor = 1; } - - /* If not an HTTP/1 message or if the status line was > 8192 bytes */ + /* If not an HTTP/1 message or + * if the status line was > 8192 bytes + */ else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) { - apr_socket_close(sock); + apr_socket_close(p_conn->sock); backend->connection = NULL; return ap_proxyerror(r, HTTP_BAD_GATEWAY, - apr_pstrcat(p, "Corrupt status line returned by remote server: ", buffer, NULL)); + apr_pstrcat(p, "Corrupt status line returned by remote " + "server: ", buffer, NULL)); } backasswards = 0; buffer[--len] = '\0'; - + buffer[12] = '\0'; r->status = atoi(&buffer[9]); - + buffer[12] = ' '; r->status_line = apr_pstrdup(p, &buffer[9]); - + /* read the headers. */ - /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ + /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/ /* Also, take care with headers with multiple occurences. */ - - r->headers_out = ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin); + + r->headers_out = ap_proxy_read_headers(r, rp, buffer, + sizeof(buffer), origin); if (r->headers_out == NULL) { - ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server, - "proxy: bad HTTP/%d.%d header returned by %s (%s)", - major, minor, r->uri, r->method); - close += 1; - } - else - { + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, + r->server, "proxy: bad HTTP/%d.%d header " + "returned by %s (%s)", major, minor, r->uri, + r->method); + p_conn->close += 1; + } else { /* strip connection listed hop-by-hop headers from response */ const char *buf; - close += ap_proxy_liststr(apr_table_get(r->headers_out, "Connection"), "close"); + p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_out, + "Connection"), + "close"); ap_proxy_clear_connection(p, r->headers_out); if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { r->content_type = apr_pstrdup(p, buf); - } + } } - + /* handle Via header in response */ if (conf->viaopt != via_off && conf->viaopt != via_block) { /* create a "Via:" response header entry and merge it */ ap_table_mergen(r->headers_out, "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), - ap_get_server_name(r), server_portstr, - AP_SERVER_BASEVERSION) - : apr_psprintf(p, "%d.%d %s%s", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - ap_get_server_name(r), server_portstr) - ); + (conf->viaopt == via_full) + ? apr_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), + server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + ap_get_server_name(r), + server_portstr) + ); } - + /* cancel keepalive if HTTP/1.0 or less */ if ((major < 1) || (minor < 1)) { - close += 1; - origin->keepalive = 0; + p_conn->close += 1; + origin->keepalive = 0; } - } - else { + } else { /* an http/0.9 response */ - backasswards = 1; - r->status = 200; - r->status_line = "200 OK"; - close += 1; + backasswards = 1; + r->status = 200; + r->status_line = "200 OK"; + p_conn->close += 1; } - - if ( r->status != HTTP_CONTINUE ) { - received_continue = 0; - } else { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, NULL, - "proxy: HTTP: received 100 CONTINUE"); - } + + if ( r->status != HTTP_CONTINUE ) { + received_continue = 0; + } else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, NULL, + "proxy: HTTP: received 100 CONTINUE"); + } + /* we must accept 3 kinds of date, but generate only 1 kind of date */ { const char *buf; if ((buf = apr_table_get(r->headers_out, "Date")) != NULL) { - apr_table_set(r->headers_out, "Date", ap_proxy_date_canon(p, buf)); + apr_table_set(r->headers_out, "Date", + ap_proxy_date_canon(p, buf)); } if ((buf = apr_table_get(r->headers_out, "Expires")) != NULL) { - apr_table_set(r->headers_out, "Expires", ap_proxy_date_canon(p, buf)); + apr_table_set(r->headers_out, "Expires", + ap_proxy_date_canon(p, buf)); } if ((buf = apr_table_get(r->headers_out, "Last-Modified")) != NULL) { - apr_table_set(r->headers_out, "Last-Modified", ap_proxy_date_canon(p, buf)); + apr_table_set(r->headers_out, "Last-Modified", + ap_proxy_date_canon(p, buf)); } } - - /* munge the Location and URI response headers according to ProxyPassReverse */ + + /* munge the Location and URI response headers according to + * ProxyPassReverse + */ { const char *buf; if ((buf = apr_table_get(r->headers_out, "Location")) != NULL) { - apr_table_set(r->headers_out, "Location", ap_proxy_location_reverse_map(r, conf, buf)); + apr_table_set(r->headers_out, "Location", + ap_proxy_location_reverse_map(r, conf, buf)); } if ((buf = apr_table_get(r->headers_out, "Content-Location")) != NULL) { - apr_table_set(r->headers_out, "Content-Location", ap_proxy_location_reverse_map(r, conf, buf)); + apr_table_set(r->headers_out, "Content-Location", + ap_proxy_location_reverse_map(r, conf, buf)); } if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) { - apr_table_set(r->headers_out, "URI", ap_proxy_location_reverse_map(r, conf, buf)); + apr_table_set(r->headers_out, "URI", + ap_proxy_location_reverse_map(r, conf, buf)); } } - + r->sent_bodyct = 1; /* Is it an HTTP/0.9 response? If so, send the extra data */ if (backasswards) { @@ -755,85 +769,184 @@ int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, e = apr_bucket_heap_create(buffer, cntr, 0, NULL); APR_BRIGADE_INSERT_TAIL(bb, e); } - + /* send body - but only if a body is expected */ - if ((!r->header_only) && /* not HEAD request */ - (r->status > 199) && /* not any 1xx response */ - (r->status != HTTP_NO_CONTENT) && /* not 204 */ - (r->status != HTTP_RESET_CONTENT) && /* not 205 */ - (r->status != HTTP_NOT_MODIFIED)) { /* not 304 */ - + if ((!r->header_only) && /* not HEAD request */ + (r->status > 199) && /* not any 1xx response */ + (r->status != HTTP_NO_CONTENT) && /* not 204 */ + (r->status != HTTP_RESET_CONTENT) && /* not 205 */ + (r->status != HTTP_NOT_MODIFIED)) { /* not 304 */ + const char *buf; apr_off_t readbytes; - + /* if chunked - insert DECHUNK filter */ - if (ap_proxy_liststr((buf = apr_table_get(r->headers_out, "Transfer-Encoding")), "chunked")) { + if (ap_proxy_liststr((buf = apr_table_get(r->headers_out, + "Transfer-Encoding")), "chunked")) { rp->read_chunked = 1; apr_table_unset(r->headers_out, "Transfer-Encoding"); if ((buf = ap_proxy_removestr(r->pool, buf, "chunked"))) { - apr_table_set(r->headers_out, "Transfer-Encoding", buf); + apr_table_set(r->headers_out, "Transfer-Encoding", buf); } ap_add_input_filter("DECHUNK", NULL, rp, origin); readbytes = -1; } - /* if content length - set the length to read */ else if ((buf = apr_table_get(r->headers_out, "Content-Length"))) { readbytes = atol(buf); } - - /* no chunked / no length therefore read till EOF and cancel keepalive */ + /* no chunked / no length therefore read till EOF and + * cancel keepalive + */ else { - close += 1; + p_conn->close += 1; } - + /* if keepalive cancelled, read to EOF */ - if (close) { + if (p_conn->close) { readbytes = -1; } - + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: start body send"); + "proxy: start body send"); /* read the body, pass it to the output filters */ while (ap_get_brigade(rp->input_filters, bb, AP_MODE_NONBLOCKING, &readbytes) == APR_SUCCESS) { #if DEBUGGING - ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy (PID %d): readbytes: %#x", getpid(), readbytes); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + r->server, "proxy (PID %d): readbytes: %#x", + getpid(), readbytes); #endif + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { - ap_pass_brigade(r->output_filters, bb); - break; + ap_pass_brigade(r->output_filters, bb); + break; } if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) { /* Ack! Phbtt! Die! User aborted! */ - close = 1; /* this causes socket close below */ + p_conn->close = 1; /* this causes socket close below */ break; } apr_brigade_cleanup(bb); } ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: end body send"); - } - else { + } else { ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, - "proxy: header only"); + "proxy: header only"); } } + return OK; +} - /* - * Step Five: Clean Up - * - * If there are no KeepAlives, or if the connection has been signalled +static +apr_status_t ap_proxy_http_cleanup(request_rec *r, proxy_http_conn_t *p_conn, + proxy_conn_rec *backend) { + /* If there are no KeepAlives, or if the connection has been signalled * to close, close the socket and clean up */ /* if the connection is < HTTP/1.1, or Connection: close, * we close the socket, otherwise we leave it open for KeepAlive support */ - if (close || (r->proto_num < HTTP_VERSION(1,1))) { - apr_socket_close(sock); - backend->connection = NULL; + if (p_conn->close || (r->proto_num < HTTP_VERSION(1,1))) { + apr_socket_close(p_conn->sock); + backend->connection = NULL; + } + 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 ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + int status; + char server_portstr[32]; + conn_rec *origin = NULL; + proxy_conn_rec *backend; + + /* Note: Memory pool allocation. + * A downstream keepalive connection is always connected to the existence + * (or not) of an upstream keepalive connection. If this is not done then + * load balancing against multiple backend servers breaks (one backend + * server ends up taking 100% of the load), and the risk is run of + * downstream keepalive connections being kept open unnecessarily. This + * keeps webservers busy and ties up resources. + * + * As a result, we allocate all sockets out of the upstream connection + * pool, and when we want to reuse a socket, we check first whether the + * connection ID of the current upstream connection is the same as that + * of the connection when the socket was opened. + */ + apr_pool_t *p = r->connection->pool; + conn_rec *c = r->connection; + apr_bucket_brigade *bb = apr_brigade_create(p); + apr_uri_components *uri = apr_palloc(r->connection->pool, sizeof(*uri)); + proxy_http_conn_t *p_conn = apr_palloc(r->connection->pool, + sizeof(*p_conn)); + + /* is it for us? */ + if (strncasecmp(url, "http:", 5)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: HTTP: rejecting URL %s", url); + return DECLINED; /* only interested in HTTP */ + } + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, + "proxy: HTTP: serving URL %s", url); + + /* create space for state information */ + backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, + &proxy_http_module); + if (!backend) { + backend = ap_pcalloc(c->pool, sizeof(proxy_conn_rec)); + backend->connection = NULL; + backend->hostname = NULL; + backend->port = 0; + ap_set_module_config(c->conn_config, &proxy_http_module, backend); + } + + /* Step One: Determine Who To Connect To */ + status = ap_proxy_http_determine_connection(p, r, p_conn, c, conf, uri, url, + proxyname, proxyport, + server_portstr); + if ( status != OK ) { + return status; + } + + /* Step Two: Make the Connection */ + status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend, + conf, proxyname); + if ( status != OK ) { + return status; + } + + /* Step Three: Send the Request */ + status = ap_proxy_http_request(p, r, p_conn, origin, conf, uri, url, bb, + server_portstr); + if ( status != OK ) { + return status; + } + + /* Step Four: Receive the Response */ + status = ap_proxy_http_process_response(p, r, p_conn, origin, backend, conf, + bb, server_portstr); + if ( status != OK ) { + return status; + } + + /* Step Five: Clean Up */ + status = ap_proxy_http_cleanup(r, p_conn, backend); + if ( status != OK ) { + return status; } return OK; -- 2.50.1