diff options
author | Stefan Fritsch <sf@sfritsch.de> | 2013-07-20 22:21:25 +0200 |
---|---|---|
committer | Stefan Fritsch <sf@sfritsch.de> | 2013-07-20 22:21:25 +0200 |
commit | 4a336a5b117419c33c29eadd6409c69df78cd586 (patch) | |
tree | c9787e4bd0f1be8f471e1883262a695a6c4e954f /modules/proxy | |
parent | 717c182588f1eb0b7ef189a709f858b44e348489 (diff) | |
download | apache2-4a336a5b117419c33c29eadd6409c69df78cd586.tar.gz |
Imported Upstream version 2.4.6upstream/2.4.6
Diffstat (limited to 'modules/proxy')
-rw-r--r-- | modules/proxy/NWGNUmakefile | 1 | ||||
-rw-r--r-- | modules/proxy/NWGNUproxy | 4 | ||||
-rw-r--r-- | modules/proxy/NWGNUproxywstunnel | 250 | ||||
-rw-r--r-- | modules/proxy/ajp_utils.c | 2 | ||||
-rw-r--r-- | modules/proxy/balancers/mod_lbmethod_heartbeat.c | 4 | ||||
-rw-r--r-- | modules/proxy/config.m4 | 3 | ||||
-rw-r--r-- | modules/proxy/mod_proxy.c | 110 | ||||
-rw-r--r-- | modules/proxy/mod_proxy.h | 57 | ||||
-rw-r--r-- | modules/proxy/mod_proxy_balancer.c | 20 | ||||
-rw-r--r-- | modules/proxy/mod_proxy_http.c | 364 | ||||
-rw-r--r-- | modules/proxy/mod_proxy_wstunnel.c | 399 | ||||
-rw-r--r-- | modules/proxy/mod_proxy_wstunnel.dsp | 123 | ||||
-rw-r--r-- | modules/proxy/proxy_util.c | 372 |
13 files changed, 1377 insertions, 332 deletions
diff --git a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile index fe491c13..dce99d16 100644 --- a/modules/proxy/NWGNUmakefile +++ b/modules/proxy/NWGNUmakefile @@ -165,6 +165,7 @@ TARGET_nlm = \ $(OBJDIR)/proxylbm_hb.nlm \ $(OBJDIR)/proxylbm_req.nlm \ $(OBJDIR)/proxylbm_traf.nlm \ + $(OBJDIR)/proxywstunnel.nlm \ $(EOLIST) # diff --git a/modules/proxy/NWGNUproxy b/modules/proxy/NWGNUproxy index 3c4f112f..443b4cb2 100644 --- a/modules/proxy/NWGNUproxy +++ b/modules/proxy/NWGNUproxy @@ -251,7 +251,7 @@ install :: nlms FORCE vpath %.c ../arch/netware -$(OBJDIR)/mod_proxy.imp: +$(OBJDIR)/mod_proxy.imp: NWGNUproxy @echo $(DL)GEN $@$(DL) @echo $(DL)# Exports of mod_proxy$(DL)> $@ @echo $(DL) (AP$(VERSION_MAJMIN))$(DL)>> $@ @@ -283,6 +283,7 @@ $(OBJDIR)/mod_proxy.imp: @echo $(DL) ap_proxy_connect_to_backend,$(DL)>> $@ @echo $(DL) ap_proxy_connection_create,$(DL)>> $@ @echo $(DL) ap_proxy_cookie_reverse_map,$(DL)>> $@ + @echo $(DL) ap_proxy_create_hdrbrgd,$(DL)>> $@ @echo $(DL) ap_proxy_define_balancer,$(DL)>> $@ @echo $(DL) ap_proxy_define_worker,$(DL)>> $@ @echo $(DL) ap_proxy_determine_connection,$(DL)>> $@ @@ -300,6 +301,7 @@ $(OBJDIR)/mod_proxy.imp: @echo $(DL) ap_proxy_is_word,$(DL)>> $@ @echo $(DL) ap_proxy_location_reverse_map,$(DL)>> $@ @echo $(DL) ap_proxy_parse_wstatus,$(DL)>> $@ + @echo $(DL) ap_proxy_pass_brigade,$(DL)>> $@ @echo $(DL) ap_proxy_post_request,$(DL)>> $@ @echo $(DL) ap_proxy_pre_http_request,$(DL)>> $@ @echo $(DL) ap_proxy_pre_request,$(DL)>> $@ diff --git a/modules/proxy/NWGNUproxywstunnel b/modules/proxy/NWGNUproxywstunnel new file mode 100644 index 00000000..ce84ce45 --- /dev/null +++ b/modules/proxy/NWGNUproxywstunnel @@ -0,0 +1,250 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(SRC)/include \ + $(STDMOD)/http \ + $(STDMOD)/proxy \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxywstunnel + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Web Socket Tunnel Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Prxy WbSkt Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_wstunnel.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_wstunnel_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c balancers +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/ajp_utils.c b/modules/proxy/ajp_utils.c index 567af918..2fe9f72d 100644 --- a/modules/proxy/ajp_utils.c +++ b/modules/proxy/ajp_utils.c @@ -113,7 +113,7 @@ cleanup: return #x;\ break; -/**· +/** * Convert numeric message type into string * @param type AJP message type * @return AJP message type as a string diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.c b/modules/proxy/balancers/mod_lbmethod_heartbeat.c index 26c81584..77fb994f 100644 --- a/modules/proxy/balancers/mod_lbmethod_heartbeat.c +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.c @@ -407,7 +407,7 @@ static void *lb_hb_create_config(apr_pool_t *p, server_rec *s) { lb_hb_ctx_t *ctx = (lb_hb_ctx_t *) apr_palloc(p, sizeof(lb_hb_ctx_t)); - ctx->path = ap_server_root_relative(p, "logs/hb.dat"); + ctx->path = ap_runtime_dir_relative(p, DEFAULT_HEARTBEAT_STORAGE); return ctx; } @@ -442,7 +442,7 @@ static const char *cmd_lb_hb_storage(cmd_parms *cmd, return err; } - ctx->path = ap_server_root_relative(p, path); + ctx->path = ap_runtime_dir_relative(p, path); return NULL; } diff --git a/modules/proxy/config.m4 b/modules/proxy/config.m4 index e91cbf4c..ce625910 100644 --- a/modules/proxy/config.m4 +++ b/modules/proxy/config.m4 @@ -20,6 +20,7 @@ proxy_fcgi_objs="mod_proxy_fcgi.lo" proxy_scgi_objs="mod_proxy_scgi.lo" proxy_fdpass_objs="mod_proxy_fdpass.lo" proxy_ajp_objs="mod_proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo ajp_utils.lo" +proxy_wstunnel_objs="mod_proxy_wstunnel.lo" proxy_balancer_objs="mod_proxy_balancer.lo" case "$host" in @@ -33,6 +34,7 @@ case "$host" in proxy_scgi_objs="$proxy_scgi_objs mod_proxy.la" proxy_fdpass_objs="$proxy_fdpass_objs mod_proxy.la" proxy_ajp_objs="$proxy_ajp_objs mod_proxy.la" + proxy_wstunnel_objs="$proxy_wstunnel_objs mod_proxy.la" proxy_balancer_objs="$proxy_balancer_objs mod_proxy.la" ;; esac @@ -52,6 +54,7 @@ APACHE_MODULE(proxy_fdpass, Apache proxy to Unix Daemon Socket module. Requires enable_proxy_fdpass=no fi ],proxy) +APACHE_MODULE(proxy_wstunnel, Apache proxy Websocket Tunnel module. Requires and is enabled by --enable-proxy., $proxy_wstunnel_objs, , $proxy_mods_enable,, proxy) APACHE_MODULE(proxy_ajp, Apache proxy AJP module. Requires and is enabled by --enable-proxy., $proxy_ajp_objs, , $proxy_mods_enable,, proxy) APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module. Requires and is enabled by --enable-proxy., $proxy_balancer_objs, , $proxy_mods_enable,, proxy) diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 0bd3a6c9..0ee2ff35 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -36,6 +36,9 @@ APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup, #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #endif +static const char * const proxy_id = "proxy"; +apr_global_mutex_t *proxy_mutex = NULL; + /* * A Web proxy module. Stages: * @@ -383,6 +386,14 @@ static const char *set_balancer_param(proxy_server_conf *conf, } } + else if (!strcasecmp(key, "failontimeout")) { + if (!strcasecmp(val, "on")) + balancer->failontimeout = 1; + else if (!strcasecmp(val, "off")) + balancer->failontimeout = 0; + else + return "failontimeout must be On|Off"; + } else if (!strcasecmp(key, "nonce")) { if (!strcasecmp(val, "None")) { *balancer->s->nonce = '\0'; @@ -871,7 +882,7 @@ static int proxy_handler(request_rec *r) int i, rc, access_status; int direct_connect = 0; const char *str; - long maxfwd; + apr_int64_t maxfwd; proxy_balancer *balancer = NULL; proxy_worker *worker = NULL; int attempts = 0, max_attempts = 0; @@ -883,8 +894,14 @@ static int proxy_handler(request_rec *r) /* handle max-forwards / OPTIONS / TRACE */ if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) { - maxfwd = strtol(str, NULL, 10); - if (maxfwd < 1) { + char *end; + maxfwd = apr_strtoi64(str, &end, 10); + if (maxfwd < 0 || maxfwd == APR_INT64_MAX || *end) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_psprintf(r->pool, + "Max-Forwards value '%s' could not be parsed", str)); + } + else if (maxfwd == 0) { switch (r->method_number) { case M_TRACE: { int access_status; @@ -905,7 +922,7 @@ static int proxy_handler(request_rec *r) return OK; } default: { - return ap_proxyerror(r, HTTP_BAD_GATEWAY, + return ap_proxyerror(r, HTTP_BAD_REQUEST, "Max-Forwards has reached zero - proxy loop?"); } } @@ -918,7 +935,7 @@ static int proxy_handler(request_rec *r) } if (maxfwd >= 0) { apr_table_setn(r->headers_in, "Max-Forwards", - apr_psprintf(r->pool, "%ld", maxfwd)); + apr_psprintf(r->pool, "%" APR_INT64_T_FMT, maxfwd)); } if (r->method_number == M_TRACE) { @@ -1160,6 +1177,10 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s) ps->req = 0; ps->max_balancers = 0; ps->bal_persist = 0; + ps->inherit = 1; + ps->inherit_set = 0; + ps->ppinherit = 1; + ps->ppinherit_set = 0; ps->bgrowth = 5; ps->bgrowth_set = 0; ps->req_set = 0; @@ -1175,7 +1196,7 @@ static void * create_proxy_config(apr_pool_t *p, server_rec *s) ps->badopt_set = 0; ps->source_address = NULL; ps->source_address_set = 0; - ps->pool = p; + apr_pool_create_ex(&ps->pool, p, NULL, NULL); return ps; } @@ -1186,13 +1207,30 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv) proxy_server_conf *base = (proxy_server_conf *) basev; proxy_server_conf *overrides = (proxy_server_conf *) overridesv; - ps->proxies = apr_array_append(p, base->proxies, overrides->proxies); + ps->inherit = (overrides->inherit_set == 0) ? base->inherit : overrides->inherit; + ps->inherit_set = overrides->inherit_set || base->inherit_set; + + ps->ppinherit = (overrides->ppinherit_set == 0) ? base->ppinherit : overrides->ppinherit; + ps->ppinherit_set = overrides->ppinherit_set || base->ppinherit_set; + + if (ps->ppinherit) { + ps->proxies = apr_array_append(p, base->proxies, overrides->proxies); + } + else { + ps->proxies = overrides->proxies; + } ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy); ps->aliases = apr_array_append(p, base->aliases, overrides->aliases); ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies); ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn); - ps->workers = apr_array_append(p, base->workers, overrides->workers); - ps->balancers = apr_array_append(p, base->balancers, overrides->balancers); + if (ps->inherit || ps->ppinherit) { + ps->workers = apr_array_append(p, base->workers, overrides->workers); + ps->balancers = apr_array_append(p, base->balancers, overrides->balancers); + } + else { + ps->workers = overrides->workers; + ps->balancers = overrides->balancers; + } ps->forward = overrides->forward ? overrides->forward : base->forward; ps->reverse = overrides->reverse ? overrides->reverse : base->reverse; @@ -1220,7 +1258,7 @@ static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv) ps->proxy_status_set = overrides->proxy_status_set || base->proxy_status_set; ps->source_address = (overrides->source_address_set == 0) ? base->source_address : overrides->source_address; ps->source_address_set = overrides->source_address_set || base->source_address_set; - ps->pool = p; + ps->pool = base->pool; return ps; } static const char *set_source_address(cmd_parms *parms, void *dummy, @@ -1890,6 +1928,26 @@ static const char *set_persist(cmd_parms *parms, void *dummy, int flag) return NULL; } +static const char *set_inherit(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->inherit = flag; + psf->inherit_set = 1; + return NULL; +} + +static const char *set_ppinherit(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->ppinherit = flag; + psf->ppinherit_set = 1; + return NULL; +} + static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; @@ -2279,6 +2337,12 @@ static const command_rec proxy_cmds[] = "Number of additional Balancers that can be added post-config"), AP_INIT_FLAG("BalancerPersist", set_persist, NULL, RSRC_CONF, "on if the balancer should persist changes on reboot/restart made via the Balancer Manager"), + AP_INIT_FLAG("BalancerInherit", set_inherit, NULL, RSRC_CONF, + "on if this server should inherit Balancers and Workers defined in the main server " + "(Setting to off recommended if using the Balancer Manager)"), + AP_INIT_FLAG("ProxyPassInherit", set_ppinherit, NULL, RSRC_CONF, + "on if this server should inherit all ProxyPass directives defined in the main server " + "(Setting to off recommended if using the Balancer Manager)"), AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF, "Configure Status: proxy status to one of: on | off | full"), AP_INIT_RAW_ARGS("ProxySet", set_proxy_param, NULL, RSRC_CONF|ACCESS_CONF, @@ -2341,6 +2405,13 @@ PROXY_DECLARE(const char *) ap_proxy_ssl_val(apr_pool_t *p, server_rec *s, static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { + apr_status_t rv = ap_global_mutex_create(&proxy_mutex, NULL, + proxy_id, NULL, s, pconf, 0); + if (rv != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02478) + "failed to create %s mutex", proxy_id); + return rv; + } proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable); proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable); @@ -2443,6 +2514,15 @@ static void child_init(apr_pool_t *p, server_rec *s) { proxy_worker *reverse = NULL; + apr_status_t rv = apr_global_mutex_child_init(&proxy_mutex, + apr_global_mutex_lockfile(proxy_mutex), + p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(02479) + "could not init proxy_mutex in child"); + exit(1); /* Ugly, but what else? */ + } + /* TODO */ while (s) { void *sconf = s->module_config; @@ -2500,11 +2580,19 @@ static void child_init(apr_pool_t *p, server_rec *s) /* * This routine is called before the server processes the configuration - * files. There is no return value. + * files. */ static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) { + apr_status_t rv = ap_mutex_register(pconf, proxy_id, NULL, + APR_LOCK_DEFAULT, 0); + if (rv != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02480) + "failed to register %s mutex", proxy_id); + return 500; /* An HTTP status would be a misnomer! */ + } + APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL, APR_HOOK_MIDDLE); /* Reset workers count on gracefull restart */ diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 5074aa16..81fd14c1 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -164,7 +164,7 @@ typedef struct { status_full } proxy_status; /* Status display options */ apr_sockaddr_t *source_address; - apr_global_mutex_t *mutex; /* global lock (needed??) */ + apr_global_mutex_t *mutex; /* global lock, for pool, etc */ ap_slotmem_instance_t *bslot; /* balancers shm data - runtime */ ap_slotmem_provider_t *storage; @@ -179,6 +179,10 @@ typedef struct { unsigned int source_address_set:1; unsigned int bgrowth_set:1; unsigned int bal_persist:1; + unsigned int inherit:1; + unsigned int inherit_set:1; + unsigned int ppinherit:1; + unsigned int ppinherit_set:1; } proxy_server_conf; @@ -446,6 +450,7 @@ struct proxy_balancer { proxy_server_conf *sconf; void *context; /* general purpose storage */ proxy_balancer_shared *s; /* Shared data */ + int failontimeout; /* Whether to mark a member in Err if IO timeout occurs */ }; struct proxy_balancer_method { @@ -915,6 +920,56 @@ PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent, proxy_dir_conf *dconf); +/** + * Create a HTTP request header brigade, old_cl_val and old_te_val as required. + * @parama p pool + * @param header_brigade header brigade to use/fill + * @param r request + * @param p_conn proxy connection rec + * @param worker selected worker + * @param conf per-server proxy config + * @param uri uri + * @param url url + * @param server_portstr port as string + * @param old_cl_val stored old content-len val + * @param old_te_val stored old TE val + * @return OK or HTTP_EXPECTATION_FAILED + */ +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, + apr_bucket_brigade *header_brigade, + request_rec *r, + proxy_conn_rec *p_conn, + proxy_worker *worker, + proxy_server_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr, + char **old_cl_val, + char **old_te_val); + +/** + * @param bucket_alloc bucket allocator + * @param r request + * @param p_conn proxy connection + * @param origin connection rec of origin + * @param bb brigade to send to origin + * @param flush flush + * @return status (OK) + */ +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, + request_rec *r, proxy_conn_rec *p_conn, + conn_rec *origin, apr_bucket_brigade *bb, + int flush); + +/** + * Clear the headers referenced by the Connection header from the given + * table, and remove the Connection header. + * @param r request + * @param headers table of headers to clear + * @return 1 if "close" was present, 0 otherwise. + */ +APR_DECLARE_OPTIONAL_FN(int, ap_proxy_clear_connection, + (request_rec *r, apr_table_t *headers)); + #define PROXY_LBMETHOD "proxylbmethod" /* The number of dynamic workers that can be added when reconfiguring. diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c index cd9987e1..0f45be7a 100644 --- a/modules/proxy/mod_proxy_balancer.c +++ b/modules/proxy/mod_proxy_balancer.c @@ -649,6 +649,17 @@ static int proxy_balancer_post_request(proxy_worker *worker, } } + if (balancer->failontimeout + && (apr_table_get(r->notes, "proxy_timedout")) != NULL) { + 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); + worker->s->status |= PROXY_WORKER_IN_ERROR; + worker->s->error_time = apr_time_now(); + + } + if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01175) "%s: Unlock failed for post_request", balancer->s->name); @@ -769,9 +780,9 @@ static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog, continue; } if (conf->bal_persist) { - type = AP_SLOTMEM_TYPE_PREGRAB | AP_SLOTMEM_TYPE_PERSIST; + type = AP_SLOTMEM_TYPE_PERSIST; } else { - type = AP_SLOTMEM_TYPE_PREGRAB; + type = 0; } if (conf->balancers->nelts) { conf->max_balancers = conf->balancers->nelts + conf->bgrowth; @@ -1160,7 +1171,7 @@ static int balancer_handler(request_rec *r) (val = apr_table_get(params, "b_nwrkr"))) { char *ret; proxy_worker *nworker; - nworker = ap_proxy_get_worker(conf->pool, bsel, conf, val); + nworker = ap_proxy_get_worker(r->pool, bsel, conf, val); if (!nworker && storage->num_free_slots(bsel->wslot)) { if ((rv = PROXY_GLOBAL_LOCK(bsel)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01194) @@ -1675,8 +1686,9 @@ static void ap_proxy_balancer_register_hook(apr_pool_t *p) * initializes */ static const char *const aszPred[] = { "mpm_winnt.c", "mod_slotmem_shm.c", NULL}; + static const char *const aszPred2[] = { "mod_proxy.c", NULL}; /* manager handler */ - ap_hook_post_config(balancer_post_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(balancer_post_config, aszPred2, NULL, APR_HOOK_MIDDLE); ap_hook_pre_config(balancer_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST); ap_hook_child_init(balancer_child_init, aszPred, NULL, APR_HOOK_MIDDLE); diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index 07b5408d..cffad2e7 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -21,6 +21,9 @@ module AP_MODULE_DECLARE_DATA proxy_http_module; +static int (*ap_proxy_clear_connection_fn)(request_rec *r, apr_table_t *headers) = + NULL; + static apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r, proxy_conn_rec *backend); @@ -178,33 +181,7 @@ static apr_table_t *ap_proxy_clean_warnings(apr_pool_t *p, apr_table_t *headers) return headers; } } -static int clear_conn_headers(void *data, const char *key, const char *val) -{ - apr_table_t *headers = ((header_dptr*)data)->table; - apr_pool_t *pool = ((header_dptr*)data)->pool; - const char *name; - char *next = apr_pstrdup(pool, val); - while (*next) { - name = next; - while (*next && !apr_isspace(*next) && (*next != ',')) { - ++next; - } - while (*next && (apr_isspace(*next) || (*next == ','))) { - *next++ = '\0'; - } - apr_table_unset(headers, name); - } - return 1; -} -static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers) -{ - header_dptr x; - x.pool = p; - x.table = headers; - apr_table_unset(headers, "Proxy-Connection"); - apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL); - apr_table_unset(headers, "Connection"); -} + static void add_te_chunked(apr_pool_t *p, apr_bucket_alloc_t *bucket_alloc, apr_bucket_brigade *header_brigade) @@ -250,44 +227,6 @@ static void terminate_headers(apr_bucket_alloc_t *bucket_alloc, APR_BRIGADE_INSERT_TAIL(header_brigade, e); } -static int pass_brigade(apr_bucket_alloc_t *bucket_alloc, - request_rec *r, proxy_conn_rec *p_conn, - conn_rec *origin, apr_bucket_brigade *bb, - int flush) -{ - apr_status_t status; - apr_off_t transferred; - - if (flush) { - apr_bucket *e = apr_bucket_flush_create(bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, e); - } - apr_brigade_length(bb, 0, &transferred); - if (transferred != -1) - p_conn->worker->s->transferred += transferred; - status = ap_pass_brigade(origin->output_filters, bb); - if (status != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084) - "pass request body failed to %pI (%s)", - p_conn->addr, p_conn->hostname); - if (origin->aborted) { - const char *ssl_note; - - if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv")) - != NULL) && (strcmp(ssl_note, "err") == 0)) { - return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, - "Error during SSL Handshake with" - " remote server"); - } - return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY; - } - else { - return HTTP_BAD_REQUEST; - } - } - apr_brigade_cleanup(bb); - return OK; -} #define MAX_MEM_SPOOL 16384 @@ -366,7 +305,7 @@ static int stream_reqbody_chunked(apr_pool_t *p, } /* The request is flushed below this loop with chunk EOS header */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0); + rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0); if (rv != OK) { return rv; } @@ -412,7 +351,7 @@ static int stream_reqbody_chunked(apr_pool_t *p, } /* Now we have headers-only, or the chunk EOS mark; flush it */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1); + rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1); return rv; } @@ -422,7 +361,7 @@ static int stream_reqbody_cl(apr_pool_t *p, conn_rec *origin, apr_bucket_brigade *header_brigade, apr_bucket_brigade *input_brigade, - const char *old_cl_val) + char *old_cl_val) { int seen_eos = 0, rv = 0; apr_status_t status = APR_SUCCESS; @@ -511,7 +450,7 @@ static int stream_reqbody_cl(apr_pool_t *p, } /* Once we hit EOS, we are ready to flush. */ - rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos); + rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos); if (rv != OK) { return rv ; } @@ -541,7 +480,7 @@ static int stream_reqbody_cl(apr_pool_t *p, * body; send it now with the flush flag */ bb = header_brigade; - return(pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1)); + return(ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1)); } return OK; @@ -685,7 +624,7 @@ static int spool_reqbody_cl(apr_pool_t *p, APR_BRIGADE_INSERT_TAIL(header_brigade, e); } /* This is all a single brigade, pass with flush flagged */ - return(pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1)); + return(ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1)); } /* @@ -752,257 +691,31 @@ int ap_proxy_http_request(apr_pool_t *p, request_rec *r, apr_bucket_brigade *temp_brigade; apr_bucket *e; char *buf; - const apr_array_header_t *headers_in_array; - const apr_table_entry_t *headers_in; - int counter; apr_status_t status; enum rb_methods {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED, RB_SPOOL_CL}; enum rb_methods rb_method = RB_INIT; - const char *old_cl_val = NULL; - const char *old_te_val = NULL; + char *old_cl_val = NULL; + char *old_te_val = NULL; apr_off_t bytes_read = 0; apr_off_t bytes; int force10, rv; - apr_table_t *headers_in_copy; - proxy_dir_conf *dconf; conn_rec *origin = p_conn->connection; - int do_100_continue; - - dconf = ap_get_module_config(r->per_dir_config, &proxy_module); - header_brigade = apr_brigade_create(p, origin->bucket_alloc); - - /* - * Send the HTTP/1.1 request to the remote server - */ - - /* - * To be compliant, we only use 100-Continue for requests with bodies. - * We also make sure we won't be talking HTTP/1.0 as well. - */ - do_100_continue = (worker->s->ping_timeout_set - && ap_request_has_body(r) - && (PROXYREQ_REVERSE == r->proxyreq) - && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0"))); if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { - /* - * According to RFC 2616 8.2.3 we are not allowed to forward an - * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return - * a HTTP_EXPECTATION_FAILED - */ if (r->expecting_100) { return HTTP_EXPECTATION_FAILED; } - buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL); force10 = 1; - p_conn->close = 1; } else { - buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); force10 = 0; } - if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { - origin->keepalive = AP_CONN_CLOSE; - p_conn->close = 1; - } - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); - if (dconf->preserve_host == 0) { - if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ - if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { - buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:", - uri->port_str, CRLF, NULL); - } else { - buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL); - } - } else { - if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { - buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", - uri->port_str, CRLF, NULL); - } else { - buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL); - } - } - } - else { - /* don't want to use r->hostname, as the incoming header might have a - * port attached - */ - const char* hostname = apr_table_get(r->headers_in,"Host"); - if (!hostname) { - hostname = r->server->server_hostname; - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092) - "no HTTP 0.9 request (with no host line) " - "on incoming request and preserve host set " - "forcing hostname to be %s for uri %s", - hostname, r->uri); - } - buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL); - } - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); - - /* handle Via */ - if (conf->viaopt == via_block) { - /* Block all outgoing Via: headers */ - apr_table_unset(r->headers_in, "Via"); - } else if (conf->viaopt != via_off) { - const char *server_name = ap_get_server_name(r); - /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, - * then the server name returned by ap_get_server_name() is the - * origin server name (which does make too much sense with Via: headers) - * so we use the proxy vhost's name instead. - */ - if (server_name == r->hostname) - server_name = r->server->server_hostname; - /* Create a "Via:" request header entry and merge it */ - /* Generate outgoing Via: header with/without server comment: */ - apr_table_mergen(r->headers_in, "Via", - (conf->viaopt == via_full) - ? apr_psprintf(p, "%d.%d %s%s (%s)", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - server_name, server_portstr, - AP_SERVER_BASEVERSION) - : apr_psprintf(p, "%d.%d %s%s", - HTTP_VERSION_MAJOR(r->proto_num), - HTTP_VERSION_MINOR(r->proto_num), - server_name, server_portstr) - ); - } - - /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test - * to backend - */ - if (do_100_continue) { - apr_table_mergen(r->headers_in, "Expect", "100-Continue"); - r->expecting_100 = 1; - } - - /* X-Forwarded-*: handling - * - * XXX Privacy Note: - * ----------------- - * - * These request headers are only really useful when the mod_proxy - * is used in a reverse proxy configuration, so that useful info - * about the client can be passed through the reverse proxy and on - * to the backend server, which may require the information to - * function properly. - * - * In a forward proxy situation, these options are a potential - * privacy violation, as information about clients behind the proxy - * are revealed to arbitrary servers out there on the internet. - * - * The HTTP/1.1 Via: header is designed for passing client - * information through proxies to a server, and should be used in - * a forward proxy configuation instead of X-Forwarded-*. See the - * ProxyVia option for details. - */ - if (dconf->add_forwarded_headers) { - if (PROXYREQ_REVERSE == r->proxyreq) { - const char *buf; - - /* Add X-Forwarded-For: so that the upstream has a chance to - * determine, where the original request came from. - */ - apr_table_mergen(r->headers_in, "X-Forwarded-For", - r->useragent_ip); - - /* Add X-Forwarded-Host: so that upstream knows what the - * original request hostname was. - */ - if ((buf = apr_table_get(r->headers_in, "Host"))) { - apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf); - } - - /* Add X-Forwarded-Server: so that upstream knows what the - * name of this proxy server is (if there are more than one) - * XXX: This duplicates Via: - do we strictly need it? - */ - apr_table_mergen(r->headers_in, "X-Forwarded-Server", - r->server->server_hostname); - } - } - proxy_run_fixups(r); - /* - * Make a copy of the headers_in table before clearing the connection - * headers as we need the connection headers later in the http output - * filter to prepare the correct response headers. - * - * Note: We need to take r->pool for apr_table_copy as the key / value - * pairs in r->headers_in have been created out of r->pool and - * p might be (and actually is) a longer living pool. - * This would trigger the bad pool ancestry abort in apr_table_copy if - * apr is compiled with APR_POOL_DEBUG. - */ - headers_in_copy = apr_table_copy(r->pool, r->headers_in); - ap_proxy_clear_connection(p, headers_in_copy); - /* send request headers */ - headers_in_array = apr_table_elts(headers_in_copy); - headers_in = (const apr_table_entry_t *) headers_in_array->elts; - for (counter = 0; counter < headers_in_array->nelts; counter++) { - if (headers_in[counter].key == NULL - || headers_in[counter].val == NULL - - /* Already sent */ - || !strcasecmp(headers_in[counter].key, "Host") - - /* Clear out hop-by-hop request headers not to send - * RFC2616 13.5.1 says we should strip these headers - */ - || !strcasecmp(headers_in[counter].key, "Keep-Alive") - || !strcasecmp(headers_in[counter].key, "TE") - || !strcasecmp(headers_in[counter].key, "Trailer") - || !strcasecmp(headers_in[counter].key, "Upgrade") - - ) { - continue; - } - /* Do we want to strip Proxy-Authorization ? - * If we haven't used it, then NO - * If we have used it then MAYBE: RFC2616 says we MAY propagate it. - * So let's make it configurable by env. - */ - if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) { - if (r->user != NULL) { /* we've authenticated */ - if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { - continue; - } - } - } - - - /* Skip Transfer-Encoding and Content-Length for now. - */ - if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) { - old_te_val = headers_in[counter].val; - continue; - } - if (!strcasecmp(headers_in[counter].key, "Content-Length")) { - old_cl_val = headers_in[counter].val; - continue; - } - - /* for sub-requests, ignore freshness/expiry headers */ - if (r->main) { - if ( !strcasecmp(headers_in[counter].key, "If-Match") - || !strcasecmp(headers_in[counter].key, "If-Modified-Since") - || !strcasecmp(headers_in[counter].key, "If-Range") - || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since") - || !strcasecmp(headers_in[counter].key, "If-None-Match")) { - continue; - } - } - - buf = apr_pstrcat(p, headers_in[counter].key, ": ", - headers_in[counter].val, CRLF, - NULL); - ap_xlate_proto_to_ascii(buf, strlen(buf)); - e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(header_brigade, e); + header_brigade = apr_brigade_create(p, origin->bucket_alloc); + rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, p_conn, + worker, conf, uri, url, server_portstr, + &old_cl_val, &old_te_val); + if (rv != OK) { + return rv; } /* We have headers, let's figure out our request body... */ @@ -1576,6 +1289,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, "error reading status line from remote " "server %s:%d", backend->hostname, backend->port); if (APR_STATUS_IS_TIMEUP(rc)) { + apr_table_set(r->notes, "proxy_timedout", "1"); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01103) "read timeout"); if (do_100_continue) { return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, "Timeout on 100-Continue"); @@ -1754,11 +1468,10 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, * ap_http_filter to know where to end. */ te = apr_table_get(r->headers_out, "Transfer-Encoding"); + /* strip connection listed hop-by-hop headers from response */ - if (ap_find_token(p, apr_table_get(r->headers_out, "Connection"), - "close")) - backend->close = 1; - ap_proxy_clear_connection(p, r->headers_out); + backend->close = ap_proxy_clear_connection_fn(r, r->headers_out); + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { ap_set_content_type(r, apr_pstrdup(p, buf)); } @@ -1770,6 +1483,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, for (i=0; hop_by_hop_hdrs[i]; ++i) { apr_table_unset(r->headers_out, hop_by_hop_hdrs[i]); } + /* Delete warnings with wrong date */ r->headers_out = ap_proxy_clean_warnings(p, r->headers_out); @@ -1837,12 +1551,16 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, * behaviour here might break something. * * So let's make it configurable. + * + * We need to set "r->expecting_100 = 1" otherwise origin + * server behaviour will apply. */ const char *policy = apr_table_get(r->subprocess_env, "proxy-interim-response"); ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "HTTP: received interim %d response", r->status); - if (!policy || !strcasecmp(policy, "RFC")) { + if (!policy + || (!strcasecmp(policy, "RFC") && ((r->expecting_100 = 1)))) { ap_send_interim_response(r, 1); } /* FIXME: refine this to be able to specify per-response-status @@ -2294,8 +2012,34 @@ cleanup: } return status; } + +/* post_config hook: */ +static int proxy_http_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + + /* proxy_http_post_config() will be called twice during startup. So, don't + * set up the static data the 1st time through. */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + + if (!ap_proxy_clear_connection_fn) { + ap_proxy_clear_connection_fn = + APR_RETRIEVE_OPTIONAL_FN(ap_proxy_clear_connection); + if (!ap_proxy_clear_connection_fn) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02477) + "mod_proxy must be loaded for mod_proxy_http"); + return !OK; + } + } + + return OK; +} + static void ap_proxy_http_register_hook(apr_pool_t *p) { + ap_hook_post_config(proxy_http_post_config, NULL, NULL, APR_HOOK_MIDDLE); proxy_hook_scheme_handler(proxy_http_handler, NULL, NULL, APR_HOOK_FIRST); proxy_hook_canon_handler(proxy_http_canon, NULL, NULL, APR_HOOK_FIRST); warn_rx = ap_pregcomp(p, "[0-9]{3}[ \t]+[^ \t]+[ \t]+\"[^\"]*\"([ \t]+\"([^\"]+)\")?", 0); diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c new file mode 100644 index 00000000..365a2054 --- /dev/null +++ b/modules/proxy/mod_proxy_wstunnel.c @@ -0,0 +1,399 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mod_proxy.h" + +module AP_MODULE_DECLARE_DATA proxy_wstunnel_module; + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +static int proxy_wstunnel_canon(request_rec *r, char *url) +{ + char *host, *path, sport[7]; + char *search = NULL; + const char *err; + char *scheme; + apr_port_t port, def_port; + + /* ap_port_of_scheme() */ + if (strncasecmp(url, "ws:", 3) == 0) { + url += 3; + scheme = "ws:"; + def_port = apr_uri_port_of_scheme("http"); + } + else if (strncasecmp(url, "wss:", 4) == 0) { + url += 4; + scheme = "wss:"; + def_port = apr_uri_port_of_scheme("https"); + } + else { + return DECLINED; + } + + port = def_port; + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url); + + /* + * do syntactic check. + * We break the URL into host, port, path, search + */ + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02439) "error parsing URL %s: %s", + url, err); + return HTTP_BAD_REQUEST; + } + + /* + * now parse path/search args, according to rfc1738: + * process the path. With proxy-nocanon set (by + * mod_proxy) we use the raw, unparsed uri + */ + if (apr_table_get(r->notes, "proxy-nocanon")) { + path = url; /* this is the raw path */ + } + else { + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); + search = r->args; + } + if (path == NULL) + return HTTP_BAD_REQUEST; + + apr_snprintf(sport, sizeof(sport), ":%d", port); + + if (ap_strchr_c(host, ':')) { + /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "//", host, sport, + "/", path, (search) ? "?" : "", + (search) ? search : "", NULL); + return OK; +} + + +static int proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o, + apr_bucket_brigade *bb, char *name) +{ + int rv; +#ifdef DEBUGGING + apr_off_t len; +#endif + + do { + apr_brigade_cleanup(bb); + rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES, + APR_NONBLOCK_READ, AP_IOBUFSIZE); + if (rv == APR_SUCCESS) { + if (c_o->aborted) + return APR_EPIPE; + if (APR_BRIGADE_EMPTY(bb)) + break; +#ifdef DEBUGGING + len = -1; + apr_brigade_length(bb, 0, &len); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02440) + "read %" APR_OFF_T_FMT + " bytes from %s", len, name); +#endif + rv = ap_pass_brigade(c_o->output_filters, bb); + if (rv == APR_SUCCESS) { + ap_fflush(c_o->output_filters, bb); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02441) + "error on %s - ap_pass_brigade", + name); + } + } else if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02442) + "error on %s - ap_get_brigade", + name); + } + } while (rv == APR_SUCCESS); + + if (APR_STATUS_IS_EAGAIN(rv)) { + rv = APR_SUCCESS; + } + return rv; +} + +/* Search thru the input filters and remove the reqtimeout one */ +static void remove_reqtimeout(ap_filter_t *next) +{ + ap_filter_t *reqto = NULL; + ap_filter_rec_t *filter; + + filter = ap_get_input_filter_handle("reqtimeout"); + if (!filter) { + return; + } + + while (next) { + if (next->frec == filter) { + reqto = next; + break; + } + next = next->next; + } + if (reqto) { + ap_remove_input_filter(reqto); + } +} + +/* + * process the request and write the response. + */ +static int ap_proxy_wstunnel_request(apr_pool_t *p, request_rec *r, + proxy_conn_rec *conn, + proxy_worker *worker, + proxy_server_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr) +{ + apr_status_t rv = APR_SUCCESS; + apr_pollset_t *pollset; + apr_pollfd_t pollfd; + const apr_pollfd_t *signalled; + apr_int32_t pollcnt, pi; + apr_int16_t pollevent; + conn_rec *c = r->connection; + apr_socket_t *sock = conn->sock; + conn_rec *backconn = conn->connection; + int client_error = 0; + char *buf; + apr_bucket_brigade *header_brigade; + apr_bucket *e; + char *old_cl_val = NULL; + char *old_te_val = NULL; + apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc); + apr_socket_t *client_socket = ap_get_conn_socket(c); + + header_brigade = apr_brigade_create(p, backconn->bucket_alloc); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "sending request"); + + rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, conn, + worker, conf, uri, url, server_portstr, + &old_cl_val, &old_te_val); + if (rv != OK) { + return rv; + } + + buf = apr_pstrcat(p, "Upgrade: WebSocket", CRLF, "Connection: Upgrade", CRLF, CRLF, NULL); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + + if ((rv = ap_proxy_pass_brigade(c->bucket_alloc, r, conn, backconn, + header_brigade, 1)) != OK) + return rv; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "setting up poll()"); + + if ((rv = apr_pollset_create(&pollset, 2, p, 0)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02443) + "error apr_pollset_create()"); + return HTTP_INTERNAL_SERVER_ERROR; + } + +#if 0 + apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1); + apr_socket_opt_set(sock, APR_SO_KEEPALIVE, 1); + apr_socket_opt_set(client_socket, APR_SO_NONBLOCK, 1); + apr_socket_opt_set(client_socket, APR_SO_KEEPALIVE, 1); +#endif + + pollfd.p = p; + pollfd.desc_type = APR_POLL_SOCKET; + pollfd.reqevents = APR_POLLIN; + pollfd.desc.s = sock; + pollfd.client_data = NULL; + apr_pollset_add(pollset, &pollfd); + + pollfd.desc.s = client_socket; + apr_pollset_add(pollset, &pollfd); + + + r->output_filters = c->output_filters; + r->proto_output_filters = c->output_filters; + r->input_filters = c->input_filters; + r->proto_input_filters = c->input_filters; + + remove_reqtimeout(r->input_filters); + + while (1) { /* Infinite loop until error (one side closes the connection) */ + if ((rv = apr_pollset_poll(pollset, -1, &pollcnt, &signalled)) + != APR_SUCCESS) { + if (APR_STATUS_IS_EINTR(rv)) { + continue; + } + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02444) "error apr_poll()"); + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02445) + "woke from poll(), i=%d", pollcnt); + + for (pi = 0; pi < pollcnt; pi++) { + const apr_pollfd_t *cur = &signalled[pi]; + + if (cur->desc.s == sock) { + pollevent = cur->rtnevents; + if (pollevent & APR_POLLIN) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02446) + "sock was readable"); + rv = proxy_wstunnel_transfer(r, backconn, c, bb, "sock"); + } + else if ((pollevent & APR_POLLERR) + || (pollevent & APR_POLLHUP)) { + rv = APR_EPIPE; + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447) + "err/hup on backconn"); + } + if (rv != APR_SUCCESS) + client_error = 1; + } + else if (cur->desc.s == client_socket) { + pollevent = cur->rtnevents; + if (pollevent & APR_POLLIN) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02448) + "client was readable"); + rv = proxy_wstunnel_transfer(r, c, backconn, bb, "client"); + } + } + else { + rv = APR_EBADF; + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02449) + "unknown socket in pollset"); + } + + } + if (rv != APR_SUCCESS) { + break; + } + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "finished with poll() - cleaning up"); + + if (client_error) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return OK; +} + +/* + */ +static int proxy_wstunnel_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + int status; + char server_portstr[32]; + proxy_conn_rec *backend = NULL; + char *scheme; + int retry; + conn_rec *c = r->connection; + apr_pool_t *p = r->pool; + apr_uri_t *uri; + + if (strncasecmp(url, "wss:", 4) == 0) { + scheme = "WSS"; + } + else if (strncasecmp(url, "ws:", 3) == 0) { + scheme = "WS"; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02450) "declining URL %s", url); + return DECLINED; + } + + uri = apr_palloc(p, sizeof(*uri)); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02451) "serving URL %s", url); + + /* create space for state information */ + status = ap_proxy_acquire_connection(scheme, &backend, worker, + r->server); + if (status != OK) { + if (backend) { + backend->close = 1; + ap_proxy_release_connection(scheme, backend, r->server); + } + return status; + } + + backend->is_ssl = 0; + backend->close = 0; + + retry = 0; + while (retry < 2) { + char *locurl = url; + /* Step One: Determine Who To Connect To */ + status = ap_proxy_determine_connection(p, r, conf, worker, backend, + uri, &locurl, proxyname, proxyport, + server_portstr, + sizeof(server_portstr)); + + if (status != OK) + break; + + /* Step Two: Make the Connection */ + if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02452) + "failed to make connection to backend: %s", + backend->hostname); + status = HTTP_SERVICE_UNAVAILABLE; + break; + } + /* Step Three: Create conn_rec */ + if (!backend->connection) { + if ((status = ap_proxy_connection_create(scheme, backend, + c, r->server)) != OK) + break; + } + + /* Step Three: Process the Request */ + status = ap_proxy_wstunnel_request(p, r, backend, worker, conf, uri, locurl, + server_portstr); + break; + } + + /* Do not close the socket */ + ap_proxy_release_connection(scheme, backend, r->server); + return status; +} + +static void ap_proxy_http_register_hook(apr_pool_t *p) +{ + proxy_hook_scheme_handler(proxy_wstunnel_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(proxy_wstunnel_canon, NULL, NULL, APR_HOOK_FIRST); +} + +AP_DECLARE_MODULE(proxy_wstunnel) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_http_register_hook /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_wstunnel.dsp b/modules/proxy/mod_proxy_wstunnel.dsp new file mode 100644 index 00000000..7123bd65 --- /dev/null +++ b/modules/proxy/mod_proxy_wstunnel.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_wstunnel" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_proxy_wstunnel - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "mod_proxy_wstunnel.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "mod_proxy_wstunnel.mak" CFG="mod_proxy_wstunnel - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "mod_proxy_wstunnel - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_wstunnel - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_wstunnel_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_proxy_wstunnel.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_wstunnel_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_proxy_wstunnel.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "mod_proxy_wstunnel - Win32 Release"
+# Name "mod_proxy_wstunnel - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\mod_proxy_wstunnel.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=.\mod_proxy.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 5ab5d914..67dc9394 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -69,6 +69,8 @@ static int lb_workers_limit = 0; const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_path; const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_domain; +extern apr_global_mutex_t *proxy_mutex; + static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); @@ -1730,12 +1732,14 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, ser else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927) "initializing worker %s local", worker->s->name); + apr_global_mutex_lock(proxy_mutex); /* Now init local worker data */ if (worker->tmutex == NULL) { rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00928) "can not create worker thread mutex"); + apr_global_mutex_unlock(proxy_mutex); return rv; } } @@ -1744,6 +1748,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, ser if (worker->cp == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929) "can not create connection pool"); + apr_global_mutex_unlock(proxy_mutex); return APR_EGENERAL; } @@ -1779,6 +1784,8 @@ PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, ser "initialized single connection worker in child %" APR_PID_T_FMT " for (%s)", getpid(), worker->s->hostname); } + apr_global_mutex_unlock(proxy_mutex); + } if (rv == APR_SUCCESS) { worker->s->status |= (PROXY_WORKER_INITIALIZED); @@ -2316,7 +2323,7 @@ static apr_status_t send_http_connect(proxy_conn_rec *backend, nbytes = sizeof(drain_buffer) - 1; while (status == APR_SUCCESS && nbytes) { status = apr_socket_recv(backend->sock, drain_buffer, &nbytes); - buffer[nbytes] = '\0'; + drain_buffer[nbytes] = '\0'; nbytes = sizeof(drain_buffer) - 1; if (strstr(drain_buffer, "\r\n\r\n") != NULL) { break; @@ -2766,15 +2773,18 @@ PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec } if (!found) { proxy_worker **runtime; + apr_global_mutex_lock(proxy_mutex); runtime = apr_array_push(b->workers); *runtime = apr_palloc(conf->pool, sizeof(proxy_worker)); + apr_global_mutex_unlock(proxy_mutex); (*runtime)->hash = shm->hash; (*runtime)->context = NULL; (*runtime)->cp = NULL; (*runtime)->balancer = b; (*runtime)->s = shm; (*runtime)->tmutex = NULL; - if ((rv = ap_proxy_initialize_worker(*runtime, s, conf->pool)) != APR_SUCCESS) { + rv = ap_proxy_initialize_worker(*runtime, s, conf->pool); + if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker"); return rv; } @@ -2834,7 +2844,365 @@ PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_prov return NULL; } +typedef struct header_connection { + apr_pool_t *pool; + apr_array_header_t *array; + const char *first; + unsigned int closed:1; +} header_connection; + +static int find_conn_headers(void *data, const char *key, const char *val) +{ + header_connection *x = data; + const char *name; + + do { + while (*val == ',') { + val++; + } + name = ap_get_token(x->pool, &val, 0); + if (!strcasecmp(name, "close")) { + x->closed = 1; + } + if (!x->first) { + x->first = name; + } + else { + const char **elt; + if (!x->array) { + x->array = apr_array_make(x->pool, 4, sizeof(char *)); + } + elt = apr_array_push(x->array); + *elt = name; + } + } while (*val); + + return 1; +} + +/** + * Remove all headers referred to by the Connection header. + */ +static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers) +{ + const char **name; + header_connection x; + + x.pool = r->pool; + x.array = NULL; + x.first = NULL; + x.closed = 0; + + apr_table_unset(headers, "Proxy-Connection"); + + apr_table_do(find_conn_headers, &x, headers, "Connection", NULL); + if (x.first) { + /* fast path - no memory allocated for one header */ + apr_table_unset(headers, "Connection"); + apr_table_unset(headers, x.first); + } + if (x.array) { + /* two or more headers */ + while ((name = apr_array_pop(x.array))) { + apr_table_unset(headers, *name); + } + } + + return x.closed; +} + +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, + apr_bucket_brigade *header_brigade, + request_rec *r, + proxy_conn_rec *p_conn, + proxy_worker *worker, + proxy_server_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr, + char **old_cl_val, + char **old_te_val) +{ + conn_rec *c = r->connection; + int counter; + char *buf; + const apr_array_header_t *headers_in_array; + const apr_table_entry_t *headers_in; + apr_table_t *headers_in_copy; + apr_bucket *e; + int do_100_continue; + conn_rec *origin = p_conn->connection; + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + + /* + * To be compliant, we only use 100-Continue for requests with bodies. + * We also make sure we won't be talking HTTP/1.0 as well. + */ + do_100_continue = (worker->s->ping_timeout_set + && ap_request_has_body(r) + && (PROXYREQ_REVERSE == r->proxyreq) + && !(apr_table_get(r->subprocess_env, "force-proxy-request-1.0"))); + + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { + /* + * According to RFC 2616 8.2.3 we are not allowed to forward an + * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return + * a HTTP_EXPECTATION_FAILED + */ + if (r->expecting_100) { + return HTTP_EXPECTATION_FAILED; + } + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL); + p_conn->close = 1; + } else { + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); + } + if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { + origin->keepalive = AP_CONN_CLOSE; + p_conn->close = 1; + } + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + if (dconf->preserve_host == 0) { + if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]:", + uri->port_str, CRLF, NULL); + } else { + buf = apr_pstrcat(p, "Host: [", uri->hostname, "]", CRLF, NULL); + } + } else { + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { + buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", + uri->port_str, CRLF, NULL); + } else { + buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL); + } + } + } + else { + /* don't want to use r->hostname, as the incoming header might have a + * port attached + */ + const char* hostname = apr_table_get(r->headers_in,"Host"); + if (!hostname) { + hostname = r->server->server_hostname; + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092) + "no HTTP 0.9 request (with no host line) " + "on incoming request and preserve host set " + "forcing hostname to be %s for uri %s", + hostname, r->uri); + } + buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL); + } + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + + /* handle Via */ + if (conf->viaopt == via_block) { + /* Block all outgoing Via: headers */ + apr_table_unset(r->headers_in, "Via"); + } else if (conf->viaopt != via_off) { + const char *server_name = ap_get_server_name(r); + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, + * then the server name returned by ap_get_server_name() is the + * origin server name (which does make too much sense with Via: headers) + * so we use the proxy vhost's name instead. + */ + if (server_name == r->hostname) + server_name = r->server->server_hostname; + /* Create a "Via:" request header entry and merge it */ + /* Generate outgoing Via: header with/without server comment: */ + apr_table_mergen(r->headers_in, "Via", + (conf->viaopt == via_full) + ? apr_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, server_portstr) + ); + } + + /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test + * to backend + */ + if (do_100_continue) { + apr_table_mergen(r->headers_in, "Expect", "100-Continue"); + r->expecting_100 = 1; + } + + /* X-Forwarded-*: handling + * + * XXX Privacy Note: + * ----------------- + * + * These request headers are only really useful when the mod_proxy + * is used in a reverse proxy configuration, so that useful info + * about the client can be passed through the reverse proxy and on + * to the backend server, which may require the information to + * function properly. + * + * In a forward proxy situation, these options are a potential + * privacy violation, as information about clients behind the proxy + * are revealed to arbitrary servers out there on the internet. + * + * The HTTP/1.1 Via: header is designed for passing client + * information through proxies to a server, and should be used in + * a forward proxy configuation instead of X-Forwarded-*. See the + * ProxyVia option for details. + */ + if (dconf->add_forwarded_headers) { + if (PROXYREQ_REVERSE == r->proxyreq) { + const char *buf; + + /* Add X-Forwarded-For: so that the upstream has a chance to + * determine, where the original request came from. + */ + apr_table_mergen(r->headers_in, "X-Forwarded-For", + r->useragent_ip); + + /* Add X-Forwarded-Host: so that upstream knows what the + * original request hostname was. + */ + if ((buf = apr_table_get(r->headers_in, "Host"))) { + apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf); + } + + /* Add X-Forwarded-Server: so that upstream knows what the + * name of this proxy server is (if there are more than one) + * XXX: This duplicates Via: - do we strictly need it? + */ + apr_table_mergen(r->headers_in, "X-Forwarded-Server", + r->server->server_hostname); + } + } + + proxy_run_fixups(r); + /* + * Make a copy of the headers_in table before clearing the connection + * headers as we need the connection headers later in the http output + * filter to prepare the correct response headers. + * + * Note: We need to take r->pool for apr_table_copy as the key / value + * pairs in r->headers_in have been created out of r->pool and + * p might be (and actually is) a longer living pool. + * This would trigger the bad pool ancestry abort in apr_table_copy if + * apr is compiled with APR_POOL_DEBUG. + */ + headers_in_copy = apr_table_copy(r->pool, r->headers_in); + ap_proxy_clear_connection(r, headers_in_copy); + /* send request headers */ + headers_in_array = apr_table_elts(headers_in_copy); + headers_in = (const apr_table_entry_t *) headers_in_array->elts; + for (counter = 0; counter < headers_in_array->nelts; counter++) { + if (headers_in[counter].key == NULL + || headers_in[counter].val == NULL + + /* Already sent */ + || !strcasecmp(headers_in[counter].key, "Host") + + /* Clear out hop-by-hop request headers not to send + * RFC2616 13.5.1 says we should strip these headers + */ + || !strcasecmp(headers_in[counter].key, "Keep-Alive") + || !strcasecmp(headers_in[counter].key, "TE") + || !strcasecmp(headers_in[counter].key, "Trailer") + || !strcasecmp(headers_in[counter].key, "Upgrade") + + ) { + continue; + } + /* Do we want to strip Proxy-Authorization ? + * If we haven't used it, then NO + * If we have used it then MAYBE: RFC2616 says we MAY propagate it. + * So let's make it configurable by env. + */ + if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) { + if (r->user != NULL) { /* we've authenticated */ + if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { + continue; + } + } + } + + /* Skip Transfer-Encoding and Content-Length for now. + */ + if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) { + *old_te_val = headers_in[counter].val; + continue; + } + if (!strcasecmp(headers_in[counter].key, "Content-Length")) { + *old_cl_val = headers_in[counter].val; + continue; + } + + /* for sub-requests, ignore freshness/expiry headers */ + if (r->main) { + if ( !strcasecmp(headers_in[counter].key, "If-Match") + || !strcasecmp(headers_in[counter].key, "If-Modified-Since") + || !strcasecmp(headers_in[counter].key, "If-Range") + || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since") + || !strcasecmp(headers_in[counter].key, "If-None-Match")) { + continue; + } + } + + buf = apr_pstrcat(p, headers_in[counter].key, ": ", + headers_in[counter].val, CRLF, + NULL); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + } + return OK; +} + +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, + request_rec *r, proxy_conn_rec *p_conn, + conn_rec *origin, apr_bucket_brigade *bb, + int flush) +{ + apr_status_t status; + apr_off_t transferred; + + if (flush) { + apr_bucket *e = apr_bucket_flush_create(bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + apr_brigade_length(bb, 0, &transferred); + if (transferred != -1) + p_conn->worker->s->transferred += transferred; + status = ap_pass_brigade(origin->output_filters, bb); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084) + "pass request body failed to %pI (%s)", + p_conn->addr, p_conn->hostname); + if (origin->aborted) { + const char *ssl_note; + + if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv")) + != NULL) && (strcmp(ssl_note, "err") == 0)) { + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, + "Error during SSL Handshake with" + " remote server"); + } + return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY; + } + else { + return HTTP_BAD_REQUEST; + } + } + apr_brigade_cleanup(bb); + return OK; +} + void proxy_util_register_hooks(apr_pool_t *p) { APR_REGISTER_OPTIONAL_FN(ap_proxy_retry_worker); + APR_REGISTER_OPTIONAL_FN(ap_proxy_clear_connection); } |