summaryrefslogtreecommitdiff
path: root/modules/proxy
diff options
context:
space:
mode:
authorStefan Fritsch <sf@sfritsch.de>2011-12-27 19:42:22 +0100
committerStefan Fritsch <sf@sfritsch.de>2011-12-27 19:42:22 +0100
commit0268977037115539ad65a26e858aa0df8d18cd13 (patch)
treef761b541b04d08b75e32efc6c293111c61a8b79c /modules/proxy
parent9e615cb6aa4afcee97f8a1646e5a586261a7b81f (diff)
downloadapache2-upstream/2.2.9.tar.gz
Upstream tarball 2.2.9upstream/2.2.9
Diffstat (limited to 'modules/proxy')
-rw-r--r--modules/proxy/ajp.h1
-rw-r--r--modules/proxy/ajp_utils.c2
-rw-r--r--modules/proxy/mod_proxy.c209
-rw-r--r--modules/proxy/mod_proxy.h22
-rw-r--r--modules/proxy/mod_proxy_ajp.c91
-rw-r--r--modules/proxy/mod_proxy_balancer.c65
-rw-r--r--modules/proxy/mod_proxy_ftp.c7
-rw-r--r--modules/proxy/mod_proxy_http.c228
-rw-r--r--modules/proxy/proxy_util.c285
9 files changed, 711 insertions, 199 deletions
diff --git a/modules/proxy/ajp.h b/modules/proxy/ajp.h
index 8c022fb3..8327e8d4 100644
--- a/modules/proxy/ajp.h
+++ b/modules/proxy/ajp.h
@@ -147,6 +147,7 @@ struct ajp_msg
#define AJP_MSG_BUFFER_SZ 8192
#define AJP_MAX_BUFFER_SZ 65536
#define AJP13_MAX_SEND_BODY_SZ (AJP_MAX_BUFFER_SZ - AJP_HEADER_SZ)
+#define AJP_PING_PONG_SZ 128
/** Send a request from web server to container*/
#define CMD_AJP13_FORWARD_REQUEST (unsigned char)2
diff --git a/modules/proxy/ajp_utils.c b/modules/proxy/ajp_utils.c
index 5a0e8772..780aeb48 100644
--- a/modules/proxy/ajp_utils.c
+++ b/modules/proxy/ajp_utils.c
@@ -31,7 +31,7 @@ apr_status_t ajp_handle_cping_cpong(apr_socket_t *sock,
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"Into ajp_handle_cping_cpong");
- rc = ajp_msg_create(r->pool, AJP_HEADER_SZ_LEN+1, &msg);
+ rc = ajp_msg_create(r->pool, AJP_PING_PONG_SZ, &msg);
if (rc != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"ajp_handle_cping_cpong: ajp_msg_create failed");
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
index de48638b..00bcfeba 100644
--- a/modules/proxy/mod_proxy.c
+++ b/modules/proxy/mod_proxy.c
@@ -168,6 +168,15 @@ static const char *set_worker_param(apr_pool_t *p,
return "KeepAlive must be On|Off";
worker->keepalive_set = 1;
}
+ else if (!strcasecmp(key, "disablereuse")) {
+ if (!strcasecmp(val, "on"))
+ worker->disablereuse = 1;
+ else if (!strcasecmp(val, "off"))
+ worker->disablereuse = 0;
+ else
+ return "DisableReuse must be On|Off";
+ worker->disablereuse_set = 1;
+ }
else if (!strcasecmp(key, "route")) {
/* Worker route.
*/
@@ -432,6 +441,59 @@ static int proxy_detect(request_rec *r)
return DECLINED;
}
+static const char *proxy_interpolate(request_rec *r, const char *str)
+{
+ /* Interpolate an env str in a configuration string
+ * Syntax ${var} --> value_of(var)
+ * Method: replace one var, and recurse on remainder of string
+ * Nothing clever here, and crap like nested vars may do silly things
+ * but we'll at least avoid sending the unwary into a loop
+ */
+ const char *start;
+ const char *end;
+ const char *var;
+ const char *val;
+ const char *firstpart;
+
+ start = ap_strstr_c(str, "${");
+ if (start == NULL) {
+ return str;
+ }
+ end = ap_strchr_c(start+2, '}');
+ if (end == NULL) {
+ return str;
+ }
+ /* OK, this is syntax we want to interpolate. Is there such a var ? */
+ var = apr_pstrndup(r->pool, start+2, end-(start+2));
+ val = apr_table_get(r->subprocess_env, var);
+ firstpart = apr_pstrndup(r->pool, str, (start-str));
+
+ if (val == NULL) {
+ return apr_pstrcat(r->pool, firstpart,
+ proxy_interpolate(r, end+1), NULL);
+ }
+ else {
+ return apr_pstrcat(r->pool, firstpart, val,
+ proxy_interpolate(r, end+1), NULL);
+ }
+}
+static apr_array_header_t *proxy_vars(request_rec *r,
+ apr_array_header_t *hdr)
+{
+ int i;
+ apr_array_header_t *ret = apr_array_make(r->pool, hdr->nelts,
+ sizeof (struct proxy_alias));
+ struct proxy_alias *old = (struct proxy_alias *) hdr->elts;
+
+ for (i = 0; i < hdr->nelts; ++i) {
+ struct proxy_alias *newcopy = apr_array_push(ret);
+ newcopy->fake = (old[i].flags & PROXYPASS_INTERPOLATE)
+ ? proxy_interpolate(r, old[i].fake) : old[i].fake;
+ newcopy->real = (old[i].flags & PROXYPASS_INTERPOLATE)
+ ? proxy_interpolate(r, old[i].real) : old[i].real;
+ }
+ return ret;
+}
static int proxy_trans(request_rec *r)
{
void *sconf = r->server->module_config;
@@ -439,6 +501,10 @@ static int proxy_trans(request_rec *r)
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
int i, len;
struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
+ proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
+ &proxy_module);
+ const char *fake;
+ const char *real;
ap_regmatch_t regm[AP_MAX_REG_MATCH];
ap_regmatch_t reg1[AP_MAX_REG_MATCH];
char *found = NULL;
@@ -459,9 +525,18 @@ static int proxy_trans(request_rec *r)
for (i = 0; i < conf->aliases->nelts; i++) {
unsigned int nocanon = ent[i].flags & PROXYPASS_NOCANON;
const char *use_uri = nocanon ? r->unparsed_uri : r->uri;
+ if ((dconf->interpolate_env == 1)
+ && (ent[i].flags & PROXYPASS_INTERPOLATE)) {
+ fake = proxy_interpolate(r, ent[i].fake);
+ real = proxy_interpolate(r, ent[i].real);
+ }
+ else {
+ fake = ent[i].fake;
+ real = ent[i].real;
+ }
if (ent[i].regex) {
if (!ap_regexec(ent[i].regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) {
- if ((ent[i].real[0] == '!') && (ent[i].real[1] == '\0')) {
+ if ((real[0] == '!') && (real[1] == '\0')) {
return DECLINED;
}
/* test that we haven't reduced the URI */
@@ -470,8 +545,7 @@ static int proxy_trans(request_rec *r)
mismatch = 1;
use_uri = r->uri;
}
- found = ap_pregsub(r->pool, ent[i].real, use_uri,
- AP_MAX_REG_MATCH,
+ found = ap_pregsub(r->pool, real, use_uri, AP_MAX_REG_MATCH,
(use_uri == r->uri) ? regm : reg1);
/* Note: The strcmp() below catches cases where there
* was no regex substitution. This is so cases like:
@@ -486,20 +560,20 @@ static int proxy_trans(request_rec *r)
*
* which may be confusing.
*/
- if (found && strcmp(found, ent[i].real)) {
+ if (found && strcmp(found, real)) {
found = apr_pstrcat(r->pool, "proxy:", found, NULL);
}
else {
- found = apr_pstrcat(r->pool, "proxy:", ent[i].real,
+ found = apr_pstrcat(r->pool, "proxy:", real,
use_uri, NULL);
}
}
}
else {
- len = alias_match(r->uri, ent[i].fake);
+ len = alias_match(r->uri, fake);
- if (len > 0) {
- if ((ent[i].real[0] == '!') && (ent[i].real[1] == '\0')) {
+ if (len != 0) {
+ if ((real[0] == '!') && (real[1] == '\0')) {
return DECLINED;
}
if (nocanon
@@ -507,7 +581,7 @@ static int proxy_trans(request_rec *r)
mismatch = 1;
use_uri = r->uri;
}
- found = apr_pstrcat(r->pool, "proxy:", ent[i].real,
+ found = apr_pstrcat(r->pool, "proxy:", real,
use_uri + len, NULL);
}
}
@@ -591,6 +665,7 @@ static int proxy_map_location(request_rec *r)
return OK;
}
+
/* -------------------------------------------------------------- */
/* Fixup the filename */
@@ -601,6 +676,8 @@ static int proxy_fixup(request_rec *r)
{
char *url, *p;
int access_status;
+ proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
+ &proxy_module);
if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
return DECLINED;
@@ -608,6 +685,17 @@ static int proxy_fixup(request_rec *r)
/* XXX: Shouldn't we try this before we run the proxy_walk? */
url = &r->filename[6];
+ if ((dconf->interpolate_env == 1) && (r->proxyreq == PROXYREQ_REVERSE)) {
+ /* create per-request copy of reverse proxy conf,
+ * and interpolate vars in it
+ */
+ proxy_req_conf *rconf = apr_palloc(r->pool, sizeof(proxy_req_conf));
+ ap_set_module_config(r->request_config, &proxy_module, rconf);
+ rconf->raliases = proxy_vars(r, dconf->raliases);
+ rconf->cookie_paths = proxy_vars(r, dconf->cookie_paths);
+ rconf->cookie_domains = proxy_vars(r, dconf->cookie_domains);
+ }
+
/* canonicalise each specific scheme */
if ((access_status = proxy_run_canon_handler(r, url))) {
return access_status;
@@ -830,12 +918,41 @@ static int proxy_handler(request_rec *r)
ents[i].hostname,
ents[i].port);
- /* an error or success */
- if (access_status != DECLINED &&
- access_status != HTTP_BAD_GATEWAY) {
- goto cleanup;
+ /* Did the scheme handler process the request? */
+ if (access_status != DECLINED) {
+ const char *cl_a;
+ char *end;
+ apr_off_t cl;
+
+ /*
+ * An fatal error or success, so no point in
+ * retrying with a direct connection.
+ */
+ if (access_status != HTTP_BAD_GATEWAY) {
+ goto cleanup;
+ }
+ cl_a = apr_table_get(r->headers_in, "Content-Length");
+ if (cl_a) {
+ apr_strtoff(&cl, cl_a, &end, 0);
+ /*
+ * The request body is of length > 0. We cannot
+ * retry with a direct connection since we already
+ * sent (parts of) the request body to the proxy
+ * and do not have any longer.
+ */
+ if (cl > 0) {
+ goto cleanup;
+ }
+ }
+ /*
+ * Transfer-Encoding was set as input header, so we had
+ * a request body. We cannot retry with a direct
+ * connection for the same reason as above.
+ */
+ if (apr_table_get(r->headers_in, "Transfer-Encoding")) {
+ goto cleanup;
+ }
}
- /* we failed to talk to the upstream proxy */
}
}
}
@@ -1002,6 +1119,7 @@ static void *create_proxy_dir_config(apr_pool_t *p, char *dummy)
new->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias));
new->cookie_path_str = apr_strmatch_precompile(p, "path=", 0);
new->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0);
+ new->interpolate_env = -1; /* unset */
return (void *) new;
}
@@ -1024,6 +1142,8 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
= apr_array_append(p, base->cookie_domains, add->cookie_domains);
new->cookie_path_str = base->cookie_path_str;
new->cookie_domain_str = base->cookie_domain_str;
+ new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env
+ : add->interpolate_env;
new->ftp_directory_charset = add->ftp_directory_charset ?
add->ftp_directory_charset :
base->ftp_directory_charset;
@@ -1140,6 +1260,9 @@ static const char *
else if (!strcasecmp(word,"nocanon")) {
flags |= PROXYPASS_NOCANON;
}
+ else if (!strcasecmp(word,"interpolate")) {
+ flags |= PROXYPASS_INTERPOLATE;
+ }
else {
char *val = strchr(word, '=');
if (!val) {
@@ -1237,31 +1360,41 @@ static const char *
}
-static const char *
- add_pass_reverse(cmd_parms *cmd, void *dconf, const char *f, const char *r)
+static const char * add_pass_reverse(cmd_parms *cmd, void *dconf, const char *f,
+ const char *r, const char *i)
{
proxy_dir_conf *conf = dconf;
struct proxy_alias *new;
-
- if (r!=NULL && cmd->path == NULL ) {
- new = apr_array_push(conf->raliases);
- new->fake = f;
- new->real = r;
- } else if (r==NULL && cmd->path != NULL) {
- new = apr_array_push(conf->raliases);
- new->fake = cmd->path;
- new->real = f;
- } else {
- if ( r == NULL)
+ const char *fake;
+ const char *real;
+ const char *interp;
+
+ if (cmd->path == NULL) {
+ fake = f;
+ real = r;
+ interp = i;
+ if (r == NULL || !strcasecmp(r, "interpolate")) {
return "ProxyPassReverse needs a path when not defined in a location";
- else
+ }
+ }
+ else {
+ fake = cmd->path;
+ real = f;
+ if (r && strcasecmp(r, "interpolate")) {
return "ProxyPassReverse can not have a path when defined in a location";
+ }
+ interp = r;
}
+ new = apr_array_push(conf->raliases);
+ new->fake = fake;
+ new->real = real;
+ new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
+
return NULL;
}
-static const char*
- cookie_path(cmd_parms *cmd, void *dconf, const char *f, const char *r)
+static const char* cookie_path(cmd_parms *cmd, void *dconf, const char *f,
+ const char *r, const char *interp)
{
proxy_dir_conf *conf = dconf;
struct proxy_alias *new;
@@ -1269,11 +1402,12 @@ static const char*
new = apr_array_push(conf->cookie_paths);
new->fake = f;
new->real = r;
+ new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
return NULL;
}
-static const char*
- cookie_domain(cmd_parms *cmd, void *dconf, const char *f, const char *r)
+static const char* cookie_domain(cmd_parms *cmd, void *dconf, const char *f,
+ const char *r, const char *interp)
{
proxy_dir_conf *conf = dconf;
struct proxy_alias *new;
@@ -1281,7 +1415,7 @@ static const char*
new = apr_array_push(conf->cookie_domains);
new->fake = f;
new->real = r;
-
+ new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
return NULL;
}
@@ -1902,15 +2036,18 @@ static const command_rec proxy_cmds[] =
"a scheme, partial URL or '*' and a proxy server"),
AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
"a regex pattern and a proxy server"),
+ AP_INIT_FLAG("ProxyPassInterpolateEnv", ap_set_flag_slot,
+ (void*)APR_OFFSETOF(proxy_dir_conf, interpolate_env),
+ RSRC_CONF|ACCESS_CONF, "Interpolate Env Vars in reverse Proxy") ,
AP_INIT_RAW_ARGS("ProxyPass", add_pass_noregex, NULL, RSRC_CONF|ACCESS_CONF,
"a virtual path and a URL"),
AP_INIT_RAW_ARGS("ProxyPassMatch", add_pass_regex, NULL, RSRC_CONF|ACCESS_CONF,
"a virtual path and a URL"),
- AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
+ AP_INIT_TAKE123("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
"a virtual path and a URL for reverse proxy behaviour"),
- AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL,
+ AP_INIT_TAKE23("ProxyPassReverseCookiePath", cookie_path, NULL,
RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
- AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL,
+ AP_INIT_TAKE23("ProxyPassReverseCookieDomain", cookie_domain, NULL,
RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
"A list of names, hosts or domains to which the proxy will not connect"),
diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h
index 3944104e..fdb48bf3 100644
--- a/modules/proxy/mod_proxy.h
+++ b/modules/proxy/mod_proxy.h
@@ -110,6 +110,7 @@ struct proxy_remote {
};
#define PROXYPASS_NOCANON 0x01
+#define PROXYPASS_INTERPOLATE 0x02
struct proxy_alias {
const char *real;
const char *fake;
@@ -213,14 +214,24 @@ typedef struct {
const apr_strmatch_pattern* cookie_path_str;
const apr_strmatch_pattern* cookie_domain_str;
const char *ftp_directory_charset;
+ int interpolate_env;
} proxy_dir_conf;
+/* if we interpolate env vars per-request, we'll need a per-request
+ * copy of the reverse proxy config
+ */
+typedef struct {
+ apr_array_header_t *raliases;
+ apr_array_header_t* cookie_paths;
+ apr_array_header_t* cookie_domains;
+} proxy_req_conf;
+
typedef struct {
conn_rec *connection;
const char *hostname;
apr_port_t port;
int is_ssl;
- apr_pool_t *pool; /* Subpool used for creating socket */
+ apr_pool_t *pool; /* Subpool for hostname and addr data */
apr_socket_t *sock; /* Connection socket */
apr_sockaddr_t *addr; /* Preparsed remote address info */
apr_uint32_t flags; /* Conection flags */
@@ -231,6 +242,11 @@ typedef struct {
#if APR_HAS_THREADS
int inreslist; /* connection in apr_reslist? */
#endif
+ apr_pool_t *scpool; /* Subpool used for socket and connection data */
+ request_rec *r; /* Request record of the frontend request
+ * which the backend currently answers. */
+ int need_flush;/* Flag to decide whether we need to flush the
+ * filter chain or not */
} proxy_conn_rec;
typedef struct {
@@ -337,6 +353,8 @@ struct proxy_worker {
apr_interval_time_t ping_timeout;
char ping_timeout_set;
char retry_set;
+ char disablereuse;
+ char disablereuse_set;
};
/*
@@ -473,6 +491,8 @@ PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade
PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key);
/* DEPRECATED (will be replaced with ap_proxy_connect_backend */
PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, server_rec *, apr_pool_t *);
+PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
+ request_rec *r);
PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c);
PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c);
PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c);
diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c
index bad2b26e..80a6e0ab 100644
--- a/modules/proxy/mod_proxy_ajp.c
+++ b/modules/proxy/mod_proxy_ajp.c
@@ -29,7 +29,8 @@ module AP_MODULE_DECLARE_DATA proxy_ajp_module;
*/
static int proxy_ajp_canon(request_rec *r, char *url)
{
- char *host, *path, *search, sport[7];
+ char *host, *path, sport[7];
+ char *search = NULL;
const char *err;
apr_port_t port = AJP13_DEF_PORT;
@@ -57,23 +58,18 @@ static int proxy_ajp_canon(request_rec *r, char *url)
}
/*
- * now parse path/search args, according to rfc1738
- *
- * N.B. if this isn't a true proxy request, then the URL _path_
- * has already been decoded. True proxy requests have
- * r->uri == r->unparsed_uri, and no others have that property.
+ * now parse path/search args, according to rfc1738:
+ * process the path. With proxy-noncanon set (by
+ * mod_proxy) we use the raw, unparsed uri
*/
- if (r->uri == r->unparsed_uri) {
- search = strchr(url, '?');
- if (search != NULL)
- *(search++) = '\0';
+ if (apr_table_get(r->notes, "proxy-nocanon")) {
+ path = url; /* this is the raw path */
}
- else
+ else {
+ path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
+ r->proxyreq);
search = r->args;
-
- /* process path */
- path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
- r->proxyreq);
+ }
if (path == NULL)
return HTTP_BAD_REQUEST;
@@ -89,6 +85,37 @@ static int proxy_ajp_canon(request_rec *r, char *url)
return OK;
}
+#define METHOD_NON_IDEMPOTENT 0
+#define METHOD_IDEMPOTENT 1
+#define METHOD_IDEMPOTENT_WITH_ARGS 2
+
+static int is_idempotent(request_rec *r)
+{
+ /*
+ * RFC2616 (9.1.2): GET, HEAD, PUT, DELETE, OPTIONS, TRACE are considered
+ * idempotent. Hint: HEAD requests use M_GET as method number as well.
+ */
+ switch (r->method_number) {
+ case M_GET:
+ case M_DELETE:
+ case M_PUT:
+ case M_OPTIONS:
+ case M_TRACE:
+ /*
+ * If the request has arguments it might have side-effects and thus
+ * it might be undesirable to resent it to a backend again
+ * automatically.
+ */
+ if (r->args) {
+ return METHOD_IDEMPOTENT_WITH_ARGS;
+ }
+ return METHOD_IDEMPOTENT;
+ /* Everything else is not considered idempotent. */
+ default:
+ return METHOD_NON_IDEMPOTENT;
+ }
+}
+
/*
* XXX: AJP Auto Flushing
*
@@ -122,7 +149,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
apr_bucket_brigade *input_brigade;
apr_bucket_brigade *output_brigade;
ajp_msg_t *msg;
- apr_size_t bufsiz;
+ apr_size_t bufsiz = 0;
char *buff;
apr_uint16_t size;
const char *tenc;
@@ -138,6 +165,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
proxy_server_conf *psf =
ap_get_module_config(r->server->module_config, &proxy_module);
apr_size_t maxsize = AJP_MSG_BUFFER_SZ;
+ int send_body = 0;
if (psf->io_buffer_size_set)
maxsize = psf->io_buffer_size;
@@ -161,8 +189,17 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
conn->worker->hostname);
if (status == AJP_EOVERFLOW)
return HTTP_BAD_REQUEST;
- else
- return HTTP_SERVICE_UNAVAILABLE;
+ else {
+ /*
+ * This is only non fatal when the method is idempotent. In this
+ * case we can dare to retry it with a different worker if we are
+ * a balancer member.
+ */
+ if (is_idempotent(r) == METHOD_IDEMPOTENT) {
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
}
/* allocate an AJP message to store the data of the buckets */
@@ -231,9 +268,14 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
"proxy: send failed to %pI (%s)",
conn->worker->cp->addr,
conn->worker->hostname);
- return HTTP_SERVICE_UNAVAILABLE;
+ /*
+ * It is fatal when we failed to send a (part) of the request
+ * body.
+ */
+ return HTTP_INTERNAL_SERVER_ERROR;
}
conn->worker->s->transferred += bufsiz;
+ send_body = 1;
}
}
@@ -249,7 +291,16 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
"proxy: read response failed from %pI (%s)",
conn->worker->cp->addr,
conn->worker->hostname);
- return HTTP_SERVICE_UNAVAILABLE;
+ /*
+ * This is only non fatal when we have not sent (parts) of a possible
+ * request body so far (we do not store it and thus cannot sent it
+ * again) and the method is idempotent. In this case we can dare to
+ * retry it with a different worker if we are a balancer member.
+ */
+ if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) {
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
}
/* parse the reponse */
result = ajp_parse_type(r, conn->data);
diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c
index d2ae88bb..bcc47cfc 100644
--- a/modules/proxy/mod_proxy_balancer.c
+++ b/modules/proxy/mod_proxy_balancer.c
@@ -23,12 +23,16 @@
#include "ap_mpm.h"
#include "apr_version.h"
#include "apr_hooks.h"
+#include "apr_uuid.h"
module AP_MODULE_DECLARE_DATA proxy_balancer_module;
+static char balancer_nonce[APR_UUID_FORMATTED_LENGTH + 1];
+
static int proxy_balancer_canon(request_rec *r, char *url)
{
- char *host, *path, *search;
+ char *host, *path;
+ char *search = NULL;
const char *err;
apr_port_t port = 0;
@@ -52,21 +56,19 @@ static int proxy_balancer_canon(request_rec *r, char *url)
url, err);
return HTTP_BAD_REQUEST;
}
- /* now parse path/search args, according to rfc1738 */
- /* N.B. if this isn't a true proxy request, then the URL _path_
- * has already been decoded. True proxy requests have r->uri
- * == r->unparsed_uri, and no others have that property.
+ /*
+ * now parse path/search args, according to rfc1738:
+ * process the path. With proxy-noncanon set (by
+ * mod_proxy) we use the raw, unparsed uri
*/
- if (r->uri == r->unparsed_uri) {
- search = strchr(url, '?');
- if (search != NULL)
- *(search++) = '\0';
+ if (apr_table_get(r->notes, "proxy-nocanon")) {
+ path = url; /* this is the raw path */
}
- else
+ else {
+ path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
+ r->proxyreq);
search = r->args;
-
- /* process path */
- path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq);
+ }
if (path == NULL)
return HTTP_BAD_REQUEST;
@@ -589,6 +591,31 @@ static void recalc_factors(proxy_balancer *balancer)
}
}
+/* post_config hook: */
+static int balancer_init(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ void *data;
+ const char *userdata_key = "mod_proxy_balancer_init";
+ apr_uuid_t uuid;
+
+ /* balancer_init() will be called twice during startup. So, only
+ * set up the static data the second time through. */
+ apr_pool_userdata_get(&data, userdata_key, s->process->pool);
+ if (!data) {
+ apr_pool_userdata_set((const void *)1, userdata_key,
+ apr_pool_cleanup_null, s->process->pool);
+ return OK;
+ }
+
+ /* Retrieve a UUID and store the nonce for the lifetime of
+ * the process. */
+ apr_uuid_get(&uuid);
+ apr_uuid_format(balancer_nonce, &uuid);
+
+ return OK;
+}
+
/* Manages the loadfactors and member status
*/
static int balancer_handler(request_rec *r)
@@ -631,6 +658,14 @@ static int balancer_handler(request_rec *r)
return HTTP_BAD_REQUEST;
}
}
+
+ /* Check that the supplied nonce matches this server's nonce;
+ * otherwise ignore all parameters, to prevent a CSRF attack. */
+ if ((name = apr_table_get(params, "nonce")) == NULL
+ || strcmp(balancer_nonce, name) != 0) {
+ apr_table_clear(params);
+ }
+
if ((name = apr_table_get(params, "b")))
bsel = ap_proxy_get_balancer(r->pool, conf,
apr_pstrcat(r->pool, "balancer://", name, NULL));
@@ -762,6 +797,7 @@ static int balancer_handler(request_rec *r)
ap_rvputs(r, "<tr>\n<td><a href=\"", r->uri, "?b=",
balancer->name + sizeof("balancer://") - 1, "&w=",
ap_escape_uri(r->pool, worker->name),
+ "&nonce=", balancer_nonce,
"\">", NULL);
ap_rvputs(r, worker->name, "</a></td>", NULL);
ap_rvputs(r, "<td>", ap_escape_html(r->pool, worker->s->route),
@@ -825,6 +861,8 @@ static int balancer_handler(request_rec *r)
ap_rvputs(r, "<input type=hidden name=\"b\" ", NULL);
ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1,
"\">\n</form>\n", NULL);
+ ap_rvputs(r, "<input type=hidden name=\"nonce\" value=\"",
+ balancer_nonce, "\">\n", NULL);
ap_rputs("<hr />\n", r);
}
ap_rputs(ap_psignature("",r), r);
@@ -1063,6 +1101,7 @@ static void ap_proxy_balancer_register_hook(apr_pool_t *p)
*/
static const char *const aszPred[] = { "mpm_winnt.c", "mod_proxy.c", NULL};
/* manager handler */
+ ap_hook_post_config(balancer_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST);
ap_hook_child_init(child_init, aszPred, NULL, APR_HOOK_MIDDLE);
proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST);
diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c
index 3cacac6e..75a2054e 100644
--- a/modules/proxy/mod_proxy_ftp.c
+++ b/modules/proxy/mod_proxy_ftp.c
@@ -314,6 +314,7 @@ static apr_status_t proxy_send_dir_filter(ap_filter_t *f,
/* basedir is either "", or "/%2f" for the "squid %2f hack" */
const char *basedir = ""; /* By default, path is relative to the $HOME dir */
char *wildcard = NULL;
+ const char *escpath;
/* Save "scheme://site" prefix without password */
site = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO);
@@ -350,13 +351,14 @@ static apr_status_t proxy_send_dir_filter(ap_filter_t *f,
str = (basedir[0] != '\0') ? "<a href=\"/%2f/\">%2f</a>/" : "";
/* print "ftp://host/" */
+ escpath = ap_escape_html(p, path);
str = apr_psprintf(p, DOCTYPE_HTML_3_2
"<html>\n <head>\n <title>%s%s%s</title>\n"
+ "<base href=\"%s%s%s\">\n"
" </head>\n"
" <body>\n <h2>Directory of "
"<a href=\"/\">%s</a>/%s",
- site, basedir, ap_escape_html(p, path),
- site, str);
+ site, basedir, escpath, site, basedir, escpath, site, str);
APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str),
p, c->bucket_alloc));
@@ -959,6 +961,7 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker,
}
/* TODO: see if ftp could use determine_connection */
backend->addr = connect_addr;
+ backend->r = r;
ap_set_module_config(c->conn_config, &proxy_ftp_module, backend);
}
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c
index e5f654bb..3ca21895 100644
--- a/modules/proxy/mod_proxy_http.c
+++ b/modules/proxy/mod_proxy_http.c
@@ -33,7 +33,8 @@ static apr_status_t ap_proxy_http_cleanup(const char *scheme,
*/
static int proxy_http_canon(request_rec *r, char *url)
{
- char *host, *path, *search, sport[7];
+ char *host, *path, sport[7];
+ char *search = NULL;
const char *err;
const char *scheme;
apr_port_t port, def_port;
@@ -67,21 +68,11 @@ static int proxy_http_canon(request_rec *r, char *url)
return HTTP_BAD_REQUEST;
}
- /* now parse path/search args, according to rfc1738 */
- /* N.B. if this isn't a true proxy request, then the URL _path_
- * has already been decoded. True proxy requests have r->uri
- * == r->unparsed_uri, and no others have that property.
- */
- if (r->uri == r->unparsed_uri) {
- search = strchr(url, '?');
- if (search != NULL)
- *(search++) = '\0';
- }
- else
- search = r->args;
-
- /* process path */
- /* In a reverse proxy, our URL has been processed, so canonicalise
+ /*
+ * now parse path/search args, according to rfc1738:
+ * process the path.
+ *
+ * In a reverse proxy, our URL has been processed, so canonicalise
* unless proxy-nocanon is set to say it's raw
* In a forward proxy, we have and MUST NOT MANGLE the original.
*/
@@ -94,6 +85,7 @@ static int proxy_http_canon(request_rec *r, char *url)
else {
path = ap_proxy_canonenc(r->pool, url, strlen(url),
enc_path, 0, r->proxyreq);
+ search = r->args;
}
break;
case PROXYREQ_PROXY:
@@ -259,7 +251,7 @@ static void terminate_headers(apr_bucket_alloc_t *bucket_alloc,
APR_BRIGADE_INSERT_TAIL(header_brigade, e);
}
-static apr_status_t pass_brigade(apr_bucket_alloc_t *bucket_alloc,
+static int pass_brigade(apr_bucket_alloc_t *bucket_alloc,
request_rec *r, proxy_conn_rec *conn,
conn_rec *origin, apr_bucket_brigade *bb,
int flush)
@@ -279,22 +271,27 @@ static apr_status_t pass_brigade(apr_bucket_alloc_t *bucket_alloc,
ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
"proxy: pass request body failed to %pI (%s)",
conn->addr, conn->hostname);
- return status;
+ if (origin->aborted) {
+ return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY;
+ }
+ else {
+ return HTTP_BAD_REQUEST;
+ }
}
apr_brigade_cleanup(bb);
- return APR_SUCCESS;
+ return OK;
}
#define MAX_MEM_SPOOL 16384
-static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
+static int stream_reqbody_chunked(apr_pool_t *p,
request_rec *r,
proxy_conn_rec *p_conn,
conn_rec *origin,
apr_bucket_brigade *header_brigade,
apr_bucket_brigade *input_brigade)
{
- int seen_eos = 0;
+ int seen_eos = 0, rv = OK;
apr_size_t hdr_len;
apr_off_t bytes;
apr_status_t status;
@@ -352,7 +349,7 @@ static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
*/
status = ap_save_brigade(NULL, &bb, &input_brigade, p);
if (status != APR_SUCCESS) {
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
header_brigade = NULL;
@@ -362,9 +359,9 @@ static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
}
/* The request is flushed below this loop with chunk EOS header */
- status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0);
- if (status != APR_SUCCESS) {
- return status;
+ rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0);
+ if (rv != OK) {
+ return rv;
}
if (seen_eos) {
@@ -376,7 +373,7 @@ static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
HUGE_STRING_LEN);
if (status != APR_SUCCESS) {
- return status;
+ return HTTP_BAD_REQUEST;
}
}
@@ -403,11 +400,11 @@ static apr_status_t stream_reqbody_chunked(apr_pool_t *p,
APR_BRIGADE_INSERT_TAIL(bb, e);
/* Now we have headers-only, or the chunk EOS mark; flush it */
- status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
- return status;
+ rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
+ return rv;
}
-static apr_status_t stream_reqbody_cl(apr_pool_t *p,
+static int stream_reqbody_cl(apr_pool_t *p,
request_rec *r,
proxy_conn_rec *p_conn,
conn_rec *origin,
@@ -415,7 +412,7 @@ static apr_status_t stream_reqbody_cl(apr_pool_t *p,
apr_bucket_brigade *input_brigade,
const char *old_cl_val)
{
- int seen_eos = 0;
+ int seen_eos = 0, rv = 0;
apr_status_t status = APR_SUCCESS;
apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
apr_bucket_brigade *bb;
@@ -428,7 +425,7 @@ static apr_status_t stream_reqbody_cl(apr_pool_t *p,
add_cl(p, bucket_alloc, header_brigade, old_cl_val);
if (APR_SUCCESS != (status = apr_strtoff(&cl_val, old_cl_val, NULL,
0))) {
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
}
terminate_headers(bucket_alloc, header_brigade);
@@ -476,7 +473,7 @@ static apr_status_t stream_reqbody_cl(apr_pool_t *p,
*/
status = ap_save_brigade(NULL, &bb, &input_brigade, p);
if (status != APR_SUCCESS) {
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
header_brigade = NULL;
@@ -486,9 +483,9 @@ static apr_status_t stream_reqbody_cl(apr_pool_t *p,
}
/* Once we hit EOS, we are ready to flush. */
- status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos);
- if (status != APR_SUCCESS) {
- return status;
+ rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos);
+ if (rv != OK) {
+ return rv ;
}
if (seen_eos) {
@@ -500,7 +497,7 @@ static apr_status_t stream_reqbody_cl(apr_pool_t *p,
HUGE_STRING_LEN);
if (status != APR_SUCCESS) {
- return status;
+ return HTTP_BAD_REQUEST;
}
}
@@ -508,7 +505,7 @@ static apr_status_t stream_reqbody_cl(apr_pool_t *p,
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"proxy: client %s given Content-Length did not match"
" number of body bytes read", r->connection->remote_ip);
- return APR_EOF;
+ return HTTP_BAD_REQUEST;
}
if (header_brigade) {
@@ -516,12 +513,13 @@ static apr_status_t stream_reqbody_cl(apr_pool_t *p,
* body; send it now with the flush flag
*/
bb = header_brigade;
- status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
+ return(pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1));
}
- return status;
+
+ return OK;
}
-static apr_status_t spool_reqbody_cl(apr_pool_t *p,
+static int spool_reqbody_cl(apr_pool_t *p,
request_rec *r,
proxy_conn_rec *p_conn,
conn_rec *origin,
@@ -562,7 +560,7 @@ static apr_status_t spool_reqbody_cl(apr_pool_t *p,
if (status != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
"proxy: search for temporary directory failed");
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
apr_filepath_merge(&template, temp_dir,
"modproxy.tmp.XXXXXX",
@@ -572,7 +570,7 @@ static apr_status_t spool_reqbody_cl(apr_pool_t *p,
ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
"proxy: creation of temporary file in directory %s failed",
temp_dir);
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
}
for (e = APR_BRIGADE_FIRST(input_brigade);
@@ -592,7 +590,7 @@ static apr_status_t spool_reqbody_cl(apr_pool_t *p,
ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
"proxy: write to temporary file %s failed",
tmpfile_name);
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
AP_DEBUG_ASSERT(bytes_read == bytes_written);
fsize += bytes_written;
@@ -612,7 +610,7 @@ static apr_status_t spool_reqbody_cl(apr_pool_t *p,
*/
status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p);
if (status != APR_SUCCESS) {
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
}
@@ -628,7 +626,7 @@ static apr_status_t spool_reqbody_cl(apr_pool_t *p,
HUGE_STRING_LEN);
if (status != APR_SUCCESS) {
- return status;
+ return HTTP_BAD_REQUEST;
}
}
@@ -662,12 +660,11 @@ static apr_status_t spool_reqbody_cl(apr_pool_t *p,
APR_BRIGADE_INSERT_TAIL(header_brigade, e);
}
/* This is all a single brigade, pass with flush flagged */
- status = pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1);
- return status;
+ return(pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1));
}
static
-apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
+int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
proxy_conn_rec *p_conn, conn_rec *origin,
proxy_server_conf *conf,
apr_uri_t *uri,
@@ -690,7 +687,7 @@ apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
const char *old_te_val = NULL;
apr_off_t bytes_read = 0;
apr_off_t bytes;
- int force10;
+ int force10, rv;
apr_table_t *headers_in_copy;
header_brigade = apr_brigade_create(p, origin->bucket_alloc);
@@ -932,7 +929,7 @@ apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"proxy: %s Transfer-Encoding is not supported",
old_te_val);
- return APR_EINVAL;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
if (old_cl_val && old_te_val) {
@@ -965,7 +962,7 @@ apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
" from %s (%s)",
p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
c->remote_ip, c->remote_host ? c->remote_host: "");
- return status;
+ return HTTP_BAD_REQUEST;
}
apr_brigade_length(temp_brigade, 1, &bytes);
@@ -987,7 +984,7 @@ apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
" to %pI (%s) from %s (%s)",
p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
c->remote_ip, c->remote_host ? c->remote_host: "");
- return status;
+ return HTTP_INTERNAL_SERVER_ERROR;
}
/* Ensure we don't hit a wall where we have a buffer too small
@@ -1101,37 +1098,38 @@ skip_body:
/* send the request body, if any. */
switch(rb_method) {
case RB_STREAM_CHUNKED:
- status = stream_reqbody_chunked(p, r, p_conn, origin, header_brigade,
+ rv = stream_reqbody_chunked(p, r, p_conn, origin, header_brigade,
input_brigade);
break;
case RB_STREAM_CL:
- status = stream_reqbody_cl(p, r, p_conn, origin, header_brigade,
+ rv = stream_reqbody_cl(p, r, p_conn, origin, header_brigade,
input_brigade, old_cl_val);
break;
case RB_SPOOL_CL:
- status = spool_reqbody_cl(p, r, p_conn, origin, header_brigade,
+ rv = spool_reqbody_cl(p, r, p_conn, origin, header_brigade,
input_brigade, (old_cl_val != NULL)
|| (old_te_val != NULL)
|| (bytes_read > 0));
break;
default:
/* shouldn't be possible */
- status = APR_EINVAL;
+ rv = HTTP_INTERNAL_SERVER_ERROR ;
break;
}
- if (status != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ if (rv != OK) {
+ /* apr_errno value has been logged in lower level method */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"proxy: pass request body failed to %pI (%s)"
" from %s (%s)",
p_conn->addr,
p_conn->hostname ? p_conn->hostname: "",
c->remote_ip,
c->remote_host ? c->remote_host: "");
- return status;
+ return rv;
}
- return APR_SUCCESS;
+ return OK;
}
static void process_proxy_header(request_rec* r, proxy_dir_conf* c,
@@ -1309,6 +1307,16 @@ apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, request_rec
return rv;
}
+/*
+ * Limit the number of interim respones we sent back to the client. Otherwise
+ * we suffer from a memory build up. Besides there is NO sense in sending back
+ * an unlimited number of interim responses to the client. Thus if we cross
+ * this limit send back a 502 (Bad Gateway).
+ */
+#ifndef AP_MAX_INTERIM_RESPONSES
+#define AP_MAX_INTERIM_RESPONSES 10
+#endif
+
static
apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
proxy_conn_rec *backend,
@@ -1323,14 +1331,15 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
apr_bucket *e;
apr_bucket_brigade *bb, *tmp_bb;
int len, backasswards;
- int interim_response; /* non-zero whilst interim 1xx responses
- * are being read. */
+ int interim_response = 0; /* non-zero whilst interim 1xx responses
+ * are being read. */
int pread_len = 0;
apr_table_t *save_table;
int backend_broke = 0;
static const char *hop_by_hop_hdrs[] =
{"Keep-Alive", "Proxy-Authenticate", "TE", "Trailer", "Upgrade", NULL};
int i;
+ const char *te = NULL;
bb = apr_brigade_create(p, c->bucket_alloc);
@@ -1358,6 +1367,56 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
"proxy: error reading status line from remote "
"server %s", backend->hostname);
+ /*
+ * If we are a reverse proxy request shutdown the connection
+ * WITHOUT ANY response to trigger a retry by the client
+ * if allowed (as for idempotent requests).
+ * BUT currently we should not do this if the request is the
+ * first request on a keepalive connection as browsers like
+ * seamonkey only display an empty page in this case and do
+ * not do a retry.
+ */
+ if (r->proxyreq == PROXYREQ_REVERSE && c->keepalives) {
+ apr_bucket *eos;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: Closing connection to client because"
+ " reading from backend server %s failed. Number"
+ " of keepalives %i", backend->hostname,
+ c->keepalives);
+ ap_proxy_backend_broke(r, bb);
+ /*
+ * Add an EOC bucket to signal the ap_http_header_filter
+ * that it should get out of our way, BUT ensure that the
+ * EOC bucket is inserted BEFORE an EOS bucket in bb as
+ * some resource filters like mod_deflate pass everything
+ * up to the EOS down the chain immediately and sent the
+ * remainder of the brigade later (or even never). But in
+ * this case the ap_http_header_filter does not get out of
+ * our way soon enough.
+ */
+ e = ap_bucket_eoc_create(c->bucket_alloc);
+ eos = APR_BRIGADE_LAST(bb);
+ while ((APR_BRIGADE_SENTINEL(bb) != eos)
+ && !APR_BUCKET_IS_EOS(eos)) {
+ eos = APR_BUCKET_PREV(eos);
+ }
+ if (eos == APR_BRIGADE_SENTINEL(bb)) {
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ }
+ else {
+ APR_BUCKET_INSERT_BEFORE(eos, e);
+ }
+ ap_pass_brigade(r->output_filters, bb);
+ /* Need to return OK to avoid sending an error message */
+ return OK;
+ }
+ else if (!c->keepalives) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: NOT Closing connection to client"
+ " although reading from backend server %s"
+ " failed.", backend->hostname);
+ }
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
"Error reading from remote server");
}
@@ -1461,6 +1520,11 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
backend->close += 1;
}
+ /*
+ * Save a possible Transfer-Encoding header as we need it later for
+ * 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 */
backend->close += ap_proxy_liststr(apr_table_get(r->headers_out,
"Connection"),
@@ -1469,7 +1533,9 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
ap_set_content_type(r, apr_pstrdup(p, buf));
}
- ap_proxy_pre_http_request(origin,rp);
+ if (!ap_is_HTTP_INFO(r->status)) {
+ ap_proxy_pre_http_request(origin, rp);
+ }
/* Clear hop-by-hop headers */
for (i=0; hop_by_hop_hdrs[i]; ++i) {
@@ -1518,7 +1584,12 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
backend->close += 1;
}
- interim_response = ap_is_HTTP_INFO(r->status);
+ if (ap_is_HTTP_INFO(r->status)) {
+ interim_response++;
+ }
+ else {
+ interim_response = 0;
+ }
if (interim_response) {
/* RFC2616 tells us to forward this.
*
@@ -1601,6 +1672,14 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
* ap_http_filter to know where to end.
*/
rp->headers_in = apr_table_copy(r->pool, r->headers_out);
+ /*
+ * Restore Transfer-Encoding header from response if we saved
+ * one before and there is none left. We need it for the
+ * ap_http_filter. See above.
+ */
+ if (te && !apr_table_get(rp->headers_in, "Transfer-Encoding")) {
+ apr_table_add(rp->headers_in, "Transfer-Encoding", te);
+ }
apr_table_unset(r->headers_out,"Transfer-Encoding");
@@ -1711,7 +1790,15 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
apr_brigade_cleanup(bb);
}
- } while (interim_response);
+ } while (interim_response && (interim_response < AP_MAX_INTERIM_RESPONSES));
+
+ /* See define of AP_MAX_INTERIM_RESPONSES for why */
+ if (interim_response >= AP_MAX_INTERIM_RESPONSES) {
+ return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+ apr_psprintf(p,
+ "Too many (%d) interim responses from origin server",
+ interim_response));
+ }
/* If our connection with the client is to be aborted, return DONE. */
if (c->aborted || backend_broke) {
@@ -1830,14 +1917,9 @@ static int proxy_http_handler(request_rec *r, proxy_worker *worker,
backend->is_ssl = is_ssl;
- /*
- * TODO: Currently we cannot handle persistent SSL backend connections,
- * because we recreate backend->connection for each request and thus
- * try to initialize an already existing SSL connection. This does
- * not work.
- */
- if (is_ssl)
- backend->close_on_recycle = 1;
+ if (is_ssl) {
+ ap_proxy_ssl_connection_cleanup(backend, r);
+ }
/* Step One: Determine Who To Connect To */
if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend,
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
index 910f3610..e8309e46 100644
--- a/modules/proxy/proxy_util.c
+++ b/modules/proxy/proxy_util.c
@@ -332,16 +332,16 @@ PROXY_DECLARE(const char *)
PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
{
- request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
+ request_rec *rp = apr_pcalloc(r->pool, sizeof(*r));
- rp->pool = c->pool;
+ rp->pool = r->pool;
rp->status = HTTP_OK;
- rp->headers_in = apr_table_make(c->pool, 50);
- rp->subprocess_env = apr_table_make(c->pool, 50);
- rp->headers_out = apr_table_make(c->pool, 12);
- rp->err_headers_out = apr_table_make(c->pool, 5);
- rp->notes = apr_table_make(c->pool, 5);
+ rp->headers_in = apr_table_make(r->pool, 50);
+ rp->subprocess_env = apr_table_make(r->pool, 50);
+ rp->headers_out = apr_table_make(r->pool, 12);
+ rp->err_headers_out = apr_table_make(r->pool, 5);
+ rp->notes = apr_table_make(r->pool, 5);
rp->server = r->server;
rp->proxyreq = r->proxyreq;
@@ -352,7 +352,7 @@ PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
rp->proto_output_filters = c->output_filters;
rp->proto_input_filters = c->input_filters;
- rp->request_config = ap_create_request_config(c->pool);
+ rp->request_config = ap_create_request_config(r->pool);
proxy_run_create_req(r, rp);
return rp;
@@ -1041,6 +1041,7 @@ PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *
PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
proxy_dir_conf *conf, const char *url)
{
+ proxy_req_conf *rconf;
struct proxy_alias *ent;
int i, l1, l2;
char *u;
@@ -1049,12 +1050,67 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
* XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
* after the hostname
*/
+ if (r->proxyreq != PROXYREQ_REVERSE) {
+ return url;
+ }
l1 = strlen(url);
- ent = (struct proxy_alias *)conf->raliases->elts;
+ if (conf->interpolate_env == 1) {
+ rconf = ap_get_module_config(r->request_config, &proxy_module);
+ ent = (struct proxy_alias *)rconf->raliases->elts;
+ }
+ else {
+ ent = (struct proxy_alias *)conf->raliases->elts;
+ }
for (i = 0; i < conf->raliases->nelts; i++) {
- l2 = strlen(ent[i].real);
- if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) {
+ proxy_server_conf *sconf = (proxy_server_conf *)
+ ap_get_module_config(r->server->module_config, &proxy_module);
+ proxy_balancer *balancer;
+ const char *real;
+ real = ent[i].real;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: real: %s", real);
+ /*
+ * First check if mapping against a balancer and see
+ * if we have such a entity. If so, then we need to
+ * find the particulars of the actual worker which may
+ * or may not be the right one... basically, we need
+ * to find which member actually handled this request.
+ */
+ if ((strncasecmp(real, "balancer:", 9) == 0) &&
+ (balancer = ap_proxy_get_balancer(r->pool, sconf, real))) {
+ int n;
+ proxy_worker *worker;
+ worker = (proxy_worker *)balancer->workers->elts;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: checking balancer: %s",
+ balancer->name);
+ for (n = 0; n < balancer->workers->nelts; n++) {
+ if (worker->port) {
+ u = apr_psprintf(r->pool, "%s://%s:%d/", worker->scheme,
+ worker->hostname, worker->port);
+ }
+ else {
+ u = apr_psprintf(r->pool, "%s://%s/", worker->scheme,
+ worker->hostname);
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: matching member (%s) and URL (%s)",
+ u, url);
+
+ l2 = strlen(u);
+ if (l1 >= l2 && strncasecmp(u, url, l2) == 0) {
+ u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: matched member (%s)", u);
+ return ap_construct_url(r->pool, u, r);
+ }
+ worker++;
+ }
+ }
+
+ l2 = strlen(real);
+ if (l1 >= l2 && strncasecmp(real, url, l2) == 0) {
u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
return ap_construct_url(r->pool, u, r);
}
@@ -1073,6 +1129,8 @@ PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
proxy_dir_conf *conf, const char *str)
{
+ proxy_req_conf *rconf = ap_get_module_config(r->request_config,
+ &proxy_module);
struct proxy_alias *ent;
size_t len = strlen(str);
const char *newpath = NULL;
@@ -1087,6 +1145,10 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
int pdiff = 0;
char *ret;
+ if (r->proxyreq != PROXYREQ_REVERSE) {
+ return str;
+ }
+
/*
* Find the match and replacement, but save replacing until we've done
* both path and domain so we know the new strlen
@@ -1097,7 +1159,12 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
pathe = ap_strchr_c(pathp, ';');
l1 = pathe ? (pathe - pathp) : strlen(pathp);
pathe = pathp + l1 ;
- ent = (struct proxy_alias *)conf->cookie_paths->elts;
+ if (conf->interpolate_env == 1) {
+ ent = (struct proxy_alias *)rconf->cookie_paths->elts;
+ }
+ else {
+ ent = (struct proxy_alias *)conf->cookie_paths->elts;
+ }
for (i = 0; i < conf->cookie_paths->nelts; i++) {
l2 = strlen(ent[i].fake);
if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
@@ -1114,7 +1181,12 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
domaine = ap_strchr_c(domainp, ';');
l1 = domaine ? (domaine - domainp) : strlen(domainp);
domaine = domainp + l1;
- ent = (struct proxy_alias *)conf->cookie_domains->elts;
+ if (conf->interpolate_env == 1) {
+ ent = (struct proxy_alias *)rconf->cookie_domains->elts;
+ }
+ else {
+ ent = (struct proxy_alias *)conf->cookie_domains->elts;
+ }
for (i = 0; i < conf->cookie_domains->nelts; i++) {
l2 = strlen(ent[i].fake);
if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
@@ -1323,6 +1395,7 @@ static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
* it can be disabled.
*/
apr_pool_create(&pool, p);
+ apr_pool_tag(pool, "proxy_worker_cp");
/*
* Alloc from the same pool as worker.
* proxy_conn_pool is permanently attached to the worker.
@@ -1550,6 +1623,9 @@ static apr_status_t connection_cleanup(void *theconn)
{
proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
proxy_worker *worker = conn->worker;
+ apr_bucket_brigade *bb;
+ conn_rec *c;
+ request_rec *r;
/*
* If the connection pool is NULL the worker
@@ -1570,13 +1646,67 @@ static apr_status_t connection_cleanup(void *theconn)
}
#endif
+ r = conn->r;
+ if (conn->need_flush && r && (r->bytes_sent || r->eos_sent)) {
+ /*
+ * We need to ensure that buckets that may have been buffered in the
+ * network filters get flushed to the network. This is needed since
+ * these buckets have been created with the bucket allocator of the
+ * backend connection. This allocator either gets destroyed if
+ * conn->close is set or the worker address is not reusable which
+ * causes the connection to the backend to be closed or it will be used
+ * again by another frontend connection that wants to recycle the
+ * backend connection.
+ * In this case we could run into nasty race conditions (e.g. if the
+ * next user of the backend connection destroys the allocator before we
+ * sent the buckets to the network).
+ *
+ * Remark 1: Only do this if buckets where sent down the chain before
+ * that could still be buffered in the network filter. This is the case
+ * if we have sent an EOS bucket or if we actually sent buckets with
+ * data down the chain. In all other cases we either have not sent any
+ * buckets at all down the chain or we only sent meta buckets that are
+ * not EOS buckets down the chain. The only meta bucket that remains in
+ * this case is the flush bucket which would have removed all possibly
+ * buffered buckets in the network filter.
+ * If we sent a flush bucket in the case where not ANY buckets were
+ * sent down the chain, we break error handling which happens AFTER us.
+ *
+ * Remark 2: Doing a setaside does not help here as the buckets remain
+ * created by the wrong allocator in this case.
+ *
+ * Remark 3: Yes, this creates a possible performance penalty in the case
+ * of pipelined requests as we may send only a small amount of data over
+ * the wire.
+ */
+ c = r->connection;
+ bb = apr_brigade_create(r->pool, c->bucket_alloc);
+ if (r->eos_sent) {
+ /*
+ * If we have already sent an EOS bucket send directly to the
+ * connection based filters. We just want to flush the buckets
+ * if something hasn't been sent to the network yet.
+ */
+ ap_fflush(c->output_filters, bb);
+ }
+ else {
+ ap_fflush(r->output_filters, bb);
+ }
+ apr_brigade_destroy(bb);
+ conn->r = NULL;
+ conn->need_flush = 0;
+ }
+
/* determine if the connection need to be closed */
- if (conn->close_on_recycle || conn->close) {
+ if (conn->close_on_recycle || conn->close || worker->disablereuse ||
+ !worker->is_address_reusable) {
apr_pool_t *p = conn->pool;
- apr_pool_clear(conn->pool);
- memset(conn, 0, sizeof(proxy_conn_rec));
+ apr_pool_clear(p);
+ conn = apr_pcalloc(p, sizeof(proxy_conn_rec));
conn->pool = p;
conn->worker = worker;
+ apr_pool_create(&(conn->scpool), p);
+ apr_pool_tag(conn->scpool, "proxy_conn_scpool");
}
#if APR_HAS_THREADS
if (worker->hmax && worker->cp->res) {
@@ -1593,11 +1723,54 @@ static apr_status_t connection_cleanup(void *theconn)
return APR_SUCCESS;
}
+static void socket_cleanup(proxy_conn_rec *conn)
+{
+ conn->sock = NULL;
+ conn->connection = NULL;
+ apr_pool_clear(conn->scpool);
+}
+
+PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
+ request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ apr_status_t rv;
+
+ /*
+ * If we have an existing SSL connection it might be possible that the
+ * server sent some SSL message we have not read so far (e.g. a SSL
+ * shutdown message if the server closed the keepalive connection while
+ * the connection was held unused in our pool).
+ * So ensure that if present (=> APR_NONBLOCK_READ) it is read and
+ * processed. We don't expect any data to be in the returned brigade.
+ */
+ if (conn->sock && conn->connection) {
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ rv = ap_get_brigade(conn->connection->input_filters, bb,
+ AP_MODE_READBYTES, APR_NONBLOCK_READ,
+ HUGE_STRING_LEN);
+ if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
+ socket_cleanup(conn);
+ }
+ if (!APR_BRIGADE_EMPTY(bb)) {
+ apr_off_t len;
+
+ rv = apr_brigade_length(bb, 0, &len);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r,
+ "proxy: SSL cleanup brigade contained %"
+ APR_OFF_T_FMT " bytes of data.", len);
+ }
+ apr_brigade_destroy(bb);
+ }
+ return APR_SUCCESS;
+}
+
/* reslist constructor */
static apr_status_t connection_constructor(void **resource, void *params,
apr_pool_t *pool)
{
apr_pool_t *ctx;
+ apr_pool_t *scpool;
proxy_conn_rec *conn;
proxy_worker *worker = (proxy_worker *)params;
@@ -1607,9 +1780,20 @@ static apr_status_t connection_constructor(void **resource, void *params,
* when disconnecting from backend.
*/
apr_pool_create(&ctx, pool);
- conn = apr_pcalloc(pool, sizeof(proxy_conn_rec));
+ apr_pool_tag(ctx, "proxy_conn_pool");
+ /*
+ * Create another subpool that manages the data for the
+ * socket and the connection member of the proxy_conn_rec struct as we
+ * destroy this data more frequently than other data in the proxy_conn_rec
+ * struct like hostname and addr (at least in the case where we have
+ * keepalive connections that timed out).
+ */
+ apr_pool_create(&scpool, ctx);
+ apr_pool_tag(scpool, "proxy_conn_scpool");
+ conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec));
conn->pool = ctx;
+ conn->scpool = scpool;
conn->worker = worker;
#if APR_HAS_THREADS
conn->inreslist = 1;
@@ -1725,8 +1909,13 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, ser
if (!worker->retry_set) {
worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
}
- /* By default address is reusable */
- worker->is_address_reusable = 1;
+ /* By default address is reusable unless DisableReuse is set */
+ if (worker->disablereuse) {
+ worker->is_address_reusable = 0;
+ }
+ else {
+ worker->is_address_reusable = 1;
+ }
#if APR_HAS_THREADS
ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
@@ -1873,11 +2062,6 @@ PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: has released connection for (%s)",
proxy_function, conn->worker->hostname);
- /* If there is a connection kill it's cleanup */
- if (conn->connection) {
- apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
- conn->connection = NULL;
- }
connection_cleanup(conn);
return OK;
@@ -1899,6 +2083,8 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
apr_status_t err = APR_SUCCESS;
apr_status_t uerr = APR_SUCCESS;
+ conn->r = r;
+
/*
* Break up the URL to determine the host to connect to
*/
@@ -1938,7 +2124,8 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
*
* TODO: Handle this much better...
*/
- if (!conn->hostname || !worker->is_address_reusable ||
+ if (!conn->hostname || !worker->is_address_reusable ||
+ worker->disablereuse ||
(r->connection->keepalives &&
(r->proxyreq == PROXYREQ_PROXY || r->proxyreq == PROXYREQ_REVERSE) &&
(strcasecmp(conn->hostname, uri->hostname) != 0) ) ) {
@@ -1950,14 +2137,7 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
conn->port = uri->port;
}
- if (conn->sock) {
- apr_socket_close(conn->sock);
- conn->sock = NULL;
- }
- if (conn->connection) {
- apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
- conn->connection = NULL;
- }
+ socket_cleanup(conn);
err = apr_sockaddr_info_get(&(conn->addr),
conn->hostname, APR_UNSPEC,
conn->port, 0,
@@ -2101,14 +2281,8 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
(proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
if (conn->sock) {
- /*
- * This increases the connection pool size
- * but the number of dropped connections is
- * relatively small compared to connection lifetime
- */
if (!(connected = is_socket_connected(conn->sock))) {
- apr_socket_close(conn->sock);
- conn->sock = NULL;
+ socket_cleanup(conn);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: backend socket is disconnected.",
proxy_function);
@@ -2117,7 +2291,7 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
while (backend_addr && !connected) {
if ((rv = apr_socket_create(&newsock, backend_addr->family,
SOCK_STREAM, APR_PROTO_TCP,
- conn->pool)) != APR_SUCCESS) {
+ conn->scpool)) != APR_SUCCESS) {
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
ap_log_error(APLOG_MARK, loglevel, rv, s,
"proxy: %s: error creating fam %d socket for target %s",
@@ -2132,6 +2306,7 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
backend_addr = backend_addr->next;
continue;
}
+ conn->connection = NULL;
#if !defined(TPF) && !defined(BEOS)
if (worker->recv_buffer_size > 0 &&
@@ -2221,13 +2396,25 @@ PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
apr_sockaddr_t *backend_addr = conn->addr;
int rc;
apr_interval_time_t current_timeout;
+ apr_bucket_alloc_t *bucket_alloc;
+
+ if (conn->connection) {
+ return OK;
+ }
/*
+ * We need to flush the buckets before we return the connection to the
+ * connection pool. See comment in connection_cleanup for why this is
+ * needed.
+ */
+ conn->need_flush = 1;
+ bucket_alloc = apr_bucket_alloc_create(conn->scpool);
+ /*
* The socket is now open, create a new backend server connection
*/
- conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
- c->id, c->sbh,
- c->bucket_alloc);
+ conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock,
+ 0, NULL,
+ bucket_alloc);
if (!conn->connection) {
/*
@@ -2239,17 +2426,9 @@ PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
"new connection to %pI (%s)", proxy_function,
backend_addr, conn->hostname);
/* XXX: Will be closed when proxy_conn is closed */
- apr_socket_close(conn->sock);
- conn->sock = NULL;
+ socket_cleanup(conn);
return HTTP_INTERNAL_SERVER_ERROR;
}
- /*
- * register the connection cleanup to client connection
- * so that the connection can be closed or reused
- */
- apr_pool_cleanup_register(c->pool, (void *)conn,
- connection_cleanup,
- apr_pool_cleanup_null);
/* For ssl connection to backend */
if (conn->is_ssl) {