diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c index 6061e53..75c2a35 100644 --- a/modules/mappers/mod_rewrite.c +++ b/modules/mappers/mod_rewrite.c @@ -4120,6 +4120,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) r->filename)); r->filename = apr_pstrcat(r->pool, "proxy:", r->filename, NULL); + apr_table_setn(r->notes, "rewrite-proxy", "1"); return 1; } diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index b9d71fa..7f96aff 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -1348,7 +1348,6 @@ static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) return new; } - static const char * add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex) { @@ -1423,6 +1422,36 @@ static const char * return add_proxy(cmd, dummy, f1, r1, 1); } +static char *de_socketfy(apr_pool_t *p, char *url) +{ + char *ptr; + /* + * We could be passed a URL during the config stage that contains + * the UDS path... ignore it + */ + if (!strncasecmp(url, "unix:", 5) && + ((ptr = ap_strchr(url, '|')) != NULL)) { + /* move past the 'unix:...|' UDS path info */ + char *ret, *c; + + ret = ptr + 1; + /* special case: "unix:....|scheme:" is OK, expand + * to "unix:....|scheme://localhost" + * */ + c = ap_strchr(ret, ':'); + if (c == NULL) { + return NULL; + } + if (c[1] == '\0') { + return apr_pstrcat(p, ret, "//localhost", NULL); + } + else { + return ret; + } + } + return url; +} + static const char * add_pass(cmd_parms *cmd, void *dummy, const char *arg, int is_regex) { @@ -1514,7 +1543,7 @@ static const char * } new->fake = apr_pstrdup(cmd->pool, f); - new->real = apr_pstrdup(cmd->pool, r); + new->real = apr_pstrdup(cmd->pool, de_socketfy(cmd->pool, r)); new->flags = flags; if (use_regex) { new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED); @@ -1550,26 +1579,41 @@ static const char * new->balancer = balancer; } else { - proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, r); + proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, new->real); int reuse = 0; if (!worker) { - const char *err = ap_proxy_define_worker(cmd->pool, &worker, NULL, conf, r, 0); + const char *err; + if (use_regex) { + err = ap_proxy_define_match_worker(cmd->pool, &worker, NULL, + conf, r, 0); + } + else { + err = ap_proxy_define_worker(cmd->pool, &worker, NULL, + conf, r, 0); + } if (err) return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL); PROXY_COPY_CONF_PARAMS(worker, conf); - } else { + } + else if ((use_regex != 0) ^ (worker->s->is_name_matchable)) { + return apr_pstrcat(cmd->temp_pool, "ProxyPass/ and " + "ProxyPassMatch/ can't be used " + "altogether with the same worker name ", + "(", worker->s->name, ")", NULL); + } + else { reuse = 1; ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, APLOGNO(01145) "Sharing worker '%s' instead of creating new worker '%s'", - worker->s->name, new->real); + ap_proxy_worker_name(cmd->pool, worker), new->real); } for (i = 0; i < arr->nelts; i++) { if (reuse) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(01146) "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing", - elts[i].key, elts[i].val, worker->s->name); + elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker)); } else { const char *err = set_worker_param(cmd->pool, worker, elts[i].key, elts[i].val); @@ -2026,7 +2070,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) } /* Try to find existing worker */ - worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, name); + worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, de_socketfy(cmd->temp_pool, name)); if (!worker) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01147) "Defining worker '%s' for balancer '%s'", @@ -2035,13 +2079,13 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01148) "Defined worker '%s' for balancer '%s'", - worker->s->name, balancer->s->name); + ap_proxy_worker_name(cmd->pool, worker), balancer->s->name); PROXY_COPY_CONF_PARAMS(worker, conf); } else { reuse = 1; ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, APLOGNO(01149) "Sharing worker '%s' instead of creating new worker '%s'", - worker->s->name, name); + ap_proxy_worker_name(cmd->pool, worker), name); } arr = apr_table_elts(params); @@ -2050,7 +2094,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) if (reuse) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(01150) "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing", - elts[i].key, elts[i].val, worker->s->name); + elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker)); } else { err = set_worker_param(cmd->pool, worker, elts[i].key, elts[i].val); @@ -2112,7 +2156,7 @@ static const char * } } else { - worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, name); + worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, de_socketfy(cmd->temp_pool, name)); if (!worker) { if (in_proxy_section) { err = ap_proxy_define_worker(cmd->pool, &worker, NULL, @@ -2170,6 +2214,7 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) char *word, *val; proxy_balancer *balancer = NULL; proxy_worker *worker = NULL; + int use_regex = 0; const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE); proxy_server_conf *sconf = @@ -2219,6 +2264,7 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) if (!r) { return "Regex could not be compiled"; } + use_regex = 1; } /* initialize our config and fetch it */ @@ -2258,14 +2304,26 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) } else { worker = ap_proxy_get_worker(cmd->temp_pool, NULL, sconf, - conf->p); + de_socketfy(cmd->temp_pool, (char*)conf->p)); if (!worker) { - err = ap_proxy_define_worker(cmd->pool, &worker, NULL, - sconf, conf->p, 0); + if (use_regex) { + err = ap_proxy_define_match_worker(cmd->pool, &worker, NULL, + sconf, conf->p, 0); + } + else { + err = ap_proxy_define_worker(cmd->pool, &worker, NULL, + sconf, conf->p, 0); + } if (err) return apr_pstrcat(cmd->temp_pool, thiscmd->name, " ", err, NULL); } + else if ((use_regex != 0) ^ (worker->s->is_name_matchable)) { + return apr_pstrcat(cmd->temp_pool, "ProxyPass/ and " + "ProxyPassMatch/ can't be used " + "altogether with the same worker name ", + "(", worker->s->name, ")", NULL); + } } if (worker == NULL && balancer == NULL) { return apr_pstrcat(cmd->pool, thiscmd->name, @@ -2570,6 +2628,8 @@ static void child_init(apr_pool_t *p, server_rec *s) ap_proxy_hashfunc(conf->forward->s->name, PROXY_HASHFUNC_FNV); /* Do not disable worker in case of errors */ conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS; + /* Mark as the "generic" worker */ + conf->forward->s->status |= PROXY_WORKER_GENERIC; ap_proxy_initialize_worker(conf->forward, s, conf->pool); /* Disable address cache for generic forward worker */ conf->forward->s->is_address_reusable = 0; @@ -2585,6 +2645,8 @@ static void child_init(apr_pool_t *p, server_rec *s) ap_proxy_hashfunc(reverse->s->name, PROXY_HASHFUNC_FNV); /* Do not disable worker in case of errors */ reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS; + /* Mark as the "generic" worker */ + reverse->s->status |= PROXY_WORKER_GENERIC; conf->reverse = reverse; ap_proxy_initialize_worker(conf->reverse, s, conf->pool); /* Disable address cache for generic reverse worker */ diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 81fd14c..4fb21c7 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -249,6 +249,7 @@ typedef struct { unsigned int need_flush:1; /* Flag to decide whether we need to flush the * filter chain or not */ unsigned int inreslist:1; /* connection in apr_reslist? */ + const char *uds_path; /* Unix domain socket path */ } proxy_conn_rec; typedef struct { @@ -269,6 +270,7 @@ struct proxy_conn_pool { #define PROXY_WORKER_INITIALIZED 0x0001 #define PROXY_WORKER_IGNORE_ERRORS 0x0002 #define PROXY_WORKER_DRAIN 0x0004 +#define PROXY_WORKER_GENERIC 0x0008 #define PROXY_WORKER_IN_SHUTDOWN 0x0010 #define PROXY_WORKER_DISABLED 0x0020 #define PROXY_WORKER_STOPPED 0x0040 @@ -280,6 +282,7 @@ struct proxy_conn_pool { #define PROXY_WORKER_INITIALIZED_FLAG 'O' #define PROXY_WORKER_IGNORE_ERRORS_FLAG 'I' #define PROXY_WORKER_DRAIN_FLAG 'N' +#define PROXY_WORKER_GENERIC_FLAG 'G' #define PROXY_WORKER_IN_SHUTDOWN_FLAG 'U' #define PROXY_WORKER_DISABLED_FLAG 'D' #define PROXY_WORKER_STOPPED_FLAG 'S' @@ -300,6 +303,8 @@ PROXY_WORKER_DISABLED | PROXY_WORKER_STOPPED | PROXY_WORKER_IN_ERROR ) #define PROXY_WORKER_IS_DRAINING(f) ( (f)->s->status & PROXY_WORKER_DRAIN ) +#define PROXY_WORKER_IS_GENERIC(f) ( (f)->s->status & PROXY_WORKER_GENERIC ) + /* default worker retry timeout in seconds */ #define PROXY_WORKER_DEFAULT_RETRY 60 @@ -341,6 +346,7 @@ typedef struct { char route[PROXY_WORKER_MAX_ROUTE_SIZE]; /* balancing route */ char redirect[PROXY_WORKER_MAX_ROUTE_SIZE]; /* temporary balancing redirection route */ char flusher[PROXY_WORKER_MAX_SCHEME_SIZE]; /* flush provider used by mod_proxy_fdpass */ + char uds_path[PROXY_WORKER_MAX_NAME_SIZE]; /* path to worker's unix domain socket if applicable */ int lbset; /* load balancer cluster set */ int retries; /* number of retries on this worker */ int lbstatus; /* Current lbstatus */ @@ -387,6 +393,7 @@ typedef struct { unsigned int keepalive_set:1; unsigned int disablereuse_set:1; unsigned int was_malloced:1; + unsigned int is_name_matchable:1; } proxy_worker_shared; #define ALIGNED_PROXY_WORKER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_worker_shared))) @@ -586,6 +593,16 @@ typedef __declspec(dllimport) const char * /* Connection pool API */ /** + * Return the user-land, UDS aware worker name + * @param p memory pool used for displaying worker name + * @param worker the worker + * @return name + */ + +PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p, + proxy_worker *worker); + +/** * Get the worker from proxy configuration * @param p memory pool used for finding worker * @param balancer the balancer that the worker belongs to @@ -615,6 +632,24 @@ PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, int do_malloc); /** + * Define and Allocate space for the ap_strcmp_match()able worker to proxy + * configuration. + * @param p memory pool to allocate worker from + * @param worker the new worker + * @param balancer the balancer that the worker belongs to + * @param conf current proxy server configuration + * @param url url containing worker name (produces match pattern) + * @param do_malloc true if shared struct should be malloced + * @return error message or NULL if successful (*worker is new worker) + */ +PROXY_DECLARE(char *) ap_proxy_define_match_worker(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + int do_malloc); + +/** * Share a defined proxy worker via shm * @param worker worker to be shared * @param shm location of shared info @@ -983,6 +1018,13 @@ APR_DECLARE_OPTIONAL_FN(int, ap_proxy_clear_connection, */ int ap_proxy_lb_workers(void); +/** + * Return the port number of a known scheme (eg: http -> 80). + * @param scheme scheme to test + * @return port number or 0 if unknown + */ +PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme); + extern module PROXY_DECLARE_DATA proxy_module; #endif /*MOD_PROXY_H*/ diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c index 3736156..cf52a7d 100644 --- a/modules/proxy/mod_proxy_ajp.c +++ b/modules/proxy/mod_proxy_ajp.c @@ -32,7 +32,7 @@ static int proxy_ajp_canon(request_rec *r, char *url) char *host, *path, sport[7]; char *search = NULL; const char *err; - apr_port_t port = AJP13_DEF_PORT; + apr_port_t port, def_port; /* ap_port_of_scheme() */ if (strncasecmp(url, "ajp:", 4) == 0) { @@ -48,6 +48,8 @@ static int proxy_ajp_canon(request_rec *r, char *url) * do syntactic check. * We break the URL into host, port, path, search */ + port = def_port = ap_proxy_port_of_scheme("ajp"); + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); if (err) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00867) "error parsing URL %s: %s", @@ -71,7 +73,10 @@ static int proxy_ajp_canon(request_rec *r, char *url) if (path == NULL) return HTTP_BAD_REQUEST; - apr_snprintf(sport, sizeof(sport), ":%d", port); + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c index 0f45be7..514b8d8 100644 --- a/modules/proxy/mod_proxy_balancer.c +++ b/modules/proxy/mod_proxy_balancer.c @@ -118,7 +118,8 @@ static void init_balancer_members(apr_pool_t *p, server_rec *s, int worker_is_initialized; proxy_worker *worker = *workers; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01158) - "Looking at %s -> %s initialized?", balancer->s->name, worker->s->name); + "Looking at %s -> %s initialized?", balancer->s->name, + ap_proxy_worker_name(p, worker)); worker_is_initialized = PROXY_WORKER_IS_INITIALIZED(worker); if (!worker_is_initialized) { ap_proxy_initialize_worker(worker, s, p); @@ -638,10 +639,11 @@ static int proxy_balancer_post_request(proxy_worker *worker, int val = ((int *)balancer->errstatuses->elts)[i]; if (r->status == val) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01174) - "%s: Forcing worker (%s) into error state " + "%s: Forcing worker (%s) into error state " "due to status code %d matching 'failonstatus' " "balancer parameter", - balancer->s->name, worker->s->name, val); + balancer->s->name, ap_proxy_worker_name(r->pool, worker), + val); worker->s->status |= PROXY_WORKER_IN_ERROR; worker->s->error_time = apr_time_now(); break; @@ -654,7 +656,7 @@ static int proxy_balancer_post_request(proxy_worker *worker, ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02460) "%s: Forcing worker (%s) into error state " "due to timeout and 'failonstatus' parameter being set", - balancer->s->name, worker->s->name); + balancer->s->name, ap_proxy_worker_name(r->pool, worker)); worker->s->status |= PROXY_WORKER_IN_ERROR; worker->s->error_time = apr_time_now(); @@ -1282,7 +1284,7 @@ static int balancer_handler(request_rec *r) worker = *workers; /* Start proxy_worker */ ap_rputs(" \n", r); - ap_rvputs(r, " ", worker->s->name, + ap_rvputs(r, " ", ap_proxy_worker_name(r->pool, worker), "\n", NULL); ap_rvputs(r, " ", worker->s->scheme, "\n", NULL); @@ -1524,7 +1526,8 @@ static int balancer_handler(request_rec *r) ap_escape_uri(r->pool, worker->s->name), "&nonce=", balancer->s->nonce, "\">", NULL); - ap_rvputs(r, worker->s->name, "", NULL); + ap_rvputs(r, (*worker->s->uds_path ? "" : ""), ap_proxy_worker_name(r->pool, worker), + (*worker->s->uds_path ? "" : ""), "", NULL); ap_rvputs(r, "", ap_escape_html(r->pool, worker->s->route), NULL); ap_rvputs(r, "", @@ -1549,7 +1552,7 @@ static int balancer_handler(request_rec *r) ap_rputs("
\n", r); if (wsel && bsel) { ap_rputs("

Edit worker settings for ", r); - ap_rvputs(r, wsel->s->name, "

\n", NULL); + ap_rvputs(r, (*wsel->s->uds_path?"":""), ap_proxy_worker_name(r->pool, wsel), (*wsel->s->uds_path?"":""), "\n", NULL); ap_rputs("
pool, action), "\">\n", NULL); ap_rputs("
\n
Load factor:pool, &url, NULL, NULL, &host, &port); if (err) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01059) @@ -96,7 +97,10 @@ static int proxy_fcgi_canon(request_rec *r, char *url) return HTTP_BAD_REQUEST; } - apr_snprintf(sport, sizeof(sport), ":%d", port); + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ @@ -930,7 +934,7 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, int status; char server_portstr[32]; conn_rec *origin = NULL; - proxy_conn_rec *backend = NULL; + proxy_conn_rec *backend; proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module); @@ -943,10 +947,7 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, "url: %s proxyname: %s proxyport: %d", url, proxyname, proxyport); - if (strncasecmp(url, "fcgi:", 5) == 0) { - url += 5; - } - else { + if (strncasecmp(url, "fcgi:", 5) != 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url); return DECLINED; } @@ -954,16 +955,14 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01078) "serving URL %s", url); /* Create space for state information */ - if (! backend) { - status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker, - r->server); - if (status != OK) { - if (backend) { - backend->close = 1; - ap_proxy_release_connection(FCGI_SCHEME, backend, r->server); - } - return status; + status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker, + r->server); + if (status != OK) { + if (backend) { + backend->close = 1; + ap_proxy_release_connection(FCGI_SCHEME, backend, r->server); } + return status; } backend->is_ssl = 0; diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index 05f33b4..f0bb0ed 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -54,7 +54,7 @@ static int proxy_http_canon(request_rec *r, char *url) else { return DECLINED; } - def_port = apr_uri_port_of_scheme(scheme); + port = def_port = ap_proxy_port_of_scheme(scheme); ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "HTTP: canonicalising URL %s", url); @@ -62,7 +62,6 @@ static int proxy_http_canon(request_rec *r, char *url) /* do syntatic check. * We break the URL into host, port, path, search */ - port = def_port; err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); if (err) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01083) diff --git a/modules/proxy/mod_proxy_scgi.c b/modules/proxy/mod_proxy_scgi.c index f77a986..6deae78 100644 --- a/modules/proxy/mod_proxy_scgi.c +++ b/modules/proxy/mod_proxy_scgi.c @@ -176,13 +176,15 @@ static int scgi_canon(request_rec *r, char *url) { char *host, sport[sizeof(":65535")]; const char *err, *path; - apr_port_t port = SCGI_DEFAULT_PORT; + apr_port_t port, def_port; if (strncasecmp(url, SCHEME "://", sizeof(SCHEME) + 2)) { return DECLINED; } url += sizeof(SCHEME); /* Keep slashes */ + port = def_port = SCGI_DEFAULT_PORT; + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); if (err) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00857) @@ -190,7 +192,12 @@ static int scgi_canon(request_rec *r, char *url) return HTTP_BAD_REQUEST; } - apr_snprintf(sport, sizeof(sport), ":%u", port); + if (port != def_port) { + apr_snprintf(sport, sizeof(sport), ":%u", port); + } + else { + sport[0] = '\0'; + } if (ap_strchr(host, ':')) { /* if literal IPv6 address */ host = apr_pstrcat(r->pool, "[", host, "]", NULL); diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 8bc9fab..dea2b10 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -21,6 +21,7 @@ #include "apr_version.h" #include "apr_hash.h" #include "proxy_util.h" +#include "ajp.h" #if APR_HAVE_UNISTD_H #include /* for getpid() */ @@ -31,6 +32,13 @@ #define apr_socket_create apr_socket_create_ex #endif +#if APR_HAVE_SYS_UN_H +#include +#endif +#if (APR_MAJOR_VERSION < 2) +#include "apr_support.h" /* for apr_wait_for_io_or_timeout() */ +#endif + APLOG_USE_MODULE(proxy); /* @@ -86,14 +94,20 @@ PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src, char *thenil; apr_size_t thelen; + /* special case handling */ + if (!dlen) { + /* XXX: APR_ENOSPACE would be better */ + return APR_EGENERAL; + } + if (!src) { + *dst = '\0'; + return APR_SUCCESS; + } thenil = apr_cpystrn(dst, src, dlen); thelen = thenil - dst; - /* Assume the typical case is smaller copying into bigger - so we have a fast return */ - if ((thelen < dlen-1) || ((strlen(src)) == thelen)) { + if (src[thelen] == '\0') { return APR_SUCCESS; } - /* XXX: APR_ENOSPACE would be better */ return APR_EGENERAL; } @@ -1218,11 +1232,11 @@ PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer, } else { action = "re-using"; } + balancer->s = shm; + balancer->s->index = i; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02337) "%s shm[%d] (0x%pp) for %s", action, i, (void *)shm, balancer->s->name); - balancer->s = shm; - balancer->s->index = i; /* the below should always succeed */ lbmethod = ap_lookup_provider(PROXY_LBMETHOD, balancer->s->lbpname, "0"); if (lbmethod) { @@ -1356,7 +1370,7 @@ static apr_status_t connection_cleanup(void *theconn) ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923) "Pooled connection 0x%pp for worker %s has been" " already returned to the connection pool.", conn, - worker->s->name); + ap_proxy_worker_name(conn->pool, worker)); return APR_SUCCESS; } @@ -1480,6 +1494,55 @@ static apr_status_t connection_destructor(void *resource, void *params, * WORKER related... */ +PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p, + proxy_worker *worker) +{ + if (!(*worker->s->uds_path) || !p) { + /* just in case */ + return worker->s->name; + } + return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name, NULL); +} + +/* + * Taken from ap_strcmp_match() : + * Match = 0, NoMatch = 1, Abort = -1, Inval = -2 + * Based loosely on sections of wildmat.c by Rich Salz + * Hmmm... shouldn't this really go component by component? + * + * Adds handling of the "\" => "" unescaping. + */ +static int ap_proxy_strcmp_ematch(const char *str, const char *expected) +{ + apr_size_t x, y; + + for (x = 0, y = 0; expected[y]; ++y, ++x) { + if ((!str[x]) && (expected[y] != '$' || !apr_isdigit(expected[y + 1]))) + return -1; + if (expected[y] == '$' && apr_isdigit(expected[y + 1])) { + while (expected[y] == '$' && apr_isdigit(expected[y + 1])) + y += 2; + if (!expected[y]) + return 0; + while (str[x]) { + int ret; + if ((ret = ap_proxy_strcmp_ematch(&str[x++], &expected[y])) != 1) + return ret; + } + return -1; + } + else if (expected[y] == '\\') { + /* NUL is an invalid char! */ + if (!expected[++y]) + return -2; + } + if (str[x] != expected[y]) + return 1; + } + /* We got all the way through the worker path without a difference */ + return 0; +} + PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, proxy_balancer *balancer, proxy_server_conf *conf, @@ -1495,6 +1558,10 @@ PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, char *url_copy; int i; + if (!url) { + return NULL; + } + c = ap_strchr_c(url, ':'); if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { return NULL; @@ -1536,11 +1603,15 @@ PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, if ( ((worker_name_length = strlen(worker->s->name)) <= url_length) && (worker_name_length >= min_match) && (worker_name_length > max_match) - && (strncmp(url_copy, worker->s->name, worker_name_length) == 0) ) { + && (worker->s->is_name_matchable + || strncmp(url_copy, worker->s->name, + worker_name_length) == 0) + && (!worker->s->is_name_matchable + || ap_proxy_strcmp_ematch(url_copy, + worker->s->name) == 0) ) { max_worker = worker; max_match = worker_name_length; } - } } else { worker = (proxy_worker *)conf->workers->elts; @@ -1548,7 +1619,12 @@ PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, if ( ((worker_name_length = strlen(worker->s->name)) <= url_length) && (worker_name_length >= min_match) && (worker_name_length > max_match) - && (strncmp(url_copy, worker->s->name, worker_name_length) == 0) ) { + && (worker->s->is_name_matchable + || strncmp(url_copy, worker->s->name, + worker_name_length) == 0) + && (!worker->s->is_name_matchable + || ap_proxy_strcmp_ematch(url_copy, + worker->s->name) == 0) ) { max_worker = worker; max_match = worker_name_length; } @@ -1573,20 +1649,47 @@ PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, int do_malloc) { int rv; - apr_uri_t uri; + apr_uri_t uri, urisock; proxy_worker_shared *wshared; - char *ptr; + char *ptr, *sockpath = NULL; + /* + * Look to see if we are using UDS: + * require format: unix:/path/foo/bar.sock|http://ignored/path2/ + * This results in talking http to the socket at /path/foo/bar.sock + */ + ptr = ap_strchr((char *)url, '|'); + if (ptr) { + *ptr = '\0'; + rv = apr_uri_parse(p, url, &urisock); + if (rv == APR_SUCCESS && !strcasecmp(urisock.scheme, "unix")) { + sockpath = ap_runtime_dir_relative(p, urisock.path);; + url = ptr+1; /* so we get the scheme for the uds */ + } + else { + *ptr = '|'; + } + } rv = apr_uri_parse(p, url, &uri); if (rv != APR_SUCCESS) { - return "Unable to parse URL"; + return apr_pstrcat(p, "Unable to parse URL: ", url, NULL); } - if (!uri.hostname || !uri.scheme) { - return "URL must be absolute!"; + if (!uri.scheme) { + return apr_pstrcat(p, "URL must be absolute!: ", url, NULL); + } + /* allow for unix:/path|http: */ + if (!uri.hostname) { + if (sockpath) { + uri.hostname = "localhost"; + } + else { + return apr_pstrcat(p, "URL must be absolute!: ", url, NULL); + } + } + else { + ap_str_tolower(uri.hostname); } - - ap_str_tolower(uri.hostname); ap_str_tolower(uri.scheme); /* * Workers can be associated w/ balancers or on their @@ -1642,6 +1745,16 @@ PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, wshared->hash.def = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_DEFAULT); wshared->hash.fnv = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_FNV); wshared->was_malloced = (do_malloc != 0); + wshared->is_name_matchable = 0; + if (sockpath) { + if (PROXY_STRNCPY(wshared->uds_path, sockpath) != APR_SUCCESS) { + return apr_psprintf(p, "worker uds path (%s) too long", sockpath); + } + + } + else { + *wshared->uds_path = '\0'; + } (*worker)->hash = wshared->hash; (*worker)->context = NULL; @@ -1652,6 +1765,24 @@ PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, return NULL; } +PROXY_DECLARE(char *) ap_proxy_define_match_worker(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + int do_malloc) +{ + char *err; + + err = ap_proxy_define_worker(p, worker, balancer, conf, url, do_malloc); + if (err) { + return err; + } + + (*worker)->s->is_name_matchable = 1; + return NULL; +} + /* * Create an already defined worker and free up memory */ @@ -1670,12 +1801,18 @@ PROXY_DECLARE(apr_status_t) ap_proxy_share_worker(proxy_worker *worker, proxy_wo } else { action = "re-using"; } - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02338) - "%s shm[%d] (0x%pp) for worker: %s", action, i, (void *)shm, - worker->s->name); - worker->s = shm; worker->s->index = i; + { + apr_pool_t *pool; + apr_pool_create(&pool, ap_server_conf->process->pool); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02338) + "%s shm[%d] (0x%pp) for worker: %s", action, i, (void *)shm, + ap_proxy_worker_name(pool, worker)); + if (pool) { + apr_pool_destroy(pool); + } + } return APR_SUCCESS; } @@ -1687,11 +1824,13 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, ser if (worker->s->status & PROXY_WORKER_INITIALIZED) { /* The worker is already initialized */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00924) - "worker %s shared already initialized", worker->s->name); + "worker %s shared already initialized", + ap_proxy_worker_name(p, worker)); } else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00925) - "initializing worker %s shared", worker->s->name); + "initializing worker %s shared", + ap_proxy_worker_name(p, worker)); /* Set default parameters */ if (!worker->s->retry_set) { worker->s->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY); @@ -1727,11 +1866,13 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, ser /* What if local is init'ed and shm isn't?? Even possible? */ if (worker->local_status & PROXY_WORKER_INITIALIZED) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00926) - "worker %s local already initialized", worker->s->name); + "worker %s local already initialized", + ap_proxy_worker_name(p, worker)); } else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927) - "initializing worker %s local", worker->s->name); + "initializing worker %s local", + ap_proxy_worker_name(p, worker)); apr_global_mutex_lock(proxy_mutex); /* Now init local worker data */ if (worker->tmutex == NULL) { @@ -1853,6 +1994,8 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, } else if (r->proxyreq == PROXYREQ_REVERSE) { if (conf->reverse) { + char *ptr; + char *ptr2; ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "*: found reverse proxy worker for %s", *url); *balancer = NULL; @@ -1864,6 +2007,36 @@ PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, * regarding the Connection header in the request. */ apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); + /* + * In the case of the generic reverse proxy, we need to see if we + * were passed a UDS url (eg: from mod_proxy) and adjust uds_path + * as required. + * + * NOTE: Here we use a quick note lookup, but we could also + * check to see if r->filename starts with 'proxy:' + */ + if (apr_table_get(r->notes, "rewrite-proxy") && + (ptr2 = ap_strcasestr(r->filename, "unix:")) && + (ptr = ap_strchr(ptr2, '|'))) { + apr_uri_t urisock; + apr_status_t rv; + *ptr = '\0'; + rv = apr_uri_parse(r->pool, ptr2, &urisock); + if (rv == APR_SUCCESS) { + char *rurl = ptr+1; + char *sockpath = ap_runtime_dir_relative(r->pool, urisock.path); + apr_table_setn(r->notes, "uds_path", sockpath); + *url = apr_pstrdup(r->pool, rurl); /* so we get the scheme for the uds */ + /* r->filename starts w/ "proxy:", so add after that */ + memmove(r->filename+6, rurl, strlen(rurl)+1); + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "*: rewrite of url due to UDS(%s): %s (%s)", + sockpath, *url, r->filename); + } + else { + *ptr = '|'; + } + } } } } @@ -2053,6 +2226,7 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, int server_port; apr_status_t err = APR_SUCCESS; apr_status_t uerr = APR_SUCCESS; + const char *uds_path; /* * Break up the URL to determine the host to connect to @@ -2065,7 +2239,7 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, NULL)); } if (!uri->port) { - uri->port = apr_uri_port_of_scheme(uri->scheme); + uri->port = ap_proxy_port_of_scheme(uri->scheme); } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00944) @@ -2093,73 +2267,117 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, * to check host and port on the conn and be careful about * spilling the cached addr from the worker. */ - if (!conn->hostname || !worker->s->is_address_reusable || - worker->s->disablereuse) { - if (proxyname) { - conn->hostname = apr_pstrdup(conn->pool, proxyname); - conn->port = proxyport; - /* - * If we have a forward proxy and the protocol is HTTPS, - * then we need to prepend a HTTP CONNECT request before - * sending our actual HTTPS requests. - * Save our real backend data for using it later during HTTP CONNECT. - */ - if (conn->is_ssl) { - const char *proxy_auth; - - forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info)); - conn->forward = forward; - forward->use_http_connect = 1; - forward->target_host = apr_pstrdup(conn->pool, uri->hostname); - forward->target_port = uri->port; - /* Do we want to pass Proxy-Authorization along? - * If we haven't used it, then YES - * If we have used it then MAYBE: RFC2616 says we MAY propagate it. - * So let's make it configurable by env. - * The logic here is the same used in mod_proxy_http. - */ - proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization"); - if (proxy_auth != NULL && - proxy_auth[0] != '\0' && - r->user == NULL && /* we haven't yet authenticated */ - apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { - forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth); - } - } + uds_path = (*worker->s->uds_path ? worker->s->uds_path : apr_table_get(r->notes, "uds_path")); + if (uds_path) { + if (conn->uds_path == NULL) { + /* use (*conn)->pool instead of worker->cp->pool to match lifetime */ + conn->uds_path = apr_pstrdup(conn->pool, uds_path); } - else { - conn->hostname = apr_pstrdup(conn->pool, uri->hostname); - conn->port = uri->port; - } - socket_cleanup(conn); - err = apr_sockaddr_info_get(&(conn->addr), - conn->hostname, APR_UNSPEC, - conn->port, 0, - conn->pool); - } - else if (!worker->cp->addr) { - if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock"); - return HTTP_INTERNAL_SERVER_ERROR; + if (conn->uds_path) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02545) + "%s: has determined UDS as %s", + uri->scheme, conn->uds_path); } + else { + /* should never happen */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02546) + "%s: cannot determine UDS (%s)", + uri->scheme, uds_path); + } /* - * Worker can have the single constant backend adress. - * The single DNS lookup is used once per worker. - * If dynamic change is needed then set the addr to NULL - * inside dynamic config to force the lookup. + * In UDS cases, some structs are NULL. Protect from de-refs + * and provide info for logging at the same time. */ - err = apr_sockaddr_info_get(&(worker->cp->addr), - conn->hostname, APR_UNSPEC, - conn->port, 0, - worker->cp->pool); - conn->addr = worker->cp->addr; - if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock"); + if (!conn->addr) { + apr_sockaddr_t *sa; + apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 0, 0, conn->pool); + conn->addr = sa; } + conn->hostname = "httpd-UDS"; + conn->port = 0; } else { - conn->addr = worker->cp->addr; + int will_reuse = worker->s->is_address_reusable && !worker->s->disablereuse; + if (!conn->hostname || !will_reuse) { + if (proxyname) { + conn->hostname = apr_pstrdup(conn->pool, proxyname); + conn->port = proxyport; + /* + * If we have a forward proxy and the protocol is HTTPS, + * then we need to prepend a HTTP CONNECT request before + * sending our actual HTTPS requests. + * Save our real backend data for using it later during HTTP CONNECT. + */ + if (conn->is_ssl) { + const char *proxy_auth; + + forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info)); + conn->forward = forward; + forward->use_http_connect = 1; + forward->target_host = apr_pstrdup(conn->pool, uri->hostname); + forward->target_port = uri->port; + /* Do we want to pass Proxy-Authorization along? + * If we haven't used it, then YES + * If we have used it then MAYBE: RFC2616 says we MAY propagate it. + * So let's make it configurable by env. + * The logic here is the same used in mod_proxy_http. + */ + proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization"); + if (proxy_auth != NULL && + proxy_auth[0] != '\0' && + r->user == NULL && /* we haven't yet authenticated */ + apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { + forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth); + } + } + } + else { + conn->hostname = apr_pstrdup(conn->pool, uri->hostname); + conn->port = uri->port; + } + if (!will_reuse) { + /* + * Only do a lookup if we should not reuse the backend address. + * Otherwise we will look it up once for the worker. + */ + err = apr_sockaddr_info_get(&(conn->addr), + conn->hostname, APR_UNSPEC, + conn->port, 0, + conn->pool); + } + socket_cleanup(conn); + } + if (will_reuse) { + /* + * Looking up the backend address for the worker only makes sense if + * we can reuse the address. + */ + if (!worker->cp->addr) { + if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* + * Worker can have the single constant backend adress. + * The single DNS lookup is used once per worker. + * If dynamic change is needed then set the addr to NULL + * inside dynamic config to force the lookup. + */ + err = apr_sockaddr_info_get(&(worker->cp->addr), + conn->hostname, APR_UNSPEC, + conn->port, 0, + worker->cp->pool); + conn->addr = worker->cp->addr; + if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock"); + } + } + else { + conn->addr = worker->cp->addr; + } + } } /* Close a possible existing socket if we are told to do so */ if (conn->close) { @@ -2360,6 +2578,52 @@ static apr_status_t send_http_connect(proxy_conn_rec *backend, } +#if APR_HAVE_SYS_UN_H +/* lifted from mod_proxy_fdpass.c; tweaked addrlen in connect() call */ +static apr_status_t socket_connect_un(apr_socket_t *sock, + struct sockaddr_un *sa) +{ + apr_status_t rv; + apr_os_sock_t rawsock; + apr_interval_time_t t; + + rv = apr_os_sock_get(&rawsock, sock); + if (rv != APR_SUCCESS) { + return rv; + } + + rv = apr_socket_timeout_get(sock, &t); + if (rv != APR_SUCCESS) { + return rv; + } + + do { + const socklen_t addrlen = APR_OFFSETOF(struct sockaddr_un, sun_path) + + strlen(sa->sun_path) + 1; + rv = connect(rawsock, (struct sockaddr*)sa, addrlen); + } while (rv == -1 && errno == EINTR); + + if ((rv == -1) && (errno == EINPROGRESS || errno == EALREADY) + && (t > 0)) { +#if APR_MAJOR_VERSION < 2 + rv = apr_wait_for_io_or_timeout(NULL, sock, 0); +#else + rv = apr_socket_wait(sock, APR_WAIT_WRITE); +#endif + + if (rv != APR_SUCCESS) { + return rv; + } + } + + if (rv == -1 && errno != EISCONN) { + return errno; + } + + return APR_SUCCESS; +} +#endif + PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, @@ -2384,93 +2648,131 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, proxy_function); } } - while (backend_addr && !connected) { - if ((rv = apr_socket_create(&newsock, backend_addr->family, - SOCK_STREAM, APR_PROTO_TCP, - conn->scpool)) != APR_SUCCESS) { - loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; - ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952) - "%s: error creating fam %d socket for target %s", - proxy_function, - backend_addr->family, - worker->s->hostname); - /* - * this could be an IPv6 address from the DNS but the - * local machine won't give us an IPv6 socket; hopefully the - * DNS returned an additional address to try - */ - backend_addr = backend_addr->next; - continue; - } - conn->connection = NULL; + while ((backend_addr || conn->uds_path) && !connected) { +#if APR_HAVE_SYS_UN_H + if (conn->uds_path) + { + struct sockaddr_un sa; - if (worker->s->recv_buffer_size > 0 && - (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF, - worker->s->recv_buffer_size))) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00953) - "apr_socket_opt_set(SO_RCVBUF): Failed to set " - "ProxyReceiveBufferSize, using default"); - } + rv = apr_socket_create(&newsock, AF_UNIX, SOCK_STREAM, 0, + conn->scpool); + if (rv != APR_SUCCESS) { + loglevel = APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(02453) + "%s: error creating Unix domain socket for " + "target %s", + proxy_function, + worker->s->hostname); + break; + } + conn->connection = NULL; - rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1); - if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00954) - "apr_socket_opt_set(APR_TCP_NODELAY): " - "Failed to set"); - } + sa.sun_family = AF_UNIX; + apr_cpystrn(sa.sun_path, conn->uds_path, sizeof(sa.sun_path)); - /* Set a timeout for connecting to the backend on the socket */ - if (worker->s->conn_timeout_set) { - apr_socket_timeout_set(newsock, worker->s->conn_timeout); - } - else if (worker->s->timeout_set) { - apr_socket_timeout_set(newsock, worker->s->timeout); - } - else if (conf->timeout_set) { - apr_socket_timeout_set(newsock, conf->timeout); - } - else { - apr_socket_timeout_set(newsock, s->timeout); - } - /* Set a keepalive option */ - if (worker->s->keepalive) { - if ((rv = apr_socket_opt_set(newsock, - APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00955) - "apr_socket_opt_set(SO_KEEPALIVE): Failed to set" - " Keepalive"); + rv = socket_connect_un(newsock, &sa); + if (rv != APR_SUCCESS) { + apr_socket_close(newsock); + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02454) + "%s: attempt to connect to Unix domain socket " + "%s (%s) failed", + proxy_function, + conn->uds_path, + worker->s->hostname); + break; } } - ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, - "%s: fam %d socket created to connect to %s", - proxy_function, backend_addr->family, worker->s->hostname); + else +#endif + { + if ((rv = apr_socket_create(&newsock, backend_addr->family, + SOCK_STREAM, APR_PROTO_TCP, + conn->scpool)) != APR_SUCCESS) { + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952) + "%s: error creating fam %d socket for " + "target %s", + proxy_function, + backend_addr->family, + worker->s->hostname); + /* + * this could be an IPv6 address from the DNS but the + * local machine won't give us an IPv6 socket; hopefully the + * DNS returned an additional address to try + */ + backend_addr = backend_addr->next; + continue; + } + conn->connection = NULL; + + if (worker->s->recv_buffer_size > 0 && + (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF, + worker->s->recv_buffer_size))) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00953) + "apr_socket_opt_set(SO_RCVBUF): Failed to set " + "ProxyReceiveBufferSize, using default"); + } - if (conf->source_address_set) { - local_addr = apr_pmemdup(conn->pool, conf->source_address, - sizeof(apr_sockaddr_t)); - local_addr->pool = conn->pool; - rv = apr_socket_bind(newsock, local_addr); - if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00956) - "%s: failed to bind socket to local address", - proxy_function); + rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1); + if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00954) + "apr_socket_opt_set(APR_TCP_NODELAY): " + "Failed to set"); } - } - /* make the connection out of the socket */ - rv = apr_socket_connect(newsock, backend_addr); + /* Set a timeout for connecting to the backend on the socket */ + if (worker->s->conn_timeout_set) { + apr_socket_timeout_set(newsock, worker->s->conn_timeout); + } + else if (worker->s->timeout_set) { + apr_socket_timeout_set(newsock, worker->s->timeout); + } + else if (conf->timeout_set) { + apr_socket_timeout_set(newsock, conf->timeout); + } + else { + apr_socket_timeout_set(newsock, s->timeout); + } + /* Set a keepalive option */ + if (worker->s->keepalive) { + if ((rv = apr_socket_opt_set(newsock, + APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00955) + "apr_socket_opt_set(SO_KEEPALIVE): Failed to set" + " Keepalive"); + } + } + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, + "%s: fam %d socket created to connect to %s", + proxy_function, backend_addr->family, worker->s->hostname); + + if (conf->source_address_set) { + local_addr = apr_pmemdup(conn->pool, conf->source_address, + sizeof(apr_sockaddr_t)); + local_addr->pool = conn->pool; + rv = apr_socket_bind(newsock, local_addr); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00956) + "%s: failed to bind socket to local address", + proxy_function); + } + } - /* if an error occurred, loop round and try again */ - if (rv != APR_SUCCESS) { - apr_socket_close(newsock); - loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; - ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957) - "%s: attempt to connect to %pI (%s) failed", - proxy_function, - backend_addr, - worker->s->hostname); - backend_addr = backend_addr->next; - continue; + /* make the connection out of the socket */ + rv = apr_socket_connect(newsock, backend_addr); + + /* if an error occurred, loop round and try again */ + if (rv != APR_SUCCESS) { + apr_socket_close(newsock); + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957) + "%s: attempt to connect to %pI (%s) failed", + proxy_function, + backend_addr, + worker->s->hostname); + backend_addr = backend_addr->next; + continue; + } } /* Set a timeout on the socket */ @@ -2486,7 +2788,7 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, conn->sock = newsock; - if (conn->forward) { + if (!conn->uds_path && conn->forward) { forward_info *forward = (forward_info *)conn->forward; /* * For HTTP CONNECT we need to prepend CONNECT request before @@ -2767,7 +3069,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec found = 1; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02402) "re-grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm, - worker->s->name); + ap_proxy_worker_name(conf->pool, worker)); break; } } @@ -3201,6 +3503,39 @@ PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, return OK; } +/* Fill in unknown schemes from apr_uri_port_of_scheme() */ + +typedef struct proxy_schemes_t { + const char *name; + apr_port_t default_port; +} proxy_schemes_t ; + +static proxy_schemes_t pschemes[] = +{ + {"fcgi", 8000}, + {"ajp", AJP13_DEF_PORT}, + {"scgi", 4000}, + { NULL, 0xFFFF } /* unknown port */ +}; + +PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme) +{ + if (scheme) { + apr_port_t port; + if ((port = apr_uri_port_of_scheme(scheme)) != 0) { + return port; + } else { + proxy_schemes_t *pscheme; + for (pscheme = pschemes; pscheme->name != NULL; ++pscheme) { + if (strcasecmp(scheme, pscheme->name) == 0) { + return pscheme->default_port; + } + } + } + } + return 0; +} + void proxy_util_register_hooks(apr_pool_t *p) { APR_REGISTER_OPTIONAL_FN(ap_proxy_retry_worker);