diff options
author | Stefan Fritsch <sf@sfritsch.de> | 2016-07-05 23:20:42 +0200 |
---|---|---|
committer | Stefan Fritsch <sf@sfritsch.de> | 2016-07-05 23:20:42 +0200 |
commit | d5ffc4eb85d71c901c85119cf873e343349e97e2 (patch) | |
tree | 564636012ef7538ed4d7096b83c994dbda76c9db /modules/proxy | |
parent | 48eddd3d39fa2668ee29198ebfb33c41d4738c21 (diff) | |
download | apache2-d5ffc4eb85d71c901c85119cf873e343349e97e2.tar.gz |
Imported Upstream version 2.4.23upstream
Diffstat (limited to 'modules/proxy')
24 files changed, 1579 insertions, 150 deletions
diff --git a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile index dce99d16..d44644f0 100644 --- a/modules/proxy/NWGNUmakefile +++ b/modules/proxy/NWGNUmakefile @@ -161,6 +161,7 @@ TARGET_nlm = \ $(OBJDIR)/proxyfcgi.nlm \ $(OBJDIR)/proxyscgi.nlm \ $(OBJDIR)/proxyexpress.nlm \ + $(OBJDIR)/proxyhcheck.nlm \ $(OBJDIR)/proxylbm_busy.nlm \ $(OBJDIR)/proxylbm_hb.nlm \ $(OBJDIR)/proxylbm_req.nlm \ diff --git a/modules/proxy/balancers/config2.m4 b/modules/proxy/balancers/config2.m4 index f7232661..f6372815 100644 --- a/modules/proxy/balancers/config2.m4 +++ b/modules/proxy/balancers/config2.m4 @@ -1,8 +1,8 @@ APACHE_MODPATH_INIT(proxy/balancers) -APACHE_MODULE(lbmethod_byrequests, Apache proxy Load balancing by request counting, , , $proxy_mods_enable) -APACHE_MODULE(lbmethod_bytraffic, Apache proxy Load balancing by traffic counting, , , $proxy_mods_enable) -APACHE_MODULE(lbmethod_bybusyness, Apache proxy Load balancing by busyness, , , $proxy_mods_enable) -APACHE_MODULE(lbmethod_heartbeat, Apache proxy Load balancing from Heartbeats, , , $proxy_mods_enable) +APACHE_MODULE(lbmethod_byrequests, Apache proxy Load balancing by request counting, , , $enable_proxy_balancer, , proxy_balancer) +APACHE_MODULE(lbmethod_bytraffic, Apache proxy Load balancing by traffic counting, , , $enable_proxy_balancer, , proxy_balancer) +APACHE_MODULE(lbmethod_bybusyness, Apache proxy Load balancing by busyness, , , $enable_proxy_balancer, , proxy_balancer) +APACHE_MODULE(lbmethod_heartbeat, Apache proxy Load balancing from Heartbeats, , , $enable_proxy_balancer, , proxy_balancer) APACHE_MODPATH_FINISH diff --git a/modules/proxy/balancers/mod_lbmethod_bybusyness.mak b/modules/proxy/balancers/mod_lbmethod_bybusyness.mak index d793c776..4a04fd68 100644 --- a/modules/proxy/balancers/mod_lbmethod_bybusyness.mak +++ b/modules/proxy/balancers/mod_lbmethod_bybusyness.mak @@ -391,14 +391,14 @@ SOURCE=..\..\..\build\win32\httpd.rc "$(INTDIR)\mod_lbmethod_bybusyness.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" "$(INTDIR)\mod_lbmethod_bybusyness.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/balancers/mod_lbmethod_byrequests.mak b/modules/proxy/balancers/mod_lbmethod_byrequests.mak index f6c95ac5..b5914a21 100644 --- a/modules/proxy/balancers/mod_lbmethod_byrequests.mak +++ b/modules/proxy/balancers/mod_lbmethod_byrequests.mak @@ -391,14 +391,14 @@ SOURCE=..\..\..\build\win32\httpd.rc "$(INTDIR)\mod_lbmethod_byrequests.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" "$(INTDIR)\mod_lbmethod_byrequests.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/balancers/mod_lbmethod_bytraffic.mak b/modules/proxy/balancers/mod_lbmethod_bytraffic.mak index d3bd5e29..fe68c2bf 100644 --- a/modules/proxy/balancers/mod_lbmethod_bytraffic.mak +++ b/modules/proxy/balancers/mod_lbmethod_bytraffic.mak @@ -391,14 +391,14 @@ SOURCE=..\..\..\build\win32\httpd.rc "$(INTDIR)\mod_lbmethod_bytraffic.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" "$(INTDIR)\mod_lbmethod_bytraffic.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.mak b/modules/proxy/balancers/mod_lbmethod_heartbeat.mak index d699aaa8..31bd4af9 100644 --- a/modules/proxy/balancers/mod_lbmethod_heartbeat.mak +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.mak @@ -391,14 +391,14 @@ SOURCE=..\..\..\build\win32\httpd.rc "$(INTDIR)\mod_lbmethod_heartbeat.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" "$(INTDIR)\mod_lbmethod_heartbeat.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /i "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/config.m4 b/modules/proxy/config.m4 index ce625910..ebb13f00 100644 --- a/modules/proxy/config.m4 +++ b/modules/proxy/config.m4 @@ -2,16 +2,22 @@ dnl modules enabled in this directory by default APACHE_MODPATH_INIT(proxy) -if test "$enable_proxy" = "shared"; then - proxy_mods_enable=shared -elif test "$enable_proxy" = "yes"; then - proxy_mods_enable=yes -else - proxy_mods_enable=most -fi - proxy_objs="mod_proxy.lo proxy_util.lo" -APACHE_MODULE(proxy, Apache proxy module, $proxy_objs, , $proxy_mods_enable) +APACHE_MODULE(proxy, Apache proxy module, $proxy_objs, , most) + +dnl set aside module selections and default, and set the module default to the +dnl same scope (shared|static) as selected for mod proxy, along with setting +dnl the default selection to "most" for remaining proxy modules, mirroring the +dnl behavior of 2.4.1 and later, but failing ./configure only if an explicitly +dnl enabled module is missing its prereqs +save_module_selection=$module_selection +save_module_default=$module_default +if test "$enable_proxy" != "no"; then + module_selection=most + if test "$enable_proxy" = "shared" -o "$enable_proxy" = "static"; then + module_default=$enable_proxy + fi +fi proxy_connect_objs="mod_proxy_connect.lo" proxy_ftp_objs="mod_proxy_ftp.lo" @@ -39,11 +45,11 @@ case "$host" in ;; esac -APACHE_MODULE(proxy_connect, Apache proxy CONNECT module. Requires and is enabled by --enable-proxy., $proxy_connect_objs, , $proxy_mods_enable,, proxy) -APACHE_MODULE(proxy_ftp, Apache proxy FTP module. Requires and is enabled by --enable-proxy., $proxy_ftp_objs, , $proxy_mods_enable,, proxy) -APACHE_MODULE(proxy_http, Apache proxy HTTP module. Requires and is enabled by --enable-proxy., $proxy_http_objs, , $proxy_mods_enable,, proxy) -APACHE_MODULE(proxy_fcgi, Apache proxy FastCGI module. Requires and is enabled by --enable-proxy., $proxy_fcgi_objs, , $proxy_mods_enable,, proxy) -APACHE_MODULE(proxy_scgi, Apache proxy SCGI module. Requires and is enabled by --enable-proxy., $proxy_scgi_objs, , $proxy_mods_enable,, proxy) +APACHE_MODULE(proxy_connect, Apache proxy CONNECT module. Requires --enable-proxy., $proxy_connect_objs, , most, , proxy) +APACHE_MODULE(proxy_ftp, Apache proxy FTP module. Requires --enable-proxy., $proxy_ftp_objs, , most, , proxy) +APACHE_MODULE(proxy_http, Apache proxy HTTP module. Requires --enable-proxy., $proxy_http_objs, , most, , proxy) +APACHE_MODULE(proxy_fcgi, Apache proxy FastCGI module. Requires --enable-proxy., $proxy_fcgi_objs, , most, , proxy) +APACHE_MODULE(proxy_scgi, Apache proxy SCGI module. Requires --enable-proxy., $proxy_scgi_objs, , most, , proxy) APACHE_MODULE(proxy_fdpass, Apache proxy to Unix Daemon Socket module. Requires --enable-proxy., $proxy_fdpass_objs, , , [ AC_CHECK_DECL(CMSG_DATA,,, [ #include <sys/types.h> @@ -54,13 +60,17 @@ 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) +APACHE_MODULE(proxy_wstunnel, Apache proxy Websocket Tunnel module. Requires --enable-proxy., $proxy_wstunnel_objs, , most, , proxy) +APACHE_MODULE(proxy_ajp, Apache proxy AJP module. Requires --enable-proxy., $proxy_ajp_objs, , most, , proxy) +APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module. Requires --enable-proxy., $proxy_balancer_objs, , most, , proxy) -APACHE_MODULE(proxy_express, mass reverse-proxy module. Requires --enable-proxy., , , $proxy_mods_enable,, proxy) +APACHE_MODULE(proxy_express, mass reverse-proxy module. Requires --enable-proxy., , , most, , proxy) +APACHE_MODULE(proxy_hcheck, [reverse-proxy health-check module. Requires --enable-proxy and --enable-watchdog.], , , most, , [proxy,watchdog]) APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) +module_selection=$save_module_selection +module_default=$save_module_default + APACHE_MODPATH_FINISH diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c index 9a584333..cdcda4f3 100644 --- a/modules/proxy/mod_proxy.c +++ b/modules/proxy/mod_proxy.c @@ -36,6 +36,40 @@ APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup, #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #endif +/* + * We do health-checks only if that (sub)module is loaded in. This + * allows for us to continue as is w/o requiring mod_watchdog for + * those implementations which aren't using health checks + */ +static APR_OPTIONAL_FN_TYPE(set_worker_hc_param) *set_worker_hc_param_f = NULL; + +/* Externals */ +proxy_hcmethods_t PROXY_DECLARE_DATA proxy_hcmethods[] = { + {NONE, "NONE", 1}, + {TCP, "TCP", 1}, + {OPTIONS, "OPTIONS", 1}, + {HEAD, "HEAD", 1}, + {GET, "GET", 1}, + {CPING, "CPING", 0}, + {PROVIDER, "PROVIDER", 0}, + {EOT, NULL, 1} +}; + +proxy_wstat_t PROXY_DECLARE_DATA proxy_wstat_tbl[] = { + {PROXY_WORKER_INITIALIZED, PROXY_WORKER_INITIALIZED_FLAG, "Init "}, + {PROXY_WORKER_IGNORE_ERRORS, PROXY_WORKER_IGNORE_ERRORS_FLAG, "Ign "}, + {PROXY_WORKER_DRAIN, PROXY_WORKER_DRAIN_FLAG, "Drn "}, + {PROXY_WORKER_GENERIC, PROXY_WORKER_GENERIC_FLAG, "Gen "}, + {PROXY_WORKER_IN_SHUTDOWN, PROXY_WORKER_IN_SHUTDOWN_FLAG, "Shut "}, + {PROXY_WORKER_DISABLED, PROXY_WORKER_DISABLED_FLAG, "Dis "}, + {PROXY_WORKER_STOPPED, PROXY_WORKER_STOPPED_FLAG, "Stop "}, + {PROXY_WORKER_IN_ERROR, PROXY_WORKER_IN_ERROR_FLAG, "Err "}, + {PROXY_WORKER_HOT_STANDBY, PROXY_WORKER_HOT_STANDBY_FLAG, "Stby "}, + {PROXY_WORKER_FREE, PROXY_WORKER_FREE_FLAG, "Free "}, + {PROXY_WORKER_HC_FAIL, PROXY_WORKER_HC_FAIL_FLAG, "HcFl "}, + {0x0, '\0', NULL} +}; + static const char * const proxy_id = "proxy"; apr_global_mutex_t *proxy_mutex = NULL; @@ -56,6 +90,7 @@ apr_global_mutex_t *proxy_mutex = NULL; /* Translate the URL into a 'filename' */ static const char *set_worker_param(apr_pool_t *p, + server_rec *s, proxy_worker *worker, const char *key, const char *val) @@ -274,7 +309,11 @@ static const char *set_worker_param(apr_pool_t *p, PROXY_STRNCPY(worker->s->flusher, val); } else { - return "unknown Worker parameter"; + if (set_worker_hc_param_f) { + return set_worker_hc_param_f(p, s, worker, key, val, NULL); + } else { + return "unknown Worker parameter"; + } } return NULL; } @@ -1170,7 +1209,8 @@ static int proxy_handler(request_rec *r) * We can not failover to another worker. * Mark the worker as unusable if member of load balancer */ - if (balancer) { + if (balancer + && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { worker->s->status |= PROXY_WORKER_IN_ERROR; worker->s->error_time = apr_time_now(); } @@ -1181,7 +1221,8 @@ static int proxy_handler(request_rec *r) * We can failover to another worker * Mark the worker as unusable if member of load balancer */ - if (balancer) { + if (balancer + && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { worker->s->status |= PROXY_WORKER_IN_ERROR; worker->s->error_time = apr_time_now(); } @@ -1675,7 +1716,7 @@ static const char * "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing", 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, + const char *err = set_worker_param(cmd->pool, s, worker, elts[i].key, elts[i].val); if (err) return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL); @@ -2159,7 +2200,7 @@ static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing", elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker)); } else { - err = set_worker_param(cmd->pool, worker, elts[i].key, + err = set_worker_param(cmd->pool, cmd->server, worker, elts[i].key, elts[i].val); if (err) return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); @@ -2244,7 +2285,7 @@ static const char * else *val++ = '\0'; if (worker) - err = set_worker_param(cmd->pool, worker, word, val); + err = set_worker_param(cmd->pool, cmd->server, worker, word, val); else err = set_balancer_param(conf, cmd->pool, balancer, word, val); @@ -2383,7 +2424,7 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) else *val++ = '\0'; if (worker) - err = set_worker_param(cmd->pool, worker, word, val); + err = set_worker_param(cmd->pool, cmd->server, worker, word, val); else err = set_balancer_param(sconf, cmd->pool, balancer, word, val); @@ -2745,6 +2786,7 @@ static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog, APR_HOOK_MIDDLE); /* Reset workers count on gracefull restart */ proxy_lb_workers = 0; + set_worker_hc_param_f = APR_RETRIEVE_OPTIONAL_FN(set_worker_hc_param); return OK; } static void register_hooks(apr_pool_t *p) @@ -2757,8 +2799,8 @@ static void register_hooks(apr_pool_t *p) * make sure that we are called after the mpm * initializes. */ - static const char *const aszPred[] = { "mpm_winnt.c", "mod_proxy_balancer.c", NULL}; - + static const char *const aszPred[] = { "mpm_winnt.c", "mod_proxy_balancer.c", + "mod_proxy_hcheck.c", NULL}; /* handler */ ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST); /* filename-to-URI translation */ diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 72dab333..f1413c56 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -75,6 +75,22 @@ enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm }; +typedef enum { + NONE, TCP, OPTIONS, HEAD, GET, CPING, PROVIDER, EOT +} hcmethod_t; + +typedef struct { + hcmethod_t method; + char *name; + int implemented; +} proxy_hcmethods_t; + +typedef struct { + unsigned int bit; + char flag; + const char *name; +} proxy_wstat_t; + #define BALANCER_PREFIX "balancer://" #if APR_CHARSET_EBCDIC @@ -139,7 +155,7 @@ typedef struct { proxy_worker *reverse; /* reverse "module-driven" proxy worker */ const char *domain; /* domain name to use in absence of a domain name in the request */ const char *id; - apr_pool_t *pool; /* Pool used for allocating this struct */ + apr_pool_t *pool; /* Pool used for allocating this struct's elements */ int req; /* true if proxy requests are enabled */ int max_balancers; /* maximum number of allowed balancers */ int bgrowth; /* number of post-config balancers can added */ @@ -270,8 +286,11 @@ struct proxy_conn_pool { proxy_conn_rec *conn; /* Single connection for prefork mpm */ }; -/* Keep below in sync with proxy_util.c! */ /* worker status bits */ +/* + * NOTE: Keep up-to-date w/ proxy_wstat_tbl[] + * in mod_proxy.c ! + */ #define PROXY_WORKER_INITIALIZED 0x0001 #define PROXY_WORKER_IGNORE_ERRORS 0x0002 #define PROXY_WORKER_DRAIN 0x0004 @@ -282,6 +301,7 @@ struct proxy_conn_pool { #define PROXY_WORKER_IN_ERROR 0x0080 #define PROXY_WORKER_HOT_STANDBY 0x0100 #define PROXY_WORKER_FREE 0x0200 +#define PROXY_WORKER_HC_FAIL 0x0400 /* worker status flags */ #define PROXY_WORKER_INITIALIZED_FLAG 'O' @@ -294,9 +314,11 @@ struct proxy_conn_pool { #define PROXY_WORKER_IN_ERROR_FLAG 'E' #define PROXY_WORKER_HOT_STANDBY_FLAG 'H' #define PROXY_WORKER_FREE_FLAG 'F' +#define PROXY_WORKER_HC_FAIL_FLAG 'C' #define PROXY_WORKER_NOT_USABLE_BITMAP ( PROXY_WORKER_IN_SHUTDOWN | \ -PROXY_WORKER_DISABLED | PROXY_WORKER_STOPPED | PROXY_WORKER_IN_ERROR ) +PROXY_WORKER_DISABLED | PROXY_WORKER_STOPPED | PROXY_WORKER_IN_ERROR | \ +PROXY_WORKER_HC_FAIL ) /* NOTE: these check the shared status */ #define PROXY_WORKER_IS_INITIALIZED(f) ( (f)->s->status & PROXY_WORKER_INITIALIZED ) @@ -310,6 +332,10 @@ PROXY_WORKER_DISABLED | PROXY_WORKER_STOPPED | PROXY_WORKER_IN_ERROR ) #define PROXY_WORKER_IS_GENERIC(f) ( (f)->s->status & PROXY_WORKER_GENERIC ) +#define PROXY_WORKER_IS_HCFAILED(f) ( (f)->s->status & PROXY_WORKER_HC_FAIL ) + +#define PROXY_WORKER_IS(f, b) ( (f)->s->status & (b) ) + /* default worker retry timeout in seconds */ #define PROXY_WORKER_DEFAULT_RETRY 60 @@ -349,6 +375,7 @@ typedef struct { } proxy_hashes ; /* Runtime worker status informations. Shared in scoreboard */ +/* The addition of member uds_path in 2.4.7 was an incompatible API change. */ typedef struct { char name[PROXY_WORKER_MAX_NAME_SIZE]; char scheme[PROXY_WORKER_MAX_SCHEME_SIZE]; /* scheme to use ajp|http|https */ @@ -403,6 +430,14 @@ typedef struct { unsigned int keepalive_set:1; unsigned int disablereuse_set:1; unsigned int was_malloced:1; + char hcuri[PROXY_WORKER_MAX_ROUTE_SIZE]; /* health check uri */ + char hcexpr[PROXY_WORKER_MAX_SCHEME_SIZE]; /* name of condition expr for health check */ + int passes; /* number of successes for check to pass */ + int pcount; /* current count of passes */ + int fails; /* number of failures for check to fail */ + int fcount; /* current count of failures */ + hcmethod_t method; /* method to use for health check */ + apr_interval_time_t interval; } proxy_worker_shared; #define ALIGNED_PROXY_WORKER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_worker_shared))) @@ -418,6 +453,11 @@ struct proxy_worker { void *context; /* general purpose storage */ }; +/* default to health check every 30 seconds */ +#define HCHECK_WATHCHDOG_DEFAULT_INTERVAL (30) +/* The watchdog runs every 2 seconds, which is also the minimal check */ +#define HCHECK_WATHCHDOG_INTERVAL (2) + /* * Time to wait (in microseconds) to find out if more data is currently * available at the backend. @@ -508,6 +548,26 @@ struct proxy_balancer_method { #define PROXY_DECLARE_DATA __declspec(dllimport) #endif +/* Using PROXY_DECLARE_OPTIONAL_HOOK instead of + * APR_DECLARE_EXTERNAL_HOOK allows build/make_nw_export.awk + * to distinguish between hooks that implement + * proxy_hook_xx and proxy_hook_get_xx in mod_proxy.c and + * those which don't. + */ +#define PROXY_DECLARE_OPTIONAL_HOOK APR_DECLARE_EXTERNAL_HOOK + +/* These 2 are in mod_proxy.c */ +extern PROXY_DECLARE_DATA proxy_hcmethods_t proxy_hcmethods[]; +extern PROXY_DECLARE_DATA proxy_wstat_t proxy_wstat_tbl[]; + +/* Following 4 from health check */ +APR_DECLARE_OPTIONAL_FN(void, hc_show_exprs, (request_rec *)); +APR_DECLARE_OPTIONAL_FN(void, hc_select_exprs, (request_rec *, const char *)); +APR_DECLARE_OPTIONAL_FN(int, hc_valid_expr, (request_rec *, const char *)); +APR_DECLARE_OPTIONAL_FN(const char *, set_worker_hc_param, + (apr_pool_t *, server_rec *, proxy_worker *, + const char *, const char *, void *)); + APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, scheme_handler, (request_rec *r, proxy_worker *worker, proxy_server_conf *conf, char *url, const char *proxyhost, apr_port_t proxyport)) @@ -1019,6 +1079,12 @@ PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, APR_DECLARE_OPTIONAL_FN(int, ap_proxy_clear_connection, (request_rec *r, apr_table_t *headers)); +/** + * @param socket socket to test + * @return TRUE if socket is connected/active + */ +PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket); + #define PROXY_LBMETHOD "proxylbmethod" /* The number of dynamic workers that can be added when reconfiguring. @@ -1040,6 +1106,13 @@ int ap_proxy_lb_workers(void); PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme); /** + * Return the name of the health check method (eg: "OPTIONS"). + * @param method method enum + * @return name of method + */ +PROXY_DECLARE (const char *) ap_proxy_show_hcmethod(hcmethod_t method); + +/** * Strip a unix domain socket (UDS) prefix from the input URL * @param p pool to allocate result from * @param url a URL potentially prefixed with a UDS path diff --git a/modules/proxy/mod_proxy.mak b/modules/proxy/mod_proxy.mak index 53a9df6d..98737d63 100644 --- a/modules/proxy/mod_proxy.mak +++ b/modules/proxy/mod_proxy.mak @@ -344,14 +344,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" "$(INTDIR)\mod_proxy.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_ajp.mak b/modules/proxy/mod_proxy_ajp.mak index 30e2a110..b14a569a 100644 --- a/modules/proxy/mod_proxy_ajp.mak +++ b/modules/proxy/mod_proxy_ajp.mak @@ -399,14 +399,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_ajp.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_ajp - Win32 Debug" "$(INTDIR)\mod_proxy_ajp.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c index 702650a9..69ad5dce 100644 --- a/modules/proxy/mod_proxy_balancer.c +++ b/modules/proxy/mod_proxy_balancer.c @@ -28,9 +28,16 @@ ap_slotmem_provider_t *storage = NULL; module AP_MODULE_DECLARE_DATA proxy_balancer_module; +static APR_OPTIONAL_FN_TYPE(set_worker_hc_param) *set_worker_hc_param_f = NULL; + static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, proxy_worker *worker, server_rec *s) = NULL; +static APR_OPTIONAL_FN_TYPE(hc_show_exprs) *hc_show_exprs_f = NULL; +static APR_OPTIONAL_FN_TYPE(hc_select_exprs) *hc_select_exprs_f = NULL; +static APR_OPTIONAL_FN_TYPE(hc_valid_expr) *hc_valid_expr_f = NULL; + + /* * Register our mutex type before the config is read so we * can adjust the mutex settings using the Mutex directive. @@ -46,7 +53,10 @@ static int balancer_pre_config(apr_pool_t *pconf, apr_pool_t *plog, if (rv != APR_SUCCESS) { return rv; } - + set_worker_hc_param_f = APR_RETRIEVE_OPTIONAL_FN(set_worker_hc_param); + hc_show_exprs_f = APR_RETRIEVE_OPTIONAL_FN(hc_show_exprs); + hc_select_exprs_f = APR_RETRIEVE_OPTIONAL_FN(hc_select_exprs); + hc_valid_expr_f = APR_RETRIEVE_OPTIONAL_FN(hc_valid_expr); return OK; } @@ -633,7 +643,8 @@ static int proxy_balancer_post_request(proxy_worker *worker, return HTTP_INTERNAL_SERVER_ERROR; } - if (!apr_is_empty_array(balancer->errstatuses)) { + if (!apr_is_empty_array(balancer->errstatuses) + && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { int i; for (i = 0; i < balancer->errstatuses->nelts; i++) { int val = ((int *)balancer->errstatuses->elts)[i]; @@ -652,6 +663,7 @@ static int proxy_balancer_post_request(proxy_worker *worker, } if (balancer->failontimeout + && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS) && (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 " @@ -920,10 +932,10 @@ static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog, static void create_radio(const char *name, unsigned int flag, request_rec *r) { - ap_rvputs(r, "<td>On <input name='", name, "' id='", name, "' value='1' type=radio", NULL); + ap_rvputs(r, "<td><label for='", name, "1'>On</label> <input name='", name, "' id='", name, "1' value='1' type=radio", NULL); if (flag) ap_rputs(" checked", r); - ap_rvputs(r, "> <br/> Off <input name='", name, "' id='", name, "' value='0' type=radio", NULL); + ap_rvputs(r, "> <br/> <label for='", name, "0'>Off</label> <input name='", name, "' id='", name, "0' value='0' type=radio", NULL); if (!flag) ap_rputs(" checked", r); ap_rputs("></td>\n", r); @@ -1093,17 +1105,27 @@ static int balancer_handler(request_rec *r) else *wsel->s->redirect = '\0'; } + /* + * TODO: Look for all 'w_status_#' keys and then loop thru + * on that # character, since the character == the flag + */ if ((val = apr_table_get(params, "w_status_I"))) { - ap_proxy_set_wstatus('I', atoi(val), wsel); + ap_proxy_set_wstatus(PROXY_WORKER_IGNORE_ERRORS_FLAG, atoi(val), wsel); } if ((val = apr_table_get(params, "w_status_N"))) { - ap_proxy_set_wstatus('N', atoi(val), wsel); + ap_proxy_set_wstatus(PROXY_WORKER_DRAIN_FLAG, atoi(val), wsel); } if ((val = apr_table_get(params, "w_status_D"))) { - ap_proxy_set_wstatus('D', atoi(val), wsel); + ap_proxy_set_wstatus(PROXY_WORKER_DISABLED_FLAG, atoi(val), wsel); } if ((val = apr_table_get(params, "w_status_H"))) { - ap_proxy_set_wstatus('H', atoi(val), wsel); + ap_proxy_set_wstatus(PROXY_WORKER_HOT_STANDBY_FLAG, atoi(val), wsel); + } + if ((val = apr_table_get(params, "w_status_S"))) { + ap_proxy_set_wstatus(PROXY_WORKER_STOPPED_FLAG, atoi(val), wsel); + } + if ((val = apr_table_get(params, "w_status_C"))) { + ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, atoi(val), wsel); } if ((val = apr_table_get(params, "w_ls"))) { int ival = atoi(val); @@ -1111,6 +1133,47 @@ static int balancer_handler(request_rec *r) wsel->s->lbset = ival; } } + if ((val = apr_table_get(params, "w_hi"))) { + int ival = atoi(val); + if (ival >= HCHECK_WATHCHDOG_INTERVAL) { + wsel->s->interval = apr_time_from_sec(ival); + } + } + if ((val = apr_table_get(params, "w_hp"))) { + int ival = atoi(val); + if (ival >= 1) { + wsel->s->passes = ival; + } + } + if ((val = apr_table_get(params, "w_hf"))) { + int ival = atoi(val); + if (ival >= 1) { + wsel->s->fails = ival; + } + } + if ((val = apr_table_get(params, "w_hm"))) { + proxy_hcmethods_t *method = proxy_hcmethods; + for (; method->name; method++) { + if (!strcasecmp(method->name, val) && method->implemented) + wsel->s->method = method->method; + } + } + if ((val = apr_table_get(params, "w_hu"))) { + if (strlen(val) && strlen(val) < sizeof(wsel->s->hcuri)) + strcpy(wsel->s->hcuri, val); + else + *wsel->s->hcuri = '\0'; + } + if (hc_valid_expr_f && (val = apr_table_get(params, "w_he"))) { + if (strlen(val) && hc_valid_expr_f(r, val) && strlen(val) < sizeof(wsel->s->hcexpr)) + strcpy(wsel->s->hcexpr, val); + else + *wsel->s->hcexpr = '\0'; + } + /* If the health check method doesn't support an expr, then null it */ + if (wsel->s->method == NONE || wsel->s->method == TCP) { + *wsel->s->hcexpr = '\0'; + } /* if enabling, we need to reset all lb params */ if (bsel && !was_usable && PROXY_WORKER_IS_USABLE(wsel)) { bsel->s->need_reset = 1; @@ -1228,7 +1291,7 @@ static int balancer_handler(request_rec *r) /* sync all timestamps */ bsel->wupdated = bsel->s->wupdated = nworker->s->updated = apr_time_now(); /* by default, all new workers are disabled */ - ap_proxy_set_wstatus('D', 1, nworker); + ap_proxy_set_wstatus(PROXY_WORKER_DISABLED_FLAG, 1, nworker); } if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01203) @@ -1331,18 +1394,7 @@ static int balancer_handler(request_rec *r) } /* Begin proxy_worker_stat */ ap_rputs(" <httpd:status>", r); - if (worker->s->status & PROXY_WORKER_DISABLED) - ap_rputs("Disabled", r); - else if (worker->s->status & PROXY_WORKER_IN_ERROR) - ap_rputs("Error", r); - else if (worker->s->status & PROXY_WORKER_STOPPED) - ap_rputs("Stopped", r); - else if (worker->s->status & PROXY_WORKER_HOT_STANDBY) - ap_rputs("Standby", r); - else if (PROXY_WORKER_IS_USABLE(worker)) - ap_rputs("OK", r); - else if (!PROXY_WORKER_IS_INITIALIZED(worker)) - ap_rputs("Uninitialized", r); + ap_rputs(ap_proxy_parse_wstatus(r->pool, worker), r); ap_rputs("</httpd:status>\n", r); if ((worker->s->error_time > 0) && apr_rfc822_date(date, worker->s->error_time) == APR_SUCCESS) { ap_rvputs(r, " <httpd:error_time>", date, @@ -1447,7 +1499,7 @@ static int balancer_handler(request_rec *r) " padding: 2px;\n" " border-style: dotted;\n" " border-color: gray;\n" - " background-color: white;\n" + " background-color: lightgray;\n" " text-align: center;\n" "}\n" "td {\n" @@ -1477,10 +1529,10 @@ static int balancer_handler(request_rec *r) for (i = 0; i < conf->balancers->nelts; i++) { ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r); - ap_rvputs(r, "<a href=\"", ap_escape_uri(r->pool, r->uri), "?b=", + ap_rvputs(r, "<a href='", ap_escape_uri(r->pool, r->uri), "?b=", balancer->s->name + sizeof(BALANCER_PREFIX) - 1, - "&nonce=", balancer->s->nonce, - "\">", NULL); + "&nonce=", balancer->s->nonce, + "'>", NULL); ap_rvputs(r, balancer->s->name, "</a> [",balancer->s->sname, "]</h3>\n", NULL); ap_rputs("\n\n<table><tr>" "<th>MaxMembers</th><th>StickySession</th><th>DisableFailover</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>" @@ -1501,9 +1553,9 @@ static int balancer_handler(request_rec *r) else { ap_rputs("<td> (None) ", r); } - ap_rprintf(r, "<td>%s</td>\n", + ap_rprintf(r, "</td><td>%s</td>\n", balancer->s->sticky_force ? "On" : "Off"); - ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>", + ap_rprintf(r, "<td>%" APR_TIME_T_FMT "</td>", apr_time_sec(balancer->s->timeout)); ap_rprintf(r, "<td>%d</td>\n", balancer->s->max_attempts); ap_rprintf(r, "<td>%s</td>\n", @@ -1520,19 +1572,22 @@ static int balancer_handler(request_rec *r) "<th>Worker URL</th>" "<th>Route</th><th>RouteRedir</th>" "<th>Factor</th><th>Set</th><th>Status</th>" - "<th>Elected</th><th>Busy</th><th>Load</th><th>To</th><th>From</th>" - "</tr>\n", r); + "<th>Elected</th><th>Busy</th><th>Load</th><th>To</th><th>From</th>", r); + if (set_worker_hc_param_f) { + ap_rputs("<th>HC Method</th><th>HC Interval</th><th>Passes</th><th>Fails</th><th>HC uri</th><th>HC Expr</th>", r); + } + ap_rputs("</tr>\n", r); workers = (proxy_worker **)balancer->workers->elts; for (n = 0; n < balancer->workers->nelts; n++) { char fbuf[50]; worker = *workers; - ap_rvputs(r, "<tr>\n<td><a href=\"", + ap_rvputs(r, "<tr>\n<td><a href='", ap_escape_uri(r->pool, r->uri), "?b=", - balancer->s->name + sizeof(BALANCER_PREFIX) - 1, "&w=", + balancer->s->name + sizeof(BALANCER_PREFIX) - 1, "&w=", ap_escape_uri(r->pool, worker->s->name), - "&nonce=", balancer->s->nonce, - "\">", NULL); + "&nonce=", balancer->s->nonce, + "'>", NULL); ap_rvputs(r, (*worker->s->uds_path ? "<i>" : ""), ap_proxy_worker_name(r->pool, worker), (*worker->s->uds_path ? "</i>" : ""), "</a></td>", NULL); ap_rvputs(r, "<td>", ap_escape_html(r->pool, worker->s->route), @@ -1549,6 +1604,14 @@ static int balancer_handler(request_rec *r) ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r); ap_rputs("</td><td>", r); ap_rputs(apr_strfsize(worker->s->read, fbuf), r); + if (set_worker_hc_param_f) { + ap_rprintf(r, "</td><td>%s</td>", ap_proxy_show_hcmethod(worker->s->method)); + ap_rprintf(r, "<td>%d</td>", (int)apr_time_sec(worker->s->interval)); + ap_rprintf(r, "<td>%d (%d)</td>", worker->s->passes,worker->s->pcount); + ap_rprintf(r, "<td>%d (%d)</td>", worker->s->fails, worker->s->fcount); + ap_rprintf(r, "<td>%s</td>", worker->s->hcuri); + ap_rprintf(r, "<td>%s", worker->s->hcexpr); + } ap_rputs("</td></tr>\n", r); ++workers; @@ -1557,35 +1620,72 @@ static int balancer_handler(request_rec *r) ++balancer; } ap_rputs("<hr />\n", r); + if (hc_show_exprs_f) { + hc_show_exprs_f(r); + } if (wsel && bsel) { ap_rputs("<h3>Edit worker settings for ", r); ap_rvputs(r, (*wsel->s->uds_path?"<i>":""), ap_proxy_worker_name(r->pool, wsel), (*wsel->s->uds_path?"</i>":""), "</h3>\n", NULL); - ap_rputs("<form method=\"POST\" enctype=\"application/x-www-form-urlencoded\" action=\"", r); - ap_rvputs(r, ap_escape_uri(r->pool, action), "\">\n", NULL); - ap_rputs("<dl>\n<table><tr><td>Load factor:</td><td><input name='w_lf' id='w_lf' type=text ", r); + ap_rputs("<form method='POST' enctype='application/x-www-form-urlencoded' action='", r); + ap_rvputs(r, ap_escape_uri(r->pool, action), "'>\n", NULL); + ap_rputs("<table><tr><td>Load factor:</td><td><input name='w_lf' id='w_lf' type=text ", r); ap_rprintf(r, "value='%d'></td></tr>\n", wsel->s->lbfactor); ap_rputs("<tr><td>LB Set:</td><td><input name='w_ls' id='w_ls' type=text ", r); ap_rprintf(r, "value='%d'></td></tr>\n", wsel->s->lbset); ap_rputs("<tr><td>Route:</td><td><input name='w_wr' id='w_wr' type=text ", r); - ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->route), + ap_rvputs(r, "value='", ap_escape_html(r->pool, wsel->s->route), NULL); - ap_rputs("\"></td></tr>\n", r); + ap_rputs("'></td></tr>\n", r); ap_rputs("<tr><td>Route Redirect:</td><td><input name='w_rr' id='w_rr' type=text ", r); - ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->redirect), + ap_rvputs(r, "value='", ap_escape_html(r->pool, wsel->s->redirect), NULL); - ap_rputs("\"></td></tr>\n", r); + ap_rputs("'></td></tr>\n", r); ap_rputs("<tr><td>Status:</td>", r); ap_rputs("<td><table><tr>" "<th>Ignore Errors</th>" "<th>Draining Mode</th>" "<th>Disabled</th>" - "<th>Hot Standby</th></tr>\n<tr>", r); - create_radio("w_status_I", (PROXY_WORKER_IGNORE_ERRORS & wsel->s->status), r); - create_radio("w_status_N", (PROXY_WORKER_DRAIN & wsel->s->status), r); - create_radio("w_status_D", (PROXY_WORKER_DISABLED & wsel->s->status), r); - create_radio("w_status_H", (PROXY_WORKER_HOT_STANDBY & wsel->s->status), r); - ap_rputs("</tr></table>\n", r); - ap_rputs("<tr><td colspan=2><input type=submit value='Submit'></td></tr>\n", r); + "<th>Hot Standby</th>", r); + if (hc_show_exprs_f) { + ap_rputs("<th>HC Fail</th>", r); + } + ap_rputs("<th>Stopped</th></tr>\n<tr>", r); + create_radio("w_status_I", (PROXY_WORKER_IS(wsel, PROXY_WORKER_IGNORE_ERRORS)), r); + create_radio("w_status_N", (PROXY_WORKER_IS(wsel, PROXY_WORKER_DRAIN)), r); + create_radio("w_status_D", (PROXY_WORKER_IS(wsel, PROXY_WORKER_DISABLED)), r); + create_radio("w_status_H", (PROXY_WORKER_IS(wsel, PROXY_WORKER_HOT_STANDBY)), r); + if (hc_show_exprs_f) { + create_radio("w_status_C", (PROXY_WORKER_IS(wsel, PROXY_WORKER_HC_FAIL)), r); + } + create_radio("w_status_S", (PROXY_WORKER_IS(wsel, PROXY_WORKER_STOPPED)), r); + ap_rputs("</tr></table></td></tr>\n", r); + if (hc_select_exprs_f) { + proxy_hcmethods_t *method = proxy_hcmethods; + ap_rputs("<tr><td colspan='2'>\n<table align='center'><tr><th>Health Check param</th><th>Value</th></tr>\n", r); + ap_rputs("<tr><td>Method</td><td><select name='w_hm'>\n", r); + for (; method->name; method++) { + if (method->implemented) { + ap_rprintf(r, "<option value='%s' %s >%s</option>\n", + method->name, + (wsel->s->method == method->method) ? "selected" : "", + method->name); + } + } + ap_rputs("</select>\n</td></tr>\n", r); + ap_rputs("<tr><td>Expr</td><td><select name='w_he'>\n", r); + hc_select_exprs_f(r, wsel->s->hcexpr); + ap_rputs("</select>\n</td></tr>\n", r); + ap_rprintf(r, "<tr><td>Interval (secs)</td><td><input name='w_hi' id='w_hi' type='text'" + "value='%d'></td></tr>\n", (int)apr_time_sec(wsel->s->interval)); + ap_rprintf(r, "<tr><td>Passes trigger</td><td><input name='w_hp' id='w_hp' type='text'" + "value='%d'></td></tr>\n", wsel->s->passes); + ap_rprintf(r, "<tr><td>Fails trigger)</td><td><input name='w_hf' id='w_hf' type='text'" + "value='%d'></td></tr>\n", wsel->s->fails); + ap_rprintf(r, "<tr><td>HC uri</td><td><input name='w_hu' id='w_hu' type='text'" + "value='%s'</td></tr>\n", ap_escape_html(r->pool, wsel->s->hcuri)); + ap_rputs("</table>\n</td></tr>\n", r); + } + ap_rputs("<tr><td colspan='2'><input type=submit value='Submit'></td></tr>\n", r); ap_rvputs(r, "</table>\n<input type=hidden name='w' id='w' ", NULL); ap_rvputs(r, "value='", ap_escape_uri(r->pool, wsel->s->name), "'>\n", NULL); ap_rvputs(r, "<input type=hidden name='b' id='b' ", NULL); @@ -1603,7 +1703,7 @@ static int balancer_handler(request_rec *r) ap_rvputs(r, bsel->s->name, "</h3>\n", NULL); ap_rputs("<form method='POST' enctype='application/x-www-form-urlencoded' action='", r); ap_rvputs(r, ap_escape_uri(r->pool, action), "'>\n", NULL); - ap_rputs("<dl>\n<table>\n", r); + ap_rputs("<table>\n", r); provs = ap_list_provider_names(r->pool, PROXY_LBMETHOD, "0"); if (provs) { ap_rputs("<tr><td>LBmethod:</td>", r); diff --git a/modules/proxy/mod_proxy_balancer.mak b/modules/proxy/mod_proxy_balancer.mak index 02561661..86d7ec5e 100644 --- a/modules/proxy/mod_proxy_balancer.mak +++ b/modules/proxy/mod_proxy_balancer.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_balancer.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_balancer - Win32 Debug" "$(INTDIR)\mod_proxy_balancer.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_connect.mak b/modules/proxy/mod_proxy_connect.mak index 40d0069b..be354dbf 100644 --- a/modules/proxy/mod_proxy_connect.mak +++ b/modules/proxy/mod_proxy_connect.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_connect.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" "$(INTDIR)\mod_proxy_connect.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_express.mak b/modules/proxy/mod_proxy_express.mak index 8bd8f29f..f656d226 100644 --- a/modules/proxy/mod_proxy_express.mak +++ b/modules/proxy/mod_proxy_express.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_express.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_express - Win32 Debug" "$(INTDIR)\mod_proxy_express.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c index 90c63c3b..19047ff6 100644 --- a/modules/proxy/mod_proxy_fcgi.c +++ b/modules/proxy/mod_proxy_fcgi.c @@ -262,11 +262,21 @@ static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r, } } - /* Strip balancer prefix */ - if (r->filename && !strncmp(r->filename, "proxy:balancer://", 17)) { - char *newfname = apr_pstrdup(r->pool, r->filename+17); - newfname = ap_strchr(newfname, '/'); - r->filename = newfname; + /* Strip proxy: prefixes */ + if (r->filename) { + char *newfname = NULL; + + if (!strncmp(r->filename, "proxy:balancer://", 17)) { + newfname = apr_pstrdup(r->pool, r->filename+17); + } + else if (!strncmp(r->filename, "proxy:fcgi://", 13)) { + newfname = apr_pstrdup(r->pool, r->filename+13); + } + + if (newfname) { + newfname = ap_strchr(newfname, '/'); + r->filename = newfname; + } } ap_add_common_vars(r); @@ -876,17 +886,17 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, char server_portstr[32]; conn_rec *origin = NULL; proxy_conn_rec *backend = NULL; + apr_uri_t *uri; proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module); apr_pool_t *p = r->pool; - apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri)); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01076) "url: %s proxyname: %s proxyport: %d", - url, proxyname, proxyport); + url, proxyname, proxyport); if (strncasecmp(url, "fcgi:", 5) != 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url); @@ -909,6 +919,7 @@ static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, backend->is_ssl = 0; /* Step One: Determine Who To Connect To */ + uri = apr_palloc(p, sizeof(*uri)); status = ap_proxy_determine_connection(p, r, conf, worker, backend, uri, &url, proxyname, proxyport, server_portstr, diff --git a/modules/proxy/mod_proxy_fcgi.mak b/modules/proxy/mod_proxy_fcgi.mak index d21bc019..4b150889 100644 --- a/modules/proxy/mod_proxy_fcgi.mak +++ b/modules/proxy/mod_proxy_fcgi.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_fcgi.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" "$(INTDIR)\mod_proxy_fcgi.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_ftp.mak b/modules/proxy/mod_proxy_ftp.mak index 323e8071..0b1ca30d 100644 --- a/modules/proxy/mod_proxy_ftp.mak +++ b/modules/proxy/mod_proxy_ftp.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_ftp.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" "$(INTDIR)\mod_proxy_ftp.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_hcheck.c b/modules/proxy/mod_proxy_hcheck.c new file mode 100644 index 00000000..6606652e --- /dev/null +++ b/modules/proxy/mod_proxy_hcheck.c @@ -0,0 +1,1175 @@ +/* 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" +#include "mod_watchdog.h" +#include "ap_slotmem.h" +#include "ap_expr.h" +#if APR_HAS_THREADS +#include "apr_thread_pool.h" +#endif + +module AP_MODULE_DECLARE_DATA proxy_hcheck_module; + +#define HCHECK_WATHCHDOG_NAME ("_proxy_hcheck_") +#define HC_THREADPOOL_SIZE (16) + +/* Why? So we can easily set/clear HC_USE_THREADS during dev testing */ +#if APR_HAS_THREADS +#define HC_USE_THREADS 1 +#else +#define HC_USE_THREADS 0 +typedef void apr_thread_pool_t; +#endif + +typedef struct { + char *name; + hcmethod_t method; + int passes; + int fails; + apr_interval_time_t interval; + char *hurl; + char *hcexpr; +} hc_template_t; + +typedef struct { + char *expr; + ap_expr_info_t *pexpr; /* parsed expression */ +} hc_condition_t; + +typedef struct { + apr_pool_t *p; + apr_bucket_alloc_t *ba; + apr_array_header_t *templates; + apr_table_t *conditions; + ap_watchdog_t *watchdog; + apr_hash_t *hcworkers; + apr_thread_pool_t *hctp; + int tpsize; + server_rec *s; +} sctx_t; + +/* Used in the HC worker via the context field */ +typedef struct { + char *path; /* The path of the original worker URL */ + char *req; /* pre-formatted HTTP/AJP request */ + proxy_worker *w; /* Pointer to the actual worker */ +} wctx_t; + +typedef struct { + apr_pool_t *ptemp; + sctx_t *ctx; + proxy_worker *worker; + apr_time_t now; +} baton_t; + +static void *hc_create_config(apr_pool_t *p, server_rec *s) +{ + sctx_t *ctx = (sctx_t *) apr_palloc(p, sizeof(sctx_t)); + apr_pool_create(&ctx->p, p); + ctx->ba = apr_bucket_alloc_create(p); + ctx->templates = apr_array_make(p, 10, sizeof(hc_template_t)); + ctx->conditions = apr_table_make(p, 10); + ctx->hcworkers = apr_hash_make(p); + ctx->tpsize = HC_THREADPOOL_SIZE; + ctx->s = s; + + return ctx; +} + +/* + * This serves double duty by not only validating (and creating) + * the health-check template, but also ties into set_worker_param() + * which does the actual setting of worker params in shm. + */ +static const char *set_worker_hc_param(apr_pool_t *p, + server_rec *s, + proxy_worker *worker, + const char *key, + const char *val, + void *v) +{ + int ival; + hc_template_t *temp; + sctx_t *ctx = (sctx_t *) ap_get_module_config(s->module_config, + &proxy_hcheck_module); + if (!worker && !v) { + return "Bad call to set_worker_hc_param()"; + } + temp = (hc_template_t *)v; + if (!strcasecmp(key, "hctemplate")) { + hc_template_t *template; + template = (hc_template_t *)ctx->templates->elts; + for (ival = 0; ival < ctx->templates->nelts; ival++, template++) { + if (!strcasecmp(template->name, val)) { + if (worker) { + worker->s->method = template->method; + worker->s->interval = template->interval; + worker->s->passes = template->passes; + worker->s->fails = template->fails; + PROXY_STRNCPY(worker->s->hcuri, template->hurl); + PROXY_STRNCPY(worker->s->hcexpr, template->hcexpr); + } else { + temp->method = template->method; + temp->interval = template->interval; + temp->passes = template->passes; + temp->fails = template->fails; + temp->hurl = apr_pstrdup(p, template->hurl); + temp->hcexpr = apr_pstrdup(p, template->hcexpr); + } + return NULL; + } + } + return apr_psprintf(p, "Unknown ProxyHCTemplate name: %s", val); + } + else if (!strcasecmp(key, "hcmethod")) { + proxy_hcmethods_t *method = proxy_hcmethods; + for (; method->name; method++) { + if (!strcasecmp(val, method->name)) { + if (!method->implemented) { + return apr_psprintf(p, "Health check method %s not (yet) implemented", + val); + } + if (worker) { + worker->s->method = method->method; + } else { + temp->method = method->method; + } + return NULL; + } + } + return "Unknown method"; + } + else if (!strcasecmp(key, "hcinterval")) { + ival = atoi(val); + if (ival < HCHECK_WATHCHDOG_INTERVAL) + return apr_psprintf(p, "Interval must be a positive value greater than %d seconds", + HCHECK_WATHCHDOG_INTERVAL); + if (worker) { + worker->s->interval = apr_time_from_sec(ival); + } else { + temp->interval = apr_time_from_sec(ival); + } + } + else if (!strcasecmp(key, "hcpasses")) { + ival = atoi(val); + if (ival < 0) + return "Passes must be a positive value"; + if (worker) { + worker->s->passes = ival; + } else { + temp->passes = ival; + } + } + else if (!strcasecmp(key, "hcfails")) { + ival = atoi(val); + if (ival < 0) + return "Fails must be a positive value"; + if (worker) { + worker->s->fails = ival; + } else { + temp->fails = ival; + } + } + else if (!strcasecmp(key, "hcuri")) { + if (strlen(val) >= sizeof(worker->s->hcuri)) + return apr_psprintf(p, "Health check uri length must be < %d characters", + (int)sizeof(worker->s->hcuri)); + if (worker) { + PROXY_STRNCPY(worker->s->hcuri, val); + } else { + temp->hurl = apr_pstrdup(p, val); + } + } + else if (!strcasecmp(key, "hcexpr")) { + hc_condition_t *cond; + cond = (hc_condition_t *)apr_table_get(ctx->conditions, val); + if (!cond) { + return apr_psprintf(p, "Unknown health check condition expr: %s", val); + } + /* This check is wonky... a known expr can't be this big. Check anyway */ + if (strlen(val) >= sizeof(worker->s->hcexpr)) + return apr_psprintf(p, "Health check uri length must be < %d characters", + (int)sizeof(worker->s->hcexpr)); + if (worker) { + PROXY_STRNCPY(worker->s->hcexpr, val); + } else { + temp->hcexpr = apr_pstrdup(p, val); + } + } + else { + return "unknown Worker hcheck parameter"; + } + return NULL; +} + +static const char *set_hc_condition(cmd_parms *cmd, void *dummy, const char *arg) +{ + char *name = NULL; + char *expr; + sctx_t *ctx; + hc_condition_t *cond; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS); + if (err) + return err; + ctx = (sctx_t *) ap_get_module_config(cmd->server->module_config, + &proxy_hcheck_module); + + name = ap_getword_conf(cmd->pool, &arg); + if (!*name) { + return apr_pstrcat(cmd->temp_pool, "Missing expression name for ", + cmd->cmd->name, NULL); + } + if (strlen(name) > (PROXY_WORKER_MAX_SCHEME_SIZE - 1)) { + return apr_psprintf(cmd->temp_pool, "Expression name limited to %d characters", + (PROXY_WORKER_MAX_SCHEME_SIZE - 1)); + } + /* get expr. Allow fancy new {...} quoting style */ + expr = ap_getword_conf2(cmd->temp_pool, &arg); + if (!*expr) { + return apr_pstrcat(cmd->temp_pool, "Missing expression for ", + cmd->cmd->name, NULL); + } + cond = apr_palloc(cmd->pool, sizeof(hc_condition_t)); + cond->pexpr = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL); + if (err) { + return apr_psprintf(cmd->temp_pool, "Could not parse expression \"%s\": %s", + expr, err); + } + cond->expr = apr_pstrdup(cmd->pool, expr); + apr_table_setn(ctx->conditions, name, (void *)cond); + expr = ap_getword_conf(cmd->temp_pool, &arg); + if (*expr) { + return "error: extra parameter(s)"; + } + + return NULL; +} + +static const char *set_hc_template(cmd_parms *cmd, void *dummy, const char *arg) +{ + char *name = NULL; + char *word, *val; + hc_template_t *template; + sctx_t *ctx; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS); + if (err) + return err; + ctx = (sctx_t *) ap_get_module_config(cmd->server->module_config, + &proxy_hcheck_module); + + name = ap_getword_conf(cmd->temp_pool, &arg); + if (!*name) { + return apr_pstrcat(cmd->temp_pool, "Missing template name for ", + cmd->cmd->name, NULL); + } + + template = (hc_template_t *)apr_array_push(ctx->templates); + + template->name = apr_pstrdup(cmd->pool, name); + template->method = template->passes = template->fails = 1; + template->interval = apr_time_from_sec(HCHECK_WATHCHDOG_DEFAULT_INTERVAL); + template->hurl = NULL; + template->hcexpr = NULL; + while (*arg) { + word = ap_getword_conf(cmd->pool, &arg); + val = strchr(word, '='); + if (!val) { + return "Invalid ProxyHCTemplate parameter. Parameter must be " + "in the form 'key=value'"; + } + else + *val++ = '\0'; + err = set_worker_hc_param(cmd->pool, ctx->s, NULL, word, val, template); + + if (err) { + /* get rid of recently pushed (bad) template */ + apr_array_pop(ctx->templates); + return apr_pstrcat(cmd->temp_pool, "ProxyHCTemplate: ", err, " ", word, "=", val, "; ", name, NULL); + } + /* No error means we have a valid template */ + } + + return NULL; +} + +#if HC_USE_THREADS +static const char *set_hc_tpsize (cmd_parms *cmd, void *dummy, const char *arg) +{ + sctx_t *ctx; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS); + if (err) + return err; + ctx = (sctx_t *) ap_get_module_config(cmd->server->module_config, + &proxy_hcheck_module); + + ctx->tpsize = atoi(arg); + if (ctx->tpsize < 0) + return "Invalid ProxyHCTPsize parameter. Parameter must be " + ">= 0"; + return NULL; +} +#endif + +/* + * Create a dummy request rec, simply so we can use ap_expr. + * Use our short-lived poll for bucket_alloc + */ +static request_rec *create_request_rec(apr_pool_t *p1, conn_rec *conn, const char *method) +{ + request_rec *r; + apr_pool_t *p; + apr_bucket_alloc_t *ba; + apr_pool_create(&p, p1); + apr_pool_tag(p, "request"); + r = apr_pcalloc(p, sizeof(request_rec)); + ba = apr_bucket_alloc_create(p); + r->pool = p; + r->connection = conn; + r->connection->bucket_alloc = ba; + r->server = conn->base_server; + + r->user = NULL; + r->ap_auth_type = NULL; + + r->allowed_methods = ap_make_method_list(p, 2); + + r->headers_in = apr_table_make(r->pool, 25); + r->trailers_in = apr_table_make(r->pool, 5); + r->subprocess_env = apr_table_make(r->pool, 25); + r->headers_out = apr_table_make(r->pool, 12); + r->err_headers_out = apr_table_make(r->pool, 5); + r->trailers_out = apr_table_make(r->pool, 5); + r->notes = apr_table_make(r->pool, 5); + + r->kept_body = apr_brigade_create(r->pool, r->connection->bucket_alloc); + r->request_config = ap_create_request_config(r->pool); + /* Must be set before we run create request hook */ + + r->proto_output_filters = conn->output_filters; + r->output_filters = r->proto_output_filters; + r->proto_input_filters = conn->input_filters; + r->input_filters = r->proto_input_filters; + r->per_dir_config = r->server->lookup_defaults; + + r->sent_bodyct = 0; /* bytect isn't for body */ + + r->read_length = 0; + r->read_body = REQUEST_NO_BODY; + + r->status = HTTP_OK; /* Until further notice */ + r->header_only = 1; + r->the_request = NULL; + + /* Begin by presuming any module can make its own path_info assumptions, + * until some module interjects and changes the value. + */ + r->used_path_info = AP_REQ_DEFAULT_PATH_INFO; + + r->useragent_addr = conn->client_addr; + r->useragent_ip = conn->client_ip; + + + /* Time to populate r with the data we have. */ + r->method = method; + /* Provide quick information about the request method as soon as known */ + r->method_number = ap_method_number_of(r->method); + if (r->method_number == M_GET && r->method[0] == 'G') { + r->header_only = 0; + } + + r->protocol = "HTTP/1.0"; + r->proto_num = HTTP_VERSION(1, 0); + + r->hostname = NULL; + + return r; +} + +static proxy_worker *hc_get_hcworker(sctx_t *ctx, proxy_worker *worker, + apr_pool_t *p) +{ + proxy_worker *hc = NULL; + const char* wptr; + apr_port_t port; + + wptr = apr_psprintf(ctx->p, "%pp", worker); + hc = (proxy_worker *)apr_hash_get(ctx->hcworkers, wptr, APR_HASH_KEY_STRING); + port = (worker->s->port ? worker->s->port : ap_proxy_port_of_scheme(worker->s->scheme)); + if (!hc) { + apr_uri_t uri; + apr_status_t rv; + const char *url = worker->s->name; + wctx_t *wctx = apr_pcalloc(ctx->p, sizeof(wctx_t)); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03248) + "Creating hc worker %s for %s://%s:%d", + wptr, worker->s->scheme, worker->s->hostname, + (int)port); + + ap_proxy_define_worker(ctx->p, &hc, NULL, NULL, worker->s->name, 0); + PROXY_STRNCPY(hc->s->name, wptr); + PROXY_STRNCPY(hc->s->hostname, worker->s->hostname); + PROXY_STRNCPY(hc->s->scheme, worker->s->scheme); + hc->hash.def = hc->s->hash.def = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_DEFAULT); + hc->hash.fnv = hc->s->hash.fnv = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_FNV); + hc->s->port = port; + /* Do not disable worker in case of errors */ + hc->s->status |= PROXY_WORKER_IGNORE_ERRORS; + /* Mark as the "generic" worker */ + hc->s->status |= PROXY_WORKER_GENERIC; + ap_proxy_initialize_worker(hc, ctx->s, ctx->p); + hc->s->is_address_reusable = worker->s->is_address_reusable; + hc->s->disablereuse = worker->s->disablereuse; + hc->s->method = worker->s->method; + rv = apr_uri_parse(p, url, &uri); + if (rv == APR_SUCCESS) { + wctx->path = apr_pstrdup(ctx->p, uri.path); + } + wctx->w = worker; + hc->context = wctx; + apr_hash_set(ctx->hcworkers, wptr, APR_HASH_KEY_STRING, hc); + } + /* This *could* have changed via the Balancer Manager */ + /* TODO */ + if (hc->s->method != worker->s->method) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03311) + "Updating hc worker %s for %s://%s:%d", + wptr, worker->s->scheme, worker->s->hostname, + (int)port); + hc->s->method = worker->s->method; + apr_hash_set(ctx->hcworkers, wptr, APR_HASH_KEY_STRING, hc); + } + return hc; +} + +static int hc_determine_connection(sctx_t *ctx, proxy_worker *worker) { + apr_status_t rv = APR_SUCCESS; + int will_reuse = worker->s->is_address_reusable && !worker->s->disablereuse; + /* + * normally, this is done in ap_proxy_determine_connection(). + * TODO: Look at using ap_proxy_determine_connection() with a + * fake request_rec + */ + if (!worker->cp->addr || !will_reuse) { + rv = apr_sockaddr_info_get(&(worker->cp->addr), worker->s->hostname, APR_UNSPEC, + worker->s->port, 0, ctx->p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03249) + "DNS lookup failure for: %s:%d", + worker->s->hostname, (int)worker->s->port); + } + } + return (rv == APR_SUCCESS ? OK : !OK); +} + +static apr_status_t hc_init_worker(sctx_t *ctx, proxy_worker *worker) { + apr_status_t rv = APR_SUCCESS; + /* + * Since this is the watchdog, workers never actually handle a + * request here, and so the local data isn't initialized (of + * course, the shared memory is). So we need to bootstrap + * worker->cp. Note, we only need do this once. + */ + if (!worker->cp) { + rv = ap_proxy_initialize_worker(worker, ctx->s, ctx->p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ctx->s, APLOGNO(03250) "Cannot init worker"); + return rv; + } + rv = (hc_determine_connection(ctx, worker) == OK ? APR_SUCCESS : APR_EGENERAL); + } + return rv; +} + +static apr_status_t backend_cleanup(const char *proxy_function, proxy_conn_rec *backend, + server_rec *s, int status) +{ + if (backend) { + backend->close = 1; + ap_proxy_release_connection(proxy_function, backend, s); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03251) + "Health check %s Status (%d) for %s.", + ap_proxy_show_hcmethod(backend->worker->s->method), + status, + backend->worker->s->name); + } + if (status != OK) { + return APR_EGENERAL; + } + return APR_SUCCESS; +} + +static int hc_get_backend(const char *proxy_function, proxy_conn_rec **backend, + proxy_worker *hc, sctx_t *ctx) +{ + int status; + status = ap_proxy_acquire_connection(proxy_function, backend, hc, ctx->s); + if (status == OK) { + (*backend)->addr = hc->cp->addr; + (*backend)->pool = ctx->p; + (*backend)->hostname = hc->s->hostname; + if (strcmp(hc->s->scheme, "https") == 0) { + if (!ap_proxy_ssl_enable(NULL)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ctx->s, APLOGNO(03252) + "mod_ssl not configured?"); + return !OK; + } + (*backend)->is_ssl = 1; + } + + } + status = hc_determine_connection(ctx, hc); + if (status == OK) { + (*backend)->addr = hc->cp->addr; + } + return status; +} + +static apr_status_t hc_check_tcp(sctx_t *ctx, apr_pool_t *ptemp, proxy_worker *worker) +{ + int status; + proxy_conn_rec *backend = NULL; + proxy_worker *hc; + + hc = hc_get_hcworker(ctx, worker, ptemp); + + status = hc_get_backend("HCTCP", &backend, hc, ctx); + if (status == OK) { + backend->addr = hc->cp->addr; + status = ap_proxy_connect_backend("HCTCP", backend, hc, ctx->s); + /* does an unconditional ap_proxy_is_socket_connected() */ + } + return backend_cleanup("HCTCP", backend, ctx->s, status); +} + +static void hc_send(sctx_t *ctx, apr_pool_t *ptemp, const char *out, proxy_conn_rec *backend) +{ + apr_bucket_brigade *tmp_bb = apr_brigade_create(ptemp, ctx->ba); + ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, ctx->s, "%s", out); + APR_BRIGADE_INSERT_TAIL(tmp_bb, apr_bucket_pool_create(out, strlen(out), ptemp, + ctx->ba)); + APR_BRIGADE_INSERT_TAIL(tmp_bb, apr_bucket_flush_create(ctx->ba)); + ap_pass_brigade(backend->connection->output_filters, tmp_bb); + apr_brigade_destroy(tmp_bb); +} + +static int hc_read_headers(sctx_t *ctx, request_rec *r) +{ + char buffer[HUGE_STRING_LEN]; + int len; + + len = ap_getline(buffer, sizeof(buffer), r, 1); + if (len <= 0) { + return !OK; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03254) + "%s", buffer); + /* for the below, see ap_proxy_http_process_response() */ + if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + int major; + char keepchar; + int proxy_status = OK; + const char *proxy_status_line = NULL; + + major = buffer[5] - '0'; + if ((major != 1) || (len >= sizeof(buffer)-1)) { + return !OK; + } + + keepchar = buffer[12]; + buffer[12] = '\0'; + proxy_status = atoi(&buffer[9]); + if (keepchar != '\0') { + buffer[12] = keepchar; + } else { + buffer[12] = ' '; + buffer[13] = '\0'; + } + proxy_status_line = apr_pstrdup(r->pool, &buffer[9]); + r->status = proxy_status; + r->status_line = proxy_status_line; + } else { + return !OK; + } + /* OK, 1st line is OK... scarf in the headers */ + while ((len = ap_getline(buffer, sizeof(buffer), r, 1)) > 0) { + char *value, *end; + if (!(value = strchr(buffer, ':'))) { + return !OK; + } + ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, ctx->s, "%s", buffer); + *value = '\0'; + ++value; + while (apr_isspace(*value)) + ++value; /* Skip to start of value */ + for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end) + *end = '\0'; + apr_table_add(r->headers_out, buffer, value); + } + return OK; +} + +static int hc_read_body (sctx_t *ctx, request_rec *r) +{ + apr_status_t rv = APR_SUCCESS; + apr_bucket_brigade *bb; + int seen_eos = 0; + + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + do { + apr_bucket *bucket, *cpy; + apr_size_t len = HUGE_STRING_LEN; + + rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_READBYTES, + APR_BLOCK_READ, len); + + if (rv != APR_SUCCESS) { + if (APR_STATUS_IS_TIMEUP(rv) || APR_STATUS_IS_EOF(rv)) { + rv = APR_SUCCESS; + break; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ctx->s, APLOGNO(03300) + "Error reading response body"); + break; + } + + for (bucket = APR_BRIGADE_FIRST(bb); + bucket != APR_BRIGADE_SENTINEL(bb); + bucket = APR_BUCKET_NEXT(bucket)) + { + if (APR_BUCKET_IS_EOS(bucket)) { + seen_eos = 1; + break; + } + if (APR_BUCKET_IS_FLUSH(bucket)) { + continue; + } + rv = apr_bucket_copy(bucket, &cpy); + if (rv != APR_SUCCESS) { + break; + } + APR_BRIGADE_INSERT_TAIL(r->kept_body, cpy); + } + apr_brigade_cleanup(bb); + } + while (!seen_eos); + return (rv == APR_SUCCESS ? OK : !OK); +} + +/* + * Send the HTTP OPTIONS, HEAD or GET request to the backend + * server associated w/ worker. If we have Conditions, + * then apply those to the resulting response, otherwise + * any status code 2xx or 3xx is considered "passing" + */ +static apr_status_t hc_check_http(sctx_t *ctx, apr_pool_t *ptemp, proxy_worker *worker) +{ + int status; + proxy_conn_rec *backend = NULL; + proxy_worker *hc; + conn_rec c; + request_rec *r; + wctx_t *wctx; + hc_condition_t *cond; + const char *method = NULL; + + hc = hc_get_hcworker(ctx, worker, ptemp); + wctx = (wctx_t *)hc->context; + + if ((status = hc_get_backend("HCOH", &backend, hc, ctx)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + if ((status = ap_proxy_connect_backend("HCOH", backend, hc, ctx->s)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + + if (!backend->connection) { + if ((status = ap_proxy_connection_create("HCOH", backend, &c, ctx->s)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + } + switch (hc->s->method) { + case OPTIONS: + if (!wctx->req) { + wctx->req = apr_psprintf(ctx->p, + "OPTIONS * HTTP/1.0\r\nHost: %s:%d\r\n\r\n", + hc->s->hostname, (int)hc->s->port); + } + method = "OPTIONS"; + break; + + case HEAD: + method = "HEAD"; + /* fallthru */ + case GET: + if (!method) { /* did we fall thru? If not, we are GET */ + method = "GET"; + } + if (!wctx->req) { + wctx->req = apr_psprintf(ctx->p, + "%s %s%s%s HTTP/1.0\r\nHost: %s:%d\r\n\r\n", + method, + (wctx->path ? wctx->path : ""), + (wctx->path && *hc->s->hcuri ? "/" : "" ), + (*hc->s->hcuri ? hc->s->hcuri : ""), + hc->s->hostname, (int)hc->s->port); + } + break; + + default: + return backend_cleanup("HCOH", backend, ctx->s, !OK); + break; + } + + hc_send(ctx, ptemp, wctx->req, backend); + + r = create_request_rec(ptemp, backend->connection, method); + if ((status = hc_read_headers(ctx, r)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + if (hc->s->method == GET) { + if ((status = hc_read_body(ctx, r)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + } + + if (*worker->s->hcexpr && + (cond = (hc_condition_t *)apr_table_get(ctx->conditions, worker->s->hcexpr)) != NULL) { + const char *err; + int ok = ap_expr_exec(r, cond->pexpr, &err); + if (ok > 0) { + status = OK; + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s, + "Condition %s for %s (%s): passed", worker->s->hcexpr, + hc->s->name, worker->s->name); + } else if (ok < 0 || err) { + status = !OK; + ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctx->s, APLOGNO(03301) + "Error on checking condition %s for %s (%s): %s", worker->s->hcexpr, + hc->s->name, worker->s->name, err); + } else { + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s, + "Condition %s for %s (%s) : failed", worker->s->hcexpr, + hc->s->name, worker->s->name); + status = !OK; + } + } else if (r->status < 200 || r->status > 399) { + status = !OK; + } + return backend_cleanup("HCOH", backend, ctx->s, status); +} + +static void *hc_check(apr_thread_t *thread, void *b) +{ + baton_t *baton = (baton_t *)b; + sctx_t *ctx = baton->ctx; + apr_time_t now = baton->now; + proxy_worker *worker = baton->worker; + apr_pool_t *ptemp = baton->ptemp; + server_rec *s = ctx->s; + apr_status_t rv; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03256) + "%sHealth checking %s", (thread ? "Threaded " : ""), worker->s->name); + + switch (worker->s->method) { + case TCP: + rv = hc_check_tcp(ctx, ptemp, worker); + break; + + case OPTIONS: + case HEAD: + case GET: + rv = hc_check_http(ctx, ptemp, worker); + break; + + default: + rv = APR_ENOTIMPL; + break; + } + if (rv == APR_ENOTIMPL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(03257) + "Somehow tried to use unimplemented hcheck method: %d", + (int)worker->s->method); + apr_pool_destroy(ptemp); + return NULL; + } + /* what state are we in ? */ + if (PROXY_WORKER_IS_HCFAILED(worker)) { + if (rv == APR_SUCCESS) { + worker->s->pcount += 1; + if (worker->s->pcount >= worker->s->passes) { + ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, 0, worker); + ap_proxy_set_wstatus(PROXY_WORKER_IN_ERROR_FLAG, 0, worker); + worker->s->pcount = 0; + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03302) + "%sHealth check ENABLING %s", (thread ? "Threaded " : ""), + worker->s->name); + + } + } + } else { + if (rv != APR_SUCCESS) { + worker->s->error_time = now; + worker->s->fcount += 1; + if (worker->s->fcount >= worker->s->fails) { + ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, 1, worker); + worker->s->fcount = 0; + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03303) + "%sHealth check DISABLING %s", (thread ? "Threaded " : ""), + worker->s->name); + } + } + } + worker->s->updated = now; + apr_pool_destroy(ptemp); + return NULL; +} + +static apr_status_t hc_watchdog_callback(int state, void *data, + apr_pool_t *pool) +{ + apr_status_t rv = APR_SUCCESS; + apr_time_t now = apr_time_now(); + proxy_balancer *balancer; + sctx_t *ctx = (sctx_t *)data; + server_rec *s = ctx->s; + proxy_server_conf *conf; + switch (state) { + case AP_WATCHDOG_STATE_STARTING: + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03258) + "%s watchdog started.", + HCHECK_WATHCHDOG_NAME); +#if HC_USE_THREADS + if (ctx->tpsize) { + rv = apr_thread_pool_create(&ctx->hctp, ctx->tpsize, + ctx->tpsize, ctx->p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03312) + "apr_thread_pool_create() with %d threads failed", + ctx->tpsize); + /* we can continue on without the threadpools */ + ctx->hctp = NULL; + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03313) + "apr_thread_pool_create() with %d threads succeeded", + ctx->tpsize); + } + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03314) + "Skipping apr_thread_pool_create()"); + ctx->hctp = NULL; + } + +#endif + break; + + case AP_WATCHDOG_STATE_RUNNING: + /* loop thru all workers */ + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, + "Run of %s watchdog.", + HCHECK_WATHCHDOG_NAME); + if (s) { + int i; + conf = (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++, balancer++) { + int n; + proxy_worker **workers; + proxy_worker *worker; + /* Have any new balancers or workers been added dynamically? */ + ap_proxy_sync_balancer(balancer, s, conf); + workers = (proxy_worker **)balancer->workers->elts; + for (n = 0; n < balancer->workers->nelts; n++) { + worker = *workers; + if (!PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED) && + (worker->s->method != NONE) && + (now > worker->s->updated + worker->s->interval)) { + baton_t *baton; + /* This pool must last the lifetime of the (possible) thread */ + apr_pool_t *ptemp; + apr_pool_create(&ptemp, ctx->p); + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, + "Checking %s worker: %s [%d] (%pp)", balancer->s->name, + worker->s->name, worker->s->method, worker); + + if ((rv = hc_init_worker(ctx, worker)) != APR_SUCCESS) { + return rv; + } + baton = apr_palloc(ptemp, sizeof(baton_t)); + baton->ctx = ctx; + baton->now = now; + baton->worker = worker; + baton->ptemp = ptemp; + + if (!ctx->hctp) { + hc_check(NULL, baton); + } +#if HC_USE_THREADS + else { + rv = apr_thread_pool_push(ctx->hctp, hc_check, (void *)baton, + APR_THREAD_TASK_PRIORITY_NORMAL, NULL); + } +#endif + } + workers++; + } + } + /* s = s->next; */ + } + break; + + case AP_WATCHDOG_STATE_STOPPING: + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03261) + "stopping %s watchdog.", + HCHECK_WATHCHDOG_NAME); +#if HC_USE_THREADS + rv = apr_thread_pool_destroy(ctx->hctp); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03315) + "apr_thread_pool_destroy() failed"); + } +#endif + ctx->hctp = NULL; + break; + } + return rv; +} + +static int hc_post_config(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + apr_status_t rv; + sctx_t *ctx; + + APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *hc_watchdog_get_instance; + APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *hc_watchdog_register_callback; + + hc_watchdog_get_instance = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_get_instance); + hc_watchdog_register_callback = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_register_callback); + if (!hc_watchdog_get_instance || !hc_watchdog_register_callback) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(03262) + "mod_watchdog is required"); + return !OK; + } + ctx = (sctx_t *) ap_get_module_config(s->module_config, + &proxy_hcheck_module); + + rv = hc_watchdog_get_instance(&ctx->watchdog, + HCHECK_WATHCHDOG_NAME, + 0, 1, p); + if (rv) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(03263) + "Failed to create watchdog instance (%s)", + HCHECK_WATHCHDOG_NAME); + return !OK; + } + rv = hc_watchdog_register_callback(ctx->watchdog, + apr_time_from_sec(HCHECK_WATHCHDOG_INTERVAL), + ctx, + hc_watchdog_callback); + if (rv) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(03264) + "Failed to register watchdog callback (%s)", + HCHECK_WATHCHDOG_NAME); + return !OK; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03265) + "watchdog callback registered (%s)", HCHECK_WATHCHDOG_NAME); + return OK; +} + +static void hc_show_exprs(request_rec *r) +{ + const apr_table_entry_t *elts; + const apr_array_header_t *hdr; + int i; + sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config, + &proxy_hcheck_module); + if (apr_is_empty_table(ctx->conditions)) + return; + + ap_rputs("\n\n<table>" + "<tr><th colspan='2'>Health check cond. expressions:</th></tr>\n" + "<tr><th>Expr name</th><th>Expression</th></tr>\n", r); + + hdr = apr_table_elts(ctx->conditions); + elts = (const apr_table_entry_t *) hdr->elts; + for (i = 0; i < hdr->nelts; ++i) { + hc_condition_t *cond; + if (!elts[i].key) { + continue; + } + cond = (hc_condition_t *)elts[i].val; + ap_rprintf(r, "<tr><td>%s</td><td>%s</td></tr>\n", + ap_escape_html(r->pool, elts[i].key), + ap_escape_html(r->pool, cond->expr)); + } + ap_rputs("</table><hr/>\n", r); +} + +static void hc_select_exprs(request_rec *r, const char *expr) +{ + const apr_table_entry_t *elts; + const apr_array_header_t *hdr; + int i; + sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config, + &proxy_hcheck_module); + if (apr_is_empty_table(ctx->conditions)) + return; + + hdr = apr_table_elts(ctx->conditions); + elts = (const apr_table_entry_t *) hdr->elts; + for (i = 0; i < hdr->nelts; ++i) { + if (!elts[i].key) { + continue; + } + ap_rprintf(r, "<option value='%s' %s >%s</option>\n", + ap_escape_html(r->pool, elts[i].key), + (!strcmp(elts[i].key, expr)) ? "selected" : "", + ap_escape_html(r->pool, elts[i].key)); + } +} + +static int hc_valid_expr(request_rec *r, const char *expr) +{ + const apr_table_entry_t *elts; + const apr_array_header_t *hdr; + int i; + sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config, + &proxy_hcheck_module); + if (apr_is_empty_table(ctx->conditions)) + return 0; + + hdr = apr_table_elts(ctx->conditions); + elts = (const apr_table_entry_t *) hdr->elts; + for (i = 0; i < hdr->nelts; ++i) { + if (!elts[i].key) { + continue; + } + if (!strcmp(elts[i].key, expr)) + return 1; + } + return 0; +} + +static const char *hc_get_body(request_rec *r) +{ + apr_off_t length; + apr_size_t len; + apr_status_t rv; + char *buf; + + if (!r || !r->kept_body) + return ""; + + rv = apr_brigade_length(r->kept_body, 1, &length); + len = (apr_size_t)length; + if (rv != APR_SUCCESS || len == 0) + return ""; + + buf = apr_palloc(r->pool, len + 1); + rv = apr_brigade_flatten(r->kept_body, buf, &len); + if (rv != APR_SUCCESS) + return ""; + buf[len] = '\0'; /* ensure */ + return (const char*)buf; +} + +static const char *hc_expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data) +{ + char *var = (char *)data; + + if (var && *var && ctx->r && strcasecmp(var, "BODY") == 0) { + return hc_get_body(ctx->r); + } + return NULL; +} + +static const char *hc_expr_func_fn(ap_expr_eval_ctx_t *ctx, const void *data, + const char *arg) +{ + char *var = (char *)arg; + + if (var && *var && ctx->r && strcasecmp(var, "BODY") == 0) { + return hc_get_body(ctx->r); + } + return NULL; +} + +static int hc_expr_lookup(ap_expr_lookup_parms *parms) +{ + switch (parms->type) { + case AP_EXPR_FUNC_VAR: + /* for now, we just handle everything that starts with HC_. + */ + if (strncasecmp(parms->name, "HC_", 3) == 0) { + *parms->func = hc_expr_var_fn; + *parms->data = parms->name + 3; + return OK; + } + break; + case AP_EXPR_FUNC_STRING: + /* Function HC() is implemented by us. + */ + if (strcasecmp(parms->name, "HC") == 0) { + *parms->func = hc_expr_func_fn; + *parms->data = parms->arg; + return OK; + } + break; + } + return DECLINED; +} + +static const command_rec command_table[] = { + AP_INIT_RAW_ARGS("ProxyHCTemplate", set_hc_template, NULL, OR_FILEINFO, + "Health check template"), + AP_INIT_RAW_ARGS("ProxyHCExpr", set_hc_condition, NULL, OR_FILEINFO, + "Define a health check condition ruleset expression"), +#if HC_USE_THREADS + AP_INIT_TAKE1("ProxyHCTPsize", set_hc_tpsize, NULL, OR_FILEINFO, + "Set size of health check thread pool"), +#endif + { NULL } +}; + +static void hc_register_hooks(apr_pool_t *p) +{ + static const char *const aszPre[] = { "mod_proxy_balancer.c", "mod_proxy.c", NULL}; + static const char *const aszSucc[] = { "mod_watchdog.c", NULL}; + APR_REGISTER_OPTIONAL_FN(set_worker_hc_param); + APR_REGISTER_OPTIONAL_FN(hc_show_exprs); + APR_REGISTER_OPTIONAL_FN(hc_select_exprs); + APR_REGISTER_OPTIONAL_FN(hc_valid_expr); + ap_hook_post_config(hc_post_config, aszPre, aszSucc, APR_HOOK_LAST); + ap_hook_expr_lookup(hc_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE); +} + +/* the main config structure */ + +AP_DECLARE_MODULE(proxy_hcheck) = +{ + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + hc_create_config, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + command_table, /* table of config file commands */ + hc_register_hooks /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_http.mak b/modules/proxy/mod_proxy_http.mak index 8849723c..c3811872 100644 --- a/modules/proxy/mod_proxy_http.mak +++ b/modules/proxy/mod_proxy_http.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_http.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" "$(INTDIR)\mod_proxy_http.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_scgi.c b/modules/proxy/mod_proxy_scgi.c index 2cbe8483..cede817a 100644 --- a/modules/proxy/mod_proxy_scgi.c +++ b/modules/proxy/mod_proxy_scgi.c @@ -509,7 +509,7 @@ static int scgi_request_status(int *status, request_rec *r) *status = HTTP_INTERNAL_SERVER_ERROR; return *status; } - } while(0); + } while (0); return OK; /* break; */ @@ -530,7 +530,7 @@ static int scgi_handler(request_rec *r, proxy_worker *worker, int status; proxy_conn_rec *backend = NULL; apr_pool_t *p = r->pool; - apr_uri_t *uri = apr_palloc(r->pool, sizeof(*uri)); + apr_uri_t *uri; char dummy; if (strncasecmp(url, SCHEME "://", sizeof(SCHEME) + 2)) { @@ -548,6 +548,7 @@ static int scgi_handler(request_rec *r, proxy_worker *worker, backend->is_ssl = 0; /* Step One: Determine Who To Connect To */ + uri = apr_palloc(p, sizeof(*uri)); status = ap_proxy_determine_connection(p, r, conf, worker, backend, uri, &url, proxyname, proxyport, &dummy, 1); diff --git a/modules/proxy/mod_proxy_scgi.mak b/modules/proxy/mod_proxy_scgi.mak index c44270a0..7ffb2484 100644 --- a/modules/proxy/mod_proxy_scgi.mak +++ b/modules/proxy/mod_proxy_scgi.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_scgi.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_scgi - Win32 Debug" "$(INTDIR)\mod_proxy_scgi.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/mod_proxy_wstunnel.mak b/modules/proxy/mod_proxy_wstunnel.mak index 635de591..530715fe 100644 --- a/modules/proxy/mod_proxy_wstunnel.mak +++ b/modules/proxy/mod_proxy_wstunnel.mak @@ -363,14 +363,14 @@ SOURCE=..\..\build\win32\httpd.rc "$(INTDIR)\mod_proxy_wstunnel.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" $(SOURCE) !ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" "$(INTDIR)\mod_proxy_wstunnel.res" : $(SOURCE) "$(INTDIR)" - $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /i "\local0\asf\build\httpd-2.4\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" $(SOURCE) + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" $(SOURCE) !ENDIF diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 763073c1..0d2c8563 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -54,24 +54,6 @@ typedef struct { const char *proxy_auth; /* Proxy authorization */ } forward_info; -/* Keep synced with mod_proxy.h! */ -static struct wstat { - unsigned int bit; - char flag; - const char *name; -} wstat_tbl[] = { - {PROXY_WORKER_INITIALIZED, PROXY_WORKER_INITIALIZED_FLAG, "Init "}, - {PROXY_WORKER_IGNORE_ERRORS, PROXY_WORKER_IGNORE_ERRORS_FLAG, "Ign "}, - {PROXY_WORKER_DRAIN, PROXY_WORKER_DRAIN_FLAG, "Drn "}, - {PROXY_WORKER_IN_SHUTDOWN, PROXY_WORKER_IN_SHUTDOWN_FLAG, "Shut "}, - {PROXY_WORKER_DISABLED, PROXY_WORKER_DISABLED_FLAG, "Dis "}, - {PROXY_WORKER_STOPPED, PROXY_WORKER_STOPPED_FLAG, "Stop "}, - {PROXY_WORKER_IN_ERROR, PROXY_WORKER_IN_ERROR_FLAG, "Err "}, - {PROXY_WORKER_HOT_STANDBY, PROXY_WORKER_HOT_STANDBY_FLAG, "Stby "}, - {PROXY_WORKER_FREE, PROXY_WORKER_FREE_FLAG, "Free "}, - {0x0, '\0', NULL} -}; - /* Global balancer counter */ int PROXY_DECLARE_DATA proxy_lb_workers = 0; static int lb_workers_limit = 0; @@ -1375,7 +1357,7 @@ static apr_status_t connection_cleanup(void *theconn) * If the connection pool is NULL the worker * cleanup has been run. Just return. */ - if (!worker->cp) { + if (!worker->cp->pool) { return APR_SUCCESS; } @@ -1498,10 +1480,11 @@ static apr_status_t connection_constructor(void **resource, void *params, static apr_status_t connection_destructor(void *resource, void *params, apr_pool_t *pool) { - proxy_conn_rec *conn = (proxy_conn_rec *)resource; + proxy_worker *worker = params; /* Destroy the pool only if not called from reslist_destroy */ - if (conn->worker->cp->pool) { + if (worker->cp->pool) { + proxy_conn_rec *conn = resource; apr_pool_destroy(conn->pool); } @@ -1698,6 +1681,7 @@ PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, memset(wshared, 0, sizeof(proxy_worker_shared)); + wshared->port = (uri.port ? uri.port : ap_proxy_port_of_scheme(uri.scheme)); if (uri.port && uri.port == ap_proxy_port_of_scheme(uri.scheme)) { uri.port = 0; } @@ -1712,11 +1696,13 @@ PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, if (PROXY_STRNCPY(wshared->hostname, uri.hostname) != APR_SUCCESS) { return apr_psprintf(p, "worker hostname (%s) too long", uri.hostname); } - wshared->port = uri.port; wshared->flush_packets = flush_off; wshared->flush_wait = PROXY_FLUSH_WAIT; wshared->is_address_reusable = 1; wshared->lbfactor = 1; + wshared->passes = 1; + wshared->fails = 1; + wshared->interval = apr_time_from_sec(HCHECK_WATHCHDOG_DEFAULT_INTERVAL); wshared->smax = -1; wshared->hash.def = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_DEFAULT); wshared->hash.fnv = ap_proxy_hashfunc(wshared->name, PROXY_HASHFUNC_FNV); @@ -1730,6 +1716,9 @@ PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, else { *wshared->uds_path = '\0'; } + if (!balancer) { + wshared->status |= PROXY_WORKER_IGNORE_ERRORS; + } (*worker)->hash = wshared->hash; (*worker)->context = NULL; @@ -1896,7 +1885,14 @@ static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worke server_rec *s) { if (worker->s->status & PROXY_WORKER_IN_ERROR) { - if (apr_time_now() > worker->s->error_time + worker->s->retry) { + if (PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(3305) + "%s: Won't retry worker (%s): stopped", + proxy_function, worker->s->hostname); + return DECLINED; + } + if ((worker->s->status & PROXY_WORKER_IGNORE_ERRORS) + || apr_time_now() > worker->s->error_time + worker->s->retry) { ++worker->s->retries; worker->s->status &= ~PROXY_WORKER_IN_ERROR; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00932) @@ -2430,7 +2426,7 @@ ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, #endif #if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK) -static int is_socket_connected(apr_socket_t *socket) +PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket) { apr_pollfd_t pfds[1]; apr_status_t status; @@ -2468,7 +2464,7 @@ static int is_socket_connected(apr_socket_t *socket) } #else -static int is_socket_connected(apr_socket_t *sock) +PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *sock) { apr_size_t buffer_len = 1; @@ -2590,12 +2586,12 @@ static apr_status_t send_http_connect(proxy_conn_rec *backend, } -#if APR_HAVE_SYS_UN_H /* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */ PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock, const char *uds_path, apr_pool_t *p) { +#if APR_HAVE_SYS_UN_H apr_status_t rv; apr_os_sock_t rawsock; apr_interval_time_t t; @@ -2637,8 +2633,10 @@ PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock, } return APR_SUCCESS; -} +#else + return APR_ENOTIMPL; #endif +} PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, @@ -2657,7 +2655,7 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); if (conn->sock) { - if (!(connected = is_socket_connected(conn->sock))) { + if (!(connected = ap_proxy_is_socket_connected(conn->sock))) { /* This clears conn->scpool (and associated data), so backup and * restore any ssl_hostname for this connection set earlier by * ap_proxy_determine_connection(). @@ -3073,7 +3071,7 @@ PROXY_DECLARE(apr_status_t) ap_proxy_set_wstatus(char c, int set, proxy_worker * { unsigned int *status = &w->s->status; char flag = toupper(c); - struct wstat *pwt = wstat_tbl; + proxy_wstat_t *pwt = proxy_wstat_tbl; while (pwt->bit) { if (flag == pwt->flag) { if (set) @@ -3091,12 +3089,15 @@ PROXY_DECLARE(char *) ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w) { char *ret = ""; unsigned int status = w->s->status; - struct wstat *pwt = wstat_tbl; + proxy_wstat_t *pwt = proxy_wstat_tbl; while (pwt->bit) { if (status & pwt->bit) ret = apr_pstrcat(p, ret, pwt->name, NULL); pwt++; } + if (!*ret) { + ret = "??? "; + } if (PROXY_WORKER_IS_USABLE(w)) ret = apr_pstrcat(p, ret, "Ok ", NULL); return ret; @@ -3498,7 +3499,9 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, } proxy_run_fixups(r); - ap_proxy_clear_connection(r, r->headers_in); + if (ap_proxy_clear_connection(r, r->headers_in) < 0) { + return HTTP_BAD_REQUEST; + } /* send request headers */ headers_in_array = apr_table_elts(r->headers_in); @@ -3624,6 +3627,8 @@ static proxy_schemes_t pschemes[] = {"fcgi", 8000}, {"ajp", AJP13_DEF_PORT}, {"scgi", SCGI_DEF_PORT}, + {"h2c", DEFAULT_HTTP_PORT}, + {"h2", DEFAULT_HTTPS_PORT}, { NULL, 0xFFFF } /* unknown port */ }; @@ -3767,6 +3772,17 @@ PROXY_DECLARE(apr_status_t) ap_proxy_transfer_between_connections( return rv; } +PROXY_DECLARE (const char *) ap_proxy_show_hcmethod(hcmethod_t method) +{ + proxy_hcmethods_t *m = proxy_hcmethods; + for (; m->name; m++) { + if (m->method == method) { + return m->name; + } + } + return "???"; +} + void proxy_util_register_hooks(apr_pool_t *p) { APR_REGISTER_OPTIONAL_FN(ap_proxy_retry_worker); |