diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:01:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:01:30 +0000 |
commit | 6beeb1b708550be0d4a53b272283e17e5e35fe17 (patch) | |
tree | 1ce8673d4aaa948e5554000101f46536a1e4cc29 /modules/proxy | |
parent | Initial commit. (diff) | |
download | apache2-6beeb1b708550be0d4a53b272283e17e5e35fe17.tar.xz apache2-6beeb1b708550be0d4a53b272283e17e5e35fe17.zip |
Adding upstream version 2.4.57.upstream/2.4.57upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/proxy')
99 files changed, 40517 insertions, 0 deletions
diff --git a/modules/proxy/.indent.pro b/modules/proxy/.indent.pro new file mode 100644 index 0000000..e2cd357 --- /dev/null +++ b/modules/proxy/.indent.pro @@ -0,0 +1,58 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-Tapr_bucket_brigade +-Tapr_pool_t +-Tap_filter_t +-Tbuf_area +-Tbuff_struct +-Tbuffy +-Tcmd_how +-Tcmd_parms +-Tcommand_rec +-Tcommand_struct +-Tconn_rec +-Tcore_dir_config +-Tcore_server_config +-Tdir_maker_func +-Tevent +-Tglobals_s +-Thandler_func +-Thandler_rec +-Tjoblist_s +-Tlisten_rec +-Tmerger_func +-Tmode_t +-Tmodule +-Tmodule_struct +-Tmutex +-Tn_long +-Tother_child_rec +-Toverrides_t +-Tparent_score +-Tpid_t +-Tpiped_log +-Tpool +-Trequest_rec +-Trequire_line +-Trlim_t +-Tscoreboard +-Tsemaphore +-Tserver_addr_rec +-Tserver_rec +-Tserver_rec_chain +-Tshort_score +-Ttable +-Ttable_entry +-Tthread +-Tu_wide_int +-Tvtime_t +-Twide_int +-Tproxy_server_conf diff --git a/modules/proxy/CHANGES b/modules/proxy/CHANGES new file mode 100644 index 0000000..b8e03a1 --- /dev/null +++ b/modules/proxy/CHANGES @@ -0,0 +1,223 @@ +****************************************** +* PLEASE NOTE: Now that development for * +* mod_proxy has been folded back into * +* the httpd-2.1 tree, this file has * +* been deprecated. Proxy changes should * +* be noted in httpd-2.x's CHANGES file. * +* This file exists for historical * +* purposes. * +****************************************** + +mod_proxy changes for httpd 2.0.29-dev + *) don't do keepalives for sub-requests. [Ian Holsman] + + *) fix up proxypass handling [Ian Holsman] + + *) don't send If-Modified-Since, Cache-Control, or If-None-Match on + a subrequest [Ian Holsman] + +mod_proxy changes for httpd 2.0.26-dev + *) Add New option 'HTTPProxyOverrideReturnedErrors'. By Turning the + Flag on, you will mask the error pages returned by the proxied + server, and will it will be handled as if your server generated + the error. This change was put in so that a 404 on a included + r-proxied component will act in the same manner as a 404 on a + included file. [Ian Holsman <ianh@cnet.com>] + +mod_proxy changes for httpd 2.0.25-dev + + *) Split proxy: space using <Proxy[Match] > directive blocks from + the <Directory[Match] > and <Files[Match] > blocks. Mod_proxy + now bypasses the directory and files testing phase (and skips + the http TRACE default handler on it's own, as well). Note that + <Location > blocks continue to be processed for proxy: requests. + [William Rowe <wrowe@covalent.net>] + + *) apr_uri type/function namespace changes in apr_uri functions + [Doug MacEachern <dougm@covalent.net>] + +mod_proxy changes for httpd 2.0.23-dev + + *) break the proxy_http_handler into multiple smaller functions. + [John Barbee <barbee@veribox.net>] + + *) Fix the proxy when the origin server sends back a 100 + Continue response. [John Barbee <barbee@veribox.net>] + + *) Change 'readbytes' from apr_size_t to apr_off_t due to change + in ap_get_brigade's parameters [John Barbee <barbee@veribox.net>] + +mod_proxy changes for httpd 2.0.20-dev + *) Timeout added for backend connections. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Fix abort code path in proxy_http.c, similar to FTP fix. + [Chuck Murcko <chuck@topsail.org>] + + *) Fix FTP ABOR command execution path. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) FTP return code variable cleanup; fixed problem in login + [Chuck Murcko <chuck@topsail.org>] + + *) Get PORT working again in the ftp proxy. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Return result code check for FTP QUIT, after fixing + problems with passive connection handling. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Reorganize ap_proxy_string_read() internally to not process eos + buckets. + [Chuck Murcko <chuck@topsail.org>] + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Remove result code check for FTP QUIT command. Some servers send + nothing at all back in response to QUIT. + [Chuck Murcko <chuck@topsail.org>] + [Victor Orlikowski <v.j.orlikowski@gte.net>] + +mod_proxy changes for httpd 2.0.19 + + *) Reverse previous patch since the core reverted. + [Chuck Murcko <chuck@topsail.org>] + + *) Remove indirection on number of bytes to read for input filters. + [Chuck Murcko <chuck@topsail.org>] + + *) Fixed a problem with directory listing corruption in the + PROXY_DIR filter. + [Graham Leggett <minfrin@sharp.fm>] + + *) mod_proxy and the proxy submodules now build properly as DSOs. + [Graham Leggett <minfrin@sharp.fm>] + + *) Stopped the HTTP proxy from trying to read entity bodies when there + wasn't one (response was 1xx, 204, 205 or 304). + [Graham Leggett <minfrin@sharp.fm>] + + *) Made sure dates were canonicalised correctly when passed to the client + browser through the HTTP proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Split each individual proxy protocol into separate modules. + [Graham Leggett <minfrin@sharp.fm>] + + *) Added Max-Forwards support for all request types so as to prevent + loops. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix warnings about byte count type on Darwin (connect handler). + [Chuck Murcko <chuck@topsail.org>] + +mod_proxy changes for httpd 2.0.18 + + *) IPV6 EPSV support for IPV6 in FTP proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) FTP directory filter works now. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed some thread-safety issues with the HTTP proxy in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) PASV FTP works now. + [Graham Leggett <minfrin@sharp.fm>] + + *) Reworked the line-at-a-time read from the control connection to + workaround a stray empty bucket returned by the HTTP_IN filter. + [Graham Leggett <minfrin@sharp.fm>] + + *) Stopped the CORE filter from sending off an HTTP response when a + CONNECT tunnel was closed. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed the poll() loop in proxy_connect.c -> it works now!!! + [Graham Leggett <minfrin@sharp.fm>] + + *) Converted send_dir() to ap_proxy_send_dir_filter() in proxy_ftp.c. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.17 + + *) Major rework of ap_proxy_ftp_handler() to use filters (begone foul + BUFF!!!). It compiles, but is untested, and the build environment needs + to be fixed to include proxy_ftp.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Cleanup of dead functions within proxy_util.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Reworked the storage of the client socket between keepalive connections + to fix some nasty problems with the socket lasting longer than the + memory pool it was allocated from. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed bug where a hostname without a "." in it (such as "localhost") + would not trigger an IP address check with ProxyBlock. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.16 + + *) Fixed ProxyBlock bugs with ap_proxy_http_handler() and + ap_proxy_connect_handler(). + [Graham Leggett <minfrin@sharp.fm>] + + *) Updated ap_proxy_connect_handler() to support APR, while + moving some common code between http_handler and connect_handler + to proxy_util.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Updated mod_proxy.html docs to include v2.0 configuration. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed problem where responses without entity bodies would cause + the directly following proxy keepalive request to fail. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.15 + + *) Added support for downstream keepalives in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Changed mod_proxy ap_proxy_http_handler() to support APR properly. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix problem where incoming response headers were not being returned + to the client in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Added X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Server to + reverse proxied request headers in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) replace INADDR_NONE with APR_INADDR_NONE [Ian Holsman <IanH@cnet.com>] + + *) Fix problem with proxy configuration where globally set + configuration options were overridden inside virtual hosts. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix ProxyReceiveBufferSize where default value was left + uninitialised. + [Graham Leggett <minfrin@sharp.fm>] + + *) Some small changes: + - Ensured hop-by-hop headers were stripped as per + RFC2616 13.5.1. + - Upgraded version code to HTTP/1.1. + - Added Connection: close until Keepalives come. + - Some cosmetic fixes and commenting. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.14 + + *) removed ProxyNoCache and ProxyCacheForceCompletion config directives, + since we no longer directly cache from this module + [Chuck Murcko <chuck@topsail.org>] + + *) removed cache + [Chuck Murcko <chuck@topsail.org>] + + *) initial rerebuild for 2.0 + [Chuck Murcko <chuck@topsail.org>] + diff --git a/modules/proxy/Makefile.in b/modules/proxy/Makefile.in new file mode 100644 index 0000000..6ea6775 --- /dev/null +++ b/modules/proxy/Makefile.in @@ -0,0 +1,4 @@ +# a modules Makefile has no explicit targets -- they will be defined by +# whatever modules are enabled. just grab special.mk to deal with this. +#SUBDIRS = balancers +include $(top_srcdir)/build/special.mk diff --git a/modules/proxy/NWGNUmakefile b/modules/proxy/NWGNUmakefile new file mode 100644 index 0000000..d44644f --- /dev/null +++ b/modules/proxy/NWGNUmakefile @@ -0,0 +1,259 @@ +# +# Declare the sub-directories to be built here +# + +SUBDIRS = \ + $(EOLIST) + +# +# Get the 'head' of the build environment. This includes default targets and +# paths to tools +# + +include $(AP_WORK)/build/NWGNUhead.inc + +# +# build this level's files + +# +# Make sure all needed macro's are defined +# + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxy.nlm \ + $(OBJDIR)/proxycon.nlm \ + $(OBJDIR)/proxyftp.nlm \ + $(OBJDIR)/proxyhtp.nlm \ + $(OBJDIR)/proxybalancer.nlm \ + $(OBJDIR)/proxyajp.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 \ + $(OBJDIR)/proxylbm_traf.nlm \ + $(OBJDIR)/proxywstunnel.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + $(call COPY,$(OBJDIR)/*.nlm, $(INSTALLBASE)/modules/) + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxy b/modules/proxy/NWGNUproxy new file mode 100644 index 0000000..d731c5a --- /dev/null +++ b/modules/proxy/NWGNUproxy @@ -0,0 +1,337 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(AP_WORK)/modules/generators \ + $(AP_WORK)/modules/ssl \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxy + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxy.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy.o \ + $(OBJDIR)/proxy_util.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @libc.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(OBJDIR)/mod_proxy.imp $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +$(OBJDIR)/mod_proxy.imp: NWGNUproxy + @echo $(DL)GEN $@$(DL) + @echo $(DL)# Exports of mod_proxy$(DL)> $@ + @echo $(DL) (AP$(VERSION_MAJMIN))$(DL)>> $@ + @echo $(DL) proxy_module,$(DL)>> $@ + @echo $(DL) proxy_hcmethods,$(DL)>> $@ + @echo $(DL) proxy_hook_canon_handler,$(DL)>> $@ + @echo $(DL) proxy_hook_get_canon_handler,$(DL)>> $@ + @echo $(DL) proxy_hook_get_post_request,$(DL)>> $@ + @echo $(DL) proxy_hook_get_pre_request,$(DL)>> $@ + @echo $(DL) proxy_hook_get_scheme_handler,$(DL)>> $@ + @echo $(DL) proxy_hook_post_request,$(DL)>> $@ + @echo $(DL) proxy_hook_pre_request,$(DL)>> $@ + @echo $(DL) proxy_hook_scheme_handler,$(DL)>> $@ + @echo $(DL) proxy_run_canon_handler,$(DL)>> $@ + @echo $(DL) proxy_run_create_req,$(DL)>> $@ + @echo $(DL) proxy_run_fixups,$(DL)>> $@ + @echo $(DL) proxy_run_post_request,$(DL)>> $@ + @echo $(DL) proxy_run_pre_request,$(DL)>> $@ + @echo $(DL) proxy_run_request_status,$(DL)>> $@ + @echo $(DL) proxy_run_scheme_handler,$(DL)>> $@ + @echo $(DL) ap_proxy_acquire_connection,$(DL)>> $@ + @echo $(DL) ap_proxy_backend_broke,$(DL)>> $@ + @echo $(DL) ap_proxy_buckets_lifetime_transform,$(DL)>> $@ + @echo $(DL) ap_proxy_c2hex,$(DL)>> $@ + @echo $(DL) ap_proxy_canon_netloc,$(DL)>> $@ + @echo $(DL) ap_proxy_canonenc,$(DL)>> $@ + @echo $(DL) ap_proxy_check_connection,$(DL)>> $@ + @echo $(DL) ap_proxy_checkproxyblock,$(DL)>> $@ + @echo $(DL) ap_proxy_checkproxyblock2,$(DL)>> $@ + @echo $(DL) ap_proxy_conn_is_https,$(DL)>> $@ + @echo $(DL) ap_proxy_connect_backend,$(DL)>> $@ + @echo $(DL) ap_proxy_connect_to_backend,$(DL)>> $@ + @echo $(DL) ap_proxy_connection_create,$(DL)>> $@ + @echo $(DL) ap_proxy_connection_reusable,$(DL)>> $@ + @echo $(DL) ap_proxy_cookie_reverse_map,$(DL)>> $@ + @echo $(DL) ap_proxy_create_hdrbrgd,$(DL)>> $@ + @echo $(DL) ap_proxy_define_balancer,$(DL)>> $@ + @echo $(DL) ap_proxy_define_worker,$(DL)>> $@ + @echo $(DL) ap_proxy_determine_connection,$(DL)>> $@ + @echo $(DL) ap_proxy_find_balancershm,$(DL)>> $@ + @echo $(DL) ap_proxy_find_workershm,$(DL)>> $@ + @echo $(DL) ap_proxy_get_balancer,$(DL)>> $@ + @echo $(DL) ap_proxy_get_worker,$(DL)>> $@ + @echo $(DL) ap_proxy_hashfunc,$(DL)>> $@ + @echo $(DL) ap_proxy_hex2c,$(DL)>> $@ + @echo $(DL) ap_proxy_initialize_balancer,$(DL)>> $@ + @echo $(DL) ap_proxy_initialize_worker,$(DL)>> $@ + @echo $(DL) ap_proxy_is_domainname,$(DL)>> $@ + @echo $(DL) ap_proxy_is_hostname,$(DL)>> $@ + @echo $(DL) ap_proxy_is_ipaddr,$(DL)>> $@ + @echo $(DL) ap_proxy_is_word,$(DL)>> $@ + @echo $(DL) ap_proxy_location_reverse_map,$(DL)>> $@ + @echo $(DL) ap_proxy_parse_wstatus,$(DL)>> $@ + @echo $(DL) ap_proxy_pass_brigade,$(DL)>> $@ + @echo $(DL) ap_proxy_port_of_scheme,$(DL)>> $@ + @echo $(DL) ap_proxy_post_request,$(DL)>> $@ + @echo $(DL) ap_proxy_pre_http_request,$(DL)>> $@ + @echo $(DL) ap_proxy_pre_request,$(DL)>> $@ + @echo $(DL) ap_proxy_release_connection,$(DL)>> $@ + @echo $(DL) ap_proxy_set_wstatus,$(DL)>> $@ + @echo $(DL) ap_proxy_share_balancer,$(DL)>> $@ + @echo $(DL) ap_proxy_share_worker,$(DL)>> $@ + @echo $(DL) ap_proxy_show_hcmethod,$(DL)>> $@ + @echo $(DL) ap_proxy_ssl_connection_cleanup,$(DL)>> $@ + @echo $(DL) ap_proxy_ssl_disable,$(DL)>> $@ + @echo $(DL) ap_proxy_ssl_enable,$(DL)>> $@ + @echo $(DL) ap_proxy_ssl_val,$(DL)>> $@ + @echo $(DL) ap_proxy_strncpy,$(DL)>> $@ + @echo $(DL) ap_proxy_sync_balancer,$(DL)>> $@ + @echo $(DL) ap_proxy_trans_match,$(DL)>> $@ + @echo $(DL) ap_proxy_transfer_between_connections,$(DL)>> $@ + @echo $(DL) ap_proxy_valid_balancer_name,$(DL)>> $@ + @echo $(DL) ap_proxy_worker_name,$(DL)>> $@ + @echo $(DL) ap_proxyerror$(DL)>> $@ + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxyajp b/modules/proxy/NWGNUproxyajp new file mode 100644 index 0000000..0f41248 --- /dev/null +++ b/modules/proxy/NWGNUproxyajp @@ -0,0 +1,264 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyajp + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy AJP Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy AJP Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxyajp.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_ajp.o \ + $(OBJDIR)/ajp_header.o \ + $(OBJDIR)/ajp_msg.o \ + $(OBJDIR)/ajp_link.o \ + $(OBJDIR)/ajp_utils.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + @libc.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_ajp_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxybalancer b/modules/proxy/NWGNUproxybalancer new file mode 100644 index 0000000..976fd50 --- /dev/null +++ b/modules/proxy/NWGNUproxybalancer @@ -0,0 +1,260 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxybalancer + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Balancer Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Prxy Blncr Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxybalancer.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_balancer.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + @libc.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_balancer_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxycon b/modules/proxy/NWGNUproxycon new file mode 100644 index 0000000..0215a49 --- /dev/null +++ b/modules/proxy/NWGNUproxycon @@ -0,0 +1,251 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxycon + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Connection Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy Conn Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxycon.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_connect.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + @libc.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_connect_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxyexpress b/modules/proxy/NWGNUproxyexpress new file mode 100644 index 0000000..84bb503 --- /dev/null +++ b/modules/proxy/NWGNUproxyexpress @@ -0,0 +1,256 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyexpress + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Express Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Prxy Xpress Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_express.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_express_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxyfcgi b/modules/proxy/NWGNUproxyfcgi new file mode 100644 index 0000000..a441222 --- /dev/null +++ b/modules/proxy/NWGNUproxyfcgi @@ -0,0 +1,261 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + -relax_pointers \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyfcgi + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Fast CGI Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy FCGI Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxyfcgi.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_fcgi.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + @libc.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_fcgi_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxyftp b/modules/proxy/NWGNUproxyftp new file mode 100644 index 0000000..ee7fbf8 --- /dev/null +++ b/modules/proxy/NWGNUproxyftp @@ -0,0 +1,260 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyftp + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy FTP Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy FTP Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxyftp.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_ftp.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + @libc.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_ftp_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxyhcheck b/modules/proxy/NWGNUproxyhcheck new file mode 100644 index 0000000..d200650 --- /dev/null +++ b/modules/proxy/NWGNUproxyhcheck @@ -0,0 +1,254 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(SRC)/include \ + $(STDMOD)/core \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyhcheck + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Health Check Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy Health Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_hcheck.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @mod_proxy.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_hcheck_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc diff --git a/modules/proxy/NWGNUproxyhtp b/modules/proxy/NWGNUproxyhtp new file mode 100644 index 0000000..0257d52 --- /dev/null +++ b/modules/proxy/NWGNUproxyhtp @@ -0,0 +1,260 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyhtp + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy HTTP Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy HTTP Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxyhtp.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_http.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + @libc.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_http_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxylbm_busy b/modules/proxy/NWGNUproxylbm_busy new file mode 100644 index 0000000..c1b91f6 --- /dev/null +++ b/modules/proxy/NWGNUproxylbm_busy @@ -0,0 +1,250 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/proxy \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxylbm_busy + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy LoadBalance by Busyness Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = LBM Busy Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_lbmethod_bybusyness.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + lbmethod_bybusyness_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c balancers +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxylbm_hb b/modules/proxy/NWGNUproxylbm_hb new file mode 100644 index 0000000..19563da --- /dev/null +++ b/modules/proxy/NWGNUproxylbm_hb @@ -0,0 +1,251 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/proxy \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxylbm_hb + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy LoadBalance by Heartbeat Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = LBM Heart Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_lbmethod_heartbeat.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + lbmethod_heartbeat_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c balancers + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxylbm_req b/modules/proxy/NWGNUproxylbm_req new file mode 100644 index 0000000..6a2c4cd --- /dev/null +++ b/modules/proxy/NWGNUproxylbm_req @@ -0,0 +1,251 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/proxy \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxylbm_req + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy LoadBalance by Requests Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = LBM Requests Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_lbmethod_byrequests.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + lbmethod_byrequests_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c balancers + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxylbm_traf b/modules/proxy/NWGNUproxylbm_traf new file mode 100644 index 0000000..1927d62 --- /dev/null +++ b/modules/proxy/NWGNUproxylbm_traf @@ -0,0 +1,251 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/proxy \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxylbm_traf + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy LoadBalance by Traffic Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = LBM Traf Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_lbmethod_bytraffic.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + lbmethod_bytraffic_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c balancers + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxyscgi b/modules/proxy/NWGNUproxyscgi new file mode 100644 index 0000000..9068c69 --- /dev/null +++ b/modules/proxy/NWGNUproxyscgi @@ -0,0 +1,260 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyscgi + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy SCGI Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy SCGI Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxyscgi.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_scgi.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + @libc.imp \ + $(EOLIST) + +# Don't link with Winsock if standard sockets are being used +ifndef USE_STDSOCKETS +FILES_nlm_Ximports += @ws2nlm.imp \ + $(EOLIST) +endif + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_scgi_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c ../arch/netware + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/NWGNUproxywstunnel b/modules/proxy/NWGNUproxywstunnel new file mode 100644 index 0000000..ce84ce4 --- /dev/null +++ b/modules/proxy/NWGNUproxywstunnel @@ -0,0 +1,250 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)/build/NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(SRC)/include \ + $(STDMOD)/http \ + $(STDMOD)/proxy \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxywstunnel + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Web Socket Tunnel Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Prxy WbSkt Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)/build/NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = $(OBJDIR)/$(NLM_NAME).nlm + +# +# If there is an LIB target, put it here +# +TARGET_lib = + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy_wstunnel.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(PRELUDE) \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + libc \ + aprlib \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @libc.imp \ + @aprlib.imp \ + @httpd.imp \ + @$(OBJDIR)/mod_proxy.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_wstunnel_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)/build/NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +vpath %.c balancers +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(APBUILD)/NWGNUtail.inc + + diff --git a/modules/proxy/ajp.h b/modules/proxy/ajp.h new file mode 100644 index 0000000..a950ee9 --- /dev/null +++ b/modules/proxy/ajp.h @@ -0,0 +1,522 @@ +/* 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. + */ + +/** + * @file ajp.h + * @brief Apache Jserv Protocol + * + * @defgroup AJP_defines mod_proxy AJP definitions + * @ingroup APACHE_INTERNAL + * @{ + */ + +#ifndef AJP_H +#define AJP_H + +#include "apr_version.h" +#include "apr.h" + +#include "apr_hooks.h" +#include "apr_lib.h" +#include "apr_strings.h" +#include "apr_buckets.h" +#include "apr_md5.h" +#include "apr_network_io.h" +#include "apr_poll.h" +#include "apr_pools.h" +#include "apr_strings.h" +#include "apr_uri.h" +#include "apr_date.h" +#include "apr_fnmatch.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#if APR_HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#if APR_HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#define AJP13_DEF_HOST "127.0.0.1" +#ifdef NETWARE +#define AJP13_DEF_PORT 9009 /* default to 9009 since 8009 is used by OS */ +#else +#define AJP13_DEF_PORT 8009 +#endif + +/* The following environment variables match mod_ssl! */ +#define AJP13_HTTPS_INDICATOR "HTTPS" +#define AJP13_SSL_PROTOCOL_INDICATOR "SSL_PROTOCOL" +#define AJP13_SSL_CLIENT_CERT_INDICATOR "SSL_CLIENT_CERT" +#define AJP13_SSL_CIPHER_INDICATOR "SSL_CIPHER" +#define AJP13_SSL_SESSION_INDICATOR "SSL_SESSION_ID" +#define AJP13_SSL_KEY_SIZE_INDICATOR "SSL_CIPHER_USEKEYSIZE" + +#ifdef AJP_USE_HTTPD_WRAP +#include "httpd_wrap.h" +#else +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#endif + +#include "mod_proxy.h" +#include "util_ebcdic.h" + +/** AJP Specific error codes + */ +/** Buffer overflow exception */ +#define AJP_EOVERFLOW (APR_OS_START_USERERR + 1) +/** Destination Buffer is to small */ +#define AJP_ETOSMALL (APR_OS_START_USERERR + 2) +/** Invalid input parameters */ +#define AJP_EINVAL (APR_OS_START_USERERR + 3) +/** Bad message signature */ +#define AJP_EBAD_SIGNATURE (APR_OS_START_USERERR + 4) +/** Incoming message too bg */ +#define AJP_ETOBIG (APR_OS_START_USERERR + 5) +/** Missing message header */ +#define AJP_ENO_HEADER (APR_OS_START_USERERR + 6) +/** Bad message header */ +#define AJP_EBAD_HEADER (APR_OS_START_USERERR + 7) +/** Bad message */ +#define AJP_EBAD_MESSAGE (APR_OS_START_USERERR + 8) +/** Cant log via AJP14 */ +#define AJP_ELOGFAIL (APR_OS_START_USERERR + 9) +/** Bad request method */ +#define AJP_EBAD_METHOD (APR_OS_START_USERERR + 10) + + +/** A structure that represents ajp message */ +typedef struct ajp_msg ajp_msg_t; + +/** A structure that represents ajp message */ +struct ajp_msg +{ + /** The buffer holding a AJP message */ + apr_byte_t *buf; + /** The length of AJP message header (defaults to AJP_HEADER_LEN) */ + apr_size_t header_len; + /** The length of AJP message */ + apr_size_t len; + /** The current read position */ + apr_size_t pos; + /** Flag indicating the origing of the message */ + int server_side; + /** The size of the buffer */ + apr_size_t max_size; +}; + +/** + * Signature for the messages sent from Apache to tomcat + */ +#define AJP13_WS_HEADER 0x1234 +#define AJP_HEADER_LEN 4 +#define AJP_HEADER_SZ_LEN 2 +#define AJP_HEADER_SZ 6 +#define AJP_MSG_BUFFER_SZ 8192 +#define AJP_MAX_BUFFER_SZ 65536 +#define AJP13_MAX_SEND_BODY_SZ (AJP_MAX_BUFFER_SZ - AJP_HEADER_SZ) +#define AJP_PING_PONG_SZ 128 + +/** Send a request from web server to container*/ +#define CMD_AJP13_FORWARD_REQUEST (unsigned char)2 +/** Write a body chunk from the servlet container to the web server */ +#define CMD_AJP13_SEND_BODY_CHUNK (unsigned char)3 +/** Send response headers from the servlet container to the web server. */ +#define CMD_AJP13_SEND_HEADERS (unsigned char)4 +/** Marks the end of response. */ +#define CMD_AJP13_END_RESPONSE (unsigned char)5 +/** Get further data from the web server if it hasn't all been transferred yet. */ +#define CMD_AJP13_GET_BODY_CHUNK (unsigned char)6 +/** The web server asks the container to shut itself down. */ +#define CMD_AJP13_SHUTDOWN (unsigned char)7 +/** Webserver ask container to take control (logon phase) */ +#define CMD_AJP13_PING (unsigned char)8 +/** Container response to cping request */ +#define CMD_AJP13_CPONG (unsigned char)9 +/** Webserver check if container is alive, since container should respond by cpong */ +#define CMD_AJP13_CPING (unsigned char)10 + +/** @} */ + +/** + * @defgroup AJP_api AJP API functions + * @ingroup MOD_PROXY + * @{ + */ +/** + * Check a new AJP Message by looking at signature and return its size + * + * @param msg AJP Message to check + * @param len Pointer to returned len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_check_header(ajp_msg_t *msg, apr_size_t *len); + +/** + * Reset an AJP Message + * + * @param msg AJP Message to reset + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_reset(ajp_msg_t *msg); + +/** + * Reuse an AJP Message + * + * @param msg AJP Message to reuse + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_reuse(ajp_msg_t *msg); + +/** + * Mark the end of an AJP Message + * + * @param msg AJP Message to end + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_end(ajp_msg_t *msg); + +/** + * Add an unsigned 32bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint32(ajp_msg_t *msg, apr_uint32_t value); + +/** + * Add an unsigned 16bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint16(ajp_msg_t *msg, apr_uint16_t value); + +/** + * Add an unsigned 8bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint8(ajp_msg_t *msg, apr_byte_t value); + +/** + * Add a String in AJP message, and transform the String in ASCII + * if convert is set and we're on an EBCDIC machine + * + * @param msg AJP Message to get value from + * @param value Pointer to String + * @param convert When set told to convert String to ASCII + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_string_ex(ajp_msg_t *msg, const char *value, + int convert); +/** + * Add a String in AJP message, and transform + * the String in ASCII if we're on an EBCDIC machine + */ +#define ajp_msg_append_string(m, v) ajp_msg_append_string_ex(m, v, 1) + +/** + * Add a String in AJP message. + */ +#define ajp_msg_append_string_ascii(m, v) ajp_msg_append_string_ex(m, v, 0) + +/** + * Add a Byte array to AJP Message + * + * @param msg AJP Message to get value from + * @param value Pointer to Byte array + * @param valuelen Byte array len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_bytes(ajp_msg_t *msg, const apr_byte_t *value, + apr_size_t valuelen); + +/** + * Get a 32bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint32(ajp_msg_t *msg, apr_uint32_t *rvalue); + +/** + * Get a 16bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue); + +/** + * Peek a 16bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue); + +/** + * Get a 8bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint8(ajp_msg_t *msg, apr_byte_t *rvalue); + +/** + * Peek a 8bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint8(ajp_msg_t *msg, apr_byte_t *rvalue); + +/** + * Get a String value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_string(ajp_msg_t *msg, const char **rvalue); + + +/** + * Get a Byte array from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @param rvalue_len Pointer where Byte array len will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_bytes(ajp_msg_t *msg, apr_byte_t **rvalue, + apr_size_t *rvalue_len); + +/** + * Create an AJP Message from pool + * + * @param pool memory pool to allocate AJP message from + * @param size size of the buffer to create + * @param rmsg Pointer to newly created AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_create(apr_pool_t *pool, apr_size_t size, ajp_msg_t **rmsg); + +/** + * Recopy an AJP Message to another + * + * @param smsg source AJP message + * @param dmsg destination AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_copy(ajp_msg_t *smsg, ajp_msg_t *dmsg); + +/** + * Serialize in an AJP Message a PING command + * + * +-----------------------+ + * | PING CMD (1 byte) | + * +-----------------------+ + * + * @param msg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_ping(ajp_msg_t *msg); + +/** + * Serialize in an AJP Message a CPING command + * + * +-----------------------+ + * | CPING CMD (1 byte) | + * +-----------------------+ + * + * @param msg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_cping(ajp_msg_t *msg); + +/** + * Dump up to the first 1024 bytes on an AJP Message + * + * @param pool pool to allocate from + * @param msg AJP Message to dump + * @param err error string to display + * @param count the number of bytes to dump + * @param buf buffer pointer for dump message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_dump(apr_pool_t *pool, ajp_msg_t *msg, char *err, + apr_size_t count, char **buf); + +/** + * Log an AJP message + * + * @param r The current request + * @param msg AJP Message to dump + * @param err error string to display + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_log(request_rec *r, ajp_msg_t *msg, char *err); + +/** + * Send an AJP message to backend + * + * @param sock backend socket + * @param msg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_ilink_send(apr_socket_t *sock, ajp_msg_t *msg); + +/** + * Receive an AJP message from backend + * + * @param sock backend socket + * @param msg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_ilink_receive(apr_socket_t *sock, ajp_msg_t *msg); + +/** + * Build the ajp header message and send it + * @param sock backend socket + * @param r current request + * @param buffsize max size of the AJP packet. + * @param uri requested uri + * @param secret authentication secret + * @return APR_SUCCESS or error + */ +apr_status_t ajp_send_header(apr_socket_t *sock, request_rec *r, + apr_size_t buffsize, + apr_uri_t *uri, + const char *secret); + +/** + * Read the ajp message and return the type of the message. + * @param sock backend socket + * @param r current request + * @param buffsize size of the buffer. + * @param msg returned AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_read_header(apr_socket_t *sock, + request_rec *r, + apr_size_t buffsize, + ajp_msg_t **msg); + +/** + * Allocate a msg to send data + * @param pool pool to allocate from + * @param ptr data buffer + * @param len the length of allocated data buffer + * @param msg returned AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_alloc_data_msg(apr_pool_t *pool, char **ptr, + apr_size_t *len, ajp_msg_t **msg); + +/** + * Send the data message + * @param sock backend socket + * @param msg AJP message to send + * @param len AJP message length + * @return APR_SUCCESS or error + */ +apr_status_t ajp_send_data_msg(apr_socket_t *sock, + ajp_msg_t *msg, apr_size_t len); + +/** + * Parse the message type + * @param r current request + * @param msg AJP message + * @return AJP message type. + */ +int ajp_parse_type(request_rec *r, ajp_msg_t *msg); + +/** + * Parse the header message from container + * @param r current request + * @param conf proxy config + * @param msg AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_parse_header(request_rec *r, proxy_dir_conf *conf, + ajp_msg_t *msg); + +/** + * Parse the message body and return data address and length + * @param r current request + * @param msg AJP message + * @param len returned AJP message length + * @param ptr returned data + * @return APR_SUCCESS or error + */ +apr_status_t ajp_parse_data(request_rec *r, ajp_msg_t *msg, + apr_uint16_t *len, char **ptr); + + +/** + * Check the reuse flag in CMD_AJP13_END_RESPONSE + * @param r current request + * @param msg AJP message + * @param reuse returned reuse flag + * @return APR_SUCCESS or error + */ +apr_status_t ajp_parse_reuse(request_rec *r, ajp_msg_t *msg, + apr_byte_t *reuse); + + +/** + * Handle the CPING/CPONG messages + * @param sock backend socket + * @param r current request + * @param timeout time window for receiving cpong reply + * @return APR_SUCCESS or error + */ +apr_status_t ajp_handle_cping_cpong(apr_socket_t *sock, + request_rec *r, + apr_interval_time_t timeout); + + +/** + * Convert numeric message type into string + * @param type AJP message type + * @return AJP message type as a string + */ +const char *ajp_type_str(int type); + +/** @} */ + +#endif /* AJP_H */ + diff --git a/modules/proxy/ajp_header.c b/modules/proxy/ajp_header.c new file mode 100644 index 0000000..a09a2e4 --- /dev/null +++ b/modules/proxy/ajp_header.c @@ -0,0 +1,893 @@ +/* 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 "ajp_header.h" +#include "ajp.h" + +APLOG_USE_MODULE(proxy_ajp); + +static const char *response_trans_headers[] = { + "Content-Type", + "Content-Language", + "Content-Length", + "Date", + "Last-Modified", + "Location", + "Set-Cookie", + "Set-Cookie2", + "Servlet-Engine", + "Status", + "WWW-Authenticate" +}; + +static const char *long_res_header_for_sc(int sc) +{ + const char *rc = NULL; + sc = sc & 0X00FF; + if (sc <= SC_RES_HEADERS_NUM && sc > 0) { + rc = response_trans_headers[sc - 1]; + } + + return rc; +} + +#define UNKNOWN_METHOD (-1) + +static int sc_for_req_header(const char *header_name) +{ + char header[16]; + apr_size_t len = strlen(header_name); + const char *p = header_name; + int i = 0; + + /* ACCEPT-LANGUAGE is the longest header + * that is of interest. + */ + if (len < 4 || len > 15) + return UNKNOWN_METHOD; + + memset(header, 0, sizeof header); + while (*p) + header[i++] = apr_toupper(*p++); + header[i] = '\0'; + p = &header[1]; + + switch (header[0]) { + case 'A': + if (memcmp(p, "CCEPT", 5) == 0) { + if (!header[6]) + return SC_ACCEPT; + else if (header[6] == '-') { + p += 6; + if (strcmp(p, "CHARSET") == 0) + return SC_ACCEPT_CHARSET; + else if (strcmp(p, "ENCODING") == 0) + return SC_ACCEPT_ENCODING; + else if (strcmp(p, "LANGUAGE") == 0) + return SC_ACCEPT_LANGUAGE; + else + return UNKNOWN_METHOD; + } + else + return UNKNOWN_METHOD; + } + else if (strcmp(p, "UTHORIZATION") == 0) + return SC_AUTHORIZATION; + else + return UNKNOWN_METHOD; + break; + case 'C': + if (strcmp(p, "OOKIE2") == 0) + return SC_COOKIE2; + else if (strcmp(p, "OOKIE") == 0) + return SC_COOKIE; + else if (strcmp(p, "ONNECTION") == 0) + return SC_CONNECTION; + else if (strcmp(p, "ONTENT-TYPE") == 0) + return SC_CONTENT_TYPE; + else if (strcmp(p, "ONTENT-LENGTH") == 0) + return SC_CONTENT_LENGTH; + else + return UNKNOWN_METHOD; + break; + case 'H': + if (strcmp(p, "OST") == 0) + return SC_HOST; + else + return UNKNOWN_METHOD; + break; + case 'P': + if (strcmp(p, "RAGMA") == 0) + return SC_PRAGMA; + else + return UNKNOWN_METHOD; + break; + case 'R': + if (strcmp(p, "EFERER") == 0) + return SC_REFERER; + else + return UNKNOWN_METHOD; + break; + case 'U': + if (strcmp(p, "SER-AGENT") == 0) + return SC_USER_AGENT; + else + return UNKNOWN_METHOD; + break; + default: + return UNKNOWN_METHOD; + } + + /* NOTREACHED */ +} + +/* Apache method number to SC methods transform table */ +static const unsigned char sc_for_req_method_table[] = { + SC_M_GET, + SC_M_PUT, + SC_M_POST, + SC_M_DELETE, + 0, /* M_CONNECT */ + SC_M_OPTIONS, + SC_M_TRACE, + 0, /* M_PATCH */ + SC_M_PROPFIND, + SC_M_PROPPATCH, + SC_M_MKCOL, + SC_M_COPY, + SC_M_MOVE, + SC_M_LOCK, + SC_M_UNLOCK, + SC_M_VERSION_CONTROL, + SC_M_CHECKOUT, + SC_M_UNCHECKOUT, + SC_M_CHECKIN, + SC_M_UPDATE, + SC_M_LABEL, + SC_M_REPORT, + SC_M_MKWORKSPACE, + SC_M_MKACTIVITY, + SC_M_BASELINE_CONTROL, + SC_M_MERGE, + 0 /* M_INVALID */ +}; + +static int sc_for_req_method_by_id(request_rec *r) +{ + int method_id = r->method_number; + if (method_id < 0 || method_id > M_INVALID) { + return UNKNOWN_METHOD; + } + else if (r->header_only) { + return SC_M_HEAD; + } + else { + return sc_for_req_method_table[method_id] ? + sc_for_req_method_table[method_id] : UNKNOWN_METHOD; + } +} + +/* + * Message structure + * + * +AJPV13_REQUEST/AJPV14_REQUEST= + request_prefix (1) (byte) + method (byte) + protocol (string) + req_uri (string) + remote_addr (string) + remote_host (string) + server_name (string) + server_port (short) + is_ssl (boolean) + num_headers (short) + num_headers*(req_header_name header_value) + + ?context (byte)(string) + ?servlet_path (byte)(string) + ?remote_user (byte)(string) + ?auth_type (byte)(string) + ?query_string (byte)(string) + ?jvm_route (byte)(string) + ?ssl_cert (byte)(string) + ?ssl_cipher (byte)(string) + ?ssl_session (byte)(string) + ?ssl_key_size (byte)(int) via JkOptions +ForwardKeySize + request_terminator (byte) + ?body content_length*(var binary) + + */ + +static apr_status_t ajp_marshal_into_msgb(ajp_msg_t *msg, + request_rec *r, + apr_uri_t *uri, + const char *secret) +{ + int method; + apr_uint32_t i, num_headers = 0; + apr_byte_t is_ssl; + char *remote_host; + const char *session_route, *envvar; + const apr_array_header_t *arr = apr_table_elts(r->subprocess_env); + const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, "Into ajp_marshal_into_msgb"); + + if ((method = sc_for_req_method_by_id(r)) == UNKNOWN_METHOD) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(02437) + "ajp_marshal_into_msgb - Sending unknown method %s as request attribute", + r->method); + method = SC_M_JK_STORED; + } + + is_ssl = (apr_byte_t) ap_proxy_conn_is_https(r->connection); + + if (r->headers_in && apr_table_elts(r->headers_in)) { + const apr_array_header_t *t = apr_table_elts(r->headers_in); + num_headers = t->nelts; + } + + remote_host = (char *)ap_get_useragent_host(r, REMOTE_HOST, NULL); + + ajp_msg_reset(msg); + + if (ajp_msg_append_uint8(msg, CMD_AJP13_FORWARD_REQUEST) || + ajp_msg_append_uint8(msg, (apr_byte_t) method) || + ajp_msg_append_string(msg, r->protocol) || + ajp_msg_append_string(msg, uri->path) || + ajp_msg_append_string(msg, r->useragent_ip) || + ajp_msg_append_string(msg, remote_host) || + ajp_msg_append_string(msg, ap_get_server_name(r)) || + ajp_msg_append_uint16(msg, (apr_uint16_t)r->connection->local_addr->port) || + ajp_msg_append_uint8(msg, is_ssl) || + ajp_msg_append_uint16(msg, (apr_uint16_t) num_headers)) { + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00968) + "ajp_marshal_into_msgb: " + "Error appending the message beginning"); + return APR_EGENERAL; + } + + for (i = 0 ; i < num_headers ; i++) { + int sc; + const apr_array_header_t *t = apr_table_elts(r->headers_in); + const apr_table_entry_t *elts = (apr_table_entry_t *)t->elts; + + if ((sc = sc_for_req_header(elts[i].key)) != UNKNOWN_METHOD) { + if (ajp_msg_append_uint16(msg, (apr_uint16_t)sc)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00969) + "ajp_marshal_into_msgb: " + "Error appending the header name"); + return AJP_EOVERFLOW; + } + } + else { + if (ajp_msg_append_string(msg, elts[i].key)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00970) + "ajp_marshal_into_msgb: " + "Error appending the header name"); + return AJP_EOVERFLOW; + } + } + + if (ajp_msg_append_string(msg, elts[i].val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00971) + "ajp_marshal_into_msgb: " + "Error appending the header value"); + return AJP_EOVERFLOW; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ajp_marshal_into_msgb: Header[%d] [%s] = [%s]", + i, elts[i].key, elts[i].val); + } + + if (secret) { + if (ajp_msg_append_uint8(msg, SC_A_SECRET) || + ajp_msg_append_string(msg, secret)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03228) + "ajp_marshal_into_msgb: " + "Error appending secret"); + return APR_EGENERAL; + } + } + + if (r->user) { + if (ajp_msg_append_uint8(msg, SC_A_REMOTE_USER) || + ajp_msg_append_string(msg, r->user)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00972) + "ajp_marshal_into_msgb: " + "Error appending the remote user"); + return AJP_EOVERFLOW; + } + } + if (r->ap_auth_type) { + if (ajp_msg_append_uint8(msg, SC_A_AUTH_TYPE) || + ajp_msg_append_string(msg, r->ap_auth_type)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00973) + "ajp_marshal_into_msgb: " + "Error appending the auth type"); + return AJP_EOVERFLOW; + } + } + /* XXXX ebcdic (args converted?) */ + if (uri->query) { + if (ajp_msg_append_uint8(msg, SC_A_QUERY_STRING) || + ajp_msg_append_string(msg, uri->query)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00974) + "ajp_marshal_into_msgb: " + "Error appending the query string"); + return AJP_EOVERFLOW; + } + } + if ((session_route = apr_table_get(r->notes, "session-route"))) { + if (ajp_msg_append_uint8(msg, SC_A_JVM_ROUTE) || + ajp_msg_append_string(msg, session_route)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00975) + "ajp_marshal_into_msgb: " + "Error appending the jvm route"); + return AJP_EOVERFLOW; + } + } +/* XXX: Is the subprocess_env a right place? + * <Location /examples> + * ProxyPass ajp://remote:8009/servlets-examples + * SetEnv SSL_SESSION_ID CUSTOM_SSL_SESSION_ID + * </Location> + */ + /* + * Only lookup SSL variables if we are currently running HTTPS. + * Furthermore ensure that only variables get set in the AJP message + * that are not NULL and not empty. + */ + if (is_ssl) { + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_CLIENT_CERT_INDICATOR)) + && envvar[0]) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_CERT) + || ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00976) + "ajp_marshal_into_msgb: " + "Error appending the SSL certificates"); + return AJP_EOVERFLOW; + } + } + + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_CIPHER_INDICATOR)) + && envvar[0]) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_CIPHER) + || ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00977) + "ajp_marshal_into_msgb: " + "Error appending the SSL ciphers"); + return AJP_EOVERFLOW; + } + } + + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_SESSION_INDICATOR)) + && envvar[0]) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_SESSION) + || ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00978) + "ajp_marshal_into_msgb: " + "Error appending the SSL session"); + return AJP_EOVERFLOW; + } + } + + /* ssl_key_size is required by Servlet 2.3 API */ + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_KEY_SIZE_INDICATOR)) + && envvar[0]) { + + if (ajp_msg_append_uint8(msg, SC_A_SSL_KEY_SIZE) + || ajp_msg_append_uint16(msg, (unsigned short) atoi(envvar))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00979) + "ajp_marshal_into_msgb: " + "Error appending the SSL key size"); + return APR_EGENERAL; + } + } + } + /* If the method was unrecognized, encode it as an attribute */ + if (method == SC_M_JK_STORED) { + if (ajp_msg_append_uint8(msg, SC_A_STORED_METHOD) + || ajp_msg_append_string(msg, r->method)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02438) + "ajp_marshal_into_msgb: " + "Error appending the method '%s' as request attribute", + r->method); + return AJP_EOVERFLOW; + } + } + /* Forward the SSL protocol name. + * Modern Tomcat versions know how to retrieve + * the protocol name from this attribute. + */ + if (is_ssl) { + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_PROTOCOL_INDICATOR)) + && envvar[0]) { + const char *key = SC_A_SSL_PROTOCOL; + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, key) || + ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02830) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + key, envvar); + return AJP_EOVERFLOW; + } + } + } + /* Forward the remote port information, which was forgotten + * from the builtin data of the AJP 13 protocol. + * Since the servlet spec allows to retrieve it via getRemotePort(), + * we provide the port to the Tomcat connector as a request + * attribute. Modern Tomcat versions know how to retrieve + * the remote port from this attribute. + */ + { + const char *key = SC_A_REQ_REMOTE_PORT; + char *val = apr_itoa(r->pool, r->useragent_addr->port); + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, key) || + ajp_msg_append_string(msg, val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00980) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + key, val); + return AJP_EOVERFLOW; + } + } + /* Forward the local ip address information, which was forgotten + * from the builtin data of the AJP 13 protocol. + * Since the servlet spec allows to retrieve it via getLocalAddr(), + * we provide the address to the Tomcat connector as a request + * attribute. Modern Tomcat versions know how to retrieve + * the local address from this attribute. + */ + { + const char *key = SC_A_REQ_LOCAL_ADDR; + char *val = r->connection->local_ip; + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, key) || + ajp_msg_append_string(msg, val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02646) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + key, val); + return AJP_EOVERFLOW; + } + } + /* Use the environment vars prefixed with AJP_ + * and pass it to the header striping that prefix. + */ + for (i = 0; i < (apr_uint32_t)arr->nelts; i++) { + if (!strncmp(elts[i].key, "AJP_", 4)) { + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, elts[i].key + 4) || + ajp_msg_append_string(msg, elts[i].val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00981) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + elts[i].key, elts[i].val); + return AJP_EOVERFLOW; + } + } + } + + if (ajp_msg_append_uint8(msg, SC_A_ARE_DONE)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00982) + "ajp_marshal_into_msgb: " + "Error appending the message end"); + return AJP_EOVERFLOW; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, + "ajp_marshal_into_msgb: Done"); + return APR_SUCCESS; +} + +/* +AJPV13_RESPONSE/AJPV14_RESPONSE:= + response_prefix (2) + status (short) + status_msg (short) + num_headers (short) + num_headers*(res_header_name header_value) + *body_chunk + terminator boolean <! -- recycle connection or not --> + +req_header_name := + sc_req_header_name | (string) + +res_header_name := + sc_res_header_name | (string) + +header_value := + (string) + +body_chunk := + length (short) + body length*(var binary) + + */ + +static int addit_dammit(void *v, const char *key, const char *val) +{ + apr_table_addn(v, key, val); + return 1; +} + +static apr_status_t ajp_unmarshal_response(ajp_msg_t *msg, + request_rec *r, + proxy_dir_conf *dconf) +{ + apr_uint16_t status; + apr_status_t rc; + const char *ptr; + apr_uint16_t num_headers; + int i; + + rc = ajp_msg_get_uint16(msg, &status); + + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00983) + "ajp_unmarshal_response: Null status"); + return rc; + } + r->status = status; + + rc = ajp_msg_get_string(msg, &ptr); + if (rc == APR_SUCCESS) { +#if APR_CHARSET_EBCDIC /* copy only if we have to */ + ptr = apr_pstrdup(r->pool, ptr); + ap_xlate_proto_from_ascii(ptr, strlen(ptr)); +#endif + r->status_line = apr_psprintf(r->pool, "%d %s", status, ptr); + } + else { + r->status_line = NULL; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "ajp_unmarshal_response: status = %d", status); + + rc = ajp_msg_get_uint16(msg, &num_headers); + if (rc == APR_SUCCESS) { + apr_table_t *save_table; + + /* First, tuck away all already existing cookies */ + /* + * Could optimize here, but just in case we want to + * also save other headers, keep this logic. + */ + save_table = apr_table_make(r->pool, num_headers + 2); + apr_table_do(addit_dammit, save_table, r->headers_out, + "Set-Cookie", NULL); + r->headers_out = save_table; + } + else { + /* + * Reset headers, but not to NULL because things below the chain expect + * this to be non NULL e.g. the ap_content_length_filter. + */ + r->headers_out = apr_table_make(r->pool, 1); + num_headers = 0; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10405) + "ajp_unmarshal_response: Bad number of headers"); + return rc; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "ajp_unmarshal_response: Number of headers is = %d", + num_headers); + + for (i = 0; i < (int)num_headers; i++) { + apr_uint16_t name; + const char *stringname; + const char *value; + rc = ajp_msg_peek_uint16(msg, &name); + if (rc != APR_SUCCESS) { + return rc; + } + + if ((name & 0XFF00) == 0XA000) { + ajp_msg_get_uint16(msg, &name); + stringname = long_res_header_for_sc(name); + if (stringname == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00984) + "ajp_unmarshal_response: " + "No such sc (%08x)", + name); + return AJP_EBAD_HEADER; + } + } + else { + name = 0; + rc = ajp_msg_get_string(msg, &stringname); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00985) + "ajp_unmarshal_response: " + "Null header name"); + return rc; + } + ap_xlate_proto_from_ascii(stringname, strlen(stringname)); + } + + rc = ajp_msg_get_string(msg, &value); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00986) + "ajp_unmarshal_response: " + "Null header value"); + return rc; + } + + /* Set-Cookie need additional processing */ + if (!ap_cstr_casecmp(stringname, "Set-Cookie")) { + value = ap_proxy_cookie_reverse_map(r, dconf, value); + } + /* Location, Content-Location, URI and Destination need additional + * processing */ + else if (!ap_cstr_casecmp(stringname, "Location") + || !ap_cstr_casecmp(stringname, "Content-Location") + || !ap_cstr_casecmp(stringname, "URI") + || !ap_cstr_casecmp(stringname, "Destination")) + { + value = ap_proxy_location_reverse_map(r, dconf, value); + } + + ap_xlate_proto_from_ascii(value, strlen(value)); + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ajp_unmarshal_response: Header[%d] [%s] = [%s]", + i, stringname, value); + + apr_table_add(r->headers_out, stringname, value); + + /* Content-type needs an additional handling */ + if (ap_cstr_casecmp(stringname, "Content-Type") == 0) { + /* add corresponding filter */ + ap_set_content_type(r, apr_pstrdup(r->pool, value)); + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ajp_unmarshal_response: ap_set_content_type to '%s'", value); + } + } + + return APR_SUCCESS; +} + +/* + * Build the ajp header message and send it + */ +apr_status_t ajp_send_header(apr_socket_t *sock, + request_rec *r, + apr_size_t buffsize, + apr_uri_t *uri, + const char *secret) +{ + ajp_msg_t *msg; + apr_status_t rc; + + rc = ajp_msg_create(r->pool, buffsize, &msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00987) + "ajp_send_header: ajp_msg_create failed"); + return rc; + } + + rc = ajp_marshal_into_msgb(msg, r, uri, secret); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00988) + "ajp_send_header: ajp_marshal_into_msgb failed"); + return rc; + } + + rc = ajp_ilink_send(sock, msg); + ajp_msg_log(r, msg, "ajp_send_header: ajp_ilink_send packet dump"); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00989) + "ajp_send_header: ajp_ilink_send failed"); + return rc; + } + + return APR_SUCCESS; +} + +/* + * Read the ajp message and return the type of the message. + */ +apr_status_t ajp_read_header(apr_socket_t *sock, + request_rec *r, + apr_size_t buffsize, + ajp_msg_t **msg) +{ + apr_byte_t result; + apr_status_t rc; + + if (*msg) { + rc = ajp_msg_reuse(*msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00990) + "ajp_read_header: ajp_msg_reuse failed"); + return rc; + } + } + else { + rc = ajp_msg_create(r->pool, buffsize, msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00991) + "ajp_read_header: ajp_msg_create failed"); + return rc; + } + } + ajp_msg_reset(*msg); + rc = ajp_ilink_receive(sock, *msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00992) + "ajp_read_header: ajp_ilink_receive failed"); + return rc; + } + ajp_msg_log(r, *msg, "ajp_read_header: ajp_ilink_receive packet dump"); + rc = ajp_msg_peek_uint8(*msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00993) + "ajp_read_header: ajp_msg_peek_uint8 failed"); + return rc; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "ajp_read_header: ajp_ilink_received %s (0x%02x)", + ajp_type_str(result), result); + return APR_SUCCESS; +} + +/* parse the msg to read the type */ +int ajp_parse_type(request_rec *r, ajp_msg_t *msg) +{ + apr_byte_t result; + ajp_msg_peek_uint8(msg, &result); + ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, r, + "ajp_parse_type: got %s (0x%02x)", + ajp_type_str(result), result); + return (int) result; +} + +/* parse the header */ +apr_status_t ajp_parse_header(request_rec *r, proxy_dir_conf *conf, + ajp_msg_t *msg) +{ + apr_byte_t result; + apr_status_t rc; + + rc = ajp_msg_get_uint8(msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00994) + "ajp_parse_headers: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_SEND_HEADERS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00995) + "ajp_parse_headers: wrong type %s (0x%02x) expecting %s (0x%02x)", + ajp_type_str(result), result, + ajp_type_str(CMD_AJP13_SEND_HEADERS), CMD_AJP13_SEND_HEADERS); + return AJP_EBAD_HEADER; + } + return ajp_unmarshal_response(msg, r, conf); +} + +/* parse the body and return data address and length */ +apr_status_t ajp_parse_data(request_rec *r, ajp_msg_t *msg, + apr_uint16_t *len, char **ptr) +{ + apr_byte_t result; + apr_status_t rc; + apr_uint16_t expected_len; + + rc = ajp_msg_get_uint8(msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00996) + "ajp_parse_data: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_SEND_BODY_CHUNK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00997) + "ajp_parse_data: wrong type %s (0x%02x) expecting %s (0x%02x)", + ajp_type_str(result), result, + ajp_type_str(CMD_AJP13_SEND_BODY_CHUNK), CMD_AJP13_SEND_BODY_CHUNK); + return AJP_EBAD_HEADER; + } + rc = ajp_msg_get_uint16(msg, len); + if (rc != APR_SUCCESS) { + return rc; + } + /* + * msg->len contains the complete length of the message including all + * headers. So the expected length for a CMD_AJP13_SEND_BODY_CHUNK is + * msg->len minus the sum of + * AJP_HEADER_LEN : The length of the header to every AJP message. + * AJP_HEADER_SZ_LEN : The header giving the size of the chunk. + * 1 : The CMD_AJP13_SEND_BODY_CHUNK indicator byte (0x03). + * 1 : The last byte of this message always seems to be + * 0x00 and is not part of the chunk. + */ + expected_len = msg->len - (AJP_HEADER_LEN + AJP_HEADER_SZ_LEN + 1 + 1); + if (*len != expected_len) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00998) + "ajp_parse_data: Wrong chunk length. Length of chunk is %i," + " expected length is %i.", *len, expected_len); + return AJP_EBAD_HEADER; + } + *ptr = (char *)&(msg->buf[msg->pos]); + return APR_SUCCESS; +} + +/* Check the reuse flag in CMD_AJP13_END_RESPONSE */ +apr_status_t ajp_parse_reuse(request_rec *r, ajp_msg_t *msg, + apr_byte_t *reuse) +{ + apr_byte_t result; + apr_status_t rc; + + rc = ajp_msg_get_uint8(msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00999) + "ajp_parse_reuse: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_END_RESPONSE) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01000) + "ajp_parse_reuse: wrong type %s (0x%02x) expecting %s (0x%02x)", + ajp_type_str(result), result, + ajp_type_str(CMD_AJP13_END_RESPONSE), CMD_AJP13_END_RESPONSE); + return AJP_EBAD_HEADER; + } + return ajp_msg_get_uint8(msg, reuse); +} + +/* + * Allocate a msg to send data + */ +apr_status_t ajp_alloc_data_msg(apr_pool_t *pool, char **ptr, apr_size_t *len, + ajp_msg_t **msg) +{ + apr_status_t rc; + + if ((rc = ajp_msg_create(pool, *len, msg)) != APR_SUCCESS) + return rc; + ajp_msg_reset(*msg); + *ptr = (char *)&((*msg)->buf[6]); + *len = *len - 6; + + return APR_SUCCESS; +} + +/* + * Send the data message + */ +apr_status_t ajp_send_data_msg(apr_socket_t *sock, + ajp_msg_t *msg, apr_size_t len) +{ + + msg->buf[4] = (apr_byte_t)((len >> 8) & 0xFF); + msg->buf[5] = (apr_byte_t)(len & 0xFF); + + msg->len += len + 2; /* + 1 XXXX where is '\0' */ + + return ajp_ilink_send(sock, msg); + +} diff --git a/modules/proxy/ajp_header.h b/modules/proxy/ajp_header.h new file mode 100644 index 0000000..4c22ac7 --- /dev/null +++ b/modules/proxy/ajp_header.h @@ -0,0 +1,195 @@ +/* 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. + */ + +/** + * @file ajp_header.h + * @brief AJP defines + * + * @addtogroup AJP_defines + * @{ + */ + +#ifndef AJP_HEADER_H +#define AJP_HEADER_H + +/* + * Conditional request attributes + * + */ +#define SC_A_CONTEXT (unsigned char)1 +#define SC_A_SERVLET_PATH (unsigned char)2 +#define SC_A_REMOTE_USER (unsigned char)3 +#define SC_A_AUTH_TYPE (unsigned char)4 +#define SC_A_QUERY_STRING (unsigned char)5 +#define SC_A_JVM_ROUTE (unsigned char)6 +#define SC_A_SSL_CERT (unsigned char)7 +#define SC_A_SSL_CIPHER (unsigned char)8 +#define SC_A_SSL_SESSION (unsigned char)9 +#define SC_A_REQ_ATTRIBUTE (unsigned char)10 +#define SC_A_SSL_KEY_SIZE (unsigned char)11 /* only in if JkOptions +ForwardKeySize */ +#define SC_A_SECRET (unsigned char)12 +#define SC_A_STORED_METHOD (unsigned char)13 +#define SC_A_ARE_DONE (unsigned char)0xFF + +/* + * AJP private request attributes + * + * The following request attribute is recognized by Tomcat + * to contain the SSL protocol name + */ +#define SC_A_SSL_PROTOCOL ("AJP_SSL_PROTOCOL") +/* + * The following request attribute is recognized by Tomcat + * to contain the forwarded remote port. + */ +#define SC_A_REQ_REMOTE_PORT ("AJP_REMOTE_PORT") +/* + * The following request attribute is recognized by Tomcat + * to contain the forwarded local ip address. + */ +#define SC_A_REQ_LOCAL_ADDR ("AJP_LOCAL_ADDR") + +/* + * Request methods, coded as numbers instead of strings. + * The list of methods was taken from Section 5.1.1 of RFC 2616, + * RFC 2518, the ACL IETF draft, and the DeltaV IESG Proposed Standard. + * Method = "OPTIONS" + * | "GET" + * | "HEAD" + * | "POST" + * | "PUT" + * | "DELETE" + * | "TRACE" + * | "PROPFIND" + * | "PROPPATCH" + * | "MKCOL" + * | "COPY" + * | "MOVE" + * | "LOCK" + * | "UNLOCK" + * | "ACL" + * | "REPORT" + * | "VERSION-CONTROL" + * | "CHECKIN" + * | "CHECKOUT" + * | "UNCHECKOUT" + * | "SEARCH" + * | "MKWORKSPACE" + * | "UPDATE" + * | "LABEL" + * | "MERGE" + * | "BASELINE-CONTROL" + * | "MKACTIVITY" + * + */ +#define SC_M_OPTIONS (unsigned char)1 +#define SC_M_GET (unsigned char)2 +#define SC_M_HEAD (unsigned char)3 +#define SC_M_POST (unsigned char)4 +#define SC_M_PUT (unsigned char)5 +#define SC_M_DELETE (unsigned char)6 +#define SC_M_TRACE (unsigned char)7 +#define SC_M_PROPFIND (unsigned char)8 +#define SC_M_PROPPATCH (unsigned char)9 +#define SC_M_MKCOL (unsigned char)10 +#define SC_M_COPY (unsigned char)11 +#define SC_M_MOVE (unsigned char)12 +#define SC_M_LOCK (unsigned char)13 +#define SC_M_UNLOCK (unsigned char)14 +#define SC_M_ACL (unsigned char)15 +#define SC_M_REPORT (unsigned char)16 +#define SC_M_VERSION_CONTROL (unsigned char)17 +#define SC_M_CHECKIN (unsigned char)18 +#define SC_M_CHECKOUT (unsigned char)19 +#define SC_M_UNCHECKOUT (unsigned char)20 +#define SC_M_SEARCH (unsigned char)21 +#define SC_M_MKWORKSPACE (unsigned char)22 +#define SC_M_UPDATE (unsigned char)23 +#define SC_M_LABEL (unsigned char)24 +#define SC_M_MERGE (unsigned char)25 +#define SC_M_BASELINE_CONTROL (unsigned char)26 +#define SC_M_MKACTIVITY (unsigned char)27 +#define SC_M_JK_STORED (unsigned char)0xFF + + +/* + * Frequent request headers, these headers are coded as numbers + * instead of strings. + * + * Accept + * Accept-Charset + * Accept-Encoding + * Accept-Language + * Authorization + * Connection + * Content-Type + * Content-Length + * Cookie + * Cookie2 + * Host + * Pragma + * Referer + * User-Agent + * + */ + +#define SC_ACCEPT (unsigned short)0xA001 +#define SC_ACCEPT_CHARSET (unsigned short)0xA002 +#define SC_ACCEPT_ENCODING (unsigned short)0xA003 +#define SC_ACCEPT_LANGUAGE (unsigned short)0xA004 +#define SC_AUTHORIZATION (unsigned short)0xA005 +#define SC_CONNECTION (unsigned short)0xA006 +#define SC_CONTENT_TYPE (unsigned short)0xA007 +#define SC_CONTENT_LENGTH (unsigned short)0xA008 +#define SC_COOKIE (unsigned short)0xA009 +#define SC_COOKIE2 (unsigned short)0xA00A +#define SC_HOST (unsigned short)0xA00B +#define SC_PRAGMA (unsigned short)0xA00C +#define SC_REFERER (unsigned short)0xA00D +#define SC_USER_AGENT (unsigned short)0xA00E + +/* + * Frequent response headers, these headers are coded as numbers + * instead of strings. + * + * Content-Type + * Content-Language + * Content-Length + * Date + * Last-Modified + * Location + * Set-Cookie + * Servlet-Engine + * Status + * WWW-Authenticate + * + */ + +#define SC_RESP_CONTENT_TYPE (unsigned short)0xA001 +#define SC_RESP_CONTENT_LANGUAGE (unsigned short)0xA002 +#define SC_RESP_CONTENT_LENGTH (unsigned short)0xA003 +#define SC_RESP_DATE (unsigned short)0xA004 +#define SC_RESP_LAST_MODIFIED (unsigned short)0xA005 +#define SC_RESP_LOCATION (unsigned short)0xA006 +#define SC_RESP_SET_COOKIE (unsigned short)0xA007 +#define SC_RESP_SET_COOKIE2 (unsigned short)0xA008 +#define SC_RESP_SERVLET_ENGINE (unsigned short)0xA009 +#define SC_RESP_STATUS (unsigned short)0xA00A +#define SC_RESP_WWW_AUTHENTICATE (unsigned short)0xA00B +#define SC_RES_HEADERS_NUM 11 + +#endif /* AJP_HEADER_H */ +/** @} */ diff --git a/modules/proxy/ajp_link.c b/modules/proxy/ajp_link.c new file mode 100644 index 0000000..4f8a9ef --- /dev/null +++ b/modules/proxy/ajp_link.c @@ -0,0 +1,115 @@ +/* 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 "ajp.h" + +APLOG_USE_MODULE(proxy_ajp); + +apr_status_t ajp_ilink_send(apr_socket_t *sock, ajp_msg_t *msg) +{ + char *buf; + apr_status_t status; + apr_size_t length; + + ajp_msg_end(msg); + + length = msg->len; + buf = (char *)msg->buf; + + do { + apr_size_t written = length; + + status = apr_socket_send(sock, buf, &written); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL, APLOGNO(01029) + "ajp_ilink_send(): send failed"); + return status; + } + length -= written; + buf += written; + } while (length); + + return APR_SUCCESS; +} + + +static apr_status_t ilink_read(apr_socket_t *sock, apr_byte_t *buf, + apr_size_t len) +{ + apr_size_t length = len; + apr_size_t rdlen = 0; + apr_status_t status; + + while (rdlen < len) { + + status = apr_socket_recv(sock, (char *)(buf + rdlen), &length); + + if (status == APR_EOF) + return status; /* socket closed. */ + else if (APR_STATUS_IS_EAGAIN(status)) + continue; + else if (status != APR_SUCCESS) + return status; /* any error. */ + + rdlen += length; + length = len - rdlen; + } + return APR_SUCCESS; +} + + +apr_status_t ajp_ilink_receive(apr_socket_t *sock, ajp_msg_t *msg) +{ + apr_status_t status; + apr_size_t hlen; + apr_size_t blen; + + hlen = msg->header_len; + + status = ilink_read(sock, msg->buf, hlen); + + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL, APLOGNO(01030) + "ajp_ilink_receive() can't receive header"); + return (APR_STATUS_IS_TIMEUP(status) ? APR_TIMEUP : AJP_ENO_HEADER); + } + + status = ajp_msg_check_header(msg, &blen); + + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01031) + "ajp_ilink_receive() received bad header"); + return AJP_EBAD_HEADER; + } + + status = ilink_read(sock, msg->buf + hlen, blen); + + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL, APLOGNO(01032) + "ajp_ilink_receive() error while receiving message body " + "of length %" APR_SIZE_T_FMT, + hlen); + return AJP_EBAD_MESSAGE; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(01033) + "ajp_ilink_receive() received packet len=%" APR_SIZE_T_FMT + "type=%d", + blen, (int)msg->buf[hlen]); + + return APR_SUCCESS; +} + diff --git a/modules/proxy/ajp_msg.c b/modules/proxy/ajp_msg.c new file mode 100644 index 0000000..3367b5d --- /dev/null +++ b/modules/proxy/ajp_msg.c @@ -0,0 +1,641 @@ +/* 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 "ajp.h" + +APLOG_USE_MODULE(proxy_ajp); + +#define AJP_MSG_DUMP_BYTES_PER_LINE 16 +/* 2 hex digits plus space plus one char per dumped byte */ +/* plus prefix plus separator plus '\0' */ +#define AJP_MSG_DUMP_PREFIX_LENGTH strlen("XXXX ") +#define AJP_MSG_DUMP_LINE_LENGTH ((AJP_MSG_DUMP_BYTES_PER_LINE * \ + strlen("XX .")) + \ + AJP_MSG_DUMP_PREFIX_LENGTH + \ + strlen(" - ") + 1) + +static char *hex_table = "0123456789ABCDEF"; + +/** + * Dump the given number of bytes on an AJP Message + * + * @param pool pool to allocate from + * @param msg AJP Message to dump + * @param err error string to display + * @param count the number of bytes to dump + * @param buf buffer pointer for dump message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_dump(apr_pool_t *pool, ajp_msg_t *msg, char *err, + apr_size_t count, char **buf) +{ + apr_size_t i, j; + char *current; + apr_size_t bl, rl; + apr_byte_t x; + apr_size_t len = msg->len; + apr_size_t line_len; + + /* Display only first "count" bytes */ + if (len > count) + len = count; + /* First the space needed for the first line */ + bl = strlen(err) + 3 * (strlen(" XXX=") + 20) + 1 + + /* Now for the data lines */ + (len + 15) / 16 * AJP_MSG_DUMP_LINE_LENGTH; + *buf = apr_palloc(pool, bl); + if (!*buf) + return APR_ENOMEM; + apr_snprintf(*buf, bl, + "%s pos=%" APR_SIZE_T_FMT + " len=%" APR_SIZE_T_FMT " max=%" APR_SIZE_T_FMT "\n", + err, msg->pos, msg->len, msg->max_size); + current = *buf + strlen(*buf); + for (i = 0; i < len; i += AJP_MSG_DUMP_BYTES_PER_LINE) { + /* Safety check: do we have enough buffer for another line? */ + rl = bl - (current - *buf); + if (AJP_MSG_DUMP_LINE_LENGTH > rl) { + *(current - 1) = '\0'; + return APR_ENOMEM; + } + apr_snprintf(current, rl, "%.4lx ", (unsigned long)i); + current += AJP_MSG_DUMP_PREFIX_LENGTH; + line_len = len - i; + if (line_len > AJP_MSG_DUMP_BYTES_PER_LINE) { + line_len = AJP_MSG_DUMP_BYTES_PER_LINE; + } + for (j = 0; j < line_len; j++) { + x = msg->buf[i + j]; + + *current++ = hex_table[x >> 4]; + *current++ = hex_table[x & 0x0f]; + *current++ = ' '; + } + *current++ = ' '; + *current++ = '-'; + *current++ = ' '; + for (j = 0; j < line_len; j++) { + x = msg->buf[i + j]; + + if (x > 0x20 && x < 0x7F) { + *current++ = x; + } + else { + *current++ = '.'; + } + } + *current++ = '\n'; + } + *(current - 1) = '\0'; + + return APR_SUCCESS; +} + +/** + * Log an AJP message + * + * @param request The current request + * @param msg AJP Message to dump + * @param err error string to display + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_log(request_rec *r, ajp_msg_t *msg, char *err) +{ + int level; + apr_size_t count; + char *buf, *next; + apr_status_t rc = APR_SUCCESS; + + if (APLOGrtrace7(r)) { + level = APLOG_TRACE7; + count = 1024; + if (APLOGrtrace8(r)) { + level = APLOG_TRACE8; + count = AJP_MAX_BUFFER_SZ; + } + rc = ajp_msg_dump(r->pool, msg, err, count, &buf); + if (rc == APR_SUCCESS) { + while ((next = ap_strchr(buf, '\n'))) { + *next = '\0'; + /* Intentional no APLOGNO */ + ap_log_rerror(APLOG_MARK, level, 0, r, "%s", buf); + buf = next + 1; + } + /* Intentional no APLOGNO */ + ap_log_rerror(APLOG_MARK, level, 0, r, "%s", buf); + } + } + return rc; +} + +/** + * Check a new AJP Message by looking at signature and return its size + * + * @param msg AJP Message to check + * @param len Pointer to returned len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_check_header(ajp_msg_t *msg, apr_size_t *len) +{ + apr_byte_t *head = msg->buf; + apr_size_t msglen; + + if (!((head[0] == 0x41 && head[1] == 0x42) || + (head[0] == 0x12 && head[1] == 0x34))) { + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01080) + "ajp_msg_check_header() got bad signature %02x%02x", + head[0], head[1]); + + return AJP_EBAD_SIGNATURE; + } + + msglen = ((head[2] & 0xff) << 8); + msglen += (head[3] & 0xFF); + + if (msglen > msg->max_size) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01081) + "ajp_msg_check_header() incoming message is " + "too big %" APR_SIZE_T_FMT ", max is %" APR_SIZE_T_FMT, + msglen, msg->max_size); + return AJP_ETOBIG; + } + + msg->len = msglen + AJP_HEADER_LEN; + msg->pos = AJP_HEADER_LEN; + *len = msglen; + + return APR_SUCCESS; +} + +/** + * Reset an AJP Message + * + * @param msg AJP Message to reset + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_reset(ajp_msg_t *msg) +{ + msg->len = AJP_HEADER_LEN; + msg->pos = AJP_HEADER_LEN; + + return APR_SUCCESS; +} + +/** + * Reuse an AJP Message + * + * @param msg AJP Message to reuse + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_reuse(ajp_msg_t *msg) +{ + apr_byte_t *buf; + apr_size_t max_size; + + buf = msg->buf; + max_size = msg->max_size; + memset(msg, 0, sizeof(ajp_msg_t)); + msg->buf = buf; + msg->max_size = max_size; + msg->header_len = AJP_HEADER_LEN; + ajp_msg_reset(msg); + return APR_SUCCESS; +} + +/** + * Mark the end of an AJP Message + * + * @param msg AJP Message to end + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_end(ajp_msg_t *msg) +{ + apr_size_t len = msg->len - AJP_HEADER_LEN; + + if (msg->server_side) { + msg->buf[0] = 0x41; + msg->buf[1] = 0x42; + } + else { + msg->buf[0] = 0x12; + msg->buf[1] = 0x34; + } + + msg->buf[2] = (apr_byte_t)((len >> 8) & 0xFF); + msg->buf[3] = (apr_byte_t)(len & 0xFF); + + return APR_SUCCESS; +} + +static APR_INLINE int ajp_log_overflow(ajp_msg_t *msg, const char *context) +{ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(03229) + "%s(): BufferOverflowException %" APR_SIZE_T_FMT + " %" APR_SIZE_T_FMT, + context, msg->pos, msg->len); + return AJP_EOVERFLOW; +} + +/** + * Add an unsigned 32bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint32(ajp_msg_t *msg, apr_uint32_t value) +{ + apr_size_t len = msg->len; + + if ((len + 4) > msg->max_size) { + return ajp_log_overflow(msg, "ajp_msg_append_uint32"); + } + + msg->buf[len] = (apr_byte_t)((value >> 24) & 0xFF); + msg->buf[len + 1] = (apr_byte_t)((value >> 16) & 0xFF); + msg->buf[len + 2] = (apr_byte_t)((value >> 8) & 0xFF); + msg->buf[len + 3] = (apr_byte_t)(value & 0xFF); + + msg->len += 4; + + return APR_SUCCESS; +} + +/** + * Add an unsigned 16bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint16(ajp_msg_t *msg, apr_uint16_t value) +{ + apr_size_t len = msg->len; + + if ((len + 2) > msg->max_size) { + return ajp_log_overflow(msg, "ajp_msg_append_uint16"); + } + + msg->buf[len] = (apr_byte_t)((value >> 8) & 0xFF); + msg->buf[len + 1] = (apr_byte_t)(value & 0xFF); + + msg->len += 2; + + return APR_SUCCESS; +} + +/** + * Add an unsigned 8bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint8(ajp_msg_t *msg, apr_byte_t value) +{ + apr_size_t len = msg->len; + + if ((len + 1) > msg->max_size) { + return ajp_log_overflow(msg, "ajp_msg_append_uint8"); + } + + msg->buf[len] = value; + msg->len += 1; + + return APR_SUCCESS; +} + +/** + * Add a String in AJP message, and transform the String in ASCII + * if convert is set and we're on an EBCDIC machine + * + * @param msg AJP Message to get value from + * @param value Pointer to String + * @param convert When set told to convert String to ASCII + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_string_ex(ajp_msg_t *msg, const char *value, + int convert) +{ + apr_size_t len; + + if (value == NULL) { + return(ajp_msg_append_uint16(msg, 0xFFFF)); + } + + len = strlen(value); + if ((msg->len + len + 3) > msg->max_size) { + return ajp_log_overflow(msg, "ajp_msg_append_cvt_string"); + } + + /* ignore error - we checked once */ + ajp_msg_append_uint16(msg, (apr_uint16_t)len); + + /* We checked for space !! */ + memcpy(msg->buf + msg->len, value, len + 1); /* including \0 */ + + if (convert) { + /* convert from EBCDIC if needed */ + ap_xlate_proto_to_ascii((char *)msg->buf + msg->len, len + 1); + } + + msg->len += len + 1; + + return APR_SUCCESS; +} + +/** + * Add a Byte array to AJP Message + * + * @param msg AJP Message to get value from + * @param value Pointer to Byte array + * @param valuelen Byte array len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_bytes(ajp_msg_t *msg, const apr_byte_t *value, + apr_size_t valuelen) +{ + if (! valuelen) { + return APR_SUCCESS; /* Shouldn't we indicate an error ? */ + } + + if ((msg->len + valuelen) > msg->max_size) { + return ajp_log_overflow(msg, "ajp_msg_append_bytes"); + } + + /* We checked for space !! */ + memcpy(msg->buf + msg->len, value, valuelen); + msg->len += valuelen; + + return APR_SUCCESS; +} + +/** + * Get a 32bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint32(ajp_msg_t *msg, apr_uint32_t *rvalue) +{ + apr_uint32_t value; + + if ((msg->pos + 3) > msg->len) { + return ajp_log_overflow(msg, "ajp_msg_get_uint32"); + } + + value = ((msg->buf[(msg->pos++)] & 0xFF) << 24); + value |= ((msg->buf[(msg->pos++)] & 0xFF) << 16); + value |= ((msg->buf[(msg->pos++)] & 0xFF) << 8); + value |= ((msg->buf[(msg->pos++)] & 0xFF)); + + *rvalue = value; + return APR_SUCCESS; +} + + +/** + * Get a 16bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue) +{ + apr_uint16_t value; + + if ((msg->pos + 1) > msg->len) { + return ajp_log_overflow(msg, "ajp_msg_get_uint16"); + } + + value = ((msg->buf[(msg->pos++)] & 0xFF) << 8); + value += ((msg->buf[(msg->pos++)] & 0xFF)); + + *rvalue = value; + return APR_SUCCESS; +} + +/** + * Peek a 16bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue) +{ + apr_uint16_t value; + + if ((msg->pos + 1) > msg->len) { + return ajp_log_overflow(msg, "ajp_msg_peek_uint16"); + } + + value = ((msg->buf[(msg->pos)] & 0xFF) << 8); + value += ((msg->buf[(msg->pos + 1)] & 0xFF)); + + *rvalue = value; + return APR_SUCCESS; +} + +/** + * Peek a 8bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint8(ajp_msg_t *msg, apr_byte_t *rvalue) +{ + if (msg->pos > msg->len) { + return ajp_log_overflow(msg, "ajp_msg_peek_uint8"); + } + + *rvalue = msg->buf[msg->pos]; + return APR_SUCCESS; +} + +/** + * Get a 8bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint8(ajp_msg_t *msg, apr_byte_t *rvalue) +{ + + if (msg->pos > msg->len) { + return ajp_log_overflow(msg, "ajp_msg_get_uint8"); + } + + *rvalue = msg->buf[msg->pos++]; + return APR_SUCCESS; +} + + +/** + * Get a String value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_string(ajp_msg_t *msg, const char **rvalue) +{ + apr_uint16_t size; + apr_size_t start; + apr_status_t status; + + status = ajp_msg_get_uint16(msg, &size); + start = msg->pos; + + if ((status != APR_SUCCESS) || (size + start > msg->max_size)) { + return ajp_log_overflow(msg, "ajp_msg_get_string"); + } + + msg->pos += (apr_size_t)size; + msg->pos++; /* a String in AJP is NULL terminated */ + + *rvalue = (const char *)(msg->buf + start); + return APR_SUCCESS; +} + + +/** + * Get a Byte array from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @param rvalueLen Pointer where Byte array len will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_bytes(ajp_msg_t *msg, apr_byte_t **rvalue, + apr_size_t *rvalue_len) +{ + apr_uint16_t size; + apr_size_t start; + apr_status_t status; + + status = ajp_msg_get_uint16(msg, &size); + /* save the current position */ + start = msg->pos; + + if ((status != APR_SUCCESS) || (size + start > msg->max_size)) { + return ajp_log_overflow(msg, "ajp_msg_get_bytes"); + } + msg->pos += (apr_size_t)size; /* only bytes, no trailer */ + + *rvalue = msg->buf + start; + *rvalue_len = size; + + return APR_SUCCESS; +} + + +/** + * Create an AJP Message from pool + * + * @param pool memory pool to allocate AJP message from + * @param size size of the buffer to create + * @param rmsg Pointer to newly created AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_create(apr_pool_t *pool, apr_size_t size, ajp_msg_t **rmsg) +{ + ajp_msg_t *msg = (ajp_msg_t *)apr_pcalloc(pool, sizeof(ajp_msg_t)); + + msg->server_side = 0; + + msg->buf = (apr_byte_t *)apr_palloc(pool, size); + msg->len = 0; + msg->header_len = AJP_HEADER_LEN; + msg->max_size = size; + *rmsg = msg; + + return APR_SUCCESS; +} + +/** + * Recopy an AJP Message to another + * + * @param smsg source AJP message + * @param dmsg destination AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_copy(ajp_msg_t *smsg, ajp_msg_t *dmsg) +{ + if (smsg->len > smsg->max_size) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(01082) + "ajp_msg_copy(): destination buffer too " + "small %" APR_SIZE_T_FMT ", max size is %" APR_SIZE_T_FMT, + smsg->len, smsg->max_size); + return AJP_ETOSMALL; + } + + memcpy(dmsg->buf, smsg->buf, smsg->len); + dmsg->len = smsg->len; + dmsg->pos = smsg->pos; + + return APR_SUCCESS; +} + + +/** + * Serialize in an AJP Message a PING command + * + * +-----------------------+ + * | PING CMD (1 byte) | + * +-----------------------+ + * + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_ping(ajp_msg_t *msg) +{ + apr_status_t rc; + ajp_msg_reset(msg); + + if ((rc = ajp_msg_append_uint8(msg, CMD_AJP13_PING)) != APR_SUCCESS) + return rc; + + return APR_SUCCESS; +} + +/** + * Serialize in an AJP Message a CPING command + * + * +-----------------------+ + * | CPING CMD (1 byte) | + * +-----------------------+ + * + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_cping(ajp_msg_t *msg) +{ + apr_status_t rc; + ajp_msg_reset(msg); + + if ((rc = ajp_msg_append_uint8(msg, CMD_AJP13_CPING)) != APR_SUCCESS) + return rc; + + return APR_SUCCESS; +} diff --git a/modules/proxy/ajp_utils.c b/modules/proxy/ajp_utils.c new file mode 100644 index 0000000..2fe9f72 --- /dev/null +++ b/modules/proxy/ajp_utils.c @@ -0,0 +1,137 @@ +/* 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 "ajp.h" + +APLOG_USE_MODULE(proxy_ajp); + +/* + * Handle the CPING/CPONG + */ +apr_status_t ajp_handle_cping_cpong(apr_socket_t *sock, + request_rec *r, + apr_interval_time_t timeout) +{ + ajp_msg_t *msg; + apr_status_t rc, rv; + apr_interval_time_t org; + apr_byte_t result; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, + "Into ajp_handle_cping_cpong"); + + rc = ajp_msg_create(r->pool, AJP_PING_PONG_SZ, &msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01007) + "ajp_handle_cping_cpong: ajp_msg_create failed"); + return rc; + } + + rc = ajp_msg_serialize_cping(msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01008) + "ajp_handle_cping_cpong: ajp_marshal_into_msgb failed"); + return rc; + } + + rc = ajp_ilink_send(sock, msg); + ajp_msg_log(r, msg, "ajp_handle_cping_cpong: ajp_ilink_send packet dump"); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01009) + "ajp_handle_cping_cpong: ajp_ilink_send failed"); + return rc; + } + + rc = apr_socket_timeout_get(sock, &org); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01010) + "ajp_handle_cping_cpong: apr_socket_timeout_get failed"); + return rc; + } + + /* Set CPING/CPONG response timeout */ + rc = apr_socket_timeout_set(sock, timeout); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01011) + "ajp_handle_cping_cpong: apr_socket_timeout_set failed"); + return rc; + } + ajp_msg_reuse(msg); + + /* Read CPONG reply */ + rv = ajp_ilink_receive(sock, msg); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01012) + "ajp_handle_cping_cpong: ajp_ilink_receive failed"); + goto cleanup; + } + + ajp_msg_log(r, msg, "ajp_handle_cping_cpong: ajp_ilink_receive packet dump"); + rv = ajp_msg_get_uint8(msg, &result); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01013) + "ajp_handle_cping_cpong: invalid CPONG message"); + goto cleanup; + } + if (result != CMD_AJP13_CPONG) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01014) + "ajp_handle_cping_cpong: awaited CPONG, received %d ", + result); + rv = APR_EGENERAL; + goto cleanup; + } + +cleanup: + /* Restore original socket timeout */ + rc = apr_socket_timeout_set(sock, org); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01015) + "ajp_handle_cping_cpong: apr_socket_timeout_set failed"); + return rc; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, + "ajp_handle_cping_cpong: Done"); + return rv; +} + + +#define case_to_str(x) case CMD_AJP13_##x:\ + return #x;\ + break; + +/** + * Convert numeric message type into string + * @param type AJP message type + * @return AJP message type as a string + */ +const char *ajp_type_str(int type) +{ + switch (type) { + case_to_str(FORWARD_REQUEST) + case_to_str(SEND_BODY_CHUNK) + case_to_str(SEND_HEADERS) + case_to_str(END_RESPONSE) + case_to_str(GET_BODY_CHUNK) + case_to_str(SHUTDOWN) + case_to_str(PING) + case_to_str(CPONG) + case_to_str(CPING) + default: + return "CMD_AJP13_UNKNOWN"; + } + +} diff --git a/modules/proxy/balancers/Makefile.in b/modules/proxy/balancers/Makefile.in new file mode 100644 index 0000000..7c5c149 --- /dev/null +++ b/modules/proxy/balancers/Makefile.in @@ -0,0 +1,3 @@ +# a modules Makefile has no explicit targets -- they will be defined by +# whatever modules are enabled. just grab special.mk to deal with this. +include $(top_srcdir)/build/special.mk diff --git a/modules/proxy/balancers/config2.m4 b/modules/proxy/balancers/config2.m4 new file mode 100644 index 0000000..f637281 --- /dev/null +++ b/modules/proxy/balancers/config2.m4 @@ -0,0 +1,8 @@ +APACHE_MODPATH_INIT(proxy/balancers) + +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.c b/modules/proxy/balancers/mod_lbmethod_bybusyness.c new file mode 100644 index 0000000..80d538c --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bybusyness.c @@ -0,0 +1,124 @@ +/* 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 "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" + +module AP_MODULE_DECLARE_DATA lbmethod_bybusyness_module; + +static APR_OPTIONAL_FN_TYPE(proxy_balancer_get_best_worker) + *ap_proxy_balancer_get_best_worker_fn = NULL; + +static int is_best_bybusyness(proxy_worker *current, proxy_worker *prev_best, void *baton) +{ + int *total_factor = (int *)baton; + + current->s->lbstatus += current->s->lbfactor; + *total_factor += current->s->lbfactor; + + return ( + !prev_best + || (current->s->busy < prev_best->s->busy) + || ( + (current->s->busy == prev_best->s->busy) + && (current->s->lbstatus > prev_best->s->lbstatus) + ) + ); +} + +static proxy_worker *find_best_bybusyness(proxy_balancer *balancer, + request_rec *r) +{ + int total_factor = 0; + proxy_worker *worker = + ap_proxy_balancer_get_best_worker_fn(balancer, r, is_best_bybusyness, + &total_factor); + + if (worker) { + worker->s->lbstatus -= total_factor; + } + + return worker; +} + +/* assumed to be mutex protected by caller */ +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) +{ + int i; + proxy_worker **worker; + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + (*worker)->s->lbstatus = 0; + (*worker)->s->busy = 0; + } + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) +{ + return APR_SUCCESS; +} + +static const proxy_balancer_method bybusyness = +{ + "bybusyness", + &find_best_bybusyness, + NULL, + &reset, + &age, + NULL +}; + +/* post_config hook: */ +static int lbmethod_bybusyness_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + + /* lbmethod_bybusyness_post_config() will be called twice during startup. So, don't + * set up the static data the 1st time through. */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + + ap_proxy_balancer_get_best_worker_fn = + APR_RETRIEVE_OPTIONAL_FN(proxy_balancer_get_best_worker); + if (!ap_proxy_balancer_get_best_worker_fn) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10151) + "mod_proxy must be loaded for mod_lbmethod_bybusyness"); + return !OK; + } + + return OK; +} + +static void register_hook(apr_pool_t *p) +{ + ap_register_provider(p, PROXY_LBMETHOD, "bybusyness", "0", &bybusyness); + ap_hook_post_config(lbmethod_bybusyness_post_config, NULL, NULL, APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(lbmethod_bybusyness) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + register_hook /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_bybusyness.dep b/modules/proxy/balancers/mod_lbmethod_bybusyness.dep new file mode 100644 index 0000000..12cbe7f --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bybusyness.dep @@ -0,0 +1,76 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_lbmethod_bybusyness.mak + +.\mod_lbmethod_bybusyness.c : \ + "..\..\..\include\ap_config.h"\ + "..\..\..\include\ap_config_layout.h"\ + "..\..\..\include\ap_expr.h"\ + "..\..\..\include\ap_hooks.h"\ + "..\..\..\include\ap_mmn.h"\ + "..\..\..\include\ap_mpm.h"\ + "..\..\..\include\ap_provider.h"\ + "..\..\..\include\ap_regex.h"\ + "..\..\..\include\ap_release.h"\ + "..\..\..\include\ap_slotmem.h"\ + "..\..\..\include\apache_noprobes.h"\ + "..\..\..\include\http_config.h"\ + "..\..\..\include\http_connection.h"\ + "..\..\..\include\http_core.h"\ + "..\..\..\include\http_log.h"\ + "..\..\..\include\http_main.h"\ + "..\..\..\include\http_protocol.h"\ + "..\..\..\include\http_request.h"\ + "..\..\..\include\http_vhost.h"\ + "..\..\..\include\httpd.h"\ + "..\..\..\include\os.h"\ + "..\..\..\include\scoreboard.h"\ + "..\..\..\include\util_cfgtree.h"\ + "..\..\..\include\util_charset.h"\ + "..\..\..\include\util_ebcdic.h"\ + "..\..\..\include\util_filter.h"\ + "..\..\..\include\util_mutex.h"\ + "..\..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\..\srclib\apr-util\include\apu.h"\ + "..\..\..\srclib\apr\include\apr.h"\ + "..\..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\..\srclib\apr\include\apr_dso.h"\ + "..\..\..\srclib\apr\include\apr_errno.h"\ + "..\..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\..\srclib\apr\include\apr_general.h"\ + "..\..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\..\srclib\apr\include\apr_hash.h"\ + "..\..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\..\srclib\apr\include\apr_lib.h"\ + "..\..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\..\srclib\apr\include\apr_poll.h"\ + "..\..\..\srclib\apr\include\apr_pools.h"\ + "..\..\..\srclib\apr\include\apr_portable.h"\ + "..\..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\..\srclib\apr\include\apr_ring.h"\ + "..\..\..\srclib\apr\include\apr_shm.h"\ + "..\..\..\srclib\apr\include\apr_strings.h"\ + "..\..\..\srclib\apr\include\apr_tables.h"\ + "..\..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\..\srclib\apr\include\apr_time.h"\ + "..\..\..\srclib\apr\include\apr_user.h"\ + "..\..\..\srclib\apr\include\apr_version.h"\ + "..\..\..\srclib\apr\include\apr_want.h"\ + "..\mod_proxy.h"\ + + +..\..\..\build\win32\httpd.rc : \ + "..\..\..\include\ap_release.h"\ + diff --git a/modules/proxy/balancers/mod_lbmethod_bybusyness.dsp b/modules/proxy/balancers/mod_lbmethod_bybusyness.dsp new file mode 100644 index 0000000..8544600 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bybusyness.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_bybusyness" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_bybusyness - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bybusyness.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bybusyness.mak" CFG="mod_lbmethod_bybusyness - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_bybusyness - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_bybusyness - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_bybusyness_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_bybusyness.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_bybusyness_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bybusyness.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_bybusyness.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_bybusyness - Win32 Release" +# Name "mod_lbmethod_bybusyness - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_bybusyness.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/balancers/mod_lbmethod_bybusyness.mak b/modules/proxy/balancers/mod_lbmethod_bybusyness.mak new file mode 100644 index 0000000..4a04fd6 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bybusyness.mak @@ -0,0 +1,408 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_lbmethod_bybusyness.dsp +!IF "$(CFG)" == "" +CFG=mod_lbmethod_bybusyness - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_lbmethod_bybusyness - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_lbmethod_bybusyness - Win32 Release" && "$(CFG)" != "mod_lbmethod_bybusyness - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bybusyness.mak" CFG="mod_lbmethod_bybusyness - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_bybusyness - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_bybusyness - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_bybusyness.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Release" "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_lbmethod_bybusyness.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" "mod_proxy_balancer - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_bybusyness.obj" + -@erase "$(INTDIR)\mod_lbmethod_bybusyness.res" + -@erase "$(INTDIR)\mod_lbmethod_bybusyness_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_bybusyness_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.exp" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.lib" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_bybusyness_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_bybusyness.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_bybusyness.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_bybusyness.so" /implib:"$(OUTDIR)\mod_lbmethod_bybusyness.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_bybusyness.obj" \ + "$(INTDIR)\mod_lbmethod_bybusyness.res" \ + "..\..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\..\Release\libhttpd.lib" \ + "..\Release\mod_proxy.lib" \ + "..\Release\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_bybusyness.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_lbmethod_bybusyness.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_bybusyness.so" + if exist .\Release\mod_lbmethod_bybusyness.so.manifest mt.exe -manifest .\Release\mod_lbmethod_bybusyness.so.manifest -outputresource:.\Release\mod_lbmethod_bybusyness.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_bybusyness.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Debug" "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_lbmethod_bybusyness.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" "mod_proxy_balancer - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_bybusyness.obj" + -@erase "$(INTDIR)\mod_lbmethod_bybusyness.res" + -@erase "$(INTDIR)\mod_lbmethod_bybusyness_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_bybusyness_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.exp" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.lib" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bybusyness.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_bybusyness_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_bybusyness.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_bybusyness.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_bybusyness.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_bybusyness.so" /implib:"$(OUTDIR)\mod_lbmethod_bybusyness.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bybusyness.so +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_bybusyness.obj" \ + "$(INTDIR)\mod_lbmethod_bybusyness.res" \ + "..\..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\..\Debug\libhttpd.lib" \ + "..\Debug\mod_proxy.lib" \ + "..\Debug\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_bybusyness.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_lbmethod_bybusyness.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_bybusyness.so" + if exist .\Debug\mod_lbmethod_bybusyness.so.manifest mt.exe -manifest .\Debug\mod_lbmethod_bybusyness.so.manifest -outputresource:.\Debug\mod_lbmethod_bybusyness.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_lbmethod_bybusyness.dep") +!INCLUDE "mod_lbmethod_bybusyness.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_lbmethod_bybusyness.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" || "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" +SOURCE=.\mod_lbmethod_bybusyness.c + +"$(INTDIR)\mod_lbmethod_bybusyness.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +"mod_proxy - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd ".\balancers" + +"mod_proxy - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd ".\balancers" + +"mod_proxy - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + +"mod_proxy_balancer - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" + cd ".\balancers" + +"mod_proxy_balancer - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Debug" + +"mod_proxy_balancer - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" + cd ".\balancers" + +"mod_proxy_balancer - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +SOURCE=..\..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_lbmethod_bybusyness - Win32 Release" + + +"$(INTDIR)\mod_lbmethod_bybusyness.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bybusyness.so" /d LONG_NAME="lbmethod_bybusyness_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/balancers/mod_lbmethod_byrequests.c b/modules/proxy/balancers/mod_lbmethod_byrequests.c new file mode 100644 index 0000000..b6cd8d8 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_byrequests.c @@ -0,0 +1,165 @@ +/* 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 "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" + +module AP_MODULE_DECLARE_DATA lbmethod_byrequests_module; + +static APR_OPTIONAL_FN_TYPE(proxy_balancer_get_best_worker) + *ap_proxy_balancer_get_best_worker_fn = NULL; + +static int is_best_byrequests(proxy_worker *current, proxy_worker *prev_best, void *baton) +{ + int *total_factor = (int *)baton; + + current->s->lbstatus += current->s->lbfactor; + *total_factor += current->s->lbfactor; + + return (!prev_best || (current->s->lbstatus > prev_best->s->lbstatus)); +} + +/* + * The idea behind the find_best_byrequests scheduler is the following: + * + * lbfactor is "how much we expect this worker to work", or "the worker's + * normalized work quota". + * + * lbstatus is "how urgent this worker has to work to fulfill its quota + * of work". + * + * We distribute each worker's work quota to the worker, and then look + * which of them needs to work most urgently (biggest lbstatus). This + * worker is then selected for work, and its lbstatus reduced by the + * total work quota we distributed to all workers. Thus the sum of all + * lbstatus does not change.(*) + * + * If some workers are disabled, the others will + * still be scheduled correctly. + * + * If a balancer is configured as follows: + * + * worker a b c d + * lbfactor 25 25 25 25 + * + * And b gets disabled, the following schedule is produced: + * + * a c d a c d a c d ... + * + * Note that the above lbfactor setting is the *exact* same as: + * + * worker a b c d + * lbfactor 1 1 1 1 + * + * Asymmetric configurations work as one would expect. For + * example: + * + * worker a b c d + * lbfactor 1 1 1 2 + * + * would have a, b and c all handling about the same + * amount of load with d handling twice what a or b + * or c handles individually. So we could see: + * + * b a d c d a c d b d ... + * + */ +static proxy_worker *find_best_byrequests(proxy_balancer *balancer, + request_rec *r) +{ + int total_factor = 0; + proxy_worker *worker = ap_proxy_balancer_get_best_worker_fn(balancer, r, is_best_byrequests, &total_factor); + + if (worker) { + worker->s->lbstatus -= total_factor; + } + + return worker; +} + +/* assumed to be mutex protected by caller */ +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) +{ + int i; + proxy_worker **worker; + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + (*worker)->s->lbstatus = 0; + } + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) +{ + return APR_SUCCESS; +} + +/* + * How to add additional lbmethods: + * 1. Create func which determines "best" candidate worker + * (eg: find_best_bytraffic, above) + * 2. Register it as a provider. + */ +static const proxy_balancer_method byrequests = +{ + "byrequests", + &find_best_byrequests, + NULL, + &reset, + &age, + NULL +}; + +/* post_config hook: */ +static int lbmethod_byrequests_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + + /* lbmethod_byrequests_post_config() will be called twice during startup. So, don't + * set up the static data the 1st time through. */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + + ap_proxy_balancer_get_best_worker_fn = + APR_RETRIEVE_OPTIONAL_FN(proxy_balancer_get_best_worker); + if (!ap_proxy_balancer_get_best_worker_fn) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10152) + "mod_proxy must be loaded for mod_lbmethod_byrequests"); + return !OK; + } + + return OK; +} + +static void register_hook(apr_pool_t *p) +{ + ap_register_provider(p, PROXY_LBMETHOD, "byrequests", "0", &byrequests); + ap_hook_post_config(lbmethod_byrequests_post_config, NULL, NULL, APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(lbmethod_byrequests) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + register_hook /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_byrequests.dep b/modules/proxy/balancers/mod_lbmethod_byrequests.dep new file mode 100644 index 0000000..acaeb54 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_byrequests.dep @@ -0,0 +1,76 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_lbmethod_byrequests.mak + +.\mod_lbmethod_byrequests.c : \ + "..\..\..\include\ap_config.h"\ + "..\..\..\include\ap_config_layout.h"\ + "..\..\..\include\ap_expr.h"\ + "..\..\..\include\ap_hooks.h"\ + "..\..\..\include\ap_mmn.h"\ + "..\..\..\include\ap_mpm.h"\ + "..\..\..\include\ap_provider.h"\ + "..\..\..\include\ap_regex.h"\ + "..\..\..\include\ap_release.h"\ + "..\..\..\include\ap_slotmem.h"\ + "..\..\..\include\apache_noprobes.h"\ + "..\..\..\include\http_config.h"\ + "..\..\..\include\http_connection.h"\ + "..\..\..\include\http_core.h"\ + "..\..\..\include\http_log.h"\ + "..\..\..\include\http_main.h"\ + "..\..\..\include\http_protocol.h"\ + "..\..\..\include\http_request.h"\ + "..\..\..\include\http_vhost.h"\ + "..\..\..\include\httpd.h"\ + "..\..\..\include\os.h"\ + "..\..\..\include\scoreboard.h"\ + "..\..\..\include\util_cfgtree.h"\ + "..\..\..\include\util_charset.h"\ + "..\..\..\include\util_ebcdic.h"\ + "..\..\..\include\util_filter.h"\ + "..\..\..\include\util_mutex.h"\ + "..\..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\..\srclib\apr-util\include\apu.h"\ + "..\..\..\srclib\apr\include\apr.h"\ + "..\..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\..\srclib\apr\include\apr_dso.h"\ + "..\..\..\srclib\apr\include\apr_errno.h"\ + "..\..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\..\srclib\apr\include\apr_general.h"\ + "..\..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\..\srclib\apr\include\apr_hash.h"\ + "..\..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\..\srclib\apr\include\apr_lib.h"\ + "..\..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\..\srclib\apr\include\apr_poll.h"\ + "..\..\..\srclib\apr\include\apr_pools.h"\ + "..\..\..\srclib\apr\include\apr_portable.h"\ + "..\..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\..\srclib\apr\include\apr_ring.h"\ + "..\..\..\srclib\apr\include\apr_shm.h"\ + "..\..\..\srclib\apr\include\apr_strings.h"\ + "..\..\..\srclib\apr\include\apr_tables.h"\ + "..\..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\..\srclib\apr\include\apr_time.h"\ + "..\..\..\srclib\apr\include\apr_user.h"\ + "..\..\..\srclib\apr\include\apr_version.h"\ + "..\..\..\srclib\apr\include\apr_want.h"\ + "..\mod_proxy.h"\ + + +..\..\..\build\win32\httpd.rc : \ + "..\..\..\include\ap_release.h"\ + diff --git a/modules/proxy/balancers/mod_lbmethod_byrequests.dsp b/modules/proxy/balancers/mod_lbmethod_byrequests.dsp new file mode 100644 index 0000000..cfa90bf --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_byrequests.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_byrequests" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_byrequests - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_byrequests.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_byrequests.mak" CFG="mod_lbmethod_byrequests - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_byrequests - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_byrequests - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_byrequests_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_byrequests.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_byrequests_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_byrequests.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_byrequests.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_byrequests - Win32 Release" +# Name "mod_lbmethod_byrequests - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_byrequests.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/balancers/mod_lbmethod_byrequests.mak b/modules/proxy/balancers/mod_lbmethod_byrequests.mak new file mode 100644 index 0000000..b5914a2 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_byrequests.mak @@ -0,0 +1,408 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_lbmethod_byrequests.dsp +!IF "$(CFG)" == "" +CFG=mod_lbmethod_byrequests - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_lbmethod_byrequests - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_lbmethod_byrequests - Win32 Release" && "$(CFG)" != "mod_lbmethod_byrequests - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_byrequests.mak" CFG="mod_lbmethod_byrequests - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_byrequests - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_byrequests - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_byrequests.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Release" "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_lbmethod_byrequests.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" "mod_proxy_balancer - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_byrequests.obj" + -@erase "$(INTDIR)\mod_lbmethod_byrequests.res" + -@erase "$(INTDIR)\mod_lbmethod_byrequests_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_byrequests_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.exp" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.lib" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_byrequests_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_byrequests.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_byrequests.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_byrequests.so" /implib:"$(OUTDIR)\mod_lbmethod_byrequests.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_byrequests.obj" \ + "$(INTDIR)\mod_lbmethod_byrequests.res" \ + "..\..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\..\Release\libhttpd.lib" \ + "..\Release\mod_proxy.lib" \ + "..\Release\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_byrequests.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_lbmethod_byrequests.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_byrequests.so" + if exist .\Release\mod_lbmethod_byrequests.so.manifest mt.exe -manifest .\Release\mod_lbmethod_byrequests.so.manifest -outputresource:.\Release\mod_lbmethod_byrequests.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_byrequests.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Debug" "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_lbmethod_byrequests.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" "mod_proxy_balancer - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_byrequests.obj" + -@erase "$(INTDIR)\mod_lbmethod_byrequests.res" + -@erase "$(INTDIR)\mod_lbmethod_byrequests_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_byrequests_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.exp" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.lib" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_byrequests.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_byrequests_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_byrequests.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_byrequests.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_byrequests.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_byrequests.so" /implib:"$(OUTDIR)\mod_lbmethod_byrequests.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_byrequests.so +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_byrequests.obj" \ + "$(INTDIR)\mod_lbmethod_byrequests.res" \ + "..\..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\..\Debug\libhttpd.lib" \ + "..\Debug\mod_proxy.lib" \ + "..\Debug\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_byrequests.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_lbmethod_byrequests.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_byrequests.so" + if exist .\Debug\mod_lbmethod_byrequests.so.manifest mt.exe -manifest .\Debug\mod_lbmethod_byrequests.so.manifest -outputresource:.\Debug\mod_lbmethod_byrequests.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_lbmethod_byrequests.dep") +!INCLUDE "mod_lbmethod_byrequests.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_lbmethod_byrequests.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" || "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" +SOURCE=.\mod_lbmethod_byrequests.c + +"$(INTDIR)\mod_lbmethod_byrequests.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +"mod_proxy - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd ".\balancers" + +"mod_proxy - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd ".\balancers" + +"mod_proxy - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + +"mod_proxy_balancer - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" + cd ".\balancers" + +"mod_proxy_balancer - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Debug" + +"mod_proxy_balancer - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" + cd ".\balancers" + +"mod_proxy_balancer - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +SOURCE=..\..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_lbmethod_byrequests - Win32 Release" + + +"$(INTDIR)\mod_lbmethod_byrequests.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_byrequests.so" /d LONG_NAME="lbmethod_byrequests_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/balancers/mod_lbmethod_bytraffic.c b/modules/proxy/balancers/mod_lbmethod_bytraffic.c new file mode 100644 index 0000000..6cfab94 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bytraffic.c @@ -0,0 +1,135 @@ +/* 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 "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" + +module AP_MODULE_DECLARE_DATA lbmethod_bytraffic_module; + +static APR_OPTIONAL_FN_TYPE(proxy_balancer_get_best_worker) + *ap_proxy_balancer_get_best_worker_fn = NULL; + +static int is_best_bytraffic(proxy_worker *current, proxy_worker *prev_best, void *baton) +{ + apr_off_t *min_traffic = (apr_off_t *)baton; + apr_off_t traffic = (current->s->transferred / current->s->lbfactor) + + (current->s->read / current->s->lbfactor); + + if (!prev_best || (traffic < *min_traffic)) { + *min_traffic = traffic; + + return TRUE; + } + + return FALSE; +} + +/* + * The idea behind the find_best_bytraffic scheduler is the following: + * + * We know the amount of traffic (bytes in and out) handled by each + * worker. We normalize that traffic by each workers' weight. So assuming + * a setup as below: + * + * worker a b c + * lbfactor 1 1 3 + * + * the scheduler will allow worker c to handle 3 times the + * traffic of a and b. If each request/response results in the + * same amount of traffic, then c would be accessed 3 times as + * often as a or b. If, for example, a handled a request that + * resulted in a large i/o bytecount, then b and c would be + * chosen more often, to even things out. + */ +static proxy_worker *find_best_bytraffic(proxy_balancer *balancer, + request_rec *r) +{ + apr_off_t min_traffic = 0; + + return ap_proxy_balancer_get_best_worker_fn(balancer, r, is_best_bytraffic, + &min_traffic); +} + +/* assumed to be mutex protected by caller */ +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) +{ + int i; + proxy_worker **worker; + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + (*worker)->s->lbstatus = 0; + (*worker)->s->busy = 0; + (*worker)->s->transferred = 0; + (*worker)->s->read = 0; + } + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) +{ + return APR_SUCCESS; +} + +static const proxy_balancer_method bytraffic = +{ + "bytraffic", + &find_best_bytraffic, + NULL, + &reset, + &age, + NULL +}; + +/* post_config hook: */ +static int lbmethod_bytraffic_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + + /* lbmethod_bytraffic_post_config() will be called twice during startup. So, don't + * set up the static data the 1st time through. */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + + ap_proxy_balancer_get_best_worker_fn = + APR_RETRIEVE_OPTIONAL_FN(proxy_balancer_get_best_worker); + if (!ap_proxy_balancer_get_best_worker_fn) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10150) + "mod_proxy must be loaded for mod_lbmethod_bytraffic"); + return !OK; + } + + return OK; +} + +static void register_hook(apr_pool_t *p) +{ + ap_register_provider(p, PROXY_LBMETHOD, "bytraffic", "0", &bytraffic); + ap_hook_post_config(lbmethod_bytraffic_post_config, NULL, NULL, APR_HOOK_MIDDLE); +} + +AP_DECLARE_MODULE(lbmethod_bytraffic) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + register_hook /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_bytraffic.dep b/modules/proxy/balancers/mod_lbmethod_bytraffic.dep new file mode 100644 index 0000000..75dfce4 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bytraffic.dep @@ -0,0 +1,76 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_lbmethod_bytraffic.mak + +.\mod_lbmethod_bytraffic.c : \ + "..\..\..\include\ap_config.h"\ + "..\..\..\include\ap_config_layout.h"\ + "..\..\..\include\ap_expr.h"\ + "..\..\..\include\ap_hooks.h"\ + "..\..\..\include\ap_mmn.h"\ + "..\..\..\include\ap_mpm.h"\ + "..\..\..\include\ap_provider.h"\ + "..\..\..\include\ap_regex.h"\ + "..\..\..\include\ap_release.h"\ + "..\..\..\include\ap_slotmem.h"\ + "..\..\..\include\apache_noprobes.h"\ + "..\..\..\include\http_config.h"\ + "..\..\..\include\http_connection.h"\ + "..\..\..\include\http_core.h"\ + "..\..\..\include\http_log.h"\ + "..\..\..\include\http_main.h"\ + "..\..\..\include\http_protocol.h"\ + "..\..\..\include\http_request.h"\ + "..\..\..\include\http_vhost.h"\ + "..\..\..\include\httpd.h"\ + "..\..\..\include\os.h"\ + "..\..\..\include\scoreboard.h"\ + "..\..\..\include\util_cfgtree.h"\ + "..\..\..\include\util_charset.h"\ + "..\..\..\include\util_ebcdic.h"\ + "..\..\..\include\util_filter.h"\ + "..\..\..\include\util_mutex.h"\ + "..\..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\..\srclib\apr-util\include\apu.h"\ + "..\..\..\srclib\apr\include\apr.h"\ + "..\..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\..\srclib\apr\include\apr_dso.h"\ + "..\..\..\srclib\apr\include\apr_errno.h"\ + "..\..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\..\srclib\apr\include\apr_general.h"\ + "..\..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\..\srclib\apr\include\apr_hash.h"\ + "..\..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\..\srclib\apr\include\apr_lib.h"\ + "..\..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\..\srclib\apr\include\apr_poll.h"\ + "..\..\..\srclib\apr\include\apr_pools.h"\ + "..\..\..\srclib\apr\include\apr_portable.h"\ + "..\..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\..\srclib\apr\include\apr_ring.h"\ + "..\..\..\srclib\apr\include\apr_shm.h"\ + "..\..\..\srclib\apr\include\apr_strings.h"\ + "..\..\..\srclib\apr\include\apr_tables.h"\ + "..\..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\..\srclib\apr\include\apr_time.h"\ + "..\..\..\srclib\apr\include\apr_user.h"\ + "..\..\..\srclib\apr\include\apr_version.h"\ + "..\..\..\srclib\apr\include\apr_want.h"\ + "..\mod_proxy.h"\ + + +..\..\..\build\win32\httpd.rc : \ + "..\..\..\include\ap_release.h"\ + diff --git a/modules/proxy/balancers/mod_lbmethod_bytraffic.dsp b/modules/proxy/balancers/mod_lbmethod_bytraffic.dsp new file mode 100644 index 0000000..3b53fd9 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bytraffic.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_bytraffic" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_bytraffic - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bytraffic.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bytraffic.mak" CFG="mod_lbmethod_bytraffic - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_bytraffic - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_bytraffic - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_bytraffic_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_bytraffic.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_bytraffic_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_bytraffic.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_bytraffic.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_bytraffic - Win32 Release" +# Name "mod_lbmethod_bytraffic - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_bytraffic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/balancers/mod_lbmethod_bytraffic.mak b/modules/proxy/balancers/mod_lbmethod_bytraffic.mak new file mode 100644 index 0000000..fe68c2b --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_bytraffic.mak @@ -0,0 +1,408 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_lbmethod_bytraffic.dsp +!IF "$(CFG)" == "" +CFG=mod_lbmethod_bytraffic - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_lbmethod_bytraffic - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_lbmethod_bytraffic - Win32 Release" && "$(CFG)" != "mod_lbmethod_bytraffic - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_bytraffic.mak" CFG="mod_lbmethod_bytraffic - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_bytraffic - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_bytraffic - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_bytraffic.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Release" "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_lbmethod_bytraffic.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" "mod_proxy_balancer - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_bytraffic.obj" + -@erase "$(INTDIR)\mod_lbmethod_bytraffic.res" + -@erase "$(INTDIR)\mod_lbmethod_bytraffic_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_bytraffic_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.exp" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.lib" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_bytraffic_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_bytraffic.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_bytraffic.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_bytraffic.so" /implib:"$(OUTDIR)\mod_lbmethod_bytraffic.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_bytraffic.obj" \ + "$(INTDIR)\mod_lbmethod_bytraffic.res" \ + "..\..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\..\Release\libhttpd.lib" \ + "..\Release\mod_proxy.lib" \ + "..\Release\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_bytraffic.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_lbmethod_bytraffic.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_bytraffic.so" + if exist .\Release\mod_lbmethod_bytraffic.so.manifest mt.exe -manifest .\Release\mod_lbmethod_bytraffic.so.manifest -outputresource:.\Release\mod_lbmethod_bytraffic.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_bytraffic.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Debug" "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_lbmethod_bytraffic.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" "mod_proxy_balancer - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_bytraffic.obj" + -@erase "$(INTDIR)\mod_lbmethod_bytraffic.res" + -@erase "$(INTDIR)\mod_lbmethod_bytraffic_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_bytraffic_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.exp" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.lib" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_bytraffic.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_bytraffic_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_bytraffic.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_bytraffic.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_bytraffic.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_bytraffic.so" /implib:"$(OUTDIR)\mod_lbmethod_bytraffic.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_bytraffic.so +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_bytraffic.obj" \ + "$(INTDIR)\mod_lbmethod_bytraffic.res" \ + "..\..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\..\Debug\libhttpd.lib" \ + "..\Debug\mod_proxy.lib" \ + "..\Debug\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_bytraffic.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_lbmethod_bytraffic.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_bytraffic.so" + if exist .\Debug\mod_lbmethod_bytraffic.so.manifest mt.exe -manifest .\Debug\mod_lbmethod_bytraffic.so.manifest -outputresource:.\Debug\mod_lbmethod_bytraffic.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_lbmethod_bytraffic.dep") +!INCLUDE "mod_lbmethod_bytraffic.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_lbmethod_bytraffic.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" || "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" +SOURCE=.\mod_lbmethod_bytraffic.c + +"$(INTDIR)\mod_lbmethod_bytraffic.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +"mod_proxy - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd ".\balancers" + +"mod_proxy - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd ".\balancers" + +"mod_proxy - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + +"mod_proxy_balancer - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" + cd ".\balancers" + +"mod_proxy_balancer - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Debug" + +"mod_proxy_balancer - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" + cd ".\balancers" + +"mod_proxy_balancer - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +SOURCE=..\..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_lbmethod_bytraffic - Win32 Release" + + +"$(INTDIR)\mod_lbmethod_bytraffic.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_bytraffic.so" /d LONG_NAME="lbmethod_bytraffic_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.c b/modules/proxy/balancers/mod_lbmethod_heartbeat.c new file mode 100644 index 0000000..5f4873a --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.c @@ -0,0 +1,466 @@ +/* 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 "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" +#include "ap_slotmem.h" +#include "heartbeat.h" + +#ifndef LBM_HEARTBEAT_MAX_LASTSEEN +/* If we haven't seen a heartbeat in the last N seconds, don't count this IP + * as allive. + */ +#define LBM_HEARTBEAT_MAX_LASTSEEN (10) +#endif + +module AP_MODULE_DECLARE_DATA lbmethod_heartbeat_module; + +static int (*ap_proxy_retry_worker_fn)(const char *proxy_function, + proxy_worker *worker, server_rec *s) = NULL; + +static const ap_slotmem_provider_t *storage = NULL; +static ap_slotmem_instance_t *hm_serversmem = NULL; + +/* + * configuration structure + * path: path of the file where the heartbeat information is stored. + */ +typedef struct lb_hb_ctx_t +{ + const char *path; +} lb_hb_ctx_t; + +typedef struct hb_server_t { + const char *ip; + int busy; + int ready; + int port; + int id; + apr_time_t seen; + proxy_worker *worker; +} hb_server_t; + +typedef struct ctx_servers { + apr_time_t now; + apr_hash_t *servers; +} ctx_servers_t; + +static void +argstr_to_table(apr_pool_t *p, char *str, apr_table_t *parms) +{ + char *key; + char *value; + char *strtok_state; + + key = apr_strtok(str, "&", &strtok_state); + while (key) { + value = strchr(key, '='); + if (value) { + *value = '\0'; /* Split the string in two */ + value++; /* Skip passed the = */ + } + else { + value = "1"; + } + ap_unescape_url(key); + ap_unescape_url(value); + apr_table_set(parms, key, value); + /* + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03230) + "Found query arg: %s = %s", key, value); + */ + key = apr_strtok(NULL, "&", &strtok_state); + } +} + +static apr_status_t readfile_heartbeats(const char *path, apr_hash_t *servers, + apr_pool_t *pool) +{ + apr_finfo_t fi; + apr_status_t rv; + apr_file_t *fp; + + if (!path) { + return APR_SUCCESS; + } + + rv = apr_file_open(&fp, path, APR_READ|APR_BINARY|APR_BUFFERED, + APR_OS_DEFAULT, pool); + + if (rv) { + return rv; + } + + rv = apr_file_info_get(&fi, APR_FINFO_SIZE, fp); + + if (rv) { + return rv; + } + + { + char *t; + int lineno = 0; + apr_bucket_alloc_t *ba = apr_bucket_alloc_create(pool); + apr_bucket_brigade *bb = apr_brigade_create(pool, ba); + apr_bucket_brigade *tmpbb = apr_brigade_create(pool, ba); + apr_table_t *hbt = apr_table_make(pool, 10); + + apr_brigade_insert_file(bb, fp, 0, fi.size, pool); + + do { + hb_server_t *server; + char buf[4096]; + apr_size_t bsize = sizeof(buf); + const char *ip, *val; + + apr_brigade_cleanup(tmpbb); + + if (APR_BRIGADE_EMPTY(bb)) { + break; + } + + rv = apr_brigade_split_line(tmpbb, bb, + APR_BLOCK_READ, sizeof(buf)); + lineno++; + + if (rv) { + return rv; + } + + apr_brigade_flatten(tmpbb, buf, &bsize); + + if (bsize == 0) { + break; + } + + buf[bsize - 1] = 0; + + /* comment */ + if (buf[0] == '#') { + continue; + } + + /* line format: <IP> <query_string>\n */ + t = strchr(buf, ' '); + if (!t) { + continue; + } + + ip = apr_pstrmemdup(pool, buf, t - buf); + t++; + + server = apr_hash_get(servers, ip, APR_HASH_KEY_STRING); + + if (server == NULL) { + server = apr_pcalloc(pool, sizeof(hb_server_t)); + server->ip = ip; + server->port = 80; + server->seen = -1; + + apr_hash_set(servers, server->ip, APR_HASH_KEY_STRING, server); + } + + apr_table_clear(hbt); + + argstr_to_table(pool, apr_pstrdup(pool, t), hbt); + + if ((val = apr_table_get(hbt, "busy"))) { + server->busy = atoi(val); + } + + if ((val = apr_table_get(hbt, "ready"))) { + server->ready = atoi(val); + } + + if ((val = apr_table_get(hbt, "lastseen"))) { + server->seen = atoi(val); + } + + if ((val = apr_table_get(hbt, "port"))) { + server->port = atoi(val); + } + + if (server->busy == 0 && server->ready != 0) { + /* Server has zero threads active, but lots of them ready, + * it likely just started up, so lets /4 the number ready, + * to prevent us from completely flooding it with all new + * requests. + */ + server->ready = server->ready / 4; + } + + } while (1); + } + + return APR_SUCCESS; +} + +static apr_status_t hm_read(void* mem, void *data, apr_pool_t *pool) +{ + hm_slot_server_t *slotserver = (hm_slot_server_t *) mem; + ctx_servers_t *ctx = (ctx_servers_t *) data; + apr_hash_t *servers = (apr_hash_t *) ctx->servers; + hb_server_t *server = apr_hash_get(servers, slotserver->ip, APR_HASH_KEY_STRING); + if (server == NULL) { + server = apr_pcalloc(pool, sizeof(hb_server_t)); + server->ip = apr_pstrdup(pool, slotserver->ip); + server->seen = -1; + + apr_hash_set(servers, server->ip, APR_HASH_KEY_STRING, server); + + } + server->busy = slotserver->busy; + server->ready = slotserver->ready; + server->seen = apr_time_sec(ctx->now - slotserver->seen); + server->id = slotserver->id; + if (server->busy == 0 && server->ready != 0) { + server->ready = server->ready / 4; + } + return APR_SUCCESS; +} +static apr_status_t readslot_heartbeats(ctx_servers_t *ctx, + apr_pool_t *pool) +{ + storage->doall(hm_serversmem, hm_read, ctx, pool); + return APR_SUCCESS; +} + + +static apr_status_t read_heartbeats(const char *path, apr_hash_t *servers, + apr_pool_t *pool) +{ + apr_status_t rv; + if (hm_serversmem) { + ctx_servers_t ctx; + ctx.now = apr_time_now(); + ctx.servers = servers; + rv = readslot_heartbeats(&ctx, pool); + } else + rv = readfile_heartbeats(path, servers, pool); + return rv; +} + +static proxy_worker *find_best_hb(proxy_balancer *balancer, + request_rec *r) +{ + apr_status_t rv; + int i; + apr_uint32_t openslots = 0; + proxy_worker **worker; + hb_server_t *server; + apr_array_header_t *up_servers; + proxy_worker *mycandidate = NULL; + apr_pool_t *tpool; + apr_hash_t *servers; + + lb_hb_ctx_t *ctx = + ap_get_module_config(r->server->module_config, + &lbmethod_heartbeat_module); + + ap_proxy_retry_worker_fn = + APR_RETRIEVE_OPTIONAL_FN(ap_proxy_retry_worker); + if (!ap_proxy_retry_worker_fn) { + /* can only happen if mod_proxy isn't loaded */ + return NULL; + } + + apr_pool_create(&tpool, r->pool); + apr_pool_tag(tpool, "lb_heartbeat_tpool"); + + servers = apr_hash_make(tpool); + + rv = read_heartbeats(ctx->path, servers, tpool); + + if (rv) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01213) + "lb_heartbeat: Unable to read heartbeats at '%s'", + ctx->path); + apr_pool_destroy(tpool); + return NULL; + } + + up_servers = apr_array_make(tpool, apr_hash_count(servers), sizeof(hb_server_t *)); + + for (i = 0; i < balancer->workers->nelts; i++) { + worker = &APR_ARRAY_IDX(balancer->workers, i, proxy_worker *); + server = apr_hash_get(servers, (*worker)->s->hostname_ex, APR_HASH_KEY_STRING); + + if (!server) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01214) + "lb_heartbeat: No server for worker %s", (*worker)->s->name_ex); + continue; + } + + if (!PROXY_WORKER_IS_USABLE(*worker)) { + ap_proxy_retry_worker_fn("BALANCER", *worker, r->server); + } + + if (PROXY_WORKER_IS_USABLE(*worker)) { + server->worker = *worker; + if (server->seen < LBM_HEARTBEAT_MAX_LASTSEEN) { + openslots += server->ready; + APR_ARRAY_PUSH(up_servers, hb_server_t *) = server; + } + } + } + + if (openslots > 0) { + apr_uint32_t c = 0; + apr_uint32_t pick = 0; + + pick = ap_random_pick(0, openslots); + + for (i = 0; i < up_servers->nelts; i++) { + server = APR_ARRAY_IDX(up_servers, i, hb_server_t *); + if (pick >= c && pick <= c + server->ready) { + mycandidate = server->worker; + } + + c += server->ready; + } + } + + apr_pool_destroy(tpool); + + return mycandidate; +} + +static apr_status_t reset(proxy_balancer *balancer, server_rec *s) +{ + return APR_SUCCESS; +} + +static apr_status_t age(proxy_balancer *balancer, server_rec *s) +{ + return APR_SUCCESS; +} + +static const proxy_balancer_method heartbeat = +{ + "heartbeat", + &find_best_hb, + NULL, + &reset, + &age, + NULL +}; + +static int lb_hb_init(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + apr_size_t size; + unsigned int num; + lb_hb_ctx_t *ctx = ap_get_module_config(s->module_config, + &lbmethod_heartbeat_module); + + /* do nothing on first call */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) + return OK; + + storage = ap_lookup_provider(AP_SLOTMEM_PROVIDER_GROUP, "shm", + AP_SLOTMEM_PROVIDER_VERSION); + if (!storage) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(02281) + "Failed to lookup provider 'shm' for '%s'. Maybe you " + "need to load mod_slotmem_shm?", + AP_SLOTMEM_PROVIDER_GROUP); + return OK; + } + + /* Try to use a slotmem created by mod_heartmonitor */ + storage->attach(&hm_serversmem, "mod_heartmonitor", &size, &num, p); + if (!hm_serversmem) + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(02282) + "No slotmem from mod_heartmonitor"); + else + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(02283) + "Using slotmem from mod_heartmonitor"); + + if (hm_serversmem) + ctx->path = "(slotmem)"; + + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + static const char * const aszPred[]={ "mod_heartmonitor.c", NULL }; + ap_register_provider(p, PROXY_LBMETHOD, "heartbeat", "0", &heartbeat); + ap_hook_post_config(lb_hb_init, aszPred, NULL, APR_HOOK_MIDDLE); +} + +static void *lb_hb_create_config(apr_pool_t *p, server_rec *s) +{ + lb_hb_ctx_t *ctx = (lb_hb_ctx_t *) apr_palloc(p, sizeof(lb_hb_ctx_t)); + + ctx->path = ap_runtime_dir_relative(p, DEFAULT_HEARTBEAT_STORAGE); + + return ctx; +} + +static void *lb_hb_merge_config(apr_pool_t *p, void *basev, void *overridesv) +{ + lb_hb_ctx_t *ps = apr_pcalloc(p, sizeof(lb_hb_ctx_t)); + lb_hb_ctx_t *base = (lb_hb_ctx_t *) basev; + lb_hb_ctx_t *overrides = (lb_hb_ctx_t *) overridesv; + + if (overrides->path) { + ps->path = apr_pstrdup(p, overrides->path); + } + else { + ps->path = apr_pstrdup(p, base->path); + } + + return ps; +} + +static const char *cmd_lb_hb_storage(cmd_parms *cmd, + void *dconf, const char *path) +{ + apr_pool_t *p = cmd->pool; + lb_hb_ctx_t *ctx = + (lb_hb_ctx_t *) ap_get_module_config(cmd->server->module_config, + &lbmethod_heartbeat_module); + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + ctx->path = ap_runtime_dir_relative(p, path); + + return NULL; +} + +static const command_rec cmds[] = { + AP_INIT_TAKE1("HeartbeatStorage", cmd_lb_hb_storage, NULL, RSRC_CONF, + "Path to read heartbeat data."), + {NULL} +}; + +AP_DECLARE_MODULE(lbmethod_heartbeat) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + lb_hb_create_config, /* create per-server config structure */ + lb_hb_merge_config, /* merge per-server config structures */ + cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.dep b/modules/proxy/balancers/mod_lbmethod_heartbeat.dep new file mode 100644 index 0000000..6000482 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.dep @@ -0,0 +1,77 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_lbmethod_heartbeat.mak + +.\mod_lbmethod_heartbeat.c : \ + "..\..\..\include\ap_config.h"\ + "..\..\..\include\ap_config_layout.h"\ + "..\..\..\include\ap_expr.h"\ + "..\..\..\include\ap_hooks.h"\ + "..\..\..\include\ap_mmn.h"\ + "..\..\..\include\ap_mpm.h"\ + "..\..\..\include\ap_provider.h"\ + "..\..\..\include\ap_regex.h"\ + "..\..\..\include\ap_release.h"\ + "..\..\..\include\ap_slotmem.h"\ + "..\..\..\include\apache_noprobes.h"\ + "..\..\..\include\heartbeat.h"\ + "..\..\..\include\http_config.h"\ + "..\..\..\include\http_connection.h"\ + "..\..\..\include\http_core.h"\ + "..\..\..\include\http_log.h"\ + "..\..\..\include\http_main.h"\ + "..\..\..\include\http_protocol.h"\ + "..\..\..\include\http_request.h"\ + "..\..\..\include\http_vhost.h"\ + "..\..\..\include\httpd.h"\ + "..\..\..\include\os.h"\ + "..\..\..\include\scoreboard.h"\ + "..\..\..\include\util_cfgtree.h"\ + "..\..\..\include\util_charset.h"\ + "..\..\..\include\util_ebcdic.h"\ + "..\..\..\include\util_filter.h"\ + "..\..\..\include\util_mutex.h"\ + "..\..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\..\srclib\apr-util\include\apu.h"\ + "..\..\..\srclib\apr\include\apr.h"\ + "..\..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\..\srclib\apr\include\apr_dso.h"\ + "..\..\..\srclib\apr\include\apr_errno.h"\ + "..\..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\..\srclib\apr\include\apr_general.h"\ + "..\..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\..\srclib\apr\include\apr_hash.h"\ + "..\..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\..\srclib\apr\include\apr_lib.h"\ + "..\..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\..\srclib\apr\include\apr_poll.h"\ + "..\..\..\srclib\apr\include\apr_pools.h"\ + "..\..\..\srclib\apr\include\apr_portable.h"\ + "..\..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\..\srclib\apr\include\apr_ring.h"\ + "..\..\..\srclib\apr\include\apr_shm.h"\ + "..\..\..\srclib\apr\include\apr_strings.h"\ + "..\..\..\srclib\apr\include\apr_tables.h"\ + "..\..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\..\srclib\apr\include\apr_time.h"\ + "..\..\..\srclib\apr\include\apr_user.h"\ + "..\..\..\srclib\apr\include\apr_version.h"\ + "..\..\..\srclib\apr\include\apr_want.h"\ + "..\mod_proxy.h"\ + + +..\..\..\build\win32\httpd.rc : \ + "..\..\..\include\ap_release.h"\ + diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.dsp b/modules/proxy/balancers/mod_lbmethod_heartbeat.dsp new file mode 100644 index 0000000..cb003b0 --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_lbmethod_heartbeat" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_lbmethod_heartbeat - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_heartbeat.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_heartbeat.mak" CFG="mod_lbmethod_heartbeat - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_heartbeat - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_heartbeat - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_lbmethod_heartbeat_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_lbmethod_heartbeat.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_lbmethod_heartbeat_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_lbmethod_heartbeat.so" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_lbmethod_heartbeat.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_lbmethod_heartbeat - Win32 Release" +# Name "mod_lbmethod_heartbeat - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_lbmethod_heartbeat.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=..\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/balancers/mod_lbmethod_heartbeat.mak b/modules/proxy/balancers/mod_lbmethod_heartbeat.mak new file mode 100644 index 0000000..31bd4af --- /dev/null +++ b/modules/proxy/balancers/mod_lbmethod_heartbeat.mak @@ -0,0 +1,408 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_lbmethod_heartbeat.dsp +!IF "$(CFG)" == "" +CFG=mod_lbmethod_heartbeat - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_lbmethod_heartbeat - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_lbmethod_heartbeat - Win32 Release" && "$(CFG)" != "mod_lbmethod_heartbeat - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_lbmethod_heartbeat.mak" CFG="mod_lbmethod_heartbeat - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_lbmethod_heartbeat - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_lbmethod_heartbeat - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_heartbeat.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Release" "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_lbmethod_heartbeat.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" "mod_proxy_balancer - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_heartbeat.obj" + -@erase "$(INTDIR)\mod_lbmethod_heartbeat.res" + -@erase "$(INTDIR)\mod_lbmethod_heartbeat_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_heartbeat_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.exp" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.lib" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_heartbeat_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_heartbeat.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_heartbeat.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_heartbeat.so" /implib:"$(OUTDIR)\mod_lbmethod_heartbeat.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_heartbeat.obj" \ + "$(INTDIR)\mod_lbmethod_heartbeat.res" \ + "..\..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\..\Release\libhttpd.lib" \ + "..\Release\mod_proxy.lib" \ + "..\Release\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_heartbeat.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_lbmethod_heartbeat.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_heartbeat.so" + if exist .\Release\mod_lbmethod_heartbeat.so.manifest mt.exe -manifest .\Release\mod_lbmethod_heartbeat.so.manifest -outputresource:.\Release\mod_lbmethod_heartbeat.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_lbmethod_heartbeat.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy_balancer - Win32 Debug" "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_lbmethod_heartbeat.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" "mod_proxy_balancer - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_lbmethod_heartbeat.obj" + -@erase "$(INTDIR)\mod_lbmethod_heartbeat.res" + -@erase "$(INTDIR)\mod_lbmethod_heartbeat_src.idb" + -@erase "$(INTDIR)\mod_lbmethod_heartbeat_src.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.exp" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.lib" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.pdb" + -@erase "$(OUTDIR)\mod_lbmethod_heartbeat.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I ".." /I "../../../include" /I "../../../srclib/apr/include" /I "../../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_lbmethod_heartbeat_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_lbmethod_heartbeat.res" /i "../../../include" /i "../../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_lbmethod_heartbeat.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_lbmethod_heartbeat.pdb" /debug /out:"$(OUTDIR)\mod_lbmethod_heartbeat.so" /implib:"$(OUTDIR)\mod_lbmethod_heartbeat.lib" /base:@..\..\..\os\win32\BaseAddr.ref,mod_lbmethod_heartbeat.so +LINK32_OBJS= \ + "$(INTDIR)\mod_lbmethod_heartbeat.obj" \ + "$(INTDIR)\mod_lbmethod_heartbeat.res" \ + "..\..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\..\Debug\libhttpd.lib" \ + "..\Debug\mod_proxy.lib" \ + "..\Debug\mod_proxy_balancer.lib" + +"$(OUTDIR)\mod_lbmethod_heartbeat.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_lbmethod_heartbeat.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_lbmethod_heartbeat.so" + if exist .\Debug\mod_lbmethod_heartbeat.so.manifest mt.exe -manifest .\Debug\mod_lbmethod_heartbeat.so.manifest -outputresource:.\Debug\mod_lbmethod_heartbeat.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_lbmethod_heartbeat.dep") +!INCLUDE "mod_lbmethod_heartbeat.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_lbmethod_heartbeat.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" || "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" +SOURCE=.\mod_lbmethod_heartbeat.c + +"$(INTDIR)\mod_lbmethod_heartbeat.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy\balancers" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy\balancers" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +"mod_proxy - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd ".\balancers" + +"mod_proxy - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd ".\balancers" + +"mod_proxy - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + +"mod_proxy_balancer - Win32 Release" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" + cd ".\balancers" + +"mod_proxy_balancer - Win32 ReleaseCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" RECURSE=1 CLEAN + cd ".\balancers" + +!ELSEIF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Debug" + +"mod_proxy_balancer - Win32 Debug" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" + cd ".\balancers" + +"mod_proxy_balancer - Win32 DebugCLEAN" : + cd ".\.." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Debug" RECURSE=1 CLEAN + cd ".\balancers" + +!ENDIF + +SOURCE=..\..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_lbmethod_heartbeat - Win32 Release" + + +"$(INTDIR)\mod_lbmethod_heartbeat.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_lbmethod_heartbeat.so" /d LONG_NAME="lbmethod_heartbeat_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/config.m4 b/modules/proxy/config.m4 new file mode 100644 index 0000000..01f09fa --- /dev/null +++ b/modules/proxy/config.m4 @@ -0,0 +1,79 @@ +dnl modules enabled in this directory by default + +APACHE_MODPATH_INIT(proxy) + +proxy_objs="mod_proxy.lo proxy_util.lo" +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" +proxy_http_objs="mod_proxy_http.lo" +proxy_fcgi_objs="mod_proxy_fcgi.lo" +proxy_scgi_objs="mod_proxy_scgi.lo" +proxy_uwsgi_objs="mod_proxy_uwsgi.lo" +proxy_fdpass_objs="mod_proxy_fdpass.lo" +proxy_ajp_objs="mod_proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo ajp_utils.lo" +proxy_wstunnel_objs="mod_proxy_wstunnel.lo" +proxy_balancer_objs="mod_proxy_balancer.lo" + +case "$host" in + *os2*) + # OS/2 DLLs must resolve all symbols at build time and + # these sub-modules need some from the main proxy module + proxy_connect_objs="$proxy_connect_objs mod_proxy.la" + proxy_ftp_objs="$proxy_ftp_objs mod_proxy.la" + proxy_http_objs="$proxy_http_objs mod_proxy.la" + proxy_fcgi_objs="$proxy_fcgi_objs mod_proxy.la" + proxy_scgi_objs="$proxy_scgi_objs mod_proxy.la" + proxy_uwsgi_objs="$proxy_uwsgi_objs mod_proxy.la" + proxy_fdpass_objs="$proxy_fdpass_objs mod_proxy.la" + proxy_ajp_objs="$proxy_ajp_objs mod_proxy.la" + proxy_wstunnel_objs="$proxy_wstunnel_objs mod_proxy.la" + proxy_balancer_objs="$proxy_balancer_objs mod_proxy.la" + ;; +esac + +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_uwsgi, Apache proxy UWSGI module. Requires --enable-proxy., $proxy_uwsgi_objs, , most, , proxy) +APACHE_MODULE(proxy_fdpass, Apache proxy to Unix Daemon Socket module. Requires --enable-proxy., $proxy_fdpass_objs, , most, [ + AC_CHECK_DECL(CMSG_DATA,,, [ + #include <sys/types.h> + #include <sys/socket.h> + ]) + if test $ac_cv_have_decl_CMSG_DATA = "no"; then + AC_MSG_WARN([Your system does not support CMSG_DATA.]) + enable_proxy_fdpass=no + fi +],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., , , 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 -I\$(top_srcdir)/modules/http2]) + +module_selection=$save_module_selection +module_default=$save_module_default + +APACHE_MODPATH_FINISH + diff --git a/modules/proxy/libproxy.exp b/modules/proxy/libproxy.exp new file mode 100644 index 0000000..a20f237 --- /dev/null +++ b/modules/proxy/libproxy.exp @@ -0,0 +1 @@ +proxy_module diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c new file mode 100644 index 0000000..537c3c2 --- /dev/null +++ b/modules/proxy/mod_proxy.c @@ -0,0 +1,3479 @@ +/* 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_core.h" +#include "apr_optional.h" +#include "apr_strings.h" +#include "scoreboard.h" +#include "mod_status.h" +#include "proxy_util.h" + +#if (MODULE_MAGIC_NUMBER_MAJOR > 20020903) +#include "mod_ssl.h" +#else +APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *)); +APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *)); +APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *, + ap_conf_vector_t *, + int proxy, int enable)); +#endif + +#ifndef MAX +#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}, + {OPTIONS11, "OPTIONS11", 1}, + {HEAD11, "HEAD11", 1}, + {GET11, "GET11", 1}, + {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_HOT_SPARE, PROXY_WORKER_HOT_SPARE_FLAG, "Spar "}, + {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; + +/* + * A Web proxy module. Stages: + * + * translate_name: set filename to proxy:<URL> + * map_to_storage: run proxy_walk (rather than directory_walk/file_walk) + * can't trust directory_walk/file_walk since these are + * not in our filesystem. Prevents mod_http from serving + * the TRACE request we will set aside to handle later. + * fix_ups: convert the URL stored in the filename to the + * canonical form. + * handler: handle proxy requests + */ + +/* -------------------------------------------------------------- */ +/* 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) +{ + + int ival; + apr_interval_time_t timeout; + + if (!strcasecmp(key, "loadfactor")) { + /* Normalized load factor. Used with BalancerMember, + * it is a number between 1 and 100. + */ + double fval = atof(val); + ival = fval * 100.0; + if (ival < 100 || ival > 10000) + return "LoadFactor must be a number between 1..100"; + worker->s->lbfactor = ival; + } + else if (!strcasecmp(key, "retry")) { + /* If set it will give the retry timeout for the worker + * The default value is 60 seconds, meaning that if + * in error state, it will be retried after that timeout. + */ + ival = atoi(val); + if (ival < 0) + return "Retry must be a positive value"; + worker->s->retry = apr_time_from_sec(ival); + worker->s->retry_set = 1; + } + else if (!strcasecmp(key, "ttl")) { + /* Time in seconds that will destroy all the connections + * that exceed the smax + */ + ival = atoi(val); + if (ival < 1) + return "TTL must be at least one second"; + worker->s->ttl = apr_time_from_sec(ival); + } + else if (!strcasecmp(key, "min")) { + /* Initial number of connections to remote + */ + ival = atoi(val); + if (ival < 0) + return "Min must be a positive number"; + worker->s->min = ival; + } + else if (!strcasecmp(key, "max")) { + /* Maximum number of connections to remote + */ + ival = atoi(val); + if (ival < 0) + return "Max must be a positive number"; + worker->s->hmax = ival; + } + /* XXX: More intelligent naming needed */ + else if (!strcasecmp(key, "smax")) { + /* Maximum number of connections to remote that + * will not be destroyed + */ + ival = atoi(val); + if (ival < 0) + return "Smax must be a positive number"; + worker->s->smax = ival; + } + else if (!strcasecmp(key, "acquire")) { + /* Acquire timeout in given unit (default is milliseconds). + * If set this will be the maximum time to + * wait for a free connection. + */ + if (ap_timeout_parameter_parse(val, &timeout, "ms") != APR_SUCCESS) + return "Acquire timeout has wrong format"; + if (timeout < 1000) + return "Acquire must be at least one millisecond"; + worker->s->acquire = timeout; + worker->s->acquire_set = 1; + } + else if (!strcasecmp(key, "timeout")) { + /* Connection timeout in seconds. + * Defaults to server timeout. + */ + ival = atoi(val); + if (ival < 1) + return "Timeout must be at least one second"; + worker->s->timeout = apr_time_from_sec(ival); + worker->s->timeout_set = 1; + } + else if (!strcasecmp(key, "iobuffersize")) { + long s = atol(val); + if (s < 512 && s) { + return "IOBufferSize must be >= 512 bytes, or 0 for system default."; + } + worker->s->io_buffer_size = (s ? s : AP_IOBUFSIZE); + worker->s->io_buffer_size_set = 1; + } + else if (!strcasecmp(key, "receivebuffersize")) { + ival = atoi(val); + if (ival < 512 && ival != 0) { + return "ReceiveBufferSize must be >= 512 bytes, or 0 for system default."; + } + worker->s->recv_buffer_size = ival; + worker->s->recv_buffer_size_set = 1; + } + else if (!strcasecmp(key, "keepalive")) { + if (!strcasecmp(val, "on")) + worker->s->keepalive = 1; + else if (!strcasecmp(val, "off")) + worker->s->keepalive = 0; + else + return "KeepAlive must be On|Off"; + worker->s->keepalive_set = 1; + } + else if (!strcasecmp(key, "disablereuse")) { + if (!strcasecmp(val, "on")) + worker->s->disablereuse = 1; + else if (!strcasecmp(val, "off")) + worker->s->disablereuse = 0; + else + return "DisableReuse must be On|Off"; + worker->s->disablereuse_set = 1; + } + else if (!strcasecmp(key, "enablereuse")) { + if (!strcasecmp(val, "on")) + worker->s->disablereuse = 0; + else if (!strcasecmp(val, "off")) + worker->s->disablereuse = 1; + else + return "EnableReuse must be On|Off"; + worker->s->disablereuse_set = 1; + } + else if (!strcasecmp(key, "route")) { + /* Worker route. + */ + if (strlen(val) >= sizeof(worker->s->route)) + return apr_psprintf(p, "Route length must be < %d characters", + (int)sizeof(worker->s->route)); + PROXY_STRNCPY(worker->s->route, val); + } + else if (!strcasecmp(key, "redirect")) { + /* Worker redirection route. + */ + if (strlen(val) >= sizeof(worker->s->redirect)) + return apr_psprintf(p, "Redirect length must be < %d characters", + (int)sizeof(worker->s->redirect)); + PROXY_STRNCPY(worker->s->redirect, val); + } + else if (!strcasecmp(key, "status")) { + const char *v; + int mode = 1; + apr_status_t rv; + /* Worker status. + */ + for (v = val; *v; v++) { + if (*v == '+') { + mode = 1; + v++; + } + else if (*v == '-') { + mode = 0; + v++; + } + rv = ap_proxy_set_wstatus(*v, mode, worker); + if (rv != APR_SUCCESS) + return "Unknown status parameter option"; + } + } + else if (!strcasecmp(key, "flushpackets")) { + if (!strcasecmp(val, "on")) + worker->s->flush_packets = flush_on; + else if (!strcasecmp(val, "off")) + worker->s->flush_packets = flush_off; + else if (!strcasecmp(val, "auto")) + worker->s->flush_packets = flush_auto; + else + return "flushpackets must be on|off|auto"; + } + else if (!strcasecmp(key, "flushwait")) { + ival = atoi(val); + if (ival > 1000 || ival < 0) { + return "flushwait must be <= 1000, or 0 for system default of 10 millseconds."; + } + if (ival == 0) + worker->s->flush_wait = PROXY_FLUSH_WAIT; + else + worker->s->flush_wait = ival * 1000; /* change to microseconds */ + } + else if (!strcasecmp(key, "ping")) { + /* Ping/Pong timeout in given unit (default is second). + */ + if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) + return "Ping/Pong timeout has wrong format"; + if (timeout < 1000) + return "Ping/Pong timeout must be at least one millisecond"; + worker->s->ping_timeout = timeout; + worker->s->ping_timeout_set = 1; + } + else if (!strcasecmp(key, "lbset")) { + ival = atoi(val); + if (ival < 0 || ival > 99) + return "lbset must be between 0 and 99"; + worker->s->lbset = ival; + } + else if (!strcasecmp(key, "connectiontimeout")) { + /* Request timeout in given unit (default is second). + * Defaults to connection timeout + */ + if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) + return "Connectiontimeout has wrong format"; + if (timeout < 1000) + return "Connectiontimeout must be at least one millisecond."; + worker->s->conn_timeout = timeout; + worker->s->conn_timeout_set = 1; + } + else if (!strcasecmp(key, "flusher")) { + if (PROXY_STRNCPY(worker->s->flusher, val) != APR_SUCCESS) { + return apr_psprintf(p, "flusher name length must be < %d characters", + (int)sizeof(worker->s->flusher)); + } + } + else if (!strcasecmp(key, "upgrade")) { + if (PROXY_STRNCPY(worker->s->upgrade, + strcasecmp(val, "ANY") ? val : "*") != APR_SUCCESS) { + return apr_psprintf(p, "upgrade protocol length must be < %d characters", + (int)sizeof(worker->s->upgrade)); + } + } + else if (!strcasecmp(key, "responsefieldsize")) { + long s = atol(val); + if (s < 0) { + return "ResponseFieldSize must be greater than 0 bytes, or 0 for system default."; + } + worker->s->response_field_size = (s ? s : HUGE_STRING_LEN); + worker->s->response_field_size_set = 1; + } + else if (!strcasecmp(key, "secret")) { + if (PROXY_STRNCPY(worker->s->secret, val) != APR_SUCCESS) { + return apr_psprintf(p, "Secret length must be < %d characters", + (int)sizeof(worker->s->secret)); + } + } + else { + 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; +} + +static const char *set_balancer_param(proxy_server_conf *conf, + apr_pool_t *p, + proxy_balancer *balancer, + const char *key, + const char *val) +{ + + int ival; + if (!strcasecmp(key, "stickysession")) { + char *path; + /* Balancer sticky session name. + * Set to something like JSESSIONID or + * PHPSESSIONID, etc.., + */ + if (strlen(val) >= sizeof(balancer->s->sticky_path)) + apr_psprintf(p, "stickysession length must be < %d characters", + (int)sizeof(balancer->s->sticky_path)); + PROXY_STRNCPY(balancer->s->sticky_path, val); + PROXY_STRNCPY(balancer->s->sticky, val); + + if ((path = strchr((char *)balancer->s->sticky, '|'))) { + *path++ = '\0'; + PROXY_STRNCPY(balancer->s->sticky_path, path); + } + } + else if (!strcasecmp(key, "stickysessionsep")) { + /* separator/delimiter for sessionid and route, + * normally '.' + */ + if (strlen(val) != 1) { + if (!strcasecmp(val, "off")) + balancer->s->sticky_separator = 0; + else + return "stickysessionsep must be a single character or Off"; + } + else + balancer->s->sticky_separator = *val; + balancer->s->sticky_separator_set = 1; + } + else if (!strcasecmp(key, "nofailover")) { + /* If set to 'on' the session will break + * if the worker is in error state or + * disabled. + */ + if (!strcasecmp(val, "on")) + balancer->s->sticky_force = 1; + else if (!strcasecmp(val, "off")) + balancer->s->sticky_force = 0; + else + return "failover must be On|Off"; + balancer->s->sticky_force_set = 1; + } + else if (!strcasecmp(key, "timeout")) { + /* Balancer timeout in seconds. + * If set this will be the maximum time to + * wait for a free worker. + * Default is not to wait. + */ + ival = atoi(val); + if (ival < 1) + return "timeout must be at least one second"; + balancer->s->timeout = apr_time_from_sec(ival); + } + else if (!strcasecmp(key, "maxattempts")) { + /* Maximum number of failover attempts before + * giving up. + */ + ival = atoi(val); + if (ival < 0) + return "maximum number of attempts must be a positive number"; + balancer->s->max_attempts = ival; + balancer->s->max_attempts_set = 1; + } + else if (!strcasecmp(key, "lbmethod")) { + proxy_balancer_method *provider; + if (strlen(val) > (sizeof(balancer->s->lbpname)-1)) + return "unknown lbmethod"; + provider = ap_lookup_provider(PROXY_LBMETHOD, val, "0"); + if (provider) { + balancer->lbmethod = provider; + if (PROXY_STRNCPY(balancer->s->lbpname, val) == APR_SUCCESS) { + balancer->lbmethod_set = 1; + return NULL; + } + else { + return "lbmethod name too large"; + } + } + return "unknown lbmethod"; + } + else if (!strcasecmp(key, "scolonpathdelim")) { + /* If set to 'on' then ';' will also be + * used as a session path separator/delim (ala + * mod_jk) + */ + if (!strcasecmp(val, "on")) + balancer->s->scolonsep = 1; + else if (!strcasecmp(val, "off")) + balancer->s->scolonsep = 0; + else + return "scolonpathdelim must be On|Off"; + balancer->s->scolonsep_set = 1; + } + else if (!strcasecmp(key, "failonstatus")) { + char *val_split; + char *status; + char *tok_state; + + val_split = apr_pstrdup(p, val); + + balancer->errstatuses = apr_array_make(p, 1, sizeof(int)); + + status = apr_strtok(val_split, ", ", &tok_state); + while (status != NULL) { + ival = atoi(status); + if (ap_is_HTTP_VALID_RESPONSE(ival)) { + *(int *)apr_array_push(balancer->errstatuses) = ival; + } + else { + return "failonstatus must be one or more HTTP response codes"; + } + status = apr_strtok(NULL, ", ", &tok_state); + } + + } + else if (!strcasecmp(key, "failontimeout")) { + if (!strcasecmp(val, "on")) + balancer->failontimeout = 1; + else if (!strcasecmp(val, "off")) + balancer->failontimeout = 0; + else + return "failontimeout must be On|Off"; + balancer->failontimeout_set = 1; + } + else if (!strcasecmp(key, "nonce")) { + if (!strcasecmp(val, "None")) { + *balancer->s->nonce = '\0'; + } + else { + if (PROXY_STRNCPY(balancer->s->nonce, val) != APR_SUCCESS) { + return "Provided nonce is too large"; + } + } + balancer->s->nonce_set = 1; + } + else if (!strcasecmp(key, "growth")) { + ival = atoi(val); + if (ival < 1 || ival > 100) /* arbitrary limit here */ + return "growth must be between 1 and 100"; + balancer->growth = ival; + balancer->growth_set = 1; + } + else if (!strcasecmp(key, "forcerecovery")) { + if (!strcasecmp(val, "on")) + balancer->s->forcerecovery = 1; + else if (!strcasecmp(val, "off")) + balancer->s->forcerecovery = 0; + else + return "forcerecovery must be On|Off"; + balancer->s->forcerecovery_set = 1; + } + else { + return "unknown Balancer parameter"; + } + return NULL; +} + +static int alias_match(const char *uri, const char *alias_fakename) +{ + const char *end_fakename = alias_fakename + strlen(alias_fakename); + const char *aliasp = alias_fakename, *urip = uri; + const char *end_uri = uri + strlen(uri); + + while (aliasp < end_fakename && urip < end_uri) { + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') + return 0; + + while (*aliasp == '/') + ++aliasp; + while (*urip == '/') + ++urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) + return 0; + } + } + + /* fixup badly encoded stuff (e.g. % as last character) */ + if (aliasp > end_fakename) { + aliasp = end_fakename; + } + if (urip > end_uri) { + urip = end_uri; + } + + /* We reach the end of the uri before the end of "alias_fakename" + * for example uri is "/" and alias_fakename "/examples" + */ + if (urip == end_uri && aliasp != end_fakename) { + return 0; + } + + /* Check last alias path component matched all the way */ + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +/* + * Inspired by mod_jk's jk_servlet_normalize(). + */ +static int alias_match_servlet(apr_pool_t *p, + const char **urip, + const char *alias) +{ + char *map; + const char *uri = *urip; + apr_array_header_t *stack; + int map_pos, uri_pos, alias_pos, first_pos; + int alias_depth = 0, depth; + + /* Both uri and alias should start with '/' */ + if (uri[0] != '/' || alias[0] != '/') { + return 0; + } + + stack = apr_array_make(p, 5, sizeof(int)); + map = apr_palloc(p, strlen(uri) + 1); + map[0] = '/'; + map[1] = '\0'; + + map_pos = uri_pos = alias_pos = first_pos = 1; + while (uri[uri_pos] != '\0') { + /* Remove path parameters ;foo=bar/ from any path segment */ + if (uri[uri_pos] == ';') { + do { + uri_pos++; + } while (uri[uri_pos] != '/' && uri[uri_pos] != '\0'); + continue; + } + + if (map[map_pos - 1] == '/') { + /* Collapse ///// sequences to / */ + if (uri[uri_pos] == '/') { + do { + uri_pos++; + } while (uri[uri_pos] == '/'); + continue; + } + + if (uri[uri_pos] == '.') { + /* Remove /./ segments */ + if (uri[uri_pos + 1] == '/' + || uri[uri_pos + 1] == ';' + || uri[uri_pos + 1] == '\0') { + uri_pos++; + if (uri[uri_pos] == '/') { + uri_pos++; + } + continue; + } + + /* Remove /xx/../ segments */ + if (uri[uri_pos + 1] == '.' + && (uri[uri_pos + 2] == '/' + || uri[uri_pos + 2] == ';' + || uri[uri_pos + 2] == '\0')) { + /* Wind map segment back the previous one */ + if (map_pos == 1) { + /* Above root */ + return 0; + } + do { + map_pos--; + } while (map[map_pos - 1] != '/'); + map[map_pos] = '\0'; + + /* Wind alias segment back, unless in deeper segment */ + if (alias_depth == stack->nelts) { + if (alias[alias_pos] == '\0') { + alias_pos--; + } + while (alias_pos > 0 && alias[alias_pos] == '/') { + alias_pos--; + } + while (alias_pos > 0 && alias[alias_pos - 1] != '/') { + alias_pos--; + } + AP_DEBUG_ASSERT(alias_pos > 0); + alias_depth--; + } + apr_array_pop(stack); + + /* Move uri forward to the next segment */ + uri_pos += 2; + if (uri[uri_pos] == '/') { + uri_pos++; + } + first_pos = 0; + continue; + } + } + if (first_pos) { + while (uri[first_pos] == '/') { + first_pos++; + } + } + + /* New segment */ + APR_ARRAY_PUSH(stack, int) = first_pos ? first_pos : uri_pos; + if (alias[alias_pos] != '\0') { + if (alias[alias_pos - 1] != '/') { + /* Remain in pair with uri segments */ + do { + alias_pos++; + } while (alias[alias_pos - 1] != '/' && alias[alias_pos]); + } + while (alias[alias_pos] == '/') { + alias_pos++; + } + if (alias[alias_pos] != '\0') { + alias_depth++; + } + } + } + + if (alias[alias_pos] != '\0') { + int *match = &APR_ARRAY_IDX(stack, alias_depth - 1, int); + if (*match) { + if (alias[alias_pos] != uri[uri_pos]) { + /* Current segment does not match */ + *match = 0; + } + else if (alias[alias_pos + 1] == '\0' + && alias[alias_pos] != '/') { + if (uri[uri_pos + 1] == ';') { + /* We'll preserve the parameters of the last + * segment if it does not end with '/', so mark + * the match as negative for below handling. + */ + *match = -(uri_pos + 1); + } + else if (uri[uri_pos + 1] != '/' + && uri[uri_pos + 1] != '\0') { + /* Last segment does not match all the way */ + *match = 0; + } + } + } + /* Don't go past the segment if the uri isn't there yet */ + if (alias[alias_pos] != '/' || uri[uri_pos] == '/') { + alias_pos++; + } + } + + if (uri[uri_pos] == '/') { + first_pos = uri_pos + 1; + } + map[map_pos++] = uri[uri_pos++]; + map[map_pos] = '\0'; + } + + /* Can't reach the end of uri before the end of the alias, + * for example if uri is "/" and alias is "/examples" + */ + if (alias[alias_pos] != '\0') { + return 0; + } + + /* Check whether each alias segment matched */ + for (depth = 0; depth < alias_depth; ++depth) { + if (!APR_ARRAY_IDX(stack, depth, int)) { + return 0; + } + } + + /* If alias_depth == stack->nelts we have a full match, i.e. + * uri == alias so we can return uri_pos as is (the end of uri) + */ + if (alias_depth < stack->nelts) { + /* Return the segment following the alias */ + uri_pos = APR_ARRAY_IDX(stack, alias_depth, int); + if (alias_depth) { + /* But if the last segment of the alias does not end with '/' + * and the corresponding segment of the uri has parameters, + * we want to forward those parameters (see above for the + * negative pos trick/mark). + */ + int pos = APR_ARRAY_IDX(stack, alias_depth - 1, int); + if (pos < 0) { + uri_pos = -pos; + } + } + } + /* If the alias lacks a trailing slash, take it from the uri (if any) */ + if (alias[alias_pos - 1] != '/' && uri[uri_pos - 1] == '/') { + uri_pos--; + } + + *urip = map; + return uri_pos; +} + +/* Detect if an absoluteURI should be proxied or not. Note that we + * have to do this during this phase because later phases are + * "short-circuiting"... i.e. translate_names will end when the first + * module returns OK. So for example, if the request is something like: + * + * GET http://othervhost/cgi-bin/printenv HTTP/1.0 + * + * mod_alias will notice the /cgi-bin part and ScriptAlias it and + * short-circuit the proxy... just because of the ordering in the + * configuration file. + */ +static int proxy_detect(request_rec *r) +{ + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + /* Ick... msvc (perhaps others) promotes ternary short results to int */ + + if (conf->req && r->parsed_uri.scheme) { + /* but it might be something vhosted */ + if (!r->parsed_uri.hostname + || ap_cstr_casecmp(r->parsed_uri.scheme, ap_http_scheme(r)) != 0 + || !ap_matches_request_vhost(r, r->parsed_uri.hostname, + (apr_port_t)(r->parsed_uri.port_str + ? r->parsed_uri.port + : ap_default_port(r)))) { + r->proxyreq = PROXYREQ_PROXY; + r->uri = r->unparsed_uri; + r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + } + } + /* We need special treatment for CONNECT proxying: it has no scheme part */ + else if (conf->req && r->method_number == M_CONNECT + && r->parsed_uri.hostname + && r->parsed_uri.port_str) { + r->proxyreq = PROXYREQ_PROXY; + r->uri = r->unparsed_uri; + r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + } + return DECLINED; +} + +static const char *proxy_interpolate(request_rec *r, const char *str) +{ + /* Interpolate an env str in a configuration string + * Syntax ${var} --> value_of(var) + * Method: replace one var, and recurse on remainder of string + * Nothing clever here, and crap like nested vars may do silly things + * but we'll at least avoid sending the unwary into a loop + */ + const char *start; + const char *end; + const char *var; + const char *val; + const char *firstpart; + + start = ap_strstr_c(str, "${"); + if (start == NULL) { + return str; + } + end = ap_strchr_c(start+2, '}'); + if (end == NULL) { + return str; + } + /* OK, this is syntax we want to interpolate. Is there such a var ? */ + var = apr_pstrmemdup(r->pool, start+2, end-(start+2)); + val = apr_table_get(r->subprocess_env, var); + firstpart = apr_pstrmemdup(r->pool, str, (start-str)); + + if (val == NULL) { + return apr_pstrcat(r->pool, firstpart, + proxy_interpolate(r, end+1), NULL); + } + else { + return apr_pstrcat(r->pool, firstpart, val, + proxy_interpolate(r, end+1), NULL); + } +} +static apr_array_header_t *proxy_vars(request_rec *r, + apr_array_header_t *hdr) +{ + int i; + apr_array_header_t *ret = apr_array_make(r->pool, hdr->nelts, + sizeof (struct proxy_alias)); + struct proxy_alias *old = (struct proxy_alias *) hdr->elts; + + for (i = 0; i < hdr->nelts; ++i) { + struct proxy_alias *newcopy = apr_array_push(ret); + newcopy->fake = (old[i].flags & PROXYPASS_INTERPOLATE) + ? proxy_interpolate(r, old[i].fake) : old[i].fake; + newcopy->real = (old[i].flags & PROXYPASS_INTERPOLATE) + ? proxy_interpolate(r, old[i].real) : old[i].real; + } + return ret; +} + +PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent, + proxy_dir_conf *dconf) +{ + int len; + const char *fake; + const char *real; + ap_regmatch_t regm[AP_MAX_REG_MATCH]; + ap_regmatch_t reg1[AP_MAX_REG_MATCH]; + char *found = NULL; + int mismatch = 0; + unsigned int nocanon = ent->flags & PROXYPASS_NOCANON; + const char *use_uri = nocanon ? r->unparsed_uri : r->uri; + const char *servlet_uri = NULL; + + if (dconf && (dconf->interpolate_env == 1) && (ent->flags & PROXYPASS_INTERPOLATE)) { + fake = proxy_interpolate(r, ent->fake); + real = proxy_interpolate(r, ent->real); + } + else { + fake = ent->fake; + real = ent->real; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(03461) + "attempting to match URI path '%s' against %s '%s' for " + "proxying", r->uri, (ent->regex ? "pattern" : "prefix"), + fake); + + if (ent->regex) { + if (!ap_regexec(ent->regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) { + if ((real[0] == '!') && (real[1] == '\0')) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03462) + "proxying is explicitly disabled for URI path " + "'%s'; declining", r->uri); + return DECLINED; + } + /* test that we haven't reduced the URI */ + if (nocanon && ap_regexec(ent->regex, r->unparsed_uri, + AP_MAX_REG_MATCH, reg1, 0)) { + mismatch = 1; + use_uri = r->uri; + } + found = ap_pregsub(r->pool, real, use_uri, AP_MAX_REG_MATCH, + (use_uri == r->uri) ? regm : reg1); + if (!found) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01135) + "Substitution in regular expression failed. " + "Replacement too long?"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Note: The strcmp() below catches cases where there + * was no regex substitution. This is so cases like: + * + * ProxyPassMatch \.gif balancer://foo + * + * will work "as expected". The upshot is that the 2 + * directives below act the exact same way (ie: $1 is implied): + * + * ProxyPassMatch ^(/.*\.gif)$ balancer://foo + * ProxyPassMatch ^(/.*\.gif)$ balancer://foo$1 + * + * which may be confusing. + */ + if (strcmp(found, real) != 0) { + found = apr_pstrcat(r->pool, "proxy:", found, NULL); + } + else { + found = apr_pstrcat(r->pool, "proxy:", real, use_uri, NULL); + } + } + } + else { + if ((ent->flags & PROXYPASS_MAP_SERVLET) == PROXYPASS_MAP_SERVLET) { + servlet_uri = r->uri; + len = alias_match_servlet(r->pool, &servlet_uri, fake); + nocanon = 0; /* ignored since servlet's normalization applies */ + } + else { + len = alias_match(r->uri, fake); + } + + if (len != 0) { + if ((real[0] == '!') && (real[1] == '\0')) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03463) + "proxying is explicitly disabled for URI path " + "'%s'; declining", r->uri); + return DECLINED; + } + if (nocanon && len != alias_match(r->unparsed_uri, fake)) { + mismatch = 1; + use_uri = r->uri; + } + found = apr_pstrcat(r->pool, "proxy:", real, use_uri + len, NULL); + } + } + if (mismatch) { + /* We made a reducing transformation, so we can't safely use + * unparsed_uri. Safe fallback is to ignore nocanon. + */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01136) + "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon"); + } + + if (found) { + unsigned int encoded = ent->flags & PROXYPASS_MAP_ENCODED; + + /* A proxy module is assigned this URL, check whether it's interested + * in the request itself (e.g. proxy_wstunnel cares about Upgrade + * requests only, and could hand over to proxy_http otherwise). + */ + int rc = proxy_run_check_trans(r, found + 6); + if (rc != OK && rc != DECLINED) { + return HTTP_CONTINUE; + } + + r->filename = found; + r->handler = "proxy-server"; + r->proxyreq = PROXYREQ_REVERSE; + if (nocanon && !mismatch) { + /* mod_proxy_http needs to be told. Different module. */ + apr_table_setn(r->notes, "proxy-nocanon", "1"); + } + if (ent->flags & PROXYPASS_NOQUERY) { + apr_table_setn(r->notes, "proxy-noquery", "1"); + } + if (encoded) { + apr_table_setn(r->notes, "proxy-noencode", "1"); + } + + if (servlet_uri) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10248) + "Servlet path '%s' (%s) matches proxy handler '%s'", + r->uri, servlet_uri, found); + /* Apply servlet normalization to r->uri so that <Location> or any + * directory context match does not have to handle path parameters. + * We change r->uri in-place so that r->parsed_uri.path is updated + * too. Since normalized servlet_uri is necessarily shorter than + * the original r->uri, strcpy() is fine. + */ + AP_DEBUG_ASSERT(strlen(r->uri) >= strlen(servlet_uri)); + strcpy(r->uri, servlet_uri); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(03464) + "URI path '%s' matches proxy handler '%s'", r->uri, + found); + } + return (encoded) ? DONE : OK; + } + + return HTTP_CONTINUE; +} + +static int proxy_trans(request_rec *r, int pre_trans) +{ + int i, enc; + struct proxy_alias *ent; + proxy_dir_conf *dconf; + proxy_server_conf *conf; + + if (r->proxyreq) { + /* someone has already set up the proxy, it was possibly ourselves + * in proxy_detect (DONE will prevent further decoding of r->uri, + * only if proxyreq is set before pre_trans already). + */ + return pre_trans ? DONE : OK; + } + + /* In early pre_trans hook, r->uri was not manipulated yet so we are + * compliant with RFC1945 at this point. Otherwise, it probably isn't + * an issue because this is a hybrid proxy/origin server. + */ + + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config, + &proxy_module); + + /* Always and only do PROXY_MAP_ENCODED mapping in pre_trans, when + * r->uri is still encoded, or we might consider for instance that + * a decoded sub-delim is now a delimiter (e.g. "%3B" => ';' for + * path parameters), which it's not. + */ + if ((pre_trans && !conf->map_encoded_one) + || (!pre_trans && conf->map_encoded_all)) { + /* Fast path, nothing at this stage */ + return DECLINED; + } + + if ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0') + || !r->uri || r->uri[0] != '/') { + return DECLINED; + } + + if (apr_table_get(r->subprocess_env, "no-proxy")) { + return DECLINED; + } + + /* short way - this location is reverse proxied? */ + if (dconf->alias) { + enc = (dconf->alias->flags & PROXYPASS_MAP_ENCODED) != 0; + if (!(pre_trans ^ enc)) { + int rv = ap_proxy_trans_match(r, dconf->alias, dconf); + if (rv != HTTP_CONTINUE) { + return rv; + } + } + } + + /* long way - walk the list of aliases, find a match */ + for (i = 0; i < conf->aliases->nelts; i++) { + ent = &((struct proxy_alias *)conf->aliases->elts)[i]; + enc = (ent->flags & PROXYPASS_MAP_ENCODED) != 0; + if (!(pre_trans ^ enc)) { + int rv = ap_proxy_trans_match(r, ent, dconf); + if (rv != HTTP_CONTINUE) { + return rv; + } + } + } + + return DECLINED; +} + +static int proxy_pre_translate_name(request_rec *r) +{ + return proxy_trans(r, 1); +} + +static int proxy_translate_name(request_rec *r) +{ + return proxy_trans(r, 0); +} + +static int proxy_walk(request_rec *r) +{ + proxy_server_conf *sconf = ap_get_module_config(r->server->module_config, + &proxy_module); + ap_conf_vector_t *per_dir_defaults = r->per_dir_config; + ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts; + ap_conf_vector_t *entry_config; + proxy_dir_conf *entry_proxy; + int num_sec = sconf->sec_proxy->nelts; + /* XXX: shouldn't we use URI here? Canonicalize it first? + * Pass over "proxy:" prefix + */ + const char *proxyname = r->filename + 6; + int j; + apr_pool_t *rxpool = NULL; + + for (j = 0; j < num_sec; ++j) + { + int nmatch = 0; + int i; + ap_regmatch_t *pmatch = NULL; + + entry_config = sec_proxy[j]; + entry_proxy = ap_get_module_config(entry_config, &proxy_module); + + if (entry_proxy->r) { + + if (entry_proxy->refs && entry_proxy->refs->nelts) { + if (!rxpool) { + apr_pool_create(&rxpool, r->pool); + apr_pool_tag(rxpool, "proxy_rxpool"); + } + nmatch = entry_proxy->refs->nelts; + pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t)); + } + + if (ap_regexec(entry_proxy->r, proxyname, nmatch, pmatch, 0)) { + continue; + } + + for (i = 0; i < nmatch; i++) { + if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 && + ((const char **)entry_proxy->refs->elts)[i]) { + apr_table_setn(r->subprocess_env, + ((const char **)entry_proxy->refs->elts)[i], + apr_pstrndup(r->pool, + proxyname + pmatch[i].rm_so, + pmatch[i].rm_eo - pmatch[i].rm_so)); + } + } + } + + else if ( + /* XXX: What about case insensitive matching ??? + * Compare regex, fnmatch or string as appropriate + * If the entry doesn't relate, then continue + */ + entry_proxy->p_is_fnmatch ? apr_fnmatch(entry_proxy->p, + proxyname, 0) : + strncmp(proxyname, entry_proxy->p, + strlen(entry_proxy->p))) { + continue; + } + per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults, + entry_config); + } + + r->per_dir_config = per_dir_defaults; + + if (rxpool) { + apr_pool_destroy(rxpool); + } + + return OK; +} + +static int proxy_map_location(request_rec *r) +{ + int access_status; + + if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) + return DECLINED; + + /* Don't let the core or mod_http map_to_storage hooks handle this, + * We don't need directory/file_walk, and we want to TRACE on our own. + */ + if ((access_status = proxy_walk(r))) { + ap_die(access_status, r); + return access_status; + } + + return OK; +} + +/* -------------------------------------------------------------- */ +/* Fixup the filename */ + +/* + * Canonicalise the URL + */ +static int proxy_fixup(request_rec *r) +{ + char *url, *p; + int access_status; + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_module); + + if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) + return DECLINED; + + /* XXX: Shouldn't we try this before we run the proxy_walk? */ + url = &r->filename[6]; + + if ((dconf->interpolate_env == 1) && (r->proxyreq == PROXYREQ_REVERSE)) { + /* create per-request copy of reverse proxy conf, + * and interpolate vars in it + */ + proxy_req_conf *rconf = apr_palloc(r->pool, sizeof(proxy_req_conf)); + ap_set_module_config(r->request_config, &proxy_module, rconf); + rconf->raliases = proxy_vars(r, dconf->raliases); + rconf->cookie_paths = proxy_vars(r, dconf->cookie_paths); + rconf->cookie_domains = proxy_vars(r, dconf->cookie_domains); + } + + /* canonicalise each specific scheme */ + if ((access_status = proxy_run_canon_handler(r, url))) { + return access_status; + } + + p = strchr(url, ':'); + if (p == NULL || p == url) + return HTTP_BAD_REQUEST; + + return OK; /* otherwise; we've done the best we can */ +} +/* Send a redirection if the request contains a hostname which is not */ +/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */ +/* servers like Netscape's allow this and access hosts from the local */ +/* domain in this case. I think it is better to redirect to a FQDN, since */ +/* these will later be found in the bookmarks files. */ +/* The "ProxyDomain" directive determines what domain will be appended */ +static int proxy_needsdomain(request_rec *r, const char *url, const char *domain) +{ + char *nuri; + const char *ref; + + /* We only want to worry about GETs */ + if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname) + return DECLINED; + + /* If host does contain a dot already, or it is "localhost", decline */ + if (strchr(r->parsed_uri.hostname, '.') != NULL /* has domain, or IPv4 literal */ + || strchr(r->parsed_uri.hostname, ':') != NULL /* IPv6 literal */ + || ap_cstr_casecmp(r->parsed_uri.hostname, "localhost") == 0) + return DECLINED; /* host name has a dot already */ + + ref = apr_table_get(r->headers_in, "Referer"); + + /* Reassemble the request, but insert the domain after the host name */ + /* Note that the domain name always starts with a dot */ + r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname, + domain, NULL); + nuri = apr_uri_unparse(r->pool, + &r->parsed_uri, + APR_URI_UNP_REVEALPASSWORD); + + apr_table_setn(r->headers_out, "Location", nuri); + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01138) + "Domain missing: %s sent to %s%s%s", r->uri, + apr_uri_unparse(r->pool, &r->parsed_uri, + APR_URI_UNP_OMITUSERINFO), + ref ? " from " : "", ref ? ref : ""); + + return HTTP_MOVED_PERMANENTLY; +} + +/* -------------------------------------------------------------- */ +/* Invoke handler */ + +static int proxy_handler(request_rec *r) +{ + char *uri, *scheme, *p; + const char *p2; + void *sconf = r->server->module_config; + proxy_server_conf *conf = (proxy_server_conf *) + ap_get_module_config(sconf, &proxy_module); + apr_array_header_t *proxies = conf->proxies; + struct proxy_remote *ents = (struct proxy_remote *) proxies->elts; + int i, rc, access_status; + int direct_connect = 0; + const char *str; + apr_int64_t maxfwd; + proxy_balancer *balancer = NULL; + proxy_worker *worker = NULL; + int attempts = 0, max_attempts = 0; + struct dirconn_entry *list = (struct dirconn_entry *)conf->dirconn->elts; + int saved_status; + + /* is this for us? */ + if (!r->filename) { + return DECLINED; + } + + if (!r->proxyreq) { + /* We may have forced the proxy handler via config or .htaccess */ + if (r->handler && + strncmp(r->handler, "proxy:", 6) == 0 && + strncmp(r->filename, "proxy:", 6) != 0) { + r->proxyreq = PROXYREQ_REVERSE; + r->filename = apr_pstrcat(r->pool, r->handler, r->filename, NULL); + } + else { + return DECLINED; + } + } else if (strncmp(r->filename, "proxy:", 6) != 0) { + return DECLINED; + } + + /* handle max-forwards / OPTIONS / TRACE */ + if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) { + char *end; + maxfwd = apr_strtoi64(str, &end, 10); + if (maxfwd < 0 || maxfwd == APR_INT64_MAX || *end) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10188) + "Max-Forwards value '%s' could not be parsed", str); + return ap_proxyerror(r, HTTP_BAD_REQUEST, + "Max-Forwards request header could not be parsed"); + } + else if (maxfwd == 0) { + switch (r->method_number) { + case M_TRACE: { + int access_status; + r->proxyreq = PROXYREQ_NONE; + access_status = ap_send_http_trace(r); + ap_die(access_status, r); + return OK; + } + case M_OPTIONS: { + int access_status; + r->proxyreq = PROXYREQ_NONE; + access_status = ap_send_http_options(r); + ap_die(access_status, r); + return OK; + } + default: { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + "Max-Forwards has reached zero - proxy loop?"); + } + } + } + maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0; + } + else { + /* set configured max-forwards */ + maxfwd = conf->maxfwd; + } + if (maxfwd >= 0) { + apr_table_setn(r->headers_in, "Max-Forwards", + apr_psprintf(r->pool, "%" APR_INT64_T_FMT, maxfwd)); + } + + if (r->method_number == M_TRACE) { + core_server_config *coreconf = (core_server_config *) + ap_get_core_module_config(sconf); + + if (coreconf->trace_enable == AP_TRACE_DISABLE) + { + /* Allow "error-notes" string to be printed by ap_send_error_response() + * Note; this goes nowhere, canned error response need an overhaul. + */ + apr_table_setn(r->notes, "error-notes", + "TRACE forbidden by server configuration"); + apr_table_setn(r->notes, "verbose-error-to", "*"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01139) + "TRACE forbidden by server configuration"); + return HTTP_METHOD_NOT_ALLOWED; + } + + /* Can't test ap_should_client_block, we aren't ready to send + * the client a 100 Continue response till the connection has + * been established + */ + if (coreconf->trace_enable != AP_TRACE_EXTENDED + && (r->read_length || r->read_chunked || r->remaining)) + { + /* Allow "error-notes" string to be printed by ap_send_error_response() + * Note; this goes nowhere, canned error response need an overhaul. + */ + apr_table_setn(r->notes, "error-notes", + "TRACE with request body is not allowed"); + apr_table_setn(r->notes, "verbose-error-to", "*"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01140) + "TRACE with request body is not allowed"); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + } + + uri = r->filename + 6; + p = strchr(uri, ':'); + if (p == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01141) + "proxy_handler no URL in %s", r->filename); + return HTTP_BAD_REQUEST; + } + + /* If the host doesn't have a domain name, add one and redirect. */ + if (conf->domain != NULL) { + rc = proxy_needsdomain(r, uri, conf->domain); + if (ap_is_HTTP_REDIRECT(rc)) + return HTTP_MOVED_PERMANENTLY; + } + + scheme = apr_pstrmemdup(r->pool, uri, p - uri); + /* Check URI's destination host against NoProxy hosts */ + /* Bypass ProxyRemote server lookup if configured as NoProxy */ + for (direct_connect = i = 0; i < conf->dirconn->nelts && + !direct_connect; i++) { + direct_connect = list[i].matcher(&list[i], r); + } +#if DEBUGGING + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + (direct_connect) ? APLOGNO(03231) "NoProxy for %s" : APLOGNO(03232) "UseProxy for %s", + r->uri); +#endif + + do { + char *url = uri; + /* Try to obtain the most suitable worker */ + access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url); + if (access_status != OK) { + /* + * Only return if access_status is not HTTP_SERVICE_UNAVAILABLE + * This gives other modules the chance to hook into the + * request_status hook and decide what to do in this situation. + */ + if (access_status != HTTP_SERVICE_UNAVAILABLE) + return access_status; + /* + * Ensure that balancer is NULL if worker is NULL to prevent + * potential problems in the post_request hook. + */ + if (!worker) + balancer = NULL; + goto cleanup; + } + + /* Initialise worker if needed, note the shared area must be initialized by the balancer logic */ + if (balancer) { + ap_proxy_initialize_worker(worker, r->server, conf->pool); + } + + if (balancer && balancer->s->max_attempts_set && !max_attempts) + max_attempts = balancer->s->max_attempts; + /* firstly, try a proxy, unless a NoProxy directive is active */ + if (!direct_connect) { + for (i = 0; i < proxies->nelts; i++) { + p2 = ap_strchr_c(ents[i].scheme, ':'); /* is it a partial URL? */ + if (strcmp(ents[i].scheme, "*") == 0 || + (ents[i].use_regex && + ap_regexec(ents[i].regexp, url, 0, NULL, 0) == 0) || + (p2 == NULL && ap_cstr_casecmp(scheme, ents[i].scheme) == 0) || + (p2 != NULL && + ap_cstr_casecmpn(url, ents[i].scheme, + strlen(ents[i].scheme)) == 0)) { + + /* handle the scheme */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01142) + "Trying to run scheme_handler against proxy"); + access_status = proxy_run_scheme_handler(r, worker, + conf, url, + ents[i].hostname, + ents[i].port); + + /* Did the scheme handler process the request? */ + if (access_status != DECLINED) { + const char *cl_a; + apr_off_t cl; + + /* + * An fatal error or success, so no point in + * retrying with a direct connection. + */ + if (access_status != HTTP_BAD_GATEWAY) { + goto cleanup; + } + + cl_a = apr_table_get(r->headers_in, "Content-Length"); + if (cl_a && (!ap_parse_strict_length(&cl, cl_a) + || cl > 0)) { + /* + * The request body is of length > 0. We cannot + * retry with a direct connection since we already + * sent (parts of) the request body to the proxy + * and do not have any longer. + */ + goto cleanup; + } + /* + * Transfer-Encoding was set as input header, so we had + * a request body. We cannot retry with a direct + * connection for the same reason as above. + */ + if (apr_table_get(r->headers_in, "Transfer-Encoding")) { + goto cleanup; + } + } + } + } + } + + /* otherwise, try it direct */ + /* N.B. what if we're behind a firewall, where we must use a proxy or + * give up?? + */ + + /* handle the scheme */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01143) + "Running scheme %s handler (attempt %d)", + scheme, attempts); + AP_PROXY_RUN(r, worker, conf, url, attempts); + access_status = proxy_run_scheme_handler(r, worker, conf, + url, NULL, 0); + if (access_status == OK + || apr_table_get(r->notes, "proxy-error-override")) + break; + else if (access_status == HTTP_INTERNAL_SERVER_ERROR) { + /* Unrecoverable server error. + * We can not failover to another worker. + * Mark the worker as unusable if member of load balancer + */ + if (balancer + && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { + worker->s->status |= PROXY_WORKER_IN_ERROR; + worker->s->error_time = apr_time_now(); + } + break; + } + else if (access_status == HTTP_SERVICE_UNAVAILABLE) { + /* Recoverable server error. + * We can failover to another worker + * Mark the worker as unusable if member of load balancer + */ + if (balancer + && !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { + worker->s->status |= PROXY_WORKER_IN_ERROR; + worker->s->error_time = apr_time_now(); + } + } + else { + /* Unrecoverable error. + * Return the origin status code to the client. + */ + break; + } + /* Try again if the worker is unusable and the service is + * unavailable. + */ + } while (!PROXY_WORKER_IS_USABLE(worker) && + max_attempts > attempts++); + + if (DECLINED == access_status) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01144) + "No protocol handler was valid for the URL %s " + "(scheme '%s'). " + "If you are using a DSO version of mod_proxy, make sure " + "the proxy submodules are included in the configuration " + "using LoadModule.", r->uri, scheme); + access_status = HTTP_INTERNAL_SERVER_ERROR; + goto cleanup; + } +cleanup: + /* + * Save current r->status and set it to the value of access_status which + * might be different (e.g. r->status could be HTTP_OK if e.g. we override + * the error page on the proxy or if the error was not generated by the + * backend itself but by the proxy e.g. a bad gateway) in order to give + * ap_proxy_post_request a chance to act correctly on the status code. + * But only do the above if access_status is not OK and not DONE, because + * in this case r->status might contain the true status and overwriting + * it with OK or DONE would be wrong. + */ + if ((access_status != OK) && (access_status != DONE)) { + saved_status = r->status; + r->status = access_status; + ap_proxy_post_request(worker, balancer, r, conf); + /* + * Only restore r->status if it has not been changed by + * ap_proxy_post_request as we assume that this change was intentional. + */ + if (r->status == access_status) { + r->status = saved_status; + } + } + else { + ap_proxy_post_request(worker, balancer, r, conf); + } + + proxy_run_request_status(&access_status, r); + AP_PROXY_RUN_FINISHED(r, attempts, access_status); + + return access_status; +} + +/* -------------------------------------------------------------- */ +/* Setup configurable data */ + +static void * create_proxy_config(apr_pool_t *p, server_rec *s) +{ + proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf)); + + ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *)); + ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote)); + ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry)); + ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry)); + ps->workers = apr_array_make(p, 10, sizeof(proxy_worker)); + ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer)); + ps->forward = NULL; + ps->reverse = NULL; + ps->domain = NULL; + ps->map_encoded_one = 0; + ps->map_encoded_all = 1; + ps->id = apr_psprintf(p, "p%x", 1); /* simply for storage size */ + ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */ + ps->viaopt_set = 0; /* 0 means default */ + ps->req = 0; + ps->max_balancers = 0; + ps->bal_persist = 0; + ps->inherit = 1; + ps->inherit_set = 0; + ps->ppinherit = 1; + ps->ppinherit_set = 0; + ps->bgrowth = 5; + ps->bgrowth_set = 0; + ps->req_set = 0; + ps->recv_buffer_size = 0; /* this default was left unset for some reason */ + ps->recv_buffer_size_set = 0; + ps->io_buffer_size = AP_IOBUFSIZE; + ps->io_buffer_size_set = 0; + ps->maxfwd = DEFAULT_MAX_FORWARDS; + ps->maxfwd_set = 0; + ps->timeout = 0; + ps->timeout_set = 0; + ps->badopt = bad_error; + ps->badopt_set = 0; + ps->source_address = NULL; + ps->source_address_set = 0; + apr_pool_create_ex(&ps->pool, p, NULL, NULL); + apr_pool_tag(ps->pool, "proxy_server_conf"); + + return ps; +} + +static apr_array_header_t *merge_balancers(apr_pool_t *p, + apr_array_header_t *base, + apr_array_header_t *overrides) +{ + proxy_balancer *b1; + proxy_balancer *b2; + proxy_balancer tmp; + int x, y, found; + apr_array_header_t *tocopy = apr_array_make(p, 1, sizeof(proxy_balancer)); + + /* Check if the balancer is defined in both override and base configs: + * a) If it is, Create copy of base balancer and change the configuration + * which can be changed by ProxyPass. + * b) Otherwise, copy the balancer to tocopy array and merge it later. + */ + b1 = (proxy_balancer *) base->elts; + for (y = 0; y < base->nelts; y++) { + b2 = (proxy_balancer *) overrides->elts; + for (x = 0, found = 0; x < overrides->nelts; x++) { + if (b1->hash.def == b2->hash.def && b1->hash.fnv == b2->hash.fnv) { + tmp = *b2; + *b2 = *b1; + b2->s = tmp.s; + + /* For shared memory entries, b2->s belongs to override + * balancer, so if some entry is not set there, we have to + * update it according to the base balancer. */ + if (*b2->s->sticky == 0 && *b1->s->sticky) { + PROXY_STRNCPY(b2->s->sticky_path, b1->s->sticky_path); + PROXY_STRNCPY(b2->s->sticky, b1->s->sticky); + } + if (!b2->s->sticky_separator_set + && b1->s->sticky_separator_set) { + b2->s->sticky_separator_set = b1->s->sticky_separator_set; + b2->s->sticky_separator = b1->s->sticky_separator; + } + if (!b2->s->timeout && b1->s->timeout) { + b2->s->timeout = b1->s->timeout; + } + if (!b2->s->max_attempts_set && b1->s->max_attempts_set) { + b2->s->max_attempts_set = b1->s->max_attempts_set; + b2->s->max_attempts = b1->s->max_attempts; + } + if (!b2->s->nonce_set && b1->s->nonce_set) { + b2->s->nonce_set = b1->s->nonce_set; + PROXY_STRNCPY(b2->s->nonce, b1->s->nonce); + } + if (!b2->s->sticky_force_set && b1->s->sticky_force_set) { + b2->s->sticky_force_set = b1->s->sticky_force_set; + b2->s->sticky_force = b1->s->sticky_force; + } + if (!b2->s->scolonsep_set && b1->s->scolonsep_set) { + b2->s->scolonsep_set = b1->s->scolonsep_set; + b2->s->scolonsep = b1->s->scolonsep; + } + if (!b2->s->forcerecovery_set && b1->s->forcerecovery_set) { + b2->s->forcerecovery_set = b1->s->forcerecovery_set; + b2->s->forcerecovery = b1->s->forcerecovery; + } + + /* For non-shared memory entries, b2 is copy of b1, so we have + * to use tmp copy of b1 to detect changes done in override. */ + if (tmp.lbmethod_set) { + b2->lbmethod_set = tmp.lbmethod_set; + b2->lbmethod = tmp.lbmethod; + } + if (tmp.growth_set) { + b2->growth_set = tmp.growth_set; + b2->growth = tmp.growth; + } + if (tmp.failontimeout_set) { + b2->failontimeout_set = tmp.failontimeout_set; + b2->failontimeout = tmp.failontimeout; + } + if (!apr_is_empty_array(tmp.errstatuses)) { + apr_array_cat(tmp.errstatuses, b2->errstatuses); + b2->errstatuses = tmp.errstatuses; + } + + found = 1; + break; + } + b2++; + } + if (!found) { + *(proxy_balancer *)apr_array_push(tocopy) = *b1; + } + b1++; + } + + return apr_array_append(p, tocopy, overrides); +} + +static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv) +{ + proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf)); + proxy_server_conf *base = (proxy_server_conf *) basev; + proxy_server_conf *overrides = (proxy_server_conf *) overridesv; + + ps->inherit = (overrides->inherit_set == 0) ? base->inherit : overrides->inherit; + ps->inherit_set = overrides->inherit_set || base->inherit_set; + + ps->ppinherit = (overrides->ppinherit_set == 0) ? base->ppinherit : overrides->ppinherit; + ps->ppinherit_set = overrides->ppinherit_set || base->ppinherit_set; + + if (ps->ppinherit) { + ps->proxies = apr_array_append(p, base->proxies, overrides->proxies); + } + else { + ps->proxies = overrides->proxies; + } + ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy); + ps->aliases = apr_array_append(p, base->aliases, overrides->aliases); + ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies); + ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn); + if (ps->inherit || ps->ppinherit) { + ps->workers = apr_array_append(p, base->workers, overrides->workers); + ps->balancers = merge_balancers(p, base->balancers, overrides->balancers); + } + else { + ps->workers = overrides->workers; + ps->balancers = overrides->balancers; + } + ps->forward = overrides->forward ? overrides->forward : base->forward; + ps->reverse = overrides->reverse ? overrides->reverse : base->reverse; + + ps->map_encoded_one = overrides->map_encoded_one || base->map_encoded_one; + ps->map_encoded_all = overrides->map_encoded_all && base->map_encoded_all; + + ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain; + ps->id = (overrides->id == NULL) ? base->id : overrides->id; + ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt; + ps->viaopt_set = overrides->viaopt_set || base->viaopt_set; + ps->req = (overrides->req_set == 0) ? base->req : overrides->req; + ps->req_set = overrides->req_set || base->req_set; + ps->bgrowth = (overrides->bgrowth_set == 0) ? base->bgrowth : overrides->bgrowth; + ps->bgrowth_set = overrides->bgrowth_set || base->bgrowth_set; + ps->max_balancers = overrides->max_balancers || base->max_balancers; + ps->bal_persist = overrides->bal_persist; + ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size; + ps->recv_buffer_size_set = overrides->recv_buffer_size_set || base->recv_buffer_size_set; + ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size; + ps->io_buffer_size_set = overrides->io_buffer_size_set || base->io_buffer_size_set; + ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd; + ps->maxfwd_set = overrides->maxfwd_set || base->maxfwd_set; + ps->timeout = (overrides->timeout_set == 0) ? base->timeout : overrides->timeout; + ps->timeout_set = overrides->timeout_set || base->timeout_set; + ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt; + ps->badopt_set = overrides->badopt_set || base->badopt_set; + ps->proxy_status = (overrides->proxy_status_set == 0) ? base->proxy_status : overrides->proxy_status; + ps->proxy_status_set = overrides->proxy_status_set || base->proxy_status_set; + ps->source_address = (overrides->source_address_set == 0) ? base->source_address : overrides->source_address; + ps->source_address_set = overrides->source_address_set || base->source_address_set; + ps->pool = base->pool; + return ps; +} +static const char *set_source_address(cmd_parms *parms, void *dummy, + const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + struct apr_sockaddr_t *addr; + + if (APR_SUCCESS == apr_sockaddr_info_get(&addr, arg, APR_UNSPEC, 0, 0, + psf->pool)) { + psf->source_address = addr; + psf->source_address_set = 1; + } + else { + return "ProxySourceAddress invalid value"; + } + + return NULL; +} + +static void *create_proxy_dir_config(apr_pool_t *p, char *dummy) +{ + proxy_dir_conf *new = + (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf)); + + /* Filled in by proxysection, when applicable */ + + /* Put these in the dir config so they work inside <Location> */ + new->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); + new->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias)); + new->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias)); + new->error_override_codes = apr_array_make(p, 10, sizeof(int)); + new->preserve_host_set = 0; + new->preserve_host = 0; + new->interpolate_env = -1; /* unset */ + new->error_override = 0; + new->error_override_set = 0; + new->add_forwarded_headers = 1; + new->add_forwarded_headers_set = 0; + new->forward_100_continue = 1; + new->forward_100_continue_set = 0; + + return (void *) new; +} + +static int int_order(const void *i1, const void *i2) +{ + return *(const int *)i1 - *(const int *)i2; +} + +static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) +{ + proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf)); + proxy_dir_conf *add = (proxy_dir_conf *) addv; + proxy_dir_conf *base = (proxy_dir_conf *) basev; + + new->p = add->p; + new->p_is_fnmatch = add->p_is_fnmatch; + new->r = add->r; + new->refs = add->refs; + + /* Put these in the dir config so they work inside <Location> */ + new->raliases = apr_array_append(p, base->raliases, add->raliases); + new->cookie_paths + = apr_array_append(p, base->cookie_paths, add->cookie_paths); + new->cookie_domains + = apr_array_append(p, base->cookie_domains, add->cookie_domains); + new->error_override_codes + = apr_array_append(p, base->error_override_codes, add->error_override_codes); + /* Keep the array sorted for binary search (since "base" and "add" are + * already sorted, it's only needed only if both are merged). + */ + if (base->error_override_codes->nelts + && add->error_override_codes->nelts) { + qsort(new->error_override_codes->elts, + new->error_override_codes->nelts, + sizeof(int), int_order); + } + new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env + : add->interpolate_env; + new->preserve_host = (add->preserve_host_set == 0) ? base->preserve_host + : add->preserve_host; + new->preserve_host_set = add->preserve_host_set || base->preserve_host_set; + new->error_override = (add->error_override_set == 0) ? base->error_override + : add->error_override; + new->error_override_set = add->error_override_set || base->error_override_set; + new->alias = (add->alias_set == 0) ? base->alias : add->alias; + new->alias_set = add->alias_set || base->alias_set; + new->add_forwarded_headers = + (add->add_forwarded_headers_set == 0) ? base->add_forwarded_headers + : add->add_forwarded_headers; + new->add_forwarded_headers_set = add->add_forwarded_headers_set + || base->add_forwarded_headers_set; + new->forward_100_continue = + (add->forward_100_continue_set == 0) ? base->forward_100_continue + : add->forward_100_continue; + new->forward_100_continue_set = add->forward_100_continue_set + || base->forward_100_continue_set; + + return new; +} + +static const char * + add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + struct proxy_remote *new; + char *p, *q; + char *r, *f, *scheme; + ap_regex_t *reg = NULL; + int port; + + r = apr_pstrdup(cmd->pool, r1); + scheme = apr_pstrdup(cmd->pool, r1); + f = apr_pstrdup(cmd->pool, f1); + p = strchr(r, ':'); + if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') { + if (regex) + return "ProxyRemoteMatch: Bad syntax for a remote proxy server"; + else + return "ProxyRemote: Bad syntax for a remote proxy server"; + } + else { + scheme[p-r] = 0; + } + q = strchr(p + 3, ':'); + if (q != NULL) { + if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) { + if (regex) + return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)"; + else + return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)"; + } + *q = '\0'; + } + else + port = -1; + *p = '\0'; + if (regex) { + reg = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED); + if (!reg) + return "Regular expression for ProxyRemoteMatch could not be compiled."; + } + else + if (strchr(f, ':') == NULL) + ap_str_tolower(f); /* lowercase scheme */ + ap_str_tolower(p + 3); /* lowercase hostname */ + + if (port == -1) { + port = apr_uri_port_of_scheme(scheme); + } + + new = apr_array_push(conf->proxies); + new->scheme = f; + new->protocol = r; + new->hostname = p + 3; + new->port = port; + new->regexp = reg; + new->use_regex = regex; + return NULL; +} + +static const char * + add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1) +{ + return add_proxy(cmd, dummy, f1, r1, 0); +} + +static const char * + add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1) +{ + return add_proxy(cmd, dummy, f1, r1, 1); +} + +PROXY_DECLARE(const char *) ap_proxy_de_socketfy(apr_pool_t *p, const char *url) +{ + const char *ptr; + /* + * We could be passed a URL during the config stage that contains + * the UDS path... ignore it + */ + if (!ap_cstr_casecmpn(url, "unix:", 5) && + ((ptr = ap_strchr_c(url + 5, '|')) != NULL)) { + /* move past the 'unix:...|' UDS path info */ + const char *ret, *c; + + ret = ptr + 1; + /* special case: "unix:....|scheme:" is OK, expand + * to "unix:....|scheme://localhost" + * */ + c = ap_strchr_c(ret, ':'); + if (c == NULL) { + return NULL; + } + if (c[1] == '\0') { + return apr_pstrcat(p, ret, "//localhost", NULL); + } + else { + return ret; + } + } + return url; +} + +static const char * + add_pass(cmd_parms *cmd, void *dummy, const char *arg, int is_regex) +{ + proxy_dir_conf *dconf = (proxy_dir_conf *)dummy; + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + struct proxy_alias *new; + char *f = cmd->path; + char *r = NULL; + const char *real; + char *word; + apr_table_t *params = apr_table_make(cmd->pool, 5); + const apr_array_header_t *arr; + const apr_table_entry_t *elts; + int i; + unsigned int worker_type = (is_regex) ? AP_PROXY_WORKER_IS_MATCH + : AP_PROXY_WORKER_IS_PREFIX; + unsigned int flags = 0; + const char *err; + + err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES); + if (err) { + return err; + } + + while (*arg) { + word = ap_getword_conf(cmd->pool, &arg); + if (!f) { + if (!strcmp(word, "~")) { + if (is_regex) { + return "ProxyPassMatch invalid syntax ('~' usage)."; + } + worker_type = AP_PROXY_WORKER_IS_MATCH; + continue; + } + f = word; + } + else if (!r) { + r = word; + } + else if (!strcasecmp(word,"nocanon")) { + flags |= PROXYPASS_NOCANON; + } + else if (!strcasecmp(word,"interpolate")) { + flags |= PROXYPASS_INTERPOLATE; + } + else if (!strcasecmp(word,"noquery")) { + flags |= PROXYPASS_NOQUERY; + } + else { + char *val = strchr(word, '='); + if (!val) { + if (cmd->path) { + if (*r == '/') { + return "ProxyPass|ProxyPassMatch can not have a path when defined in " + "a location."; + } + else { + return "Invalid ProxyPass|ProxyPassMatch parameter. Parameter must " + "be in the form 'key=value'."; + } + } + else { + return "Invalid ProxyPass|ProxyPassMatch parameter. Parameter must be " + "in the form 'key=value'."; + } + } + else { + *val++ = '\0'; + } + if (!strcasecmp(word, "mapping")) { + if (!strcasecmp(val, "encoded")) { + flags |= PROXYPASS_MAP_ENCODED; + } + else if (!strcasecmp(val, "servlet")) { + flags |= PROXYPASS_MAP_SERVLET; + } + else { + return "unknown mapping"; + } + } + else { + apr_table_setn(params, word, val); + } + } + } + if (flags & PROXYPASS_MAP_ENCODED) { + conf->map_encoded_one = 1; + } + else { + conf->map_encoded_all = 0; + } + + if (r == NULL) { + return "ProxyPass|ProxyPassMatch needs a path when not defined in a location"; + } + if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, r))) { + return "ProxyPass|ProxyPassMatch uses an invalid \"unix:\" URL"; + } + + + /* if per directory, save away the single alias */ + if (cmd->path) { + dconf->alias = apr_pcalloc(cmd->pool, sizeof(struct proxy_alias)); + dconf->alias_set = 1; + new = dconf->alias; + if (apr_fnmatch_test(f)) { + worker_type = AP_PROXY_WORKER_IS_MATCH; + } + } + /* if per server, add to the alias array */ + else { + new = apr_array_push(conf->aliases); + } + + new->fake = apr_pstrdup(cmd->pool, f); + new->real = apr_pstrdup(cmd->pool, real); + new->flags = flags; + if (worker_type & AP_PROXY_WORKER_IS_MATCH) { + new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED); + if (new->regex == NULL) + return "Regular expression could not be compiled."; + } + else { + new->regex = NULL; + } + + if (r[0] == '!' && r[1] == '\0') + return NULL; + + arr = apr_table_elts(params); + elts = (const apr_table_entry_t *)arr->elts; + /* Distinguish the balancer from worker */ + if (ap_proxy_valid_balancer_name(r, 9)) { + proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r, 0); + char *fake_copy; + + /* + * In the regex case supplying a fake URL doesn't make sense as it + * cannot be parsed anyway with apr_uri_parse later on in + * ap_proxy_define_balancer / ap_proxy_update_balancer + */ + if (worker_type & AP_PROXY_WORKER_IS_MATCH) { + fake_copy = NULL; + } + else { + fake_copy = f; + } + if (!balancer) { + const char *err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, r, fake_copy, 0); + if (err) + return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL); + } + else { + ap_proxy_update_balancer(cmd->pool, balancer, fake_copy); + } + for (i = 0; i < arr->nelts; i++) { + const char *err = set_balancer_param(conf, cmd->pool, balancer, elts[i].key, + elts[i].val); + if (err) + return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL); + } + new->balancer = balancer; + } + else { + int reuse = 0; + proxy_worker *worker = ap_proxy_get_worker_ex(cmd->temp_pool, NULL, + conf, new->real, + worker_type); + if (!worker) { + const char *err; + err = ap_proxy_define_worker_ex(cmd->pool, &worker, NULL, + conf, r, worker_type); + if (err) + return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL); + + PROXY_COPY_CONF_PARAMS(worker, conf); + } + else { + reuse = 1; + ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, APLOGNO(01145) + "Sharing worker '%s' instead of creating new worker '%s'", + ap_proxy_worker_name(cmd->pool, worker), new->real); + } + + for (i = 0; i < arr->nelts; i++) { + if (reuse) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(01146) + "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing", + elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker)); + } else { + 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); + } + } + } + return NULL; +} + +static const char * + add_pass_noregex(cmd_parms *cmd, void *dummy, const char *arg) +{ + return add_pass(cmd, dummy, arg, 0); +} + +static const char * + add_pass_regex(cmd_parms *cmd, void *dummy, const char *arg) +{ + return add_pass(cmd, dummy, arg, 1); +} + + +static const char * add_pass_reverse(cmd_parms *cmd, void *dconf, const char *f, + const char *r, const char *i) +{ + proxy_dir_conf *conf = dconf; + struct proxy_alias *new; + const char *fake; + const char *real; + const char *interp; + const char *err; + + err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES); + if (err) { + return err; + } + + if (cmd->path == NULL) { + if (r == NULL || !strcasecmp(r, "interpolate")) { + return "ProxyPassReverse needs a path when not defined in a location"; + } + fake = f; + real = r; + interp = i; + } + else { + if (r && strcasecmp(r, "interpolate")) { + return "ProxyPassReverse can not have a path when defined in a location"; + } + fake = cmd->path; + real = f; + interp = r; + } + + new = apr_array_push(conf->raliases); + new->fake = fake; + new->real = real; + new->flags = interp ? PROXYPASS_INTERPOLATE : 0; + + return NULL; +} +static const char* cookie_path(cmd_parms *cmd, void *dconf, const char *f, + const char *r, const char *interp) +{ + proxy_dir_conf *conf = dconf; + struct proxy_alias *new; + + new = apr_array_push(conf->cookie_paths); + new->fake = f; + new->real = r; + new->flags = interp ? PROXYPASS_INTERPOLATE : 0; + + return NULL; +} +static const char* cookie_domain(cmd_parms *cmd, void *dconf, const char *f, + const char *r, const char *interp) +{ + proxy_dir_conf *conf = dconf; + struct proxy_alias *new; + + new = apr_array_push(conf->cookie_domains); + new->fake = f; + new->real = r; + new->flags = interp ? PROXYPASS_INTERPOLATE : 0; + return NULL; +} + +static const char * + set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + struct noproxy_entry *new; + struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts; + struct apr_sockaddr_t *addr; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i = 0; i < conf->noproxies->nelts; i++) { + if (strcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */ + found = 1; + break; + } + } + + if (!found) { + new = apr_array_push(conf->noproxies); + new->name = arg; + if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) { + new->addr = addr; + } + else { + new->addr = NULL; + } + } + return NULL; +} + + +/* Similar to set_proxy_exclude(), but defining directly connected hosts, + * which should never be accessed via the configured ProxyRemote servers + */ +static const char * + set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + struct dirconn_entry *New; + struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i = 0; i < conf->dirconn->nelts; i++) { + if (strcasecmp(arg, list[i].name) == 0) { + found = 1; + break; + } + } + + if (!found) { + New = apr_array_push(conf->dirconn); + New->name = apr_pstrdup(parms->pool, arg); + New->hostaddr = NULL; + + if (ap_proxy_is_ipaddr(New, parms->pool)) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03018) + "Parsed addr %s", inet_ntoa(New->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03019) + "Parsed mask %s", inet_ntoa(New->mask)); +#endif + } + else if (ap_proxy_is_domainname(New, parms->pool)) { + ap_str_tolower(New->name); +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03020) + "Parsed domain %s", New->name); +#endif + } + else if (ap_proxy_is_hostname(New, parms->pool)) { + ap_str_tolower(New->name); +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03021) + "Parsed host %s", New->name); +#endif + } + else { + ap_proxy_is_word(New, parms->pool); +#if DEBUGGING + fprintf(stderr, "Parsed word %s\n", New->name); +#endif + } + } + return NULL; +} + +static const char * + set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (arg[0] != '.') + return "ProxyDomain: domain name must start with a dot."; + + psf->domain = arg; + return NULL; +} + +static const char * + set_proxy_req(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->req = flag; + psf->req_set = 1; + return NULL; +} + +static const char * + set_proxy_error_override(cmd_parms *parms, void *dconf, const char *arg) +{ + proxy_dir_conf *conf = dconf; + + if (strcasecmp(arg, "Off") == 0) { + conf->error_override = 0; + conf->error_override_set = 1; + } + else if (strcasecmp(arg, "On") == 0) { + conf->error_override = 1; + conf->error_override_set = 1; + } + else if (conf->error_override_set == 1) { + int *newcode; + int argcode, i; + if (!apr_isdigit(arg[0])) + return "ProxyErrorOverride: status codes to intercept must be numeric"; + if (!conf->error_override) + return "ProxyErrorOverride: status codes must follow a value of 'on'"; + + argcode = strtol(arg, NULL, 10); + if (!ap_is_HTTP_ERROR(argcode)) + return "ProxyErrorOverride: status codes to intercept must be valid HTTP Status Codes >=400 && <600"; + + newcode = apr_array_push(conf->error_override_codes); + *newcode = argcode; + + /* Keep the array sorted for binary search. */ + for (i = conf->error_override_codes->nelts - 1; i > 0; --i) { + int *oldcode = &((int *)conf->error_override_codes->elts)[i - 1]; + if (*oldcode <= argcode) { + break; + } + *newcode = *oldcode; + *oldcode = argcode; + newcode = oldcode; + } + } + else + return "ProxyErrorOverride first parameter must be one of: off | on"; + + return NULL; +} + +static const char * + add_proxy_http_headers(cmd_parms *parms, void *dconf, int flag) +{ + proxy_dir_conf *conf = dconf; + conf->add_forwarded_headers = flag; + conf->add_forwarded_headers_set = 1; + return NULL; +} +static const char * + set_preserve_host(cmd_parms *parms, void *dconf, int flag) +{ + proxy_dir_conf *conf = dconf; + + conf->preserve_host = flag; + conf->preserve_host_set = 1; + return NULL; +} +static const char * + forward_100_continue(cmd_parms *parms, void *dconf, int flag) +{ + proxy_dir_conf *conf = dconf; + conf->forward_100_continue = flag; + conf->forward_100_continue_set = 1; + return NULL; +} + +static const char * + set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int s = atoi(arg); + if (s < 512 && s != 0) { + return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default."; + } + + psf->recv_buffer_size = s; + psf->recv_buffer_size_set = 1; + return NULL; +} + +static const char * + set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + long s = atol(arg); + if (s < 512 && s) { + return "ProxyIOBufferSize must be >= 512 bytes, or 0 for system default."; + } + psf->io_buffer_size = (s ? s : AP_IOBUFSIZE); + psf->io_buffer_size_set = 1; + return NULL; +} + +static const char * + set_max_forwards(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + long s = atol(arg); + + psf->maxfwd = s; + psf->maxfwd_set = 1; + return NULL; +} +static const char* + set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int timeout; + + timeout = atoi(arg); + if (timeout<1) { + return "Proxy Timeout must be at least 1 second."; + } + psf->timeout_set = 1; + psf->timeout = apr_time_from_sec(timeout); + + return NULL; +} + +static const char* + set_via_opt(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (strcasecmp(arg, "Off") == 0) + psf->viaopt = via_off; + else if (strcasecmp(arg, "On") == 0) + psf->viaopt = via_on; + else if (strcasecmp(arg, "Block") == 0) + psf->viaopt = via_block; + else if (strcasecmp(arg, "Full") == 0) + psf->viaopt = via_full; + else { + return "ProxyVia must be one of: " + "off | on | full | block"; + } + + psf->viaopt_set = 1; + return NULL; +} + +static const char* + set_bad_opt(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (strcasecmp(arg, "IsError") == 0) + psf->badopt = bad_error; + else if (strcasecmp(arg, "Ignore") == 0) + psf->badopt = bad_ignore; + else if (strcasecmp(arg, "StartBody") == 0) + psf->badopt = bad_body; + else { + return "ProxyBadHeader must be one of: " + "IsError | Ignore | StartBody"; + } + + psf->badopt_set = 1; + return NULL; +} + +static const char* + set_status_opt(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (strcasecmp(arg, "Off") == 0) + psf->proxy_status = status_off; + else if (strcasecmp(arg, "On") == 0) + psf->proxy_status = status_on; + else if (strcasecmp(arg, "Full") == 0) + psf->proxy_status = status_full; + else { + return "ProxyStatus must be one of: " + "off | on | full"; + } + + psf->proxy_status_set = 1; + return NULL; +} + +static const char *set_bgrowth(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + int growth = atoi(arg); + if (growth < 0 || growth > 1000) { + return "BalancerGrowth must be between 0 and 1000"; + } + psf->bgrowth = growth; + psf->bgrowth_set = 1; + + return NULL; +} + +static const char *set_persist(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->bal_persist = flag; + return NULL; +} + +static const char *set_inherit(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->inherit = flag; + psf->inherit_set = 1; + return NULL; +} + +static const char *set_ppinherit(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->ppinherit = flag; + psf->ppinherit_set = 1; + return NULL; +} + +static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + proxy_balancer *balancer; + proxy_worker *worker; + char *path = cmd->path; + char *name = NULL; + const char *real; + char *word; + apr_table_t *params = apr_table_make(cmd->pool, 5); + const apr_array_header_t *arr; + const apr_table_entry_t *elts; + int reuse = 0; + int i; + /* XXX: Should this be NOT_IN_DIRECTORY|NOT_IN_FILES? */ + const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS); + if (err) + return err; + + if (cmd->path) + path = apr_pstrdup(cmd->pool, cmd->path); + + while (*arg) { + char *val; + word = ap_getword_conf(cmd->pool, &arg); + val = strchr(word, '='); + + if (!val) { + if (!path) + path = word; + else if (!name) + name = word; + else { + if (cmd->path) + return "BalancerMember can not have a balancer name when defined in a location"; + else + return "Invalid BalancerMember parameter. Parameter must " + "be in the form 'key=value'"; + } + } else { + *val++ = '\0'; + apr_table_setn(params, word, val); + } + } + if (!path) + return "BalancerMember must define balancer name when outside <Proxy > section"; + if (!name) + return "BalancerMember must define remote proxy server"; + if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) { + return "BalancerMember uses an invalid \"unix:\" URL"; + } + + ap_str_tolower(path); /* lowercase scheme://hostname */ + + /* Try to find the balancer */ + balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, path, 0); + if (!balancer) { + err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, path, "/", 0); + if (err) + return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); + } + + /* Try to find existing worker */ + worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, real); + if (!worker) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01147) + "Defining worker '%s' for balancer '%s'", + name, balancer->s->name); + if ((err = ap_proxy_define_worker(cmd->pool, &worker, balancer, conf, name, 0)) != NULL) + return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01148) + "Defined worker '%s' for balancer '%s'", + ap_proxy_worker_name(cmd->pool, worker), balancer->s->name); + PROXY_COPY_CONF_PARAMS(worker, conf); + } else { + reuse = 1; + ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, APLOGNO(01149) + "Sharing worker '%s' instead of creating new worker '%s'", + ap_proxy_worker_name(cmd->pool, worker), name); + } + if (!worker->section_config) { + worker->section_config = balancer->section_config; + } + + arr = apr_table_elts(params); + elts = (const apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + if (reuse) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(01150) + "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing", + elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker)); + } else { + 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); + } + } + + return NULL; +} + +static const char * + set_proxy_param(cmd_parms *cmd, void *dummy, const char *arg) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + char *name = NULL; + char *word, *val; + proxy_balancer *balancer = NULL; + proxy_worker *worker = NULL; + unsigned int worker_type = 0; + int in_proxy_section = 0; + /* XXX: Should this be NOT_IN_DIRECTORY|NOT_IN_FILES? */ + const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS); + if (err) + return err; + + if (cmd->directive->parent && + strncasecmp(cmd->directive->parent->directive, + "<Proxy", 6) == 0) { + const char *pargs = cmd->directive->parent->args; + /* Directive inside <Proxy section + * Parent directive arg is the worker/balancer name. + */ + name = ap_getword_conf(cmd->temp_pool, &pargs); + if ((word = ap_strchr(name, '>'))) + *word = '\0'; + if (strncasecmp(cmd->directive->parent->directive + 6, + "Match", 5) == 0) { + worker_type = AP_PROXY_WORKER_IS_MATCH; + } + else { + worker_type = AP_PROXY_WORKER_IS_PREFIX; + } + in_proxy_section = 1; + } + else { + /* Standard set directive with worker/balancer + * name as first param. + */ + name = ap_getword_conf(cmd->temp_pool, &arg); + } + + if (ap_proxy_valid_balancer_name(name, 9)) { + balancer = ap_proxy_get_balancer(cmd->pool, conf, name, 0); + if (!balancer) { + if (in_proxy_section) { + err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, name, "/", 0); + if (err) + return apr_pstrcat(cmd->temp_pool, "ProxySet ", + err, NULL); + } + else + return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '", + name, "' Balancer.", NULL); + } + } + else { + const char *real; + + if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, name))) { + return "ProxySet uses an invalid \"unix:\" URL"; + } + + worker = ap_proxy_get_worker_ex(cmd->temp_pool, NULL, conf, + real, worker_type); + if (!worker) { + if (in_proxy_section) { + err = ap_proxy_define_worker_ex(cmd->pool, &worker, NULL, + conf, name, worker_type); + if (err) + return apr_pstrcat(cmd->temp_pool, "ProxySet ", + err, NULL); + } + else + return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '", + name, "' Worker.", NULL); + } + } + + while (*arg) { + word = ap_getword_conf(cmd->pool, &arg); + val = strchr(word, '='); + if (!val) { + return "Invalid ProxySet parameter. Parameter must be " + "in the form 'key=value'"; + } + else + *val++ = '\0'; + if (worker) + err = set_worker_param(cmd->pool, cmd->server, worker, word, val); + else + err = set_balancer_param(conf, cmd->pool, balancer, word, val); + + if (err) + return apr_pstrcat(cmd->temp_pool, "ProxySet: ", err, " ", word, "=", val, "; ", name, NULL); + } + + return NULL; +} + +static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config) +{ + proxy_server_conf *sconf = ap_get_module_config(s->module_config, + &proxy_module); + void **new_space = (void **)apr_array_push(sconf->sec_proxy); + + *new_space = dir_config; +} + +static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) +{ + const char *errmsg; + const char *endp = ap_strrchr_c(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + proxy_dir_conf *conf; + ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool); + ap_regex_t *r = NULL; + const command_rec *thiscmd = cmd->cmd; + char *word, *val; + proxy_balancer *balancer = NULL; + proxy_worker *worker = NULL; + unsigned int worker_type = AP_PROXY_WORKER_IS_PREFIX; + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_CONTEXT); + proxy_server_conf *sconf = + (proxy_server_conf *) ap_get_module_config(cmd->server->module_config, &proxy_module); + + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + "> directive missing closing '>'", NULL); + } + + arg = apr_pstrndup(cmd->pool, arg, endp-arg); + + if (!arg) { + if (thiscmd->cmd_data) + return "<ProxyMatch > block must specify a path"; + else + return "<Proxy > block must specify a path"; + } + + cmd->path = ap_getword_conf(cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF|PROXY_CONF; + + if (!strncasecmp(cmd->path, "proxy:", 6)) + cmd->path += 6; + + /* XXX Ignore case? What if we proxy a case-insensitive server?!? + * While we are at it, shouldn't we also canonicalize the entire + * scheme? See proxy_fixup() + */ + if (thiscmd->cmd_data) { /* <ProxyMatch> */ + r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED); + if (!r) { + return "Regex could not be compiled"; + } + worker_type = AP_PROXY_WORKER_IS_MATCH; + } + + /* initialize our config and fetch it */ + conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path, + &proxy_module, cmd->pool); + + errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf); + if (errmsg != NULL) + return errmsg; + + conf->r = r; + conf->p = cmd->path; + conf->p_is_fnmatch = apr_fnmatch_test(conf->p); + + if (r) { + conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *)); + ap_regname(r, conf->refs, AP_REG_MATCH, 1); + } + + ap_add_per_proxy_conf(cmd->server, new_dir_conf); + + if (*arg != '\0') { + if (thiscmd->cmd_data) + return "Multiple <ProxyMatch> arguments not (yet) supported."; + if (conf->p_is_fnmatch) + return apr_pstrcat(cmd->pool, thiscmd->name, + "> arguments are not supported for wildchar url.", + NULL); + if (!ap_strchr_c(conf->p, ':')) + return apr_pstrcat(cmd->pool, thiscmd->name, + "> arguments are not supported for non url.", + NULL); + if (ap_proxy_valid_balancer_name((char *)conf->p, 9)) { + balancer = ap_proxy_get_balancer(cmd->pool, sconf, conf->p, 0); + if (!balancer) { + err = ap_proxy_define_balancer(cmd->pool, &balancer, + sconf, conf->p, "/", 0); + if (err) + return apr_pstrcat(cmd->temp_pool, thiscmd->name, + " ", err, NULL); + } + if (!balancer->section_config) { + balancer->section_config = new_dir_conf; + } + } + else { + const char *real; + + if (!(real = ap_proxy_de_socketfy(cmd->temp_pool, conf->p))) { + return "<Proxy/ProxyMatch > uses an invalid \"unix:\" URL"; + } + + worker = ap_proxy_get_worker_ex(cmd->temp_pool, NULL, sconf, + real, worker_type); + if (!worker) { + err = ap_proxy_define_worker_ex(cmd->pool, &worker, NULL, sconf, + conf->p, worker_type); + if (err) + return apr_pstrcat(cmd->temp_pool, thiscmd->name, + " ", err, NULL); + } + if (!worker->section_config) { + worker->section_config = new_dir_conf; + } + } + if (worker == NULL && balancer == NULL) { + return apr_pstrcat(cmd->pool, thiscmd->name, + "> arguments are supported only for workers.", + NULL); + } + while (*arg) { + word = ap_getword_conf(cmd->pool, &arg); + val = strchr(word, '='); + if (!val) { + return "Invalid Proxy parameter. Parameter must be " + "in the form 'key=value'"; + } + else + *val++ = '\0'; + if (worker) + err = set_worker_param(cmd->pool, cmd->server, worker, word, val); + else + err = set_balancer_param(sconf, cmd->pool, balancer, + word, val); + if (err) + return apr_pstrcat(cmd->temp_pool, thiscmd->name, " ", err, " ", + word, "=", val, "; ", conf->p, NULL); + } + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static const command_rec proxy_cmds[] = +{ + AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF, + "Container for directives affecting resources located in the proxied " + "location"), + AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF, + "Container for directives affecting resources located in the proxied " + "location, in regular expression syntax"), + AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF, + "on if the true proxy requests should be accepted"), + AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF, + "a scheme, partial URL or '*' and a proxy server"), + AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF, + "a regex pattern and a proxy server"), + AP_INIT_FLAG("ProxyPassInterpolateEnv", ap_set_flag_slot_char, + (void*)APR_OFFSETOF(proxy_dir_conf, interpolate_env), + RSRC_CONF|ACCESS_CONF, "Interpolate Env Vars in reverse Proxy") , + AP_INIT_RAW_ARGS("ProxyPass", add_pass_noregex, NULL, RSRC_CONF|ACCESS_CONF, + "a virtual path and a URL"), + AP_INIT_RAW_ARGS("ProxyPassMatch", add_pass_regex, NULL, RSRC_CONF|ACCESS_CONF, + "a virtual path and a URL"), + AP_INIT_TAKE123("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF, + "a virtual path and a URL for reverse proxy behaviour"), + AP_INIT_TAKE23("ProxyPassReverseCookiePath", cookie_path, NULL, + RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"), + AP_INIT_TAKE23("ProxyPassReverseCookieDomain", cookie_domain, NULL, + RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"), + AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, + "A list of names, hosts or domains to which the proxy will not connect"), + AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, + "Receive buffer size for outgoing HTTP and FTP connections in bytes"), + AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF, + "IO buffer size for outgoing HTTP and FTP connections in bytes"), + AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF, + "The maximum number of proxies a request may be forwarded through."), + AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, + "A list of domains, hosts, or subnets to which the proxy will connect directly"), + AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, + "The default intranet domain name (in absence of a domain in the URL)"), + AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF, + "Configure Via: proxy header header to one of: on | off | block | full"), + AP_INIT_ITERATE("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF|ACCESS_CONF, + "use our error handling pages instead of the servers' we are proxying"), + AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF|ACCESS_CONF, + "on if we should preserve host header while proxying"), + AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF, + "Set the timeout (in seconds) for a proxied connection. " + "This overrides the server timeout"), + AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF, + "How to handle bad header line in response: IsError | Ignore | StartBody"), + AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF, + "A balancer name and scheme with list of params"), + AP_INIT_TAKE1("BalancerGrowth", set_bgrowth, NULL, RSRC_CONF, + "Number of additional Balancers that can be added post-config"), + AP_INIT_FLAG("BalancerPersist", set_persist, NULL, RSRC_CONF, + "on if the balancer should persist changes on reboot/restart made via the Balancer Manager"), + AP_INIT_FLAG("BalancerInherit", set_inherit, NULL, RSRC_CONF, + "on if this server should inherit Balancers and Workers defined in the main server " + "(Setting to off recommended if using the Balancer Manager)"), + AP_INIT_FLAG("ProxyPassInherit", set_ppinherit, NULL, RSRC_CONF, + "on if this server should inherit all ProxyPass directives defined in the main server " + "(Setting to off recommended if using the Balancer Manager)"), + AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF, + "Configure Status: proxy status to one of: on | off | full"), + AP_INIT_RAW_ARGS("ProxySet", set_proxy_param, NULL, RSRC_CONF|ACCESS_CONF, + "A balancer or worker name with list of params"), + AP_INIT_TAKE1("ProxySourceAddress", set_source_address, NULL, RSRC_CONF, + "Configure local source IP used for request forward"), + AP_INIT_FLAG("ProxyAddHeaders", add_proxy_http_headers, NULL, RSRC_CONF|ACCESS_CONF, + "on if X-Forwarded-* headers should be added or completed"), + AP_INIT_FLAG("Proxy100Continue", forward_100_continue, NULL, RSRC_CONF|ACCESS_CONF, + "on if 100-Continue should be forwarded to the origin server, off if the " + "proxy should handle it by itself"), + {NULL} +}; + +static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL; +static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL; +static APR_OPTIONAL_FN_TYPE(ssl_engine_set) *proxy_ssl_engine = NULL; + +PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c) +{ + /* + * if c == NULL just check if the optional function was imported + * else run the optional function so ssl filters are inserted + */ + if (c == NULL) { + return ap_ssl_has_outgoing_handlers(); + } + return ap_ssl_bind_outgoing(c, NULL, 1) == OK; +} + +PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c) +{ + return ap_ssl_bind_outgoing(c, NULL, 0) == OK; +} + +PROXY_DECLARE(int) ap_proxy_ssl_engine(conn_rec *c, + ap_conf_vector_t *per_dir_config, + int enable) +{ + /* + * if c == NULL just check if the optional function was imported + * else run the optional function so ssl filters are inserted + */ + if (c == NULL) { + return ap_ssl_has_outgoing_handlers(); + } + return ap_ssl_bind_outgoing(c, per_dir_config, enable) == OK; +} + +PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c) +{ + return ap_ssl_conn_is_ssl(c); +} + +PROXY_DECLARE(const char *) ap_proxy_ssl_val(apr_pool_t *p, server_rec *s, + conn_rec *c, request_rec *r, + const char *var) +{ + return ap_ssl_var_lookup(p, s, c, r, var); +} + +static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *main_s) +{ + server_rec *s = main_s; + apr_status_t rv = ap_global_mutex_create(&proxy_mutex, NULL, + proxy_id, NULL, s, pconf, 0); + if (rv != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02478) + "failed to create %s mutex", proxy_id); + return rv; + } + + proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable); + proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable); + proxy_ssl_engine = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_set); + ap_proxy_strmatch_path = apr_strmatch_precompile(pconf, "path=", 0); + ap_proxy_strmatch_domain = apr_strmatch_precompile(pconf, "domain=", 0); + + for (; s; s = s->next) { + int rc, i; + proxy_server_conf *sconf = + ap_get_module_config(s->module_config, &proxy_module); + ap_conf_vector_t **sections = + (ap_conf_vector_t **)sconf->sec_proxy->elts; + + for (i = 0; i < sconf->sec_proxy->nelts; ++i) { + rc = proxy_run_section_post_config(pconf, ptemp, plog, + s, sections[i]); + if (rc != OK && rc != DECLINED) { + return rc; + } + } + } + + return OK; +} + +/* + * proxy Extension to mod_status + */ +static int proxy_status_hook(request_rec *r, int flags) +{ + int i, n; + void *sconf = r->server->module_config; + proxy_server_conf *conf = (proxy_server_conf *) + ap_get_module_config(sconf, &proxy_module); + proxy_balancer *balancer = NULL; + proxy_worker **worker = NULL; + + if (conf->balancers->nelts == 0 || + conf->proxy_status == status_off) + return OK; + + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++) { + if (!(flags & AP_STATUS_SHORT)) { + ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r); + ap_rvputs(r, balancer->s->name, "</h1>\n\n", NULL); + ap_rputs("\n\n<table border=\"0\"><tr>" + "<th>SSes</th><th>Timeout</th><th>Method</th>" + "</tr>\n<tr>", r); + if (*balancer->s->sticky) { + if (strcmp(balancer->s->sticky, balancer->s->sticky_path)) { + ap_rvputs(r, "<td>", balancer->s->sticky, " | ", + balancer->s->sticky_path, NULL); + } + else { + ap_rvputs(r, "<td>", balancer->s->sticky, NULL); + } + } + else { + ap_rputs("<td> - ", r); + } + ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>", + apr_time_sec(balancer->s->timeout)); + ap_rprintf(r, "<td>%s</td>\n", + balancer->lbmethod->name); + ap_rputs("</table>\n", r); + ap_rputs("\n\n<table border=\"0\"><tr>" + "<th>Sch</th><th>Host</th><th>Stat</th>" + "<th>Route</th><th>Redir</th>" + "<th>F</th><th>Set</th><th>Acc</th><th>Busy</th><th>Wr</th><th>Rd</th>" + "</tr>\n", r); + } + else { + ap_rprintf(r, "ProxyBalancer[%d]Name: %s\n", i, balancer->s->name); + } + + worker = (proxy_worker **)balancer->workers->elts; + for (n = 0; n < balancer->workers->nelts; n++) { + char fbuf[50]; + if (!(flags & AP_STATUS_SHORT)) { + ap_rvputs(r, "<tr>\n<td>", (*worker)->s->scheme, "</td>", NULL); + ap_rvputs(r, "<td>", (*worker)->s->hostname_ex, "</td><td>", NULL); + ap_rvputs(r, ap_proxy_parse_wstatus(r->pool, *worker), NULL); + ap_rvputs(r, "</td><td>", (*worker)->s->route, NULL); + ap_rvputs(r, "</td><td>", (*worker)->s->redirect, NULL); + ap_rprintf(r, "</td><td>%.2f</td>", (float)((*worker)->s->lbfactor)/100.0); + ap_rprintf(r, "<td>%d</td>", (*worker)->s->lbset); + ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", + (*worker)->s->elected); + ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td><td>", + (*worker)->s->busy); + ap_rputs(apr_strfsize((*worker)->s->transferred, fbuf), r); + ap_rputs("</td><td>", r); + ap_rputs(apr_strfsize((*worker)->s->read, fbuf), r); + ap_rputs("</td>\n", r); + + /* TODO: Add the rest of dynamic worker data */ + ap_rputs("</tr>\n", r); + } + else { + ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Name: %s\n", + i, n, (*worker)->s->name_ex); + ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Status: %s\n", + i, n, ap_proxy_parse_wstatus(r->pool, *worker)); + ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Elected: %" + APR_SIZE_T_FMT "\n", + i, n, (*worker)->s->elected); + ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Busy: %" + APR_SIZE_T_FMT "\n", + i, n, (*worker)->s->busy); + ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Sent: %" + APR_OFF_T_FMT "K\n", + i, n, (*worker)->s->transferred >> 10); + ap_rprintf(r, "ProxyBalancer[%d]Worker[%d]Rcvd: %" + APR_OFF_T_FMT "K\n", + i, n, (*worker)->s->read >> 10); + + /* TODO: Add the rest of dynamic worker data */ + } + + ++worker; + } + if (!(flags & AP_STATUS_SHORT)) { + ap_rputs("</table>\n", r); + } + ++balancer; + } + if (!(flags & AP_STATUS_SHORT)) { + ap_rputs("<hr /><table>\n" + "<tr><th>SSes</th><td>Sticky session name</td></tr>\n" + "<tr><th>Timeout</th><td>Balancer Timeout</td></tr>\n" + "<tr><th>Sch</th><td>Connection scheme</td></tr>\n" + "<tr><th>Host</th><td>Backend Hostname</td></tr>\n" + "<tr><th>Stat</th><td>Worker status</td></tr>\n" + "<tr><th>Route</th><td>Session Route</td></tr>\n" + "<tr><th>Redir</th><td>Session Route Redirection</td></tr>\n" + "<tr><th>F</th><td>Load Balancer Factor</td></tr>\n" + "<tr><th>Acc</th><td>Number of uses</td></tr>\n" + "<tr><th>Wr</th><td>Number of bytes transferred</td></tr>\n" + "<tr><th>Rd</th><td>Number of bytes read</td></tr>\n" + "</table>", r); + } + + return OK; +} + +static void child_init(apr_pool_t *p, server_rec *s) +{ + proxy_worker *reverse = NULL; + + apr_status_t rv = apr_global_mutex_child_init(&proxy_mutex, + apr_global_mutex_lockfile(proxy_mutex), + p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(02479) + "could not init proxy_mutex in child"); + exit(1); /* Ugly, but what else? */ + } + + /* TODO */ + while (s) { + void *sconf = s->module_config; + proxy_server_conf *conf; + proxy_worker *worker; + int i; + + conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module); + /* + * NOTE: non-balancer members don't use shm at all... + * after all, why should they? + */ + worker = (proxy_worker *)conf->workers->elts; + for (i = 0; i < conf->workers->nelts; i++, worker++) { + ap_proxy_initialize_worker(worker, s, p); + } + /* Create and initialize forward worker if defined */ + if (conf->req_set && conf->req) { + proxy_worker *forward; + ap_proxy_define_worker(conf->pool, &forward, NULL, NULL, + "http://www.apache.org", 0); + conf->forward = forward; + PROXY_STRNCPY(conf->forward->s->name, "proxy:forward"); + PROXY_STRNCPY(conf->forward->s->name_ex, "proxy:forward"); + PROXY_STRNCPY(conf->forward->s->hostname, "*"); /* for compatibility */ + PROXY_STRNCPY(conf->forward->s->hostname_ex, "*"); + PROXY_STRNCPY(conf->forward->s->scheme, "*"); + conf->forward->hash.def = conf->forward->s->hash.def = + ap_proxy_hashfunc(conf->forward->s->name_ex, PROXY_HASHFUNC_DEFAULT); + conf->forward->hash.fnv = conf->forward->s->hash.fnv = + ap_proxy_hashfunc(conf->forward->s->name_ex, PROXY_HASHFUNC_FNV); + /* Do not disable worker in case of errors */ + conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS; + /* Mark as the "generic" worker */ + conf->forward->s->status |= PROXY_WORKER_GENERIC; + ap_proxy_initialize_worker(conf->forward, s, p); + /* Disable address cache for generic forward worker */ + conf->forward->s->is_address_reusable = 0; + } + if (!reverse) { + ap_proxy_define_worker(conf->pool, &reverse, NULL, NULL, + "http://www.apache.org", 0); + PROXY_STRNCPY(reverse->s->name, "proxy:reverse"); + PROXY_STRNCPY(reverse->s->name_ex, "proxy:reverse"); + PROXY_STRNCPY(reverse->s->hostname, "*"); /* for compatibility */ + PROXY_STRNCPY(reverse->s->hostname_ex, "*"); + PROXY_STRNCPY(reverse->s->scheme, "*"); + reverse->hash.def = reverse->s->hash.def = + ap_proxy_hashfunc(reverse->s->name_ex, PROXY_HASHFUNC_DEFAULT); + reverse->hash.fnv = reverse->s->hash.fnv = + ap_proxy_hashfunc(reverse->s->name_ex, PROXY_HASHFUNC_FNV); + /* Do not disable worker in case of errors */ + reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS; + /* Mark as the "generic" worker */ + reverse->s->status |= PROXY_WORKER_GENERIC; + conf->reverse = reverse; + ap_proxy_initialize_worker(conf->reverse, s, p); + /* Disable address cache for generic reverse worker */ + reverse->s->is_address_reusable = 0; + } + conf->reverse = reverse; + s = s->next; + } +} + +/* + * This routine is called before the server processes the configuration + * files. + */ +static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp) +{ + apr_status_t rv = ap_mutex_register(pconf, proxy_id, NULL, + APR_LOCK_DEFAULT, 0); + if (rv != APR_SUCCESS) { + ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02480) + "failed to register %s mutex", proxy_id); + return 500; /* An HTTP status would be a misnomer! */ + } + + APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL, + APR_HOOK_MIDDLE); + /* Reset workers count on graceful 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) +{ + /* fixup before mod_rewrite, so that the proxied url will not + * escaped accidentally by our fixup. + */ + static const char * const aszSucc[] = { "mod_rewrite.c", NULL}; + /* Only the mpm_winnt has child init hook handler. + * make sure that we are called after the mpm + * initializes. + */ + 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 */ + ap_hook_pre_translate_name(proxy_pre_translate_name, NULL, NULL, + APR_HOOK_MIDDLE); + ap_hook_translate_name(proxy_translate_name, aszSucc, NULL, + APR_HOOK_FIRST); + /* walk <Proxy > entries and suppress default TRACE behavior */ + ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST); + /* fixups */ + ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST); + /* post read_request handling */ + ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST); + /* pre config handling */ + ap_hook_pre_config(proxy_pre_config, NULL, NULL, APR_HOOK_MIDDLE); + /* post config handling */ + ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE); + /* child init handling */ + ap_hook_child_init(child_init, aszPred, NULL, APR_HOOK_MIDDLE); + + /* register optional functions within proxy_util.c */ + proxy_util_register_hooks(p); +} + +AP_DECLARE_MODULE(proxy) = +{ + STANDARD20_MODULE_STUFF, + create_proxy_dir_config, /* create per-directory config structure */ + merge_proxy_dir_config, /* merge per-directory config structures */ + create_proxy_config, /* create per-server config structure */ + merge_proxy_config, /* merge per-server config structures */ + proxy_cmds, /* command table */ + register_hooks +}; + +APR_HOOK_STRUCT( + APR_HOOK_LINK(scheme_handler) + APR_HOOK_LINK(canon_handler) + APR_HOOK_LINK(pre_request) + APR_HOOK_LINK(post_request) + APR_HOOK_LINK(request_status) + APR_HOOK_LINK(check_trans) +) + +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, + (request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyhost, + apr_port_t proxyport),(r,worker,conf, + url,proxyhost,proxyport),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, check_trans, + (request_rec *r, const char *url), + (r, url), DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler, + (request_rec *r, char *url),(r, + url),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, pre_request, ( + proxy_worker **worker, + proxy_balancer **balancer, + request_rec *r, + proxy_server_conf *conf, + char **url),(worker,balancer, + r,conf,url),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, post_request, + (proxy_worker *worker, + proxy_balancer *balancer, + request_rec *r, + proxy_server_conf *conf),(worker, + balancer,r,conf),DECLINED) +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, section_post_config, + (apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s, + ap_conf_vector_t *section_config), + (p, ptemp, plog, s, section_config), + OK, DECLINED) +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups, + (request_rec *r), (r), + OK, DECLINED) +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, request_status, + (int *status, request_rec *r), + (status, r), + OK, DECLINED) diff --git a/modules/proxy/mod_proxy.dep b/modules/proxy/mod_proxy.dep new file mode 100644 index 0000000..43d8070 --- /dev/null +++ b/modules/proxy/mod_proxy.dep @@ -0,0 +1,153 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy.mak + +.\mod_proxy.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\mod_core.h"\ + "..\..\include\os.h"\ + "..\..\include\scoreboard.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + "..\generators\mod_status.h"\ + "..\ssl\mod_ssl.h"\ + ".\mod_proxy.h"\ + ".\proxy_util.h"\ + + +.\proxy_util.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_mpm.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\scoreboard.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_support.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\ajp.h"\ + ".\mod_proxy.h"\ + ".\proxy_util.h"\ + ".\scgi.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy.dsp b/modules/proxy/mod_proxy.dsp new file mode 100644 index 0000000..30e2a85 --- /dev/null +++ b/modules/proxy/mod_proxy.dsp @@ -0,0 +1,127 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy.mak" CFG="mod_proxy - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../ssl" /I "../http2" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../generators" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fd"Release\mod_proxy_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /debug /out:".\Release\mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../ssl" /I "../http2" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../generators" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fd"Debug\mod_proxy_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy - Win32 Release" +# Name "mod_proxy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy.c +# End Source File +# Begin Source File + +SOURCE=.\proxy_util.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h new file mode 100644 index 0000000..c51145e --- /dev/null +++ b/modules/proxy/mod_proxy.h @@ -0,0 +1,1516 @@ +/* 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. + */ + +#ifndef MOD_PROXY_H +#define MOD_PROXY_H + +/** + * @file mod_proxy.h + * @brief Proxy Extension Module for Apache + * + * @defgroup MOD_PROXY mod_proxy + * @ingroup APACHE_MODS + * @{ + */ + +#include "apr_hooks.h" +#include "apr_optional.h" +#include "apr.h" +#include "apr_lib.h" +#include "apr_strings.h" +#include "apr_buckets.h" +#include "apr_md5.h" +#include "apr_network_io.h" +#include "apr_pools.h" +#include "apr_strings.h" +#include "apr_uri.h" +#include "apr_date.h" +#include "apr_strmatch.h" +#include "apr_fnmatch.h" +#include "apr_reslist.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" +#include "apr_uuid.h" +#include "util_mutex.h" +#include "apr_global_mutex.h" +#include "apr_thread_mutex.h" + +#include "httpd.h" +#include "http_config.h" +#include "ap_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_vhost.h" +#include "http_main.h" +#include "http_log.h" +#include "http_connection.h" +#include "http_ssl.h" +#include "util_filter.h" +#include "util_ebcdic.h" +#include "ap_provider.h" +#include "ap_slotmem.h" + +#if APR_HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#if APR_HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +/* for proxy_canonenc() */ +enum enctype { + enc_path, enc_search, enc_user, enc_fpath, enc_parm +}; + +/* Flags for ap_proxy_canonenc_ex */ +#define PROXY_CANONENC_FORCEDEC 0x01 +#define PROXY_CANONENC_NOENCODEDSLASHENCODING 0x02 + +typedef enum { + NONE, TCP, OPTIONS, HEAD, GET, CPING, PROVIDER, OPTIONS11, HEAD11, GET11, 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 +#define CRLF "\r\n" +#else /*APR_CHARSET_EBCDIC*/ +#define CRLF "\015\012" +#endif /*APR_CHARSET_EBCDIC*/ + +/* default Max-Forwards header setting */ +/* Set this to -1, which complies with RFC2616 by not setting + * max-forwards if the client didn't send it to us. + */ +#define DEFAULT_MAX_FORWARDS -1 + +typedef struct proxy_balancer proxy_balancer; +typedef struct proxy_worker proxy_worker; +typedef struct proxy_conn_pool proxy_conn_pool; +typedef struct proxy_balancer_method proxy_balancer_method; + +/* static information about a remote proxy */ +struct proxy_remote { + const char *scheme; /* the schemes handled by this proxy, or '*' */ + const char *protocol; /* the scheme used to talk to this proxy */ + const char *hostname; /* the hostname of this proxy */ + ap_regex_t *regexp; /* compiled regex (if any) for the remote */ + int use_regex; /* simple boolean. True if we have a regex pattern */ + apr_port_t port; /* the port for this proxy */ +}; + +#define PROXYPASS_NOCANON 0x01 +#define PROXYPASS_INTERPOLATE 0x02 +#define PROXYPASS_NOQUERY 0x04 +#define PROXYPASS_MAP_ENCODED 0x08 +#define PROXYPASS_MAP_SERVLET 0x18 /* + MAP_ENCODED */ +struct proxy_alias { + const char *real; + const char *fake; + ap_regex_t *regex; + unsigned int flags; + proxy_balancer *balancer; /* only valid for reverse-proxys */ +}; + +struct dirconn_entry { + char *name; + struct in_addr addr, mask; + struct apr_sockaddr_t *hostaddr; + int (*matcher) (struct dirconn_entry * This, request_rec *r); +}; + +struct noproxy_entry { + const char *name; + struct apr_sockaddr_t *addr; +}; + +typedef struct { + apr_array_header_t *proxies; + apr_array_header_t *sec_proxy; + apr_array_header_t *aliases; + apr_array_header_t *noproxies; + apr_array_header_t *dirconn; + apr_array_header_t *workers; /* non-balancer workers, eg ProxyPass http://example.com */ + apr_array_header_t *balancers; /* list of balancers @ config time */ + proxy_worker *forward; /* forward proxy worker */ + 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'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 */ + enum { + via_off, + via_on, + via_block, + via_full + } viaopt; /* how to deal with proxy Via: headers */ + apr_size_t recv_buffer_size; + apr_size_t io_buffer_size; + long maxfwd; + apr_interval_time_t timeout; + enum { + bad_error, + bad_ignore, + bad_body + } badopt; /* how to deal with bad headers */ + enum { + status_off, + status_on, + status_full + } proxy_status; /* Status display options */ + apr_sockaddr_t *source_address; + apr_global_mutex_t *mutex; /* global lock, for pool, etc */ + ap_slotmem_instance_t *bslot; /* balancers shm data - runtime */ + ap_slotmem_provider_t *storage; + + unsigned int req_set:1; + unsigned int viaopt_set:1; + unsigned int recv_buffer_size_set:1; + unsigned int io_buffer_size_set:1; + unsigned int maxfwd_set:1; + unsigned int timeout_set:1; + unsigned int badopt_set:1; + unsigned int proxy_status_set:1; + unsigned int source_address_set:1; + unsigned int bgrowth_set:1; + unsigned int bal_persist:1; + unsigned int inherit:1; + unsigned int inherit_set:1; + unsigned int ppinherit:1; + unsigned int ppinherit_set:1; + unsigned int map_encoded_one:1; + unsigned int map_encoded_all:1; +} proxy_server_conf; + +typedef struct { + const char *p; /* The path */ + ap_regex_t *r; /* Is this a regex? */ + +/* FIXME + * ProxyPassReverse and friends are documented as working inside + * <Location>. But in fact they never have done in the case of + * more than one <Location>, because the server_conf can't see it. + * We need to move them to the per-dir config. + * Discussed in February 2005: + * http://marc.theaimsgroup.com/?l=apache-httpd-dev&m=110726027118798&w=2 + */ + apr_array_header_t *raliases; + apr_array_header_t* cookie_paths; + apr_array_header_t* cookie_domains; + signed char p_is_fnmatch; /* Is the path an fnmatch candidate? */ + signed char interpolate_env; + struct proxy_alias *alias; + + /** + * the following setting masks the error page + * returned from the 'proxied server' and just + * forwards the status code upwards. + * This allows the main server (us) to generate + * the error page, (so it will look like a error + * returned from the rest of the system + */ + unsigned int error_override:1; + unsigned int preserve_host:1; + unsigned int preserve_host_set:1; + unsigned int error_override_set:1; + unsigned int alias_set:1; + unsigned int add_forwarded_headers:1; + unsigned int add_forwarded_headers_set:1; + + /** Named back references */ + apr_array_header_t *refs; + + unsigned int forward_100_continue:1; + unsigned int forward_100_continue_set:1; + + apr_array_header_t *error_override_codes; +} proxy_dir_conf; + +/* if we interpolate env vars per-request, we'll need a per-request + * copy of the reverse proxy config + */ +typedef struct { + apr_array_header_t *raliases; + apr_array_header_t* cookie_paths; + apr_array_header_t* cookie_domains; +} proxy_req_conf; + +typedef struct { + conn_rec *connection; + request_rec *r; /* Request record of the backend request + * that is used over the backend connection. */ + proxy_worker *worker; /* Connection pool this connection belongs to */ + apr_pool_t *pool; /* Subpool for hostname and addr data */ + const char *hostname; + apr_sockaddr_t *addr; /* Preparsed remote address info */ + apr_pool_t *scpool; /* Subpool used for socket and connection data */ + apr_socket_t *sock; /* Connection socket */ + void *data; /* per scheme connection data */ + void *forward; /* opaque forward proxy data */ + apr_uint32_t flags; /* Connection flags */ + apr_port_t port; + unsigned int is_ssl:1; + unsigned int close:1; /* Close 'this' connection */ + unsigned int need_flush:1; /* Flag to decide whether we need to flush the + * filter chain or not */ + unsigned int inreslist:1; /* connection in apr_reslist? */ + const char *uds_path; /* Unix domain socket path */ + const char *ssl_hostname;/* Hostname (SNI) in use by SSL connection */ + apr_bucket_brigade *tmp_bb;/* Temporary brigade created with the connection + * and its scpool/bucket_alloc (NULL before), + * must be left cleaned when used (locally). + */ +} proxy_conn_rec; + +typedef struct { + float cache_completion; /* completion percentage */ + int content_length; /* length of the content */ +} proxy_completion; + +/* Connection pool */ +struct proxy_conn_pool { + apr_pool_t *pool; /* The pool used in constructor and destructor calls */ + apr_sockaddr_t *addr; /* Preparsed remote address info */ + apr_reslist_t *res; /* Connection resource list */ + proxy_conn_rec *conn; /* Single connection for prefork mpm */ + apr_pool_t *dns_pool; /* The pool used for worker scoped DNS resolutions */ +}; + +#define AP_VOLATILIZE_T(T, x) (*(T volatile *)&(x)) + +/* 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 +#define PROXY_WORKER_GENERIC 0x0008 +#define PROXY_WORKER_IN_SHUTDOWN 0x0010 +#define PROXY_WORKER_DISABLED 0x0020 +#define PROXY_WORKER_STOPPED 0x0040 +#define PROXY_WORKER_IN_ERROR 0x0080 +#define PROXY_WORKER_HOT_STANDBY 0x0100 +#define PROXY_WORKER_FREE 0x0200 +#define PROXY_WORKER_HC_FAIL 0x0400 +#define PROXY_WORKER_HOT_SPARE 0x0800 + +/* worker status flags */ +#define PROXY_WORKER_INITIALIZED_FLAG 'O' +#define PROXY_WORKER_IGNORE_ERRORS_FLAG 'I' +#define PROXY_WORKER_DRAIN_FLAG 'N' +#define PROXY_WORKER_GENERIC_FLAG 'G' +#define PROXY_WORKER_IN_SHUTDOWN_FLAG 'U' +#define PROXY_WORKER_DISABLED_FLAG 'D' +#define PROXY_WORKER_STOPPED_FLAG 'S' +#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_HOT_SPARE_FLAG 'R' + +#define PROXY_WORKER_NOT_USABLE_BITMAP ( PROXY_WORKER_IN_SHUTDOWN | \ +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 ) + +#define PROXY_WORKER_IS_STANDBY(f) ( (f)->s->status & PROXY_WORKER_HOT_STANDBY ) + +#define PROXY_WORKER_IS_SPARE(f) ( (f)->s->status & PROXY_WORKER_HOT_SPARE ) + +#define PROXY_WORKER_IS_USABLE(f) ( ( !( (f)->s->status & PROXY_WORKER_NOT_USABLE_BITMAP) ) && \ + PROXY_WORKER_IS_INITIALIZED(f) ) + +#define PROXY_WORKER_IS_DRAINING(f) ( (f)->s->status & PROXY_WORKER_DRAIN ) + +#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_ERROR(f) ( (f)->s->status & PROXY_WORKER_IN_ERROR ) + +#define PROXY_WORKER_IS(f, b) ( (f)->s->status & (b) ) + +/* default worker retry timeout in seconds */ +#define PROXY_WORKER_DEFAULT_RETRY 60 + +/* Some max char string sizes, for shm fields */ +#define PROXY_WORKER_MAX_SCHEME_SIZE 16 +#define PROXY_WORKER_MAX_ROUTE_SIZE 64 +#define PROXY_BALANCER_MAX_ROUTE_SIZE PROXY_WORKER_MAX_ROUTE_SIZE +#define PROXY_WORKER_MAX_NAME_SIZE 96 +#define PROXY_BALANCER_MAX_NAME_SIZE PROXY_WORKER_MAX_NAME_SIZE +#define PROXY_WORKER_MAX_HOSTNAME_SIZE 64 +#define PROXY_BALANCER_MAX_HOSTNAME_SIZE PROXY_WORKER_MAX_HOSTNAME_SIZE +#define PROXY_BALANCER_MAX_STICKY_SIZE 64 +#define PROXY_WORKER_MAX_SECRET_SIZE 64 + +#define PROXY_RFC1035_HOSTNAME_SIZE 256 +#define PROXY_WORKER_EXT_NAME_SIZE 384 + +/* RFC-1035 mentions limits of 255 for host-names and 253 for domain-names, + * dotted together(?) this would fit the below size (+ trailing NUL). + */ +#define PROXY_WORKER_RFC1035_NAME_SIZE 512 + +#define PROXY_MAX_PROVIDER_NAME_SIZE 16 + +#define PROXY_STRNCPY(dst, src) ap_proxy_strncpy((dst), (src), (sizeof(dst))) + +#define PROXY_COPY_CONF_PARAMS(w, c) \ +do { \ +(w)->s->timeout = (c)->timeout; \ +(w)->s->timeout_set = (c)->timeout_set; \ +(w)->s->recv_buffer_size = (c)->recv_buffer_size; \ +(w)->s->recv_buffer_size_set = (c)->recv_buffer_size_set; \ +(w)->s->io_buffer_size = (c)->io_buffer_size; \ +(w)->s->io_buffer_size_set = (c)->io_buffer_size_set; \ +} while (0) + +#define PROXY_SHOULD_PING_100_CONTINUE(w, r) \ + ((w)->s->ping_timeout_set \ + && (PROXYREQ_REVERSE == (r)->proxyreq) \ + && ap_request_has_body((r))) + +#define PROXY_DO_100_CONTINUE(w, r) \ + (PROXY_SHOULD_PING_100_CONTINUE(w, r) \ + && !apr_table_get((r)->subprocess_env, "force-proxy-request-1.0")) + +/* use 2 hashes */ +typedef struct { + unsigned int def; + unsigned int fnv; +} proxy_hashes ; + +/* Runtime worker status information. 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 */ + char hostname[PROXY_WORKER_MAX_HOSTNAME_SIZE]; /* remote backend address (deprecated, use hostname_ex below) */ + char route[PROXY_WORKER_MAX_ROUTE_SIZE]; /* balancing route */ + char redirect[PROXY_WORKER_MAX_ROUTE_SIZE]; /* temporary balancing redirection route */ + char flusher[PROXY_WORKER_MAX_SCHEME_SIZE]; /* flush provider used by mod_proxy_fdpass */ + char uds_path[PROXY_WORKER_MAX_NAME_SIZE]; /* path to worker's unix domain socket if applicable */ + int lbset; /* load balancer cluster set */ + int retries; /* number of retries on this worker */ + int lbstatus; /* Current lbstatus */ + int lbfactor; /* dynamic lbfactor */ + int min; /* Desired minimum number of available connections */ + int smax; /* Soft maximum on the total number of connections */ + int hmax; /* Hard maximum on the total number of connections */ + int flush_wait; /* poll wait time in microseconds if flush_auto */ + int index; /* shm array index */ + proxy_hashes hash; /* hash of worker name */ + unsigned int status; /* worker status bitfield */ + enum { + flush_off, + flush_on, + flush_auto + } flush_packets; /* control AJP flushing */ + apr_time_t updated; /* timestamp of last update for dynamic workers, or queue-time of HC workers */ + apr_time_t error_time; /* time of the last error */ + apr_interval_time_t ttl; /* maximum amount of time in seconds a connection + * may be available while exceeding the soft limit */ + apr_interval_time_t retry; /* retry interval */ + apr_interval_time_t timeout; /* connection timeout */ + apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */ + apr_interval_time_t ping_timeout; + apr_interval_time_t conn_timeout; + apr_size_t recv_buffer_size; + apr_size_t io_buffer_size; + apr_size_t elected; /* Number of times the worker was elected */ + apr_size_t busy; /* busyness factor */ + apr_port_t port; + apr_off_t transferred;/* Number of bytes transferred to remote */ + apr_off_t read; /* Number of bytes read from remote */ + void *context; /* general purpose storage */ + unsigned int keepalive:1; + unsigned int disablereuse:1; + unsigned int is_address_reusable:1; + unsigned int retry_set:1; + unsigned int timeout_set:1; + unsigned int acquire_set:1; + unsigned int ping_timeout_set:1; + unsigned int conn_timeout_set:1; + unsigned int recv_buffer_size_set:1; + unsigned int io_buffer_size_set:1; + unsigned int keepalive_set:1; + unsigned int disablereuse_set:1; + unsigned int was_malloced:1; + unsigned int is_name_matchable: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; + char upgrade[PROXY_WORKER_MAX_SCHEME_SIZE];/* upgrade protocol used by mod_proxy_wstunnel */ + char hostname_ex[PROXY_RFC1035_HOSTNAME_SIZE]; /* RFC1035 compliant version of the remote backend address */ + apr_size_t response_field_size; /* Size of proxy response buffer in bytes. */ + unsigned int response_field_size_set:1; + char secret[PROXY_WORKER_MAX_SECRET_SIZE]; /* authentication secret (e.g. AJP13) */ + char name_ex[PROXY_WORKER_EXT_NAME_SIZE]; /* Extended name (>96 chars for 2.4.x) */ +} proxy_worker_shared; + +#define ALIGNED_PROXY_WORKER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_worker_shared))) + +/* Worker configuration */ +struct proxy_worker { + proxy_hashes hash; /* hash of worker name */ + unsigned int local_status; /* status of per-process worker */ + proxy_conn_pool *cp; /* Connection pool to use */ + proxy_worker_shared *s; /* Shared data */ + proxy_balancer *balancer; /* which balancer am I in? */ +#if APR_HAS_THREADS + apr_thread_mutex_t *tmutex; /* Thread lock for updating address cache */ +#endif + void *context; /* general purpose storage */ + ap_conf_vector_t *section_config; /* <Proxy>-section wherein defined */ +}; + +/* 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. + */ +#define PROXY_FLUSH_WAIT 10000 + +typedef struct { + char sticky_path[PROXY_BALANCER_MAX_STICKY_SIZE]; /* URL sticky session identifier */ + char sticky[PROXY_BALANCER_MAX_STICKY_SIZE]; /* sticky session identifier */ + char lbpname[PROXY_MAX_PROVIDER_NAME_SIZE]; /* lbmethod provider name */ + char nonce[APR_UUID_FORMATTED_LENGTH + 1]; + char name[PROXY_BALANCER_MAX_NAME_SIZE]; + char sname[PROXY_BALANCER_MAX_NAME_SIZE]; + char vpath[PROXY_BALANCER_MAX_ROUTE_SIZE]; + char vhost[PROXY_BALANCER_MAX_HOSTNAME_SIZE]; + apr_interval_time_t timeout; /* Timeout for waiting on free connection */ + apr_time_t wupdated; /* timestamp of last change to workers list */ + int max_attempts; /* Number of attempts before failing */ + int index; /* shm array index */ + proxy_hashes hash; + unsigned int sticky_force:1; /* Disable failover for sticky sessions */ + unsigned int scolonsep:1; /* true if ';' seps sticky session paths */ + unsigned int max_attempts_set:1; + unsigned int was_malloced:1; + unsigned int need_reset:1; + unsigned int vhosted:1; + unsigned int inactive:1; + unsigned int forcerecovery:1; + char sticky_separator; /* separator for sessionid/route */ + unsigned int forcerecovery_set:1; + unsigned int scolonsep_set:1; + unsigned int sticky_force_set:1; + unsigned int nonce_set:1; + unsigned int sticky_separator_set:1; +} proxy_balancer_shared; + +#define ALIGNED_PROXY_BALANCER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_balancer_shared))) + +struct proxy_balancer { + apr_array_header_t *workers; /* initially configured workers */ + apr_array_header_t *errstatuses; /* statuses to force members into error */ + ap_slotmem_instance_t *wslot; /* worker shm data - runtime */ + ap_slotmem_provider_t *storage; + int growth; /* number of post-config workers can added */ + int max_workers; /* maximum number of allowed workers */ + proxy_hashes hash; + apr_time_t wupdated; /* timestamp of last change to workers list */ + proxy_balancer_method *lbmethod; + apr_global_mutex_t *gmutex; /* global lock for updating list of workers */ +#if APR_HAS_THREADS + apr_thread_mutex_t *tmutex; /* Thread lock for updating shm */ +#endif + proxy_server_conf *sconf; + void *context; /* general purpose storage */ + proxy_balancer_shared *s; /* Shared data */ + int failontimeout; /* Whether to mark a member in Err if IO timeout occurs */ + unsigned int failontimeout_set:1; + unsigned int growth_set:1; + unsigned int lbmethod_set:1; + ap_conf_vector_t *section_config; /* <Proxy>-section wherein defined */ +}; + +struct proxy_balancer_method { + const char *name; /* name of the load balancer method*/ + proxy_worker *(*finder)(proxy_balancer *balancer, + request_rec *r); + void *context; /* general purpose storage */ + apr_status_t (*reset)(proxy_balancer *balancer, server_rec *s); + apr_status_t (*age)(proxy_balancer *balancer, server_rec *s); + apr_status_t (*updatelbstatus)(proxy_balancer *balancer, proxy_worker *elected, server_rec *s); +}; + +#define PROXY_THREAD_LOCK(x) ( (x) && (x)->tmutex ? apr_thread_mutex_lock((x)->tmutex) : APR_SUCCESS) +#define PROXY_THREAD_UNLOCK(x) ( (x) && (x)->tmutex ? apr_thread_mutex_unlock((x)->tmutex) : APR_SUCCESS) + +#define PROXY_GLOBAL_LOCK(x) ( (x) && (x)->gmutex ? apr_global_mutex_lock((x)->gmutex) : APR_SUCCESS) +#define PROXY_GLOBAL_UNLOCK(x) ( (x) && (x)->gmutex ? apr_global_mutex_unlock((x)->gmutex) : APR_SUCCESS) + +/* hooks */ + +/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and + * PROXY_DECLARE_DATA with appropriate export and import tags for the platform + */ +#if !defined(WIN32) +#define PROXY_DECLARE(type) type +#define PROXY_DECLARE_NONSTD(type) type +#define PROXY_DECLARE_DATA +#elif defined(PROXY_DECLARE_STATIC) +#define PROXY_DECLARE(type) type __stdcall +#define PROXY_DECLARE_NONSTD(type) type +#define PROXY_DECLARE_DATA +#elif defined(PROXY_DECLARE_EXPORT) +#define PROXY_DECLARE(type) __declspec(dllexport) type __stdcall +#define PROXY_DECLARE_NONSTD(type) __declspec(dllexport) type +#define PROXY_DECLARE_DATA __declspec(dllexport) +#else +#define PROXY_DECLARE(type) __declspec(dllimport) type __stdcall +#define PROXY_DECLARE_NONSTD(type) __declspec(dllimport) type +#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, section_post_config, + (apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s, + ap_conf_vector_t *section_config)) + +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)) +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, check_trans, + (request_rec *r, const char *url)) +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, canon_handler, + (request_rec *r, char *url)) + +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, create_req, (request_rec *r, request_rec *pr)) +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, fixups, (request_rec *r)) + + +/** + * pre request hook. + * It will return the most suitable worker at the moment + * and corresponding balancer. + * The url is rewritten from balancer://cluster/uri to scheme://host:port/uri + * and then the scheme_handler is called. + * + */ +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, pre_request, (proxy_worker **worker, + proxy_balancer **balancer, + request_rec *r, + proxy_server_conf *conf, char **url)) +/** + * post request hook. + * It is called after request for updating runtime balancer status. + */ +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, post_request, (proxy_worker *worker, + proxy_balancer *balancer, request_rec *r, + proxy_server_conf *conf)) + +/** + * request status hook + * It is called after all proxy processing has been done. This gives other + * modules a chance to create default content on failure, for example + */ +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, request_status, + (int *status, request_rec *r)) + +/* proxy_util.c */ + +PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src, + apr_size_t dlen); +PROXY_DECLARE(int) ap_proxy_hex2c(const char *x); +PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x); +PROXY_DECLARE(char *)ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len, enum enctype t, + int flags, int proxyreq); +PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, + int forcedec, int proxyreq); +PROXY_DECLARE(char *)ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, + char **passwordp, char **hostp, apr_port_t *port); +PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message); +PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr); + +/** Test whether the hostname/address of the request are blocked by the ProxyBlock + * configuration. + * @param r request + * @param conf server configuration + * @param hostname hostname from request URI + * @param addr resolved address of hostname, or NULL if not known + * @return OK on success, or else an error + */ +PROXY_DECLARE(int) ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *conf, + const char *hostname, apr_sockaddr_t *addr); + +PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r); +/* DEPRECATED (will be replaced with ap_proxy_connect_backend */ +PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, request_rec *); +/* DEPRECATED (will be replaced with ap_proxy_check_connection */ +PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn, + request_rec *r); +PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c); +PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c); +PROXY_DECLARE(int) ap_proxy_ssl_engine(conn_rec *c, + ap_conf_vector_t *per_dir_config, + int enable); +PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c); +PROXY_DECLARE(const char *) ap_proxy_ssl_val(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *var); + +/* Header mapping functions, and a typedef of their signature */ +PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *url); +PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, proxy_dir_conf *conf, const char *str); + +#if !defined(WIN32) +typedef const char *(*ap_proxy_header_reverse_map_fn)(request_rec *, + proxy_dir_conf *, const char *); +#elif defined(PROXY_DECLARE_STATIC) +typedef const char *(__stdcall *ap_proxy_header_reverse_map_fn)(request_rec *, + proxy_dir_conf *, const char *); +#elif defined(PROXY_DECLARE_EXPORT) +typedef __declspec(dllexport) const char * + (__stdcall *ap_proxy_header_reverse_map_fn)(request_rec *, + proxy_dir_conf *, const char *); +#else +typedef __declspec(dllimport) const char * + (__stdcall *ap_proxy_header_reverse_map_fn)(request_rec *, + proxy_dir_conf *, const char *); +#endif + + +/* Connection pool API */ +/** + * Return the user-land, UDS aware worker name + * @param p memory pool used for displaying worker name + * @param worker the worker + * @return name + */ + +PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p, + proxy_worker *worker); + +/** + * Return whether a worker upgrade configuration matches Upgrade header + * @param p memory pool used for displaying worker name + * @param worker the worker + * @param upgrade the Upgrade header to match + * @param dflt default protocol (NULL for none) + * @return 1 (true) or 0 (false) + */ +PROXY_DECLARE(int) ap_proxy_worker_can_upgrade(apr_pool_t *p, + const proxy_worker *worker, + const char *upgrade, + const char *dflt); + +/* Bitmask for ap_proxy_{define,get}_worker_ex(). */ +#define AP_PROXY_WORKER_IS_PREFIX (1u << 0) +#define AP_PROXY_WORKER_IS_MATCH (1u << 1) +#define AP_PROXY_WORKER_IS_MALLOCED (1u << 2) +#define AP_PROXY_WORKER_NO_UDS (1u << 3) + +/** + * Get the worker from proxy configuration, looking for either PREFIXED or + * MATCHED or both types of workers according to given mask + * @param p memory pool used for finding worker + * @param balancer the balancer that the worker belongs to + * @param conf current proxy server configuration + * @param url url to find the worker from + * @param mask bitmask of AP_PROXY_WORKER_IS_* + * @return proxy_worker or NULL if not found + */ +PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker_ex(apr_pool_t *p, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + unsigned int mask); + +/** + * Get the worker from proxy configuration, both types + * @param p memory pool used for finding worker + * @param balancer the balancer that the worker belongs to + * @param conf current proxy server configuration + * @param url url to find the worker from + * @return proxy_worker or NULL if not found + */ +PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url); + +/** + * Define and Allocate space for the worker to proxy configuration, of either + * PREFIXED or MATCHED type according to given mask + * @param p memory pool to allocate worker from + * @param worker the new worker + * @param balancer the balancer that the worker belongs to + * @param conf current proxy server configuration + * @param url url containing worker name + * @param mask bitmask of AP_PROXY_WORKER_IS_* + * @return error message or NULL if successful (*worker is new worker) + */ +PROXY_DECLARE(char *) ap_proxy_define_worker_ex(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + unsigned int mask); + + /** + * Define and Allocate space for the worker to proxy configuration + * @param p memory pool to allocate worker from + * @param worker the new worker + * @param balancer the balancer that the worker belongs to + * @param conf current proxy server configuration + * @param url url containing worker name + * @param do_malloc true if shared struct should be malloced + * @return error message or NULL if successful (*worker is new worker) + */ +PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + int do_malloc); + +/** + * Define and Allocate space for the ap_strcmp_match()able worker to proxy + * configuration. + * @param p memory pool to allocate worker from + * @param worker the new worker + * @param balancer the balancer that the worker belongs to + * @param conf current proxy server configuration + * @param url url containing worker name (produces match pattern) + * @param do_malloc true if shared struct should be malloced + * @return error message or NULL if successful (*worker is new worker) + * @deprecated Replaced by ap_proxy_define_worker_ex() + */ +PROXY_DECLARE(char *) ap_proxy_define_match_worker(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + int do_malloc); + +/** + * Share a defined proxy worker via shm + * @param worker worker to be shared + * @param shm location of shared info + * @param i index into shm + * @return APR_SUCCESS or error code + */ +PROXY_DECLARE(apr_status_t) ap_proxy_share_worker(proxy_worker *worker, + proxy_worker_shared *shm, + int i); + +/** + * Initialize the worker by setting up worker connection pool and mutex + * @param worker worker to initialize + * @param s current server record + * @param p memory pool used for mutex and connection pool + * @return APR_SUCCESS or error code + */ +PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, + server_rec *s, + apr_pool_t *p); + +/** + * Verifies valid balancer name (eg: balancer://foo) + * @param name name to test + * @param i number of chars to test; 0 for all. + * @return true/false + */ +PROXY_DECLARE(int) ap_proxy_valid_balancer_name(char *name, int i); + + +/** + * Get the balancer from proxy configuration + * @param p memory pool used for temporary storage while finding balancer + * @param conf current proxy server configuration + * @param url url to find the worker from; must have balancer:// prefix + * @param careactive true if we care if the balancer is active or not + * @return proxy_balancer or NULL if not found + */ +PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p, + proxy_server_conf *conf, + const char *url, + int careactive); + +/** + * Update the balancer's vhost related fields + * @param p memory pool used for temporary storage while finding balancer + * @param balancer balancer to be updated + * @param url url to find vhost info + * @return error string or NULL if OK + */ +PROXY_DECLARE(char *) ap_proxy_update_balancer(apr_pool_t *p, + proxy_balancer *balancer, + const char *url); + +/** + * Define and Allocate space for the balancer to proxy configuration + * @param p memory pool to allocate balancer from + * @param balancer the new balancer + * @param conf current proxy server configuration + * @param url url containing balancer name + * @param alias alias/fake-path to this balancer + * @param do_malloc true if shared struct should be malloced + * @return error message or NULL if successful + */ +PROXY_DECLARE(char *) ap_proxy_define_balancer(apr_pool_t *p, + proxy_balancer **balancer, + proxy_server_conf *conf, + const char *url, + const char *alias, + int do_malloc); + +/** + * Share a defined proxy balancer via shm + * @param balancer balancer to be shared + * @param shm location of shared info + * @param i index into shm + * @return APR_SUCCESS or error code + */ +PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer, + proxy_balancer_shared *shm, + int i); + +/** + * Initialize the balancer as needed + * @param balancer balancer to initialize + * @param s current server record + * @param p memory pool used for mutex and connection pool + * @return APR_SUCCESS or error code + */ +PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balancer, + server_rec *s, + apr_pool_t *p); + +typedef int (proxy_is_best_callback_fn_t)(proxy_worker *current, proxy_worker *prev_best, void *baton); + +/** + * Retrieve the best worker in a balancer for the current request + * @param balancer balancer for which to find the best worker + * @param r current request record + * @param is_best a callback function provide by the lbmethod + * that determines if the current worker is best + * @param baton an lbmethod-specific context pointer (baton) + * passed to the is_best callback + * @return the best worker to be used for the request + */ +PROXY_DECLARE(proxy_worker *) ap_proxy_balancer_get_best_worker(proxy_balancer *balancer, + request_rec *r, + proxy_is_best_callback_fn_t *is_best, + void *baton); +/* + * Needed by the lb modules. + */ +APR_DECLARE_OPTIONAL_FN(proxy_worker *, proxy_balancer_get_best_worker, + (proxy_balancer *balancer, + request_rec *r, + proxy_is_best_callback_fn_t *is_best, + void *baton)); + +/** + * Find the shm of the worker as needed + * @param storage slotmem provider + * @param slot slotmem instance + * @param worker worker to find + * @param index pointer to index within slotmem of worker + * @return pointer to shm of worker, or NULL + */ +PROXY_DECLARE(proxy_worker_shared *) ap_proxy_find_workershm(ap_slotmem_provider_t *storage, + ap_slotmem_instance_t *slot, + proxy_worker *worker, + unsigned int *index); + +/** + * Find the shm of the balancer as needed + * @param storage slotmem provider + * @param slot slotmem instance + * @param balancer balancer of shm to find + * @param index pointer to index within slotmem of balancer + * @return pointer to shm of balancer, or NULL + */ +PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_provider_t *storage, + ap_slotmem_instance_t *slot, + proxy_balancer *balancer, + unsigned int *index); + +/** + * Get the most suitable worker and/or balancer for the request + * @param worker worker used for processing request + * @param balancer balancer used for processing request + * @param r current request + * @param conf current proxy server configuration + * @param url request url that balancer can rewrite. + * @return OK or HTTP_XXX error + * @note It calls balancer pre_request hook if the url starts with balancer:// + * The balancer then rewrites the url to particular worker, like http://host:port + */ +PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, + proxy_balancer **balancer, + request_rec *r, + proxy_server_conf *conf, + char **url); +/** + * Post request worker and balancer cleanup + * @param worker worker used for processing request + * @param balancer balancer used for processing request + * @param r current request + * @param conf current proxy server configuration + * @return OK or HTTP_XXX error + * @note Whenever the pre_request is called, the post_request has to be + * called too. + */ +PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker, + proxy_balancer *balancer, + request_rec *r, + proxy_server_conf *conf); + +/** + * Determine backend hostname and port + * @param p memory pool used for processing + * @param r current request + * @param conf current proxy server configuration + * @param worker worker used for processing request + * @param conn proxy connection struct + * @param uri processed uri + * @param url request url + * @param proxyname are we connecting directly or via a proxy + * @param proxyport proxy host port + * @param server_portstr Via headers server port, must be non-NULL + * @param server_portstr_size size of the server_portstr buffer; must + * be at least one, even if the protocol doesn't use this + * @return OK or HTTP_XXX error + */ +PROXY_DECLARE(int) ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, + proxy_server_conf *conf, + proxy_worker *worker, + proxy_conn_rec *conn, + apr_uri_t *uri, + char **url, + const char *proxyname, + apr_port_t proxyport, + char *server_portstr, + int server_portstr_size); + +/** + * Mark a worker for retry + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param worker worker used for retrying + * @param s current server record + * @return OK if marked for retry, DECLINED otherwise + * @note The error status of the worker will cleared if the retry interval has + * elapsed since the last error. + */ +APR_DECLARE_OPTIONAL_FN(int, ap_proxy_retry_worker, + (const char *proxy_function, proxy_worker *worker, server_rec *s)); + +/** + * Acquire a connection from worker connection pool + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param worker worker used for obtaining connection + * @param s current server record + * @return OK or HTTP_XXX error + * @note If the connection limit has been reached, the function will + * block until a connection becomes available or the timeout has + * elapsed. + */ +PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, + proxy_conn_rec **conn, + proxy_worker *worker, + server_rec *s); +/** + * Release a connection back to worker connection pool + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param s current server record + * @return OK or HTTP_XXX error + * @note The connection will be closed if conn->close_on_release is set + */ +PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, + proxy_conn_rec *conn, + server_rec *s); + +#define PROXY_CHECK_CONN_EMPTY (1 << 0) +/** + * Check a connection to the backend + * @param scheme calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param server current server record + * @param max_blank_lines how many blank lines to consume, + * or zero for none (considered data) + * @param flags PROXY_CHECK_* bitmask + * @return APR_SUCCESS: connection established, + * APR_ENOTEMPTY: connection established with data, + * APR_ENOSOCKET: not connected, + * APR_EINVAL: worker in error state (unusable), + * other: connection closed/aborted (remotely) + */ +PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme, + proxy_conn_rec *conn, + server_rec *server, + unsigned max_blank_lines, + int flags); + +/** + * Make a connection to the backend + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param worker connection worker + * @param s current server record + * @return OK or HTTP_XXX error + * @note In case the socket already exists for conn, just check the link + * status. + */ +PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, + proxy_conn_rec *conn, + proxy_worker *worker, + server_rec *s); + +/** + * Make a connection to a Unix Domain Socket (UDS) path + * @param sock UDS to connect + * @param uds_path UDS path to connect to + * @param p pool to make the sock addr + * @return APR_SUCCESS or error status + */ +PROXY_DECLARE(apr_status_t) ap_proxy_connect_uds(apr_socket_t *sock, + const char *uds_path, + apr_pool_t *p); +/** + * Make a connection record for backend connection + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param c client connection record (unused, deprecated) + * @param s current server record + * @return OK or HTTP_XXX error + * @note The function will return immediately if conn->connection + * is already set, + */ +PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function, + proxy_conn_rec *conn, + conn_rec *c, server_rec *s); + +/** + * Make a connection record for backend connection, using request dir config + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param r current request record + * @return OK or HTTP_XXX error + * @note The function will return immediately if conn->connection + * is already set, + */ +PROXY_DECLARE(int) ap_proxy_connection_create_ex(const char *proxy_function, + proxy_conn_rec *conn, + request_rec *r); +/** + * Determine if proxy connection can potentially be reused at the + * end of this request. + * @param conn proxy connection + * @return non-zero if reusable, 0 otherwise + * @note Even if this function returns non-zero, the connection may + * be subsequently marked for closure. + */ +PROXY_DECLARE(int) ap_proxy_connection_reusable(proxy_conn_rec *conn); + +/** + * Signal the upstream chain that the connection to the backend broke in the + * middle of the response. This is done by sending an error bucket with + * status HTTP_BAD_GATEWAY and an EOS bucket up the filter chain. + * @param r current request record of client request + * @param brigade The brigade that is sent through the output filter chain + */ +PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r, + apr_bucket_brigade *brigade); + +/** + * Return a hash based on the passed string + * @param str string to produce hash from + * @param method hashing method to use + * @return hash as unsigned int + */ + +typedef enum { PROXY_HASHFUNC_DEFAULT, PROXY_HASHFUNC_APR, PROXY_HASHFUNC_FNV } proxy_hash_t; + +PROXY_DECLARE(unsigned int) ap_proxy_hashfunc(const char *str, proxy_hash_t method); + + +/** + * Set/unset the worker status bitfield depending on flag + * @param c flag + * @param set set or unset bit + * @param w worker to use + * @return APR_SUCCESS if valid flag + */ +PROXY_DECLARE(apr_status_t) ap_proxy_set_wstatus(char c, int set, proxy_worker *w); + + +/** + * Create readable representation of worker status bitfield + * @param p pool + * @param w worker to use + * @return string representation of status + */ +PROXY_DECLARE(char *) ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w); + + +/** + * Sync balancer and workers based on any updates w/i shm + * @param b balancer to check/update member list of + * @param s server rec + * @param conf config + * @return APR_SUCCESS if all goes well + */ +PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, + server_rec *s, + proxy_server_conf *conf); + +/** + * Configure and create workers (and balancer) in mod_balancer. + * @param r request + * @param params table with the parameters like b=mycluster etc. + * @return 404 when the worker/balancer doesn't exist, + * 400 if something is invalid + * 200 for success. + */ +APR_DECLARE_OPTIONAL_FN(apr_status_t, balancer_manage, + (request_rec *, apr_table_t *params)); + +/** + * Find the matched alias for this request and setup for proxy handler + * @param r request + * @param ent proxy_alias record + * @param dconf per-dir config or NULL + * @return OK if the alias matched, + * DONE if the alias matched and r->uri was normalized so + * no further transformation should happen on it, + * DECLINED if proxying is disabled for this alias, + * HTTP_CONTINUE if the alias did not match + */ +PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, + struct proxy_alias *ent, + proxy_dir_conf *dconf); + +/** + * Create a HTTP request header brigade, old_cl_val and old_te_val as required. + * @param p pool + * @param header_brigade header brigade to use/fill + * @param r request + * @param p_conn proxy connection rec + * @param worker selected worker + * @param conf per-server proxy config + * @param uri uri + * @param url url + * @param server_portstr port as string + * @param old_cl_val stored old content-len val + * @param old_te_val stored old TE val + * @return OK or HTTP_EXPECTATION_FAILED + */ +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, + apr_bucket_brigade *header_brigade, + request_rec *r, + proxy_conn_rec *p_conn, + proxy_worker *worker, + proxy_server_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr, + char **old_cl_val, + char **old_te_val); + +/** + * Prefetch the client request body (in memory), up to a limit. + * Read what's in the client pipe. If nonblocking is set and read is EAGAIN, + * pass a FLUSH bucket to the backend and read again in blocking mode. + * @param r client request + * @param backend backend connection + * @param input_brigade input brigade to use/fill + * @param block blocking or non-blocking mode + * @param bytes_read number of bytes read + * @param max_read maximum number of bytes to read + * @return OK or HTTP_* error code + * @note max_read is rounded up to APR_BUCKET_BUFF_SIZE + */ +PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r, + proxy_conn_rec *backend, + apr_bucket_brigade *input_brigade, + apr_read_type_e block, + apr_off_t *bytes_read, + apr_off_t max_read); + +/** + * Spool the client request body to memory, or disk above given limit. + * @param r client request + * @param backend backend connection + * @param input_brigade input brigade to use/fill + * @param bytes_spooled number of bytes spooled + * @param max_mem_spool maximum number of in-memory bytes + * @return OK or HTTP_* error code + */ +PROXY_DECLARE(int) ap_proxy_spool_input(request_rec *r, + proxy_conn_rec *backend, + apr_bucket_brigade *input_brigade, + apr_off_t *bytes_spooled, + apr_off_t max_mem_spool); + +/** + * Read what's in the client pipe. If the read would block (EAGAIN), + * pass a FLUSH bucket to the backend and read again in blocking mode. + * @param r client request + * @param backend backend connection + * @param input_brigade brigade to use/fill + * @param max_read maximum number of bytes to read + * @return OK or HTTP_* error code + */ +PROXY_DECLARE(int) ap_proxy_read_input(request_rec *r, + proxy_conn_rec *backend, + apr_bucket_brigade *input_brigade, + apr_off_t max_read); + +/** + * @param bucket_alloc bucket allocator + * @param r request + * @param p_conn proxy connection + * @param origin connection rec of origin + * @param bb brigade to send to origin + * @param flush flush + * @return status (OK) + */ +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, + request_rec *r, proxy_conn_rec *p_conn, + conn_rec *origin, apr_bucket_brigade *bb, + int flush); + +struct proxy_tunnel_conn; /* opaque */ +typedef struct { + request_rec *r; + const char *scheme; + apr_pollset_t *pollset; + apr_array_header_t *pfds; + apr_interval_time_t timeout; + struct proxy_tunnel_conn *client, + *origin; + apr_size_t read_buf_size; + int replied; /* TODO 2.5+: one bit to merge in below bitmask */ + unsigned int nohalfclose :1; +} proxy_tunnel_rec; + +/** + * Create a tunnel, to be activated by ap_proxy_tunnel_run(). + * @param tunnel tunnel created + * @param r client request + * @param c_o connection to origin + * @param scheme caller proxy scheme (connect, ws(s), http(s), ...) + * @return APR_SUCCESS or error status + */ +PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **tunnel, + request_rec *r, conn_rec *c_o, + const char *scheme); + +/** + * Forward anything from either side of the tunnel to the other, + * until one end aborts or a polling timeout/error occurs. + * @param tunnel tunnel to run + * @return OK if completion is full, HTTP_GATEWAY_TIME_OUT on timeout + * or another HTTP_ error otherwise. + */ +PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel); + +/** + * Clear the headers referenced by the Connection header from the given + * table, and remove the Connection header. + * @param r request + * @param headers table of headers to clear + * @return 1 if "close" was present, 0 otherwise. + */ +APR_DECLARE_OPTIONAL_FN(int, ap_proxy_clear_connection, + (request_rec *r, apr_table_t *headers)); + +/** + * Do a AJP CPING and wait for CPONG on the socket + * + */ +APR_DECLARE_OPTIONAL_FN(apr_status_t, ajp_handle_cping_cpong, + (apr_socket_t *sock, request_rec *r, + apr_interval_time_t timeout)); + + +/** + * @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. + * If this limit is reached you must stop and restart the server. + */ +#define PROXY_DYNAMIC_BALANCER_LIMIT 16 + +/** + * Calculate maximum number of workers in scoreboard. + * @return number of workers to allocate in the scoreboard + */ +int ap_proxy_lb_workers(void); + +/** + * Returns 1 if a response with the given status should be overridden. + * + * @param conf proxy directory configuration + * @param code http status code + * @return 1 if code is considered an error-code, 0 otherwise + */ +PROXY_DECLARE(int) ap_proxy_should_override(proxy_dir_conf *conf, int code); + +/** + * Return the port number of a known scheme (eg: http -> 80). + * @param scheme scheme to test + * @return port number or 0 if unknown + */ +PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme); + +/** + * 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 + * @return URL with the UDS prefix removed + */ +PROXY_DECLARE(const char *) ap_proxy_de_socketfy(apr_pool_t *p, const char *url); + +/* + * Transform buckets from one bucket allocator to another one by creating a + * transient bucket for each data bucket and let it use the data read from + * the old bucket. Metabuckets are transformed by just recreating them. + * Attention: Currently only the following bucket types are handled: + * + * All data buckets + * FLUSH + * EOS + * + * If an other bucket type is found its type is logged as a debug message + * and APR_EGENERAL is returned. + * + * @param r request_rec of the actual request. Used for logging purposes + * @param from the bucket brigade to take the buckets from + * @param to the bucket brigade to store the transformed buckets + * @return apr_status_t of the operation. Either APR_SUCCESS or + * APR_EGENERAL + */ +PROXY_DECLARE(apr_status_t) ap_proxy_buckets_lifetime_transform(request_rec *r, + apr_bucket_brigade *from, + apr_bucket_brigade *to); + +/* + * The flags for ap_proxy_transfer_between_connections(), where for legacy and + * compatibility reasons FLUSH_EACH and FLUSH_AFTER are boolean values. + */ +#define AP_PROXY_TRANSFER_FLUSH_EACH (0x00) +#define AP_PROXY_TRANSFER_FLUSH_AFTER (0x01) +#define AP_PROXY_TRANSFER_YIELD_PENDING (0x02) +#define AP_PROXY_TRANSFER_YIELD_MAX_READS (0x04) + +/* + * Sends all data that can be read non blocking from the input filter chain of + * c_i and send it down the output filter chain of c_o. For reading it uses + * the bucket brigade bb_i which should be created from the bucket allocator + * associated with c_i. For sending through the output filter chain it uses + * the bucket brigade bb_o which should be created from the bucket allocator + * associated with c_o. In order to get the buckets from bb_i to bb_o + * ap_proxy_buckets_lifetime_transform is used. + * + * @param r request_rec of the actual request. Used for logging purposes + * @param c_i inbound connection conn_rec + * @param c_o outbound connection conn_rec + * @param bb_i bucket brigade for pulling data from the inbound connection + * @param bb_o bucket brigade for sending data through the outbound connection + * @param name string for logging from where data was pulled + * @param sent if not NULL will be set to 1 if data was sent through c_o + * @param bsize maximum amount of data pulled in one iteration from c_i + * @param flags AP_PROXY_TRANSFER_* bitmask + * @return apr_status_t of the operation. Could be any error returned from + * either the input filter chain of c_i or the output filter chain + * of c_o, APR_EPIPE if the outgoing connection was aborted, or + * APR_INCOMPLETE if AP_PROXY_TRANSFER_YIELD_PENDING was set and + * the output stack gets full before the input stack is exhausted. + */ +PROXY_DECLARE(apr_status_t) ap_proxy_transfer_between_connections( + request_rec *r, + conn_rec *c_i, + conn_rec *c_o, + apr_bucket_brigade *bb_i, + apr_bucket_brigade *bb_o, + const char *name, + int *sent, + apr_off_t bsize, + int flags); + +extern module PROXY_DECLARE_DATA proxy_module; + +#endif /*MOD_PROXY_H*/ +/** @} */ diff --git a/modules/proxy/mod_proxy.mak b/modules/proxy/mod_proxy.mak new file mode 100644 index 0000000..1fda86c --- /dev/null +++ b/modules/proxy/mod_proxy.mak @@ -0,0 +1,361 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy - Win32 Release" && "$(CFG)" != "mod_proxy - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy.mak" CFG="mod_proxy - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy.obj" + -@erase "$(INTDIR)\mod_proxy.res" + -@erase "$(INTDIR)\mod_proxy_src.idb" + -@erase "$(INTDIR)\mod_proxy_src.pdb" + -@erase "$(INTDIR)\proxy_util.obj" + -@erase "$(OUTDIR)\mod_proxy.exp" + -@erase "$(OUTDIR)\mod_proxy.lib" + -@erase "$(OUTDIR)\mod_proxy.pdb" + -@erase "$(OUTDIR)\mod_proxy.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../ssl" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../generators" /I "../http2" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy.pdb" /debug /out:"$(OUTDIR)\mod_proxy.so" /implib:"$(OUTDIR)\mod_proxy.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy.obj" \ + "$(INTDIR)\proxy_util.obj" \ + "$(INTDIR)\mod_proxy.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" + +"$(OUTDIR)\mod_proxy.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy.so" + if exist .\Release\mod_proxy.so.manifest mt.exe -manifest .\Release\mod_proxy.so.manifest -outputresource:.\Release\mod_proxy.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy.obj" + -@erase "$(INTDIR)\mod_proxy.res" + -@erase "$(INTDIR)\mod_proxy_src.idb" + -@erase "$(INTDIR)\mod_proxy_src.pdb" + -@erase "$(INTDIR)\proxy_util.obj" + -@erase "$(OUTDIR)\mod_proxy.exp" + -@erase "$(OUTDIR)\mod_proxy.lib" + -@erase "$(OUTDIR)\mod_proxy.pdb" + -@erase "$(OUTDIR)\mod_proxy.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../ssl" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../generators" /I "../http2" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy.pdb" /debug /out:"$(OUTDIR)\mod_proxy.so" /implib:"$(OUTDIR)\mod_proxy.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy.obj" \ + "$(INTDIR)\proxy_util.obj" \ + "$(INTDIR)\mod_proxy.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" + +"$(OUTDIR)\mod_proxy.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy.so" + if exist .\Debug\mod_proxy.so.manifest mt.exe -manifest .\Debug\mod_proxy.so.manifest -outputresource:.\Debug\mod_proxy.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy.dep") +!INCLUDE "mod_proxy.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy - Win32 Release" || "$(CFG)" == "mod_proxy - Win32 Debug" +SOURCE=.\mod_proxy.c + +"$(INTDIR)\mod_proxy.obj" : $(SOURCE) "$(INTDIR)" + + +SOURCE=.\proxy_util.c + +"$(INTDIR)\proxy_util.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + + +"$(INTDIR)\mod_proxy.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy.so" /d LONG_NAME="proxy_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c new file mode 100644 index 0000000..65773ce --- /dev/null +++ b/modules/proxy/mod_proxy_ajp.c @@ -0,0 +1,888 @@ +/* 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. + */ + +/* AJP routines for Apache proxy */ + +#include "mod_proxy.h" +#include "ajp.h" + +module AP_MODULE_DECLARE_DATA proxy_ajp_module; + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +static int proxy_ajp_canon(request_rec *r, char *url) +{ + char *host, *path, sport[7]; + char *search = NULL; + const char *err; + apr_port_t port, def_port; + + /* ap_port_of_scheme() */ + if (ap_cstr_casecmpn(url, "ajp:", 4) == 0) { + url += 4; + } + else { + return DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url); + + /* + * do syntactic check. + * We break the URL into host, port, path, search + */ + port = def_port = ap_proxy_port_of_scheme("ajp"); + + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00867) "error parsing URL %s: %s", + url, err); + return HTTP_BAD_REQUEST; + } + + /* + * now parse path/search args, according to rfc1738: + * process the path. With proxy-nocanon set (by + * mod_proxy) we use the raw, unparsed uri + */ + if (apr_table_get(r->notes, "proxy-nocanon")) { + path = url; /* this is the raw path */ + } + else if (apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the encoded path already */ + search = r->args; + } + else { + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + search = r->args; + } + /* + * If we have a raw control character or a ' ' in nocanon path or + * r->args, correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10418) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + if (search && *ap_scan_vchar_obstext(search)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10406) + "To be forwarded query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + if (ap_strchr_c(host, ':')) { + /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + r->filename = apr_pstrcat(r->pool, "proxy:ajp://", host, sport, + "/", path, (search) ? "?" : "", + (search) ? search : "", NULL); + return OK; +} + +#define METHOD_NON_IDEMPOTENT 0 +#define METHOD_IDEMPOTENT 1 +#define METHOD_IDEMPOTENT_WITH_ARGS 2 + +static int is_idempotent(request_rec *r) +{ + /* + * RFC2616 (9.1.2): GET, HEAD, PUT, DELETE, OPTIONS, TRACE are considered + * idempotent. Hint: HEAD requests use M_GET as method number as well. + */ + switch (r->method_number) { + case M_GET: + case M_DELETE: + case M_PUT: + case M_OPTIONS: + case M_TRACE: + /* + * If the request has arguments it might have side-effects and thus + * it might be undesirable to resend it to a backend again + * automatically. + */ + if (r->args) { + return METHOD_IDEMPOTENT_WITH_ARGS; + } + return METHOD_IDEMPOTENT; + /* Everything else is not considered idempotent. */ + default: + return METHOD_NON_IDEMPOTENT; + } +} + +static apr_off_t get_content_length(request_rec * r) +{ + apr_off_t len = 0; + + if (r->main == NULL) { + const char *clp = apr_table_get(r->headers_in, "Content-Length"); + + if (clp && !ap_parse_strict_length(&len, clp)) { + len = -1; /* parse error */ + } + } + + return len; +} + +/* + * XXX: AJP Auto Flushing + * + * When processing CMD_AJP13_SEND_BODY_CHUNK AJP messages we will do a poll + * with FLUSH_WAIT milliseconds timeout to determine if more data is currently + * available at the backend. If there is no more data available, we flush + * the data to the client by adding a flush bucket to the brigade we pass + * up the filter chain. + * This is only a bandaid to fix the AJP/1.3 protocol shortcoming of not + * sending (actually not having defined) a flush message, when the data + * should be flushed to the client. As soon as this protocol shortcoming is + * fixed this code should be removed. + * + * For further discussion see PR37100. + * http://issues.apache.org/bugzilla/show_bug.cgi?id=37100 + */ + +/* + * process the request and write the response. + */ +static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, + proxy_conn_rec *conn, + conn_rec *origin, + proxy_dir_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr) +{ + apr_status_t status; + int result; + apr_bucket *e; + apr_bucket_brigade *input_brigade; + apr_bucket_brigade *output_brigade; + ajp_msg_t *msg; + apr_size_t bufsiz = 0; + char *buff; + char *send_body_chunk_buff; + apr_uint16_t size; + apr_byte_t conn_reuse = 0; + const char *tenc; + int havebody = 1; + int client_failed = 0; + int backend_failed = 0; + apr_off_t bb_len; + int data_sent = 0; + int request_ended = 0; + int headers_sent = 0; + int rv = OK; + apr_int32_t conn_poll_fd; + apr_pollfd_t *conn_poll; + proxy_server_conf *psf = + ap_get_module_config(r->server->module_config, &proxy_module); + apr_size_t maxsize = AJP_MSG_BUFFER_SZ; + int send_body = 0; + apr_off_t content_length = 0; + int original_status = r->status; + const char *original_status_line = r->status_line; + const char *secret = NULL; + + if (psf->io_buffer_size_set) + maxsize = psf->io_buffer_size; + if (maxsize > AJP_MAX_BUFFER_SZ) + maxsize = AJP_MAX_BUFFER_SZ; + else if (maxsize < AJP_MSG_BUFFER_SZ) + maxsize = AJP_MSG_BUFFER_SZ; + maxsize = APR_ALIGN(maxsize, 1024); + + if (*conn->worker->s->secret) + secret = conn->worker->s->secret; + + /* + * Send the AJP request to the remote server + */ + + /* send request headers */ + status = ajp_send_header(conn->sock, r, maxsize, uri, secret); + if (status != APR_SUCCESS) { + conn->close = 1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00868) + "request failed to %pI (%s:%d)", + conn->worker->cp->addr, + conn->worker->s->hostname_ex, + (int)conn->worker->s->port); + if (status == AJP_EOVERFLOW) + return HTTP_BAD_REQUEST; + else if (status == AJP_EBAD_METHOD) { + return HTTP_NOT_IMPLEMENTED; + } else { + /* + * This is only non fatal when the method is idempotent. In this + * case we can dare to retry it with a different worker if we are + * a balancer member. + */ + if (is_idempotent(r) == METHOD_IDEMPOTENT) { + return HTTP_SERVICE_UNAVAILABLE; + } + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + /* allocate an AJP message to store the data of the buckets */ + bufsiz = maxsize; + status = ajp_alloc_data_msg(r->pool, &buff, &bufsiz, &msg); + if (status != APR_SUCCESS) { + /* We had a failure: Close connection to backend */ + conn->close = 1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00869) + "ajp_alloc_data_msg failed"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* read the first block of data */ + input_brigade = apr_brigade_create(p, r->connection->bucket_alloc); + tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); + if (tenc) { + if (ap_cstr_casecmp(tenc, "chunked") == 0) { + /* The AJP protocol does not want body data yet */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00870) + "request is chunked"); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10396) + "%s Transfer-Encoding is not supported", + tenc); + /* We had a failure: Close connection to backend */ + conn->close = 1; + return HTTP_INTERNAL_SERVER_ERROR; + } + } else { + /* Get client provided Content-Length header */ + content_length = get_content_length(r); + if (content_length < 0) { + status = APR_EINVAL; + } + else { + status = ap_get_brigade(r->input_filters, input_brigade, + AP_MODE_READBYTES, APR_BLOCK_READ, + maxsize - AJP_HEADER_SZ); + } + if (status != APR_SUCCESS) { + /* We had a failure: Close connection to backend */ + conn->close = 1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00871) + "ap_get_brigade failed"); + apr_brigade_destroy(input_brigade); + return ap_map_http_request_error(status, HTTP_BAD_REQUEST); + } + + /* have something */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00872) "APR_BUCKET_IS_EOS"); + } + + /* Try to send something */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00873) + "data to read (max %" APR_SIZE_T_FMT + " at %" APR_SIZE_T_FMT ")", bufsiz, msg->pos); + + status = apr_brigade_flatten(input_brigade, buff, &bufsiz); + if (status != APR_SUCCESS) { + /* We had a failure: Close connection to backend */ + conn->close = 1; + apr_brigade_destroy(input_brigade); + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00874) + "apr_brigade_flatten"); + return HTTP_INTERNAL_SERVER_ERROR; + } + apr_brigade_cleanup(input_brigade); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00875) + "got %" APR_SIZE_T_FMT " bytes of data", bufsiz); + if (bufsiz > 0) { + status = ajp_send_data_msg(conn->sock, msg, bufsiz); + ajp_msg_log(r, msg, "First ajp_send_data_msg: ajp_ilink_send packet dump"); + if (status != APR_SUCCESS) { + /* We had a failure: Close connection to backend */ + conn->close = 1; + apr_brigade_destroy(input_brigade); + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00876) + "send failed to %pI (%s:%d)", + conn->worker->cp->addr, + conn->worker->s->hostname_ex, + (int)conn->worker->s->port); + /* + * It is fatal when we failed to send a (part) of the request + * body. + */ + return HTTP_INTERNAL_SERVER_ERROR; + } + conn->worker->s->transferred += bufsiz; + send_body = 1; + } + else if (content_length > 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00877) + "read zero bytes, expecting" + " %" APR_OFF_T_FMT " bytes", + content_length); + /* + * We can only get here if the client closed the connection + * to us without sending the body. + * Now the connection is in the wrong state on the backend. + * Sending an empty data msg doesn't help either as it does + * not move this connection to the correct state on the backend + * for later resusage by the next request again. + * Close it to clean things up. + */ + conn->close = 1; + apr_brigade_destroy(input_brigade); + return HTTP_BAD_REQUEST; + } + } + + /* read the response */ + conn->data = NULL; + status = ajp_read_header(conn->sock, r, maxsize, + (ajp_msg_t **)&(conn->data)); + if (status != APR_SUCCESS) { + /* We had a failure: Close connection to backend */ + conn->close = 1; + apr_brigade_destroy(input_brigade); + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00878) + "read response failed from %pI (%s:%d)", + conn->worker->cp->addr, + conn->worker->s->hostname_ex, + (int)conn->worker->s->port); + + /* If we had a successful cping/cpong and then a timeout + * we assume it is a request that cause a back-end timeout, + * but doesn't affect the whole worker. + */ + if (APR_STATUS_IS_TIMEUP(status) && + conn->worker->s->ping_timeout_set) { + return HTTP_GATEWAY_TIME_OUT; + } + + /* + * This is only non fatal when we have not sent (parts) of a possible + * request body so far (we do not store it and thus cannot send it + * again) and the method is idempotent. In this case we can dare to + * retry it with a different worker if we are a balancer member. + */ + if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) { + return HTTP_SERVICE_UNAVAILABLE; + } + return HTTP_INTERNAL_SERVER_ERROR; + } + /* parse the response */ + result = ajp_parse_type(r, conn->data); + output_brigade = apr_brigade_create(p, r->connection->bucket_alloc); + + /* + * Prepare apr_pollfd_t struct for possible later check if there is currently + * data available from the backend (do not flush response to client) + * or not (flush response to client) + */ + conn_poll = apr_pcalloc(p, sizeof(apr_pollfd_t)); + conn_poll->reqevents = APR_POLLIN; + conn_poll->desc_type = APR_POLL_SOCKET; + conn_poll->desc.s = conn->sock; + + bufsiz = maxsize; + for (;;) { + switch (result) { + case CMD_AJP13_GET_BODY_CHUNK: + if (havebody) { + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + /* This is the end */ + bufsiz = 0; + havebody = 0; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00879) + "APR_BUCKET_IS_EOS"); + } else { + status = ap_get_brigade(r->input_filters, input_brigade, + AP_MODE_READBYTES, + APR_BLOCK_READ, + maxsize - AJP_HEADER_SZ); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00880) + "ap_get_brigade failed"); + if (APR_STATUS_IS_TIMEUP(status)) { + rv = HTTP_REQUEST_TIME_OUT; + } + else if (status == AP_FILTER_ERROR) { + rv = AP_FILTER_ERROR; + } + client_failed = 1; + break; + } + bufsiz = maxsize; + status = apr_brigade_flatten(input_brigade, buff, + &bufsiz); + apr_brigade_cleanup(input_brigade); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00881) + "apr_brigade_flatten failed"); + rv = HTTP_INTERNAL_SERVER_ERROR; + client_failed = 1; + break; + } + } + + ajp_msg_reset(msg); + /* will go in ajp_send_data_msg */ + status = ajp_send_data_msg(conn->sock, msg, bufsiz); + ajp_msg_log(r, msg, "ajp_send_data_msg after CMD_AJP13_GET_BODY_CHUNK: ajp_ilink_send packet dump"); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00882) + "ajp_send_data_msg failed"); + backend_failed = 1; + break; + } + conn->worker->s->transferred += bufsiz; + } else { + /* + * something is wrong TC asks for more body but we are + * already at the end of the body data + */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00883) + "ap_proxy_ajp_request error read after end"); + backend_failed = 1; + } + break; + case CMD_AJP13_SEND_HEADERS: + if (headers_sent) { + /* Do not send anything to the client. + * Backend already send us the headers. + */ + backend_failed = 1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00884) + "Backend sent headers twice."); + break; + } + /* AJP13_SEND_HEADERS: process them */ + status = ajp_parse_header(r, conf, conn->data); + if (status != APR_SUCCESS) { + backend_failed = 1; + } + else if ((r->status == 401) && conf->error_override) { + const char *buf; + const char *wa = "WWW-Authenticate"; + if ((buf = apr_table_get(r->headers_out, wa))) { + apr_table_set(r->err_headers_out, wa, buf); + } else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00885) + "ap_proxy_ajp_request: origin server " + "sent 401 without WWW-Authenticate header"); + } + } + headers_sent = 1; + break; + case CMD_AJP13_SEND_BODY_CHUNK: + /* AJP13_SEND_BODY_CHUNK: piece of data */ + status = ajp_parse_data(r, conn->data, &size, &send_body_chunk_buff); + if (status == APR_SUCCESS) { + /* If we are overriding the errors, we can't put the content + * of the page into the brigade. + */ + if (!ap_proxy_should_override(conf, r->status)) { + /* AJP13_SEND_BODY_CHUNK with zero length + * is explicit flush message + */ + if (size == 0) { + if (headers_sent) { + e = apr_bucket_flush_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00886) + "Ignoring flush message " + "received before headers"); + } + } + else { + apr_status_t rv; + + /* Handle the case where the error document is itself reverse + * proxied and was successful. We must maintain any previous + * error status so that an underlying error (eg HTTP_NOT_FOUND) + * doesn't become an HTTP_OK. + */ + if (ap_proxy_should_override(conf, original_status)) { + r->status = original_status; + r->status_line = original_status_line; + } + + e = apr_bucket_transient_create(send_body_chunk_buff, size, + r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + + if ((conn->worker->s->flush_packets == flush_on) || + ((conn->worker->s->flush_packets == flush_auto) && + ((rv = apr_poll(conn_poll, 1, &conn_poll_fd, + conn->worker->s->flush_wait)) + != APR_SUCCESS) && + APR_STATUS_IS_TIMEUP(rv))) { + e = apr_bucket_flush_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + } + apr_brigade_length(output_brigade, 0, &bb_len); + if (bb_len != -1) + conn->worker->s->read += bb_len; + } + if (headers_sent) { + if (ap_pass_brigade(r->output_filters, + output_brigade) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00887) + "error processing body.%s", + r->connection->aborted ? + " Client aborted connection." : ""); + client_failed = 1; + } + data_sent = 1; + apr_brigade_cleanup(output_brigade); + } + } + } + else { + backend_failed = 1; + } + break; + case CMD_AJP13_END_RESPONSE: + /* If we are overriding the errors, we must not send anything to + * the client, especially as the brigade already contains headers. + * So do nothing here, and it will be cleaned up below. + */ + status = ajp_parse_reuse(r, conn->data, &conn_reuse); + if (status != APR_SUCCESS) { + backend_failed = 1; + } + if (!ap_proxy_should_override(conf, r->status)) { + e = apr_bucket_eos_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + if (ap_pass_brigade(r->output_filters, + output_brigade) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00888) + "error processing end"); + client_failed = 1; + } + /* XXX: what about flush here? See mod_jk */ + data_sent = 1; + } + request_ended = 1; + break; + default: + backend_failed = 1; + break; + } + + /* + * If connection has been aborted by client: Stop working. + * Pretend we are done (data_sent) to avoid further processing. + */ + if (r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02821) + "client connection aborted"); + /* no response yet (or ever), set status for access log */ + if (!headers_sent) { + r->status = HTTP_BAD_REQUEST; + } + client_failed = 1; + /* return DONE */ + data_sent = 1; + break; + } + + /* + * We either have finished successfully or we failed. + * So bail out + */ + if ((result == CMD_AJP13_END_RESPONSE) + || backend_failed || client_failed) + break; + + /* read the response */ + status = ajp_read_header(conn->sock, r, maxsize, + (ajp_msg_t **)&(conn->data)); + if (status != APR_SUCCESS) { + backend_failed = 1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00889) + "ajp_read_header failed"); + break; + } + result = ajp_parse_type(r, conn->data); + } + apr_brigade_destroy(input_brigade); + + /* + * Clear output_brigade to remove possible buckets that remained there + * after an error. + */ + apr_brigade_cleanup(output_brigade); + + if (backend_failed || client_failed) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00890) + "Processing of request failed backend: %i, client: %i", + backend_failed, client_failed); + /* We had a failure: Close connection to backend */ + conn->close = 1; + if (data_sent) { + /* Return DONE to avoid error messages being added to the stream */ + rv = DONE; + } + } + else if (!request_ended) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00891) + "Processing of request didn't terminate cleanly"); + /* We had a failure: Close connection to backend */ + conn->close = 1; + backend_failed = 1; + if (data_sent) { + /* Return DONE to avoid error messages being added to the stream */ + rv = DONE; + } + } + else if (!conn_reuse) { + /* Our backend signalled connection close */ + conn->close = 1; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00892) + "got response from %pI (%s:%d)", + conn->worker->cp->addr, + conn->worker->s->hostname_ex, + (int)conn->worker->s->port); + + if (ap_proxy_should_override(conf, r->status)) { + /* clear r->status for override error, otherwise ErrorDocument + * thinks that this is a recursive error, and doesn't find the + * custom error page + */ + rv = r->status; + r->status = HTTP_OK; + /* + * prevent proxy_handler() from treating this as an + * internal error. + */ + apr_table_setn(r->notes, "proxy-error-override", "1"); + } + else { + rv = OK; + } + } + + if (backend_failed) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00893) + "dialog to %pI (%s:%d) failed", + conn->worker->cp->addr, + conn->worker->s->hostname_ex, + (int)conn->worker->s->port); + /* + * If we already send data, signal a broken backend connection + * upwards in the chain. + */ + if (data_sent) { + ap_proxy_backend_broke(r, output_brigade); + } else if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) { + /* + * This is only non fatal when we have not send (parts) of a possible + * request body so far (we do not store it and thus cannot send it + * again) and the method is idempotent. In this case we can dare to + * retry it with a different worker if we are a balancer member. + */ + rv = HTTP_SERVICE_UNAVAILABLE; + } else { + /* If we had a successful cping/cpong and then a timeout + * we assume it is a request that cause a back-end timeout, + * but doesn't affect the whole worker. + */ + if (APR_STATUS_IS_TIMEUP(status) && + conn->worker->s->ping_timeout_set) { + apr_table_setn(r->notes, "proxy_timedout", "1"); + rv = HTTP_GATEWAY_TIME_OUT; + } + else { + rv = HTTP_INTERNAL_SERVER_ERROR; + } + } + } + else if (client_failed) { + int level = (r->connection->aborted) ? APLOG_DEBUG : APLOG_ERR; + ap_log_rerror(APLOG_MARK, level, status, r, APLOGNO(02822) + "dialog with client %pI failed", + r->connection->client_addr); + if (rv == OK) { + rv = HTTP_BAD_REQUEST; + } + } + + /* + * Ensure that we sent an EOS bucket thru the filter chain, if we already + * have sent some data. Maybe ap_proxy_backend_broke was called and added + * one to the brigade already (no longer making it empty). So we should + * not do this in this case. + */ + if (data_sent && !r->eos_sent && !r->connection->aborted + && APR_BRIGADE_EMPTY(output_brigade)) { + e = apr_bucket_eos_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + } + + /* If we have added something to the brigade above, send it */ + if (!APR_BRIGADE_EMPTY(output_brigade) + && ap_pass_brigade(r->output_filters, output_brigade) != APR_SUCCESS) { + rv = AP_FILTER_ERROR; + } + + apr_brigade_destroy(output_brigade); + + if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { + conn->close = 1; + } + + return rv; +} + +/* + * This handles ajp:// URLs + */ +static int proxy_ajp_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + int status; + char server_portstr[32]; + conn_rec *origin = NULL; + proxy_conn_rec *backend = NULL; + const char *scheme = "AJP"; + int retry; + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_module); + apr_pool_t *p = r->pool; + apr_uri_t *uri; + + if (ap_cstr_casecmpn(url, "ajp:", 4) != 0) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00894) "declining URL %s", url); + return DECLINED; + } + + uri = apr_palloc(p, sizeof(*uri)); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00895) "serving URL %s", url); + + /* create space for state information */ + status = ap_proxy_acquire_connection(scheme, &backend, worker, + r->server); + if (status != OK) { + if (backend) { + backend->close = 1; + ap_proxy_release_connection(scheme, backend, r->server); + } + return status; + } + + backend->is_ssl = 0; + backend->close = 0; + + retry = 0; + while (retry < 2) { + char *locurl = url; + /* Step One: Determine Who To Connect To */ + status = ap_proxy_determine_connection(p, r, conf, worker, backend, + uri, &locurl, proxyname, proxyport, + server_portstr, + sizeof(server_portstr)); + + if (status != OK) + break; + + /* Step Two: Make the Connection */ + if (ap_proxy_check_connection(scheme, backend, r->server, 0, + PROXY_CHECK_CONN_EMPTY) + && ap_proxy_connect_backend(scheme, backend, worker, + r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00896) + "failed to make connection to backend: %s", + backend->hostname); + status = HTTP_SERVICE_UNAVAILABLE; + break; + } + + /* Handle CPING/CPONG */ + if (worker->s->ping_timeout_set) { + status = ajp_handle_cping_cpong(backend->sock, r, + worker->s->ping_timeout); + /* + * In case the CPING / CPONG failed for the first time we might be + * just out of luck and got a faulty backend connection, but the + * backend might be healthy nevertheless. So ensure that the backend + * TCP connection gets closed and try it once again. + */ + if (status != APR_SUCCESS) { + backend->close = 1; + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00897) + "cping/cpong failed to %pI (%s:%d)", + worker->cp->addr, worker->s->hostname_ex, + (int)worker->s->port); + status = HTTP_SERVICE_UNAVAILABLE; + retry++; + continue; + } + } + /* Step Three: Process the Request */ + status = ap_proxy_ajp_request(p, r, backend, origin, dconf, uri, locurl, + server_portstr); + break; + } + + /* Do not close the socket */ + ap_proxy_release_connection(scheme, backend, r->server); + return status; +} + +static void ap_proxy_http_register_hook(apr_pool_t *p) +{ + proxy_hook_scheme_handler(proxy_ajp_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(proxy_ajp_canon, NULL, NULL, APR_HOOK_FIRST); + APR_REGISTER_OPTIONAL_FN(ajp_handle_cping_cpong); +} + +AP_DECLARE_MODULE(proxy_ajp) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_http_register_hook /* register hooks */ +}; + diff --git a/modules/proxy/mod_proxy_ajp.dep b/modules/proxy/mod_proxy_ajp.dep new file mode 100644 index 0000000..475859a --- /dev/null +++ b/modules/proxy/mod_proxy_ajp.dep @@ -0,0 +1,356 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_ajp.mak + +.\mod_proxy_ajp.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\ajp.h"\ + ".\mod_proxy.h"\ + + +.\ajp_header.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\ajp.h"\ + ".\ajp_header.h"\ + ".\mod_proxy.h"\ + + +.\ajp_link.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\ajp.h"\ + ".\mod_proxy.h"\ + + +.\ajp_msg.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\ajp.h"\ + ".\mod_proxy.h"\ + + +.\ajp_utils.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\ajp.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_ajp.dsp b/modules/proxy/mod_proxy_ajp.dsp new file mode 100644 index 0000000..74d7b30 --- /dev/null +++ b/modules/proxy/mod_proxy_ajp.dsp @@ -0,0 +1,151 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_ajp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_ajp - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ajp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ajp.mak" CFG="mod_proxy_ajp - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_ajp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_ajp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_ajp_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /debug /out:".\Release\mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_ajp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_ajp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_ajp_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_ajp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_ajp - Win32 Release" +# Name "mod_proxy_ajp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_ajp.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Group "Ajp Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ajp.h +# End Source File +# Begin Source File + +SOURCE=.\ajp_header.c +# End Source File +# Begin Source File + +SOURCE=.\ajp_header.h +# End Source File +# Begin Source File + +SOURCE=.\ajp_link.c +# End Source File +# Begin Source File + +SOURCE=.\ajp_msg.c +# End Source File +# Begin Source File + +SOURCE=.\ajp_utils.c +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_ajp.mak b/modules/proxy/mod_proxy_ajp.mak new file mode 100644 index 0000000..b14a569 --- /dev/null +++ b/modules/proxy/mod_proxy_ajp.mak @@ -0,0 +1,416 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_ajp.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_ajp - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_ajp - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_ajp - Win32 Release" && "$(CFG)" != "mod_proxy_ajp - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ajp.mak" CFG="mod_proxy_ajp - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_ajp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_ajp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_ajp.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_ajp.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\ajp_header.obj" + -@erase "$(INTDIR)\ajp_link.obj" + -@erase "$(INTDIR)\ajp_msg.obj" + -@erase "$(INTDIR)\ajp_utils.obj" + -@erase "$(INTDIR)\mod_proxy_ajp.obj" + -@erase "$(INTDIR)\mod_proxy_ajp.res" + -@erase "$(INTDIR)\mod_proxy_ajp_src.idb" + -@erase "$(INTDIR)\mod_proxy_ajp_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_ajp.exp" + -@erase "$(OUTDIR)\mod_proxy_ajp.lib" + -@erase "$(OUTDIR)\mod_proxy_ajp.pdb" + -@erase "$(OUTDIR)\mod_proxy_ajp.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_ajp_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_ajp.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_ajp.pdb" /debug /out:"$(OUTDIR)\mod_proxy_ajp.so" /implib:"$(OUTDIR)\mod_proxy_ajp.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_ajp.obj" \ + "$(INTDIR)\ajp_header.obj" \ + "$(INTDIR)\ajp_link.obj" \ + "$(INTDIR)\ajp_msg.obj" \ + "$(INTDIR)\ajp_utils.obj" \ + "$(INTDIR)\mod_proxy_ajp.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_ajp.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_ajp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_ajp.so" + if exist .\Release\mod_proxy_ajp.so.manifest mt.exe -manifest .\Release\mod_proxy_ajp.so.manifest -outputresource:.\Release\mod_proxy_ajp.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_ajp - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_ajp.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_ajp.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\ajp_header.obj" + -@erase "$(INTDIR)\ajp_link.obj" + -@erase "$(INTDIR)\ajp_msg.obj" + -@erase "$(INTDIR)\ajp_utils.obj" + -@erase "$(INTDIR)\mod_proxy_ajp.obj" + -@erase "$(INTDIR)\mod_proxy_ajp.res" + -@erase "$(INTDIR)\mod_proxy_ajp_src.idb" + -@erase "$(INTDIR)\mod_proxy_ajp_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_ajp.exp" + -@erase "$(OUTDIR)\mod_proxy_ajp.lib" + -@erase "$(OUTDIR)\mod_proxy_ajp.pdb" + -@erase "$(OUTDIR)\mod_proxy_ajp.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_ajp_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_ajp.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_ajp.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_ajp.pdb" /debug /out:"$(OUTDIR)\mod_proxy_ajp.so" /implib:"$(OUTDIR)\mod_proxy_ajp.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_ajp.obj" \ + "$(INTDIR)\ajp_header.obj" \ + "$(INTDIR)\ajp_link.obj" \ + "$(INTDIR)\ajp_msg.obj" \ + "$(INTDIR)\ajp_utils.obj" \ + "$(INTDIR)\mod_proxy_ajp.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_ajp.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_ajp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_ajp.so" + if exist .\Debug\mod_proxy_ajp.so.manifest mt.exe -manifest .\Debug\mod_proxy_ajp.so.manifest -outputresource:.\Debug\mod_proxy_ajp.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_ajp.dep") +!INCLUDE "mod_proxy_ajp.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_ajp.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" || "$(CFG)" == "mod_proxy_ajp - Win32 Debug" +SOURCE=.\mod_proxy_ajp.c + +"$(INTDIR)\mod_proxy_ajp.obj" : $(SOURCE) "$(INTDIR)" + + +SOURCE=.\ajp_header.c + +"$(INTDIR)\ajp_header.obj" : $(SOURCE) "$(INTDIR)" + + +SOURCE=.\ajp_link.c + +"$(INTDIR)\ajp_link.obj" : $(SOURCE) "$(INTDIR)" + + +SOURCE=.\ajp_msg.c + +"$(INTDIR)\ajp_msg.obj" : $(SOURCE) "$(INTDIR)" + + +SOURCE=.\ajp_utils.c + +"$(INTDIR)\ajp_utils.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_ajp - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_ajp - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_ajp - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_ajp - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_ajp - Win32 Release" + + +"$(INTDIR)\mod_proxy_ajp.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_ajp.so" /d LONG_NAME="proxy_ajp_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c new file mode 100644 index 0000000..b8b452d --- /dev/null +++ b/modules/proxy/mod_proxy_balancer.c @@ -0,0 +1,2101 @@ +/* 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. + */ + +/* Load balancer module for Apache proxy */ + +#include "mod_proxy.h" +#include "scoreboard.h" +#include "ap_mpm.h" +#include "apr_version.h" +#include "ap_hooks.h" +#include "apr_date.h" +#include "util_md5.h" +#include "mod_watchdog.h" + +static const char *balancer_mutex_type = "proxy-balancer-shm"; +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. + */ +static int balancer_pre_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp) +{ + + apr_status_t rv; + + rv = ap_mutex_register(pconf, balancer_mutex_type, NULL, + APR_LOCK_DEFAULT, 0); + 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; +} + +#if 0 +extern void proxy_update_members(proxy_balancer **balancer, request_rec *r, + proxy_server_conf *conf); +#endif + +static int proxy_balancer_canon(request_rec *r, char *url) +{ + char *host, *path; + char *search = NULL; + const char *err; + apr_port_t port = 0; + + /* TODO: offset of BALANCER_PREFIX ?? */ + if (ap_cstr_casecmpn(url, "balancer:", 9) == 0) { + url += 9; + } + else { + return DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url); + + /* do syntatic check. + * We break the URL into host, port, path, search + */ + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01157) + "error parsing URL %s: %s", + url, err); + return HTTP_BAD_REQUEST; + } + /* + * now parse path/search args, according to rfc1738: + * process the path. With proxy-noncanon set (by + * mod_proxy) we use the raw, unparsed uri + */ + if (apr_table_get(r->notes, "proxy-nocanon")) { + path = url; /* this is the raw path */ + } + else if (apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the encoded path already */ + search = r->args; + } + else { + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + search = r->args; + } + /* + * If we have a raw control character or a ' ' in nocanon path or + * r->args, correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10416) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + if (search && *ap_scan_vchar_obstext(search)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10407) + "To be forwarded query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + r->filename = apr_pstrcat(r->pool, "proxy:" BALANCER_PREFIX, host, + "/", path, (search) ? "?" : "", (search) ? search : "", NULL); + + r->path_info = apr_pstrcat(r->pool, "/", path, NULL); + + return OK; +} + +static void init_balancer_members(apr_pool_t *p, server_rec *s, + proxy_balancer *balancer) +{ + int i; + proxy_worker **workers; + + workers = (proxy_worker **)balancer->workers->elts; + + for (i = 0; i < balancer->workers->nelts; i++) { + int worker_is_initialized; + proxy_worker *worker = *workers; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01158) + "Looking at %s -> %s initialized?", balancer->s->name, + ap_proxy_worker_name(p, worker)); + worker_is_initialized = PROXY_WORKER_IS_INITIALIZED(worker); + if (!worker_is_initialized) { + ap_proxy_initialize_worker(worker, s, p); + } + ++workers; + } + + /* Set default number of attempts to the number of + * workers. + */ + if (!balancer->s->max_attempts_set && balancer->workers->nelts > 1) { + balancer->s->max_attempts = balancer->workers->nelts - 1; + balancer->s->max_attempts_set = 1; + } +} + +/* Retrieve the parameter with the given name + * Something like 'JSESSIONID=12345...N' + */ +static char *get_path_param(apr_pool_t *pool, char *url, + const char *name, int scolon_sep) +{ + char *path = NULL; + char *pathdelims = "?&"; + + if (scolon_sep) { + pathdelims = ";?&"; + } + for (path = strstr(url, name); path; path = strstr(path + 1, name)) { + path += strlen(name); + if (*path == '=') { + /* + * Session path was found, get its value + */ + ++path; + if (*path) { + char *q; + path = apr_strtok(apr_pstrdup(pool, path), pathdelims, &q); + return path; + } + } + } + return NULL; +} + +static char *get_cookie_param(request_rec *r, const char *name) +{ + const char *cookies; + const char *start_cookie; + + if ((cookies = apr_table_get(r->headers_in, "Cookie"))) { + for (start_cookie = ap_strstr_c(cookies, name); start_cookie; + start_cookie = ap_strstr_c(start_cookie + 1, name)) { + if (start_cookie == cookies || + start_cookie[-1] == ';' || + start_cookie[-1] == ',' || + isspace(start_cookie[-1])) { + + start_cookie += strlen(name); + while(*start_cookie && isspace(*start_cookie)) + ++start_cookie; + if (*start_cookie++ == '=' && *start_cookie) { + /* + * Session cookie was found, get its value + */ + char *end_cookie, *cookie; + cookie = apr_pstrdup(r->pool, start_cookie); + if ((end_cookie = strchr(cookie, ';')) != NULL) + *end_cookie = '\0'; + if((end_cookie = strchr(cookie, ',')) != NULL) + *end_cookie = '\0'; + return cookie; + } + } + } + } + return NULL; +} + +/* Find the worker that has the 'route' defined + */ +static proxy_worker *find_route_worker(proxy_balancer *balancer, + const char *route, request_rec *r, + int recursion) +{ + int i; + int checking_standby; + int checked_standby; + + proxy_worker **workers; + + checking_standby = checked_standby = 0; + while (!checked_standby) { + workers = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, workers++) { + proxy_worker *worker = *workers; + if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) + continue; + if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) { + if (PROXY_WORKER_IS_USABLE(worker)) { + return worker; + } else { + /* + * If the worker is in error state run + * retry on that worker. It will be marked as + * operational if the retry timeout is elapsed. + * The worker might still be unusable, but we try + * anyway. + */ + ap_proxy_retry_worker_fn("BALANCER", worker, r->server); + if (PROXY_WORKER_IS_USABLE(worker)) { + return worker; + } else { + /* + * We have a worker that is unusable. + * It can be in error or disabled, but in case + * it has a redirection set use that redirection worker. + * This enables to safely remove the member from the + * balancer. Of course you will need some kind of + * session replication between those two remote. + * Also check that we haven't gone thru all the + * balancer members by means of redirects. + * This should avoid redirect cycles. + */ + if ((*worker->s->redirect) + && (recursion < balancer->workers->nelts)) { + proxy_worker *rworker = NULL; + rworker = find_route_worker(balancer, worker->s->redirect, + r, recursion + 1); + /* Check if the redirect worker is usable */ + if (rworker && !PROXY_WORKER_IS_USABLE(rworker)) { + /* + * If the worker is in error state run + * retry on that worker. It will be marked as + * operational if the retry timeout is elapsed. + * The worker might still be unusable, but we try + * anyway. + */ + ap_proxy_retry_worker_fn("BALANCER", rworker, r->server); + } + if (rworker && PROXY_WORKER_IS_USABLE(rworker)) + return rworker; + } + } + } + } + } + checked_standby = checking_standby++; + } + return NULL; +} + +static proxy_worker *find_session_route(proxy_balancer *balancer, + request_rec *r, + char **route, + const char **sticky_used, + char **url) +{ + proxy_worker *worker = NULL; + + if (!*balancer->s->sticky) + return NULL; + /* Try to find the sticky route inside url */ + *route = get_path_param(r->pool, *url, balancer->s->sticky_path, balancer->s->scolonsep); + if (*route) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01159) + "Found value %s for stickysession %s", + *route, balancer->s->sticky_path); + *sticky_used = balancer->s->sticky_path; + } + else { + *route = get_cookie_param(r, balancer->s->sticky); + if (*route) { + *sticky_used = balancer->s->sticky; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01160) + "Found value %s for stickysession %s", + *route, balancer->s->sticky); + } + } + /* + * If we found a value for stickysession, find the first '.' (or whatever + * sticky_separator is set to) within. Everything after '.' (if present) + * is our route. + */ + if ((*route) && (balancer->s->sticky_separator != 0) && ((*route = strchr(*route, balancer->s->sticky_separator)) != NULL )) + (*route)++; + if ((*route) && (**route)) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01161) "Found route %s", *route); + /* We have a route in path or in cookie + * Find the worker that has this route defined. + */ + worker = find_route_worker(balancer, *route, r, 1); + if (worker && strcmp(*route, worker->s->route)) { + /* + * Notice that the route of the worker chosen is different from + * the route supplied by the client. + */ + apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01162) + "Route changed from %s to %s", + *route, worker->s->route); + } + return worker; + } + else + return NULL; +} + +static proxy_worker *find_best_worker(proxy_balancer *balancer, + request_rec *r) +{ + proxy_worker *candidate = NULL; + apr_status_t rv; + +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01163) + "%s: Lock failed for find_best_worker()", + balancer->s->name); + return NULL; + } +#endif + + candidate = (*balancer->lbmethod->finder)(balancer, r); + + if (candidate) + candidate->s->elected++; + +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01164) + "%s: Unlock failed for find_best_worker()", + balancer->s->name); + } +#endif + + if (candidate == NULL) { + /* All the workers are in error state or disabled. + * If the balancer has a timeout sleep for a while + * and try again to find the worker. The chances are + * that some other thread will release a connection. + * By default the timeout is not set, and the server + * returns SERVER_BUSY. + */ + if (balancer->s->timeout) { + /* XXX: This can perhaps be build using some + * smarter mechanism, like tread_cond. + * But since the statuses can came from + * different children, use the provided algo. + */ + apr_interval_time_t timeout = balancer->s->timeout; + apr_interval_time_t step, tval = 0; + /* Set the timeout to 0 so that we don't + * end in infinite loop + */ + balancer->s->timeout = 0; + step = timeout / 100; + while (tval < timeout) { + apr_sleep(step); + /* Try again */ + if ((candidate = find_best_worker(balancer, r))) + break; + tval += step; + } + /* restore the timeout */ + balancer->s->timeout = timeout; + } + } + + return candidate; + +} + +static int rewrite_url(request_rec *r, proxy_worker *worker, + char **url) +{ + const char *scheme = strstr(*url, "://"); + const char *path = NULL; + + if (scheme) + path = ap_strchr_c(scheme + 3, '/'); + + /* we break the URL into host, port, uri */ + if (!worker) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool, + "missing worker. URI cannot be parsed: ", *url, + NULL)); + } + + *url = apr_pstrcat(r->pool, worker->s->name_ex, path, NULL); + + return OK; +} + +static void force_recovery(proxy_balancer *balancer, server_rec *s) +{ + int i; + int ok = 0; + proxy_worker **worker; + + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) { + ok = 1; + break; + } + else { + /* Try if we can recover */ + ap_proxy_retry_worker_fn("BALANCER", *worker, s); + if (!((*worker)->s->status & PROXY_WORKER_IN_ERROR)) { + ok = 1; + break; + } + } + } + if (!ok && balancer->s->forcerecovery) { + /* If all workers are in error state force the recovery. + */ + worker = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, worker++) { + ++(*worker)->s->retries; + (*worker)->s->status &= ~PROXY_WORKER_IN_ERROR; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01165) + "%s: Forcing recovery for worker (%s:%d)", + balancer->s->name, (*worker)->s->hostname_ex, + (int)(*worker)->s->port); + } + } +} + +static apr_status_t decrement_busy_count(void *worker_) +{ + proxy_worker *worker = worker_; + + if (worker->s->busy) { + worker->s->busy--; + } + + return APR_SUCCESS; +} + +static int proxy_balancer_pre_request(proxy_worker **worker, + proxy_balancer **balancer, + request_rec *r, + proxy_server_conf *conf, char **url) +{ + int access_status; + proxy_worker *runtime; + char *route = NULL; + const char *sticky = NULL; + apr_status_t rv; + + *worker = NULL; + /* Step 1: check if the url is for us + * The url we can handle starts with 'balancer://' + * If balancer is already provided skip the search + * for balancer, because this is failover attempt. + */ + if (!*balancer && + !(*balancer = ap_proxy_get_balancer(r->pool, conf, *url, 1))) + return DECLINED; + + /* Step 2: Lock the LoadBalancer + * XXX: perhaps we need the process lock here + */ +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_LOCK(*balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01166) + "%s: Lock failed for pre_request", (*balancer)->s->name); + return DECLINED; + } +#endif + + /* Step 3: force recovery */ + force_recovery(*balancer, r->server); + + /* Step 3.5: Update member list for the balancer */ + /* TODO: Implement as provider! */ + ap_proxy_sync_balancer(*balancer, r->server, conf); + + /* Step 4: find the session route */ + runtime = find_session_route(*balancer, r, &route, &sticky, url); + if (runtime) { + if ((*balancer)->lbmethod && (*balancer)->lbmethod->updatelbstatus) { + /* Call the LB implementation */ + (*balancer)->lbmethod->updatelbstatus(*balancer, runtime, r->server); + } + else { /* Use the default one */ + int i, total_factor = 0; + proxy_worker **workers; + /* We have a sticky load balancer + * Update the workers status + * so that even session routes get + * into account. + */ + workers = (proxy_worker **)(*balancer)->workers->elts; + for (i = 0; i < (*balancer)->workers->nelts; i++) { + /* Take into calculation only the workers that are + * not in error state or not disabled. + */ + if (PROXY_WORKER_IS_USABLE(*workers)) { + (*workers)->s->lbstatus += (*workers)->s->lbfactor; + total_factor += (*workers)->s->lbfactor; + } + workers++; + } + runtime->s->lbstatus -= total_factor; + } + runtime->s->elected++; + + *worker = runtime; + } + else if (route && (*balancer)->s->sticky_force) { + int i, member_of = 0; + proxy_worker **workers; + /* + * We have a route provided that doesn't match the + * balancer name. See if the provider route is the + * member of the same balancer in which case return 503 + */ + workers = (proxy_worker **)(*balancer)->workers->elts; + for (i = 0; i < (*balancer)->workers->nelts; i++) { + if (*((*workers)->s->route) && strcmp((*workers)->s->route, route) == 0) { + member_of = 1; + break; + } + workers++; + } + if (member_of) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01167) + "%s: All workers are in error state for route (%s)", + (*balancer)->s->name, route); +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01168) + "%s: Unlock failed for pre_request", + (*balancer)->s->name); + } +#endif + return HTTP_SERVICE_UNAVAILABLE; + } + } + +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_UNLOCK(*balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01169) + "%s: Unlock failed for pre_request", + (*balancer)->s->name); + } +#endif + if (!*worker) { + runtime = find_best_worker(*balancer, r); + if (!runtime) { + if ((*balancer)->workers->nelts) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01170) + "%s: All workers are in error state", + (*balancer)->s->name); + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01171) + "%s: No workers in balancer", + (*balancer)->s->name); + } + + return HTTP_SERVICE_UNAVAILABLE; + } + if (*(*balancer)->s->sticky && runtime) { + /* + * This balancer has sticky sessions and the client either has not + * supplied any routing information or all workers for this route + * including possible redirect and hotstandby workers are in error + * state, but we have found another working worker for this + * balancer where we can send the request. Thus notice that we have + * changed the route to the backend. + */ + apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); + } + *worker = runtime; + } + + (*worker)->s->busy++; + apr_pool_cleanup_register(r->pool, *worker, decrement_busy_count, + apr_pool_cleanup_null); + + /* Add balancer/worker info to env. */ + apr_table_setn(r->subprocess_env, + "BALANCER_NAME", (*balancer)->s->name); + apr_table_setn(r->subprocess_env, + "BALANCER_WORKER_NAME", (*worker)->s->name_ex); + apr_table_setn(r->subprocess_env, + "BALANCER_WORKER_ROUTE", (*worker)->s->route); + + /* Rewrite the url from 'balancer://url' + * to the 'worker_scheme://worker_hostname[:worker_port]/url' + * This replaces the balancers fictional name with the + * real hostname of the elected worker. + */ + access_status = rewrite_url(r, *worker, url); + /* Add the session route to request notes if present */ + if (route) { + apr_table_setn(r->notes, "session-sticky", sticky); + apr_table_setn(r->notes, "session-route", route); + + /* Add session info to env. */ + apr_table_setn(r->subprocess_env, + "BALANCER_SESSION_STICKY", sticky); + apr_table_setn(r->subprocess_env, + "BALANCER_SESSION_ROUTE", route); + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01172) + "%s: worker (%s) rewritten to %s", + (*balancer)->s->name, (*worker)->s->name_ex, *url); + + return access_status; +} + +static int proxy_balancer_post_request(proxy_worker *worker, + proxy_balancer *balancer, + request_rec *r, + proxy_server_conf *conf) +{ + + apr_status_t rv; + +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01173) + "%s: Lock failed for post_request", + balancer->s->name); + return HTTP_INTERNAL_SERVER_ERROR; + } +#endif + + 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]; + if (r->status == val) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01174) + "%s: Forcing worker (%s) into error state " + "due to status code %d matching 'failonstatus' " + "balancer parameter", + balancer->s->name, ap_proxy_worker_name(r->pool, worker), + val); + worker->s->status |= PROXY_WORKER_IN_ERROR; + worker->s->error_time = apr_time_now(); + break; + } + } + } + + 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 " + "due to timeout and 'failontimeout' parameter being set", + balancer->s->name, ap_proxy_worker_name(r->pool, worker)); + worker->s->status |= PROXY_WORKER_IN_ERROR; + worker->s->error_time = apr_time_now(); + + } +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01175) + "%s: Unlock failed for post_request", balancer->s->name); + } +#endif + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01176) + "proxy_balancer_post_request for (%s)", balancer->s->name); + + return OK; +} + +static void recalc_factors(proxy_balancer *balancer) +{ + int i; + proxy_worker **workers; + + + /* Recalculate lbfactors */ + workers = (proxy_worker **)balancer->workers->elts; + /* Special case if there is only one worker its + * load factor will always be 100 + */ + if (balancer->workers->nelts == 1) { + (*workers)->s->lbstatus = (*workers)->s->lbfactor = 100; + return; + } + for (i = 0; i < balancer->workers->nelts; i++) { + /* Update the status entries */ + workers[i]->s->lbstatus = workers[i]->s->lbfactor; + } +} + +static apr_status_t lock_remove(void *data) +{ + int i; + proxy_balancer *balancer; + server_rec *s = data; + void *sconf = s->module_config; + proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++, balancer++) { + if (balancer->gmutex) { + apr_global_mutex_destroy(balancer->gmutex); + balancer->gmutex = NULL; + } + } + return(0); +} + +/* + * Compute an ID for a vhost based on what makes it selected by requests. + * The second and more Host(s)/IP(s):port(s), and the ServerAlias(es) are + * optional (see make_servers_ids() below). + */ + static const char *make_server_id(server_rec *s, apr_pool_t *p, int full) +{ + apr_md5_ctx_t md5_ctx; + unsigned char md5[APR_MD5_DIGESTSIZE]; + char id[2 * APR_MD5_DIGESTSIZE + 1]; + char host_ip[64]; /* for any IPv[46] string */ + server_addr_rec *sar; + int i; + + apr_md5_init(&md5_ctx); + for (sar = s->addrs; sar; sar = sar->next) { + host_ip[0] = '\0'; + apr_sockaddr_ip_getbuf(host_ip, sizeof host_ip, sar->host_addr); + apr_md5_update(&md5_ctx, (void *)sar->virthost, strlen(sar->virthost)); + apr_md5_update(&md5_ctx, (void *)host_ip, strlen(host_ip)); + apr_md5_update(&md5_ctx, (void *)&sar->host_port, + sizeof(sar->host_port)); + if (!full) { + break; + } + } + if (s->server_hostname) { + apr_md5_update(&md5_ctx, (void *)s->server_hostname, + strlen(s->server_hostname)); + } + if (full) { + if (s->names) { + for (i = 0; i < s->names->nelts; ++i) { + const char *name = APR_ARRAY_IDX(s->names, i, char *); + apr_md5_update(&md5_ctx, (void *)name, strlen(name)); + } + } + if (s->wild_names) { + for (i = 0; i < s->wild_names->nelts; ++i) { + const char *name = APR_ARRAY_IDX(s->wild_names, i, char *); + apr_md5_update(&md5_ctx, (void *)name, strlen(name)); + } + } + } + apr_md5_final(md5, &md5_ctx); + ap_bin2hex(md5, APR_MD5_DIGESTSIZE, id); + + return apr_pstrmemdup(p, id, sizeof(id) - 1); +} + +/* + * First try to compute an unique ID for each vhost with minimal criteria, + * that is the first Host/IP:port and ServerName. For most cases this should + * be enough and avoids changing the ID unnecessarily across restart (or + * stop/start w.r.t. persisted files) for things that this module does not + * care about. + * + * But if it's not enough (collisions) do a second pass for the full monty, + * that is additionally the other Host(s)/IP(s):port(s) and ServerAlias(es). + * + * Finally, for pathological configs where this is still not enough, let's + * append a counter to duplicates, because we really want that ID to be unique + * even if the vhost will never be selected to handle requests at run time, at + * load time a duplicate may steal the original slotmems (depending on its + * balancers' configurations), see how mod_slotmem_shm reuses slots/files based + * solely on this ID and resets them if the sizes don't match. + */ +static apr_array_header_t *make_servers_ids(server_rec *main_s, apr_pool_t *p) +{ + server_rec *s = main_s; + apr_array_header_t *ids = apr_array_make(p, 10, sizeof(const char *)); + apr_hash_t *dups = apr_hash_make(p); + int idx, *dup, full_monty = 0; + const char *id; + + for (idx = 0, s = main_s; s; s = s->next, ++idx) { + id = make_server_id(s, p, 0); + dup = apr_hash_get(dups, id, APR_HASH_KEY_STRING); + apr_hash_set(dups, id, APR_HASH_KEY_STRING, + apr_pmemdup(p, &idx, sizeof(int))); + if (dup) { + full_monty = 1; + APR_ARRAY_IDX(ids, *dup, const char *) = NULL; + APR_ARRAY_PUSH(ids, const char *) = NULL; + } + else { + APR_ARRAY_PUSH(ids, const char *) = id; + } + } + if (full_monty) { + apr_hash_clear(dups); + for (idx = 0, s = main_s; s; s = s->next, ++idx) { + id = APR_ARRAY_IDX(ids, idx, const char *); + if (id) { + /* Preserve non-duplicates */ + continue; + } + id = make_server_id(s, p, 1); + if (apr_hash_get(dups, id, APR_HASH_KEY_STRING)) { + id = apr_psprintf(p, "%s_%x", id, idx); + } + else { + apr_hash_set(dups, id, APR_HASH_KEY_STRING, (void *)-1); + } + APR_ARRAY_IDX(ids, idx, const char *) = id; + } + } + + return ids; +} + +/* post_config hook: */ +static int balancer_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + apr_status_t rv; + proxy_server_conf *conf; + ap_slotmem_instance_t *new = NULL; + apr_time_t tstamp; + apr_array_header_t *ids; + int idx; + + /* balancer_post_config() will be called twice during startup. So, don't + * set up the static data the 1st time through. */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + + ap_proxy_retry_worker_fn = + APR_RETRIEVE_OPTIONAL_FN(ap_proxy_retry_worker); + if (!ap_proxy_retry_worker_fn) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02230) + "mod_proxy must be loaded for mod_proxy_balancer"); + return !OK; + } + + /* + * Get slotmem setups + */ + storage = ap_lookup_provider(AP_SLOTMEM_PROVIDER_GROUP, "shm", + AP_SLOTMEM_PROVIDER_VERSION); + if (!storage) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01177) + "Failed to lookup provider 'shm' for '%s': is " + "mod_slotmem_shm loaded??", + AP_SLOTMEM_PROVIDER_GROUP); + return !OK; + } + + ids = make_servers_ids(s, ptemp); + + tstamp = apr_time_now(); + /* + * Go thru each Vhost and create the shared mem slotmem for + * each balancer's workers + */ + for (idx = 0; s; ++idx) { + int i,j; + const char *id; + proxy_balancer *balancer; + ap_slotmem_type_t type; + void *sconf = s->module_config; + conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module); + /* + * During create_proxy_config() we created a dummy id. Now that + * we have identifying info, we can create the real id + */ + id = APR_ARRAY_IDX(ids, idx, const char *); + conf->id = apr_psprintf(pconf, "p%x", + ap_proxy_hashfunc(id, PROXY_HASHFUNC_DEFAULT)); + if (conf->bslot) { + /* Shared memory already created for this proxy_server_conf. + */ + s = s->next; + continue; + } + if (conf->bal_persist) { + type = AP_SLOTMEM_TYPE_PERSIST | AP_SLOTMEM_TYPE_CLEARINUSE; + } else { + type = 0; + } + if (conf->balancers->nelts) { + conf->max_balancers = conf->balancers->nelts + conf->bgrowth; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01178) "Doing balancers create: %d, %d (%d)", + (int)ALIGNED_PROXY_BALANCER_SHARED_SIZE, + (int)conf->balancers->nelts, conf->max_balancers); + + rv = storage->create(&new, conf->id, + ALIGNED_PROXY_BALANCER_SHARED_SIZE, + conf->max_balancers, type, pconf); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01179) "balancer slotmem_create failed"); + return !OK; + } + conf->bslot = new; + } + conf->storage = storage; + + /* Initialize shared scoreboard data */ + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++, balancer++) { + proxy_worker **workers; + proxy_worker *worker; + proxy_balancer_shared *bshm; + const char *sname; + unsigned int index; + + /* now that we have the right id, we need to redo the sname field */ + ap_pstr2_alnum(pconf, balancer->s->name + sizeof(BALANCER_PREFIX) - 1, + &sname); + sname = apr_pstrcat(pconf, conf->id, "_", sname, NULL); + PROXY_STRNCPY(balancer->s->sname, sname); /* We know this will succeed */ + + balancer->max_workers = balancer->workers->nelts + balancer->growth; + /* Create global mutex */ + rv = ap_global_mutex_create(&(balancer->gmutex), NULL, balancer_mutex_type, + balancer->s->sname, s, pconf, 0); + if (rv != APR_SUCCESS || !balancer->gmutex) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01180) + "mutex creation of %s : %s failed", balancer_mutex_type, + balancer->s->sname); + return HTTP_INTERNAL_SERVER_ERROR; + } + apr_pool_cleanup_register(pconf, (void *)s, lock_remove, + apr_pool_cleanup_null); + + /* setup shm for balancers */ + bshm = ap_proxy_find_balancershm(storage, conf->bslot, balancer, &index); + if (bshm) { + if ((rv = storage->fgrab(conf->bslot, index)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02408) "balancer slotmem_fgrab failed"); + return !OK; + } + } + else { + if ((rv = storage->grab(conf->bslot, &index)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01181) "balancer slotmem_grab failed"); + return !OK; + } + if ((rv = storage->dptr(conf->bslot, index, (void *)&bshm)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01182) "balancer slotmem_dptr failed"); + return !OK; + } + } + if ((rv = ap_proxy_share_balancer(balancer, bshm, index)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01183) "Cannot share balancer"); + return !OK; + } + + /* create slotmem slots for workers */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01184) "Doing workers create: %s (%s), %d, %d [%u]", + balancer->s->name, balancer->s->sname, + (int)ALIGNED_PROXY_WORKER_SHARED_SIZE, + (int)balancer->max_workers, i); + + rv = storage->create(&new, balancer->s->sname, + ALIGNED_PROXY_WORKER_SHARED_SIZE, + balancer->max_workers, type, pconf); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01185) "worker slotmem_create failed"); + return !OK; + } + balancer->wslot = new; + balancer->storage = storage; + + /* sync all timestamps */ + balancer->wupdated = balancer->s->wupdated = tstamp; + + /* now go thru each worker */ + workers = (proxy_worker **)balancer->workers->elts; + for (j = 0; j < balancer->workers->nelts; j++, workers++) { + proxy_worker_shared *shm; + + worker = *workers; + + shm = ap_proxy_find_workershm(storage, balancer->wslot, worker, &index); + if (shm) { + if ((rv = storage->fgrab(balancer->wslot, index)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(02409) "worker slotmem_fgrab failed"); + return !OK; + } + } + else { + if ((rv = storage->grab(balancer->wslot, &index)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01186) "worker slotmem_grab failed"); + return !OK; + + } + if ((rv = storage->dptr(balancer->wslot, index, (void *)&shm)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01187) "worker slotmem_dptr failed"); + return !OK; + } + } + if ((rv = ap_proxy_share_worker(worker, shm, index)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(01188) "Cannot share worker"); + return !OK; + } + worker->s->updated = tstamp; + } + if (conf->bal_persist) { + /* We could have just read-in a persisted config. Force a sync. */ + balancer->wupdated--; + ap_proxy_sync_balancer(balancer, s, conf); + } + } + s = s->next; + } + + return OK; +} + +static void create_radio(const char *name, unsigned int flag, request_rec *r) +{ + 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/> <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); +} + +static void push2table(const char *input, apr_table_t *params, + const char *allowed[], apr_pool_t *p) +{ + char *args; + char *tok, *val; + char *key; + + if (input == NULL) { + return; + } + args = apr_pstrdup(p, input); + + key = apr_strtok(args, "&", &tok); + while (key) { + val = strchr(key, '='); + if (val) { + *val++ = '\0'; + } + else { + val = ""; + } + ap_unescape_url(key); + ap_unescape_url(val); + /* hcuri, worker name, balancer name, at least are escaped when building the form, so twice */ + ap_unescape_url(val); + if (allowed == NULL) { /* allow all */ + apr_table_set(params, key, val); + } + else { + const char **ok = allowed; + while (*ok) { + if (strcmp(*ok, key) == 0) { + apr_table_set(params, key, val); + break; + } + ok++; + } + } + key = apr_strtok(NULL, "&", &tok); + } +} + +/* Returns non-zero if the Referer: header value passed matches the + * host of the request. */ +static int safe_referer(request_rec *r, const char *ref) +{ + apr_uri_t uri; + + if (apr_uri_parse(r->pool, ref, &uri) || !uri.hostname) + return 0; + + return strcasecmp(uri.hostname, ap_get_server_name(r)) == 0; +} + +/* + * Process the paramters and add or update the worker of the + * balancer. Must only be called if the nonce has been validated to + * match, to avoid XSS attacks. + */ +static int balancer_process_balancer_worker(request_rec *r, proxy_server_conf *conf, + proxy_balancer *bsel, + proxy_worker *wsel, + apr_table_t *params) + +{ + apr_status_t rv; + /* First set the params */ + if (wsel) { + const char *val; + int was_usable = PROXY_WORKER_IS_USABLE(wsel); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01192) "settings worker params"); + + if ((val = apr_table_get(params, "w_lf"))) { + int ival; + double fval = atof(val); + ival = fval * 100.0; + if (ival >= 100 && ival <= 10000) { + wsel->s->lbfactor = ival; + if (bsel) + recalc_factors(bsel); + } + } + if ((val = apr_table_get(params, "w_wr"))) { + if (strlen(val) && strlen(val) < sizeof(wsel->s->route)) + strcpy(wsel->s->route, val); + else + *wsel->s->route = '\0'; + } + if ((val = apr_table_get(params, "w_rr"))) { + if (strlen(val) && strlen(val) < sizeof(wsel->s->redirect)) + strcpy(wsel->s->redirect, val); + 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(PROXY_WORKER_IGNORE_ERRORS_FLAG, atoi(val), wsel); + } + if ((val = apr_table_get(params, "w_status_N"))) { + ap_proxy_set_wstatus(PROXY_WORKER_DRAIN_FLAG, atoi(val), wsel); + } + if ((val = apr_table_get(params, "w_status_D"))) { + ap_proxy_set_wstatus(PROXY_WORKER_DISABLED_FLAG, atoi(val), wsel); + } + if ((val = apr_table_get(params, "w_status_H"))) { + ap_proxy_set_wstatus(PROXY_WORKER_HOT_STANDBY_FLAG, atoi(val), wsel); + } + if ((val = apr_table_get(params, "w_status_R"))) { + ap_proxy_set_wstatus(PROXY_WORKER_HOT_SPARE_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); + if (ival >= 0 && ival <= 99) { + wsel->s->lbset = ival; + } + } + if ((val = apr_table_get(params, "w_hi"))) { + apr_interval_time_t hci; + if (ap_timeout_parameter_parse(val, &hci, "ms") == APR_SUCCESS) { + if (hci >= AP_WD_TM_SLICE) { + wsel->s->interval = hci; + } + } + } + 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 (!ap_cstr_casecmp(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->method == CPING) { + *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; + } + + } + + if (bsel) { + const char *val; + int ival; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01193) + "settings balancer params"); + if ((val = apr_table_get(params, "b_lbm"))) { + if ((strlen(val) < (sizeof(bsel->s->lbpname)-1)) && + strcmp(val, bsel->s->lbpname)) { + proxy_balancer_method *lbmethod; + lbmethod = ap_lookup_provider(PROXY_LBMETHOD, val, "0"); + if (lbmethod) { + PROXY_STRNCPY(bsel->s->lbpname, val); + bsel->lbmethod = lbmethod; + bsel->s->wupdated = apr_time_now(); + bsel->s->need_reset = 1; + } + } + } + if ((val = apr_table_get(params, "b_tmo"))) { + ival = atoi(val); + if (ival >= 0 && ival <= 7200) { /* 2 hrs enuff? */ + bsel->s->timeout = apr_time_from_sec(ival); + } + } + if ((val = apr_table_get(params, "b_max"))) { + ival = atoi(val); + if (ival >= 0 && ival <= 99) { + bsel->s->max_attempts = ival; + } + } + if ((val = apr_table_get(params, "b_sforce"))) { + ival = atoi(val); + bsel->s->sticky_force = (ival != 0); + } + if ((val = apr_table_get(params, "b_ss")) && *val) { + if (strlen(val) < (sizeof(bsel->s->sticky_path)-1)) { + if (*val == '-' && *(val+1) == '\0') + *bsel->s->sticky_path = *bsel->s->sticky = '\0'; + else { + char *path; + PROXY_STRNCPY(bsel->s->sticky_path, val); + PROXY_STRNCPY(bsel->s->sticky, val); + + if ((path = strchr((char *)bsel->s->sticky, '|'))) { + *path++ = '\0'; + PROXY_STRNCPY(bsel->s->sticky_path, path); + } + } + } + } + if ((val = apr_table_get(params, "b_wyes")) && + (*val == '1' && *(val+1) == '\0') && + (val = apr_table_get(params, "b_nwrkr"))) { + char *ret; + proxy_worker *nworker; + nworker = ap_proxy_get_worker(r->pool, bsel, conf, val); + if (!nworker && storage->num_free_slots(bsel->wslot)) { +#if APR_HAS_THREADS + if ((rv = PROXY_GLOBAL_LOCK(bsel)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01194) + "%s: Lock failed for adding worker", + bsel->s->name); + } +#endif + ret = ap_proxy_define_worker(conf->pool, &nworker, bsel, conf, val, 0); + if (!ret) { + unsigned int index; + proxy_worker_shared *shm; + PROXY_COPY_CONF_PARAMS(nworker, conf); + if ((rv = storage->grab(bsel->wslot, &index)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01195) + "worker slotmem_grab failed"); +#if APR_HAS_THREADS + if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01196) + "%s: Unlock failed for adding worker", + bsel->s->name); + } +#endif + return HTTP_BAD_REQUEST; + } + if ((rv = storage->dptr(bsel->wslot, index, (void *)&shm)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01197) + "worker slotmem_dptr failed"); +#if APR_HAS_THREADS + if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01198) + "%s: Unlock failed for adding worker", + bsel->s->name); + } +#endif + return HTTP_BAD_REQUEST; + } + if ((rv = ap_proxy_share_worker(nworker, shm, index)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01199) + "Cannot share worker"); +#if APR_HAS_THREADS + if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01200) + "%s: Unlock failed for adding worker", + bsel->s->name); + } +#endif + return HTTP_BAD_REQUEST; + } + if ((rv = ap_proxy_initialize_worker(nworker, r->server, conf->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, APLOGNO(01201) + "Cannot init worker"); +#if APR_HAS_THREADS + if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01202) + "%s: Unlock failed for adding worker", + bsel->s->name); + } +#endif + return HTTP_BAD_REQUEST; + } + /* 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(PROXY_WORKER_DISABLED_FLAG, 1, nworker); + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10163) + "%s: failed to add worker %s", + bsel->s->name, val); +#if APR_HAS_THREADS + PROXY_GLOBAL_UNLOCK(bsel); +#endif + return HTTP_BAD_REQUEST; + } +#if APR_HAS_THREADS + if ((rv = PROXY_GLOBAL_UNLOCK(bsel)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01203) + "%s: Unlock failed for adding worker", + bsel->s->name); + } +#endif + } else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10164) + "%s: failed to add worker %s", + bsel->s->name, val); + return HTTP_BAD_REQUEST; + } + + } + + } + return APR_SUCCESS; +} + +/* + * Process a request for balancer or worker management from another module + */ +static apr_status_t balancer_manage(request_rec *r, apr_table_t *params) +{ + void *sconf; + proxy_server_conf *conf; + proxy_balancer *bsel = NULL; + proxy_worker *wsel = NULL; + const char *name; + sconf = r->server->module_config; + conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + /* Process the parameters */ + if ((name = apr_table_get(params, "b"))) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage " + "balancer: %s", name); + bsel = ap_proxy_get_balancer(r->pool, conf, + apr_pstrcat(r->pool, BALANCER_PREFIX, name, NULL), 0); + } + + if ((name = apr_table_get(params, "w"))) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage " + "worker: %s", name); + wsel = ap_proxy_get_worker(r->pool, bsel, conf, name); + } + if (bsel) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage " + "balancer: %s", bsel->s->name); + return(balancer_process_balancer_worker(r, conf, bsel, wsel, params)); + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "balancer_manage failed: " + "No balancer!"); + return HTTP_BAD_REQUEST; +} + +/* + * builds the page and links to configure via HTLM or XML. + */ +static void balancer_display_page(request_rec *r, proxy_server_conf *conf, + proxy_balancer *bsel, + proxy_worker *wsel, + int usexml) +{ + const char *action; + proxy_balancer *balancer; + proxy_worker *worker; + proxy_worker **workers; + int i, n; + action = ap_construct_url(r->pool, r->uri, r); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01204) "genning page"); + + if (usexml) { + char date[APR_RFC822_DATE_LEN]; + ap_set_content_type(r, "text/xml"); + ap_rputs("<?xml version='1.0' encoding='UTF-8' ?>\n", r); + ap_rputs("<httpd:manager xmlns:httpd='http://httpd.apache.org'>\n", r); + ap_rputs(" <httpd:balancers>\n", r); + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++) { + ap_rputs(" <httpd:balancer>\n", r); + /* Start proxy_balancer */ + ap_rvputs(r, " <httpd:name>", balancer->s->name, "</httpd:name>\n", NULL); + if (*balancer->s->sticky) { + ap_rvputs(r, " <httpd:stickysession>", ap_escape_html(r->pool, balancer->s->sticky), + "</httpd:stickysession>\n", NULL); + ap_rprintf(r, + " <httpd:nofailover>%s</httpd:nofailover>\n", + (balancer->s->sticky_force ? "On" : "Off")); + } + ap_rprintf(r, + " <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>", + apr_time_sec(balancer->s->timeout)); + if (balancer->s->max_attempts_set) { + ap_rprintf(r, + " <httpd:maxattempts>%d</httpd:maxattempts>\n", + balancer->s->max_attempts); + } + ap_rvputs(r, " <httpd:lbmethod>", balancer->lbmethod->name, + "</httpd:lbmethod>\n", NULL); + if (*balancer->s->sticky) { + ap_rprintf(r, + " <httpd:scolonpathdelim>%s</httpd:scolonpathdelim>\n", + (balancer->s->scolonsep ? "On" : "Off")); + } + /* End proxy_balancer */ + ap_rputs(" <httpd:workers>\n", r); + workers = (proxy_worker **)balancer->workers->elts; + for (n = 0; n < balancer->workers->nelts; n++) { + worker = *workers; + /* Start proxy_worker */ + ap_rputs(" <httpd:worker>\n", r); + ap_rvputs(r, " <httpd:name>", ap_proxy_worker_name(r->pool, worker), + "</httpd:name>\n", NULL); + ap_rvputs(r, " <httpd:scheme>", worker->s->scheme, + "</httpd:scheme>\n", NULL); + ap_rvputs(r, " <httpd:hostname>", worker->s->hostname_ex, + "</httpd:hostname>\n", NULL); + ap_rprintf(r, " <httpd:loadfactor>%.2f</httpd:loadfactor>\n", + (float)(worker->s->lbfactor)/100.0); + ap_rprintf(r, + " <httpd:port>%d</httpd:port>\n", + worker->s->port); + ap_rprintf(r, " <httpd:min>%d</httpd:min>\n", + worker->s->min); + ap_rprintf(r, " <httpd:smax>%d</httpd:smax>\n", + worker->s->smax); + ap_rprintf(r, " <httpd:max>%d</httpd:max>\n", + worker->s->hmax); + ap_rprintf(r, + " <httpd:ttl>%" APR_TIME_T_FMT "</httpd:ttl>\n", + apr_time_sec(worker->s->ttl)); + if (worker->s->timeout_set) { + ap_rprintf(r, + " <httpd:timeout>%" APR_TIME_T_FMT "</httpd:timeout>\n", + apr_time_sec(worker->s->timeout)); + } + if (worker->s->acquire_set) { + ap_rprintf(r, + " <httpd:acquire>%" APR_TIME_T_FMT "</httpd:acquire>\n", + apr_time_msec(worker->s->acquire)); + } + if (worker->s->recv_buffer_size_set) { + ap_rprintf(r, + " <httpd:recv_buffer_size>%" APR_SIZE_T_FMT "</httpd:recv_buffer_size>\n", + worker->s->recv_buffer_size); + } + if (worker->s->io_buffer_size_set) { + ap_rprintf(r, + " <httpd:io_buffer_size>%" APR_SIZE_T_FMT "</httpd:io_buffer_size>\n", + worker->s->io_buffer_size); + } + if (worker->s->keepalive_set) { + ap_rprintf(r, + " <httpd:keepalive>%s</httpd:keepalive>\n", + (worker->s->keepalive ? "On" : "Off")); + } + /* Begin proxy_worker_stat */ + ap_rputs(" <httpd:status>", 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, + "</httpd:error_time>\n", NULL); + } + ap_rprintf(r, + " <httpd:retries>%d</httpd:retries>\n", + worker->s->retries); + ap_rprintf(r, + " <httpd:lbstatus>%d</httpd:lbstatus>\n", + worker->s->lbstatus); + ap_rprintf(r, + " <httpd:loadfactor>%.2f</httpd:loadfactor>\n", + (float)(worker->s->lbfactor)/100.0); + ap_rprintf(r, + " <httpd:transferred>%" APR_OFF_T_FMT "</httpd:transferred>\n", + worker->s->transferred); + ap_rprintf(r, + " <httpd:read>%" APR_OFF_T_FMT "</httpd:read>\n", + worker->s->read); + ap_rprintf(r, + " <httpd:elected>%" APR_SIZE_T_FMT "</httpd:elected>\n", + worker->s->elected); + ap_rvputs(r, " <httpd:route>", + ap_escape_html(r->pool, worker->s->route), + "</httpd:route>\n", NULL); + ap_rvputs(r, " <httpd:redirect>", + ap_escape_html(r->pool, worker->s->redirect), + "</httpd:redirect>\n", NULL); + ap_rprintf(r, + " <httpd:busy>%" APR_SIZE_T_FMT "</httpd:busy>\n", + worker->s->busy); + ap_rprintf(r, " <httpd:lbset>%d</httpd:lbset>\n", + worker->s->lbset); + /* End proxy_worker_stat */ + if (!ap_cstr_casecmp(worker->s->scheme, "ajp")) { + ap_rputs(" <httpd:flushpackets>", r); + switch (worker->s->flush_packets) { + case flush_off: + ap_rputs("Off", r); + break; + case flush_on: + ap_rputs("On", r); + break; + case flush_auto: + ap_rputs("Auto", r); + break; + } + ap_rputs("</httpd:flushpackets>\n", r); + if (worker->s->flush_packets == flush_auto) { + ap_rprintf(r, + " <httpd:flushwait>%d</httpd:flushwait>\n", + worker->s->flush_wait); + } + if (worker->s->ping_timeout_set) { + ap_rprintf(r, + " <httpd:ping>%" APR_TIME_T_FMT "</httpd:ping>", + apr_time_msec(worker->s->ping_timeout)); + } + } + if (worker->s->disablereuse_set) { + ap_rprintf(r, + " <httpd:disablereuse>%s</httpd:disablereuse>\n", + (worker->s->disablereuse ? "On" : "Off")); + } + if (worker->s->conn_timeout_set) { + ap_rprintf(r, + " <httpd:connectiontimeout>%" APR_TIME_T_FMT "</httpd:connectiontimeout>\n", + apr_time_msec(worker->s->conn_timeout)); + } + if (worker->s->retry_set) { + ap_rprintf(r, + " <httpd:retry>%" APR_TIME_T_FMT "</httpd:retry>\n", + apr_time_sec(worker->s->retry)); + } + ap_rputs(" </httpd:worker>\n", r); + ++workers; + } + ap_rputs(" </httpd:workers>\n", r); + ap_rputs(" </httpd:balancer>\n", r); + ++balancer; + } + ap_rputs(" </httpd:balancers>\n", r); + ap_rputs("</httpd:manager>", r); + } + else { + ap_set_content_type(r, "text/html; charset=ISO-8859-1"); + ap_rputs(DOCTYPE_HTML_3_2 + "<html><head><title>Balancer Manager</title>\n", r); + ap_rputs("<style type='text/css'>\n" + "table {\n" + " border-width: 1px;\n" + " border-spacing: 3px;\n" + " border-style: solid;\n" + " border-color: gray;\n" + " border-collapse: collapse;\n" + " background-color: white;\n" + " text-align: center;\n" + "}\n" + "th {\n" + " border-width: 1px;\n" + " padding: 2px;\n" + " border-style: dotted;\n" + " border-color: gray;\n" + " background-color: lightgray;\n" + " text-align: center;\n" + "}\n" + "td {\n" + " border-width: 1px;\n" + " padding: 2px;\n" + " border-style: dotted;\n" + " border-color: gray;\n" + " background-color: white;\n" + " text-align: center;\n" + "}\n" + "</style>\n</head>\n", r); + ap_rputs("<body><h1>Load Balancer Manager for ", r); + ap_rvputs(r, ap_escape_html(r->pool, ap_get_server_name(r)), + "</h1>\n\n", NULL); + ap_rvputs(r, "<dl><dt>Server Version: ", + ap_get_server_description(), "</dt>\n", NULL); + ap_rvputs(r, "<dt>Server Built: ", + ap_get_server_built(), "</dt>\n", NULL); + ap_rvputs(r, "<dt>Balancer changes will ", conf->bal_persist ? "" : "NOT ", + "be persisted on restart.</dt>", NULL); + ap_rvputs(r, "<dt>Balancers are ", conf->inherit ? "" : "NOT ", + "inherited from main server.</dt>", NULL); + ap_rvputs(r, "<dt>ProxyPass settings are ", conf->ppinherit ? "" : "NOT ", + "inherited from main server.</dt>", NULL); + ap_rputs("</dl>\n", r); + balancer = (proxy_balancer *)conf->balancers->elts; + 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=", + balancer->s->name + sizeof(BALANCER_PREFIX) - 1, + "&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>" + "<th>Path</th><th>Active</th></tr>\n<tr>", r); + /* the below is a safe cast, since the number of slots total will + * never be more than max_workers, which is restricted to int */ + ap_rprintf(r, "<td>%d [%d Used]</td>\n", balancer->max_workers, + balancer->max_workers - (int)storage->num_free_slots(balancer->wslot)); + if (*balancer->s->sticky) { + if (strcmp(balancer->s->sticky, balancer->s->sticky_path)) { + ap_rvputs(r, "<td>", ap_escape_html(r->pool, balancer->s->sticky), " | ", + ap_escape_html(r->pool, balancer->s->sticky_path), NULL); + } + else { + ap_rvputs(r, "<td>", ap_escape_html(r->pool, balancer->s->sticky), NULL); + } + } + else { + ap_rputs("<td> (None) ", r); + } + ap_rprintf(r, "</td><td>%s</td>\n", + balancer->s->sticky_force ? "On" : "Off"); + 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", + balancer->s->lbpname); + ap_rputs("<td>", r); + if (*balancer->s->vhost) { + ap_rvputs(r, balancer->s->vhost, " -> ", NULL); + } + ap_rvputs(r, balancer->s->vpath, "</td>\n", NULL); + ap_rprintf(r, "<td>%s</td>\n", + !balancer->s->inactive ? "Yes" : "No"); + ap_rputs("</tr>\n</table>\n<br />", r); + ap_rputs("\n\n<table><tr>" + "<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>", 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_escape_uri(r->pool, r->uri), "?b=", + balancer->s->name + sizeof(BALANCER_PREFIX) - 1, "&w=", + ap_escape_uri(r->pool, worker->s->name_ex), + "&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), + NULL); + ap_rvputs(r, "</td><td>", + ap_escape_html(r->pool, worker->s->redirect), NULL); + ap_rprintf(r, "</td><td>%.2f</td>", (float)(worker->s->lbfactor)/100.0); + ap_rprintf(r, "<td>%d</td><td>", worker->s->lbset); + ap_rvputs(r, ap_proxy_parse_wstatus(r->pool, worker), NULL); + ap_rputs("</td>", r); + ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->elected); + ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td>", worker->s->busy); + ap_rprintf(r, "<td>%d</td><td>", worker->s->lbstatus); + 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>%" APR_TIME_T_FMT "ms</td>", apr_time_as_msec(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>", ap_escape_html(r->pool, worker->s->hcuri)); + ap_rprintf(r, "<td>%s", worker->s->hcexpr); + } + ap_rputs("</td></tr>\n", r); + + ++workers; + } + ap_rputs("</table>\n", 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("<table><tr><td>Load factor:</td><td><input name='w_lf' id='w_lf' type=text ", r); + ap_rprintf(r, "value='%.2f'></td></tr>\n", (float)(wsel->s->lbfactor)/100.0); + 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), + NULL); + 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), + NULL); + 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>" + "<th>Hot Spare</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); + create_radio("w_status_R", (PROXY_WORKER_IS(wsel, PROXY_WORKER_HOT_SPARE)), 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 (ms)</td><td><input name='w_hi' id='w_hi' type='text' " + "value='%" APR_TIME_T_FMT "'></td></tr>\n", apr_time_as_msec(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_ex), "\">\n", NULL); + ap_rvputs(r, "<input type=hidden name='b' id='b' ", NULL); + ap_rvputs(r, "value=\"", ap_escape_html(r->pool, bsel->s->name + sizeof(BALANCER_PREFIX) - 1), + "\">\n", NULL); + ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='", + bsel->s->nonce, "'>\n", NULL); + ap_rputs("</form>\n", r); + ap_rputs("<hr />\n", r); + } else if (bsel) { + const apr_array_header_t *provs; + const ap_list_provider_names_t *pname; + int i; + ap_rputs("<h3>Edit balancer settings for ", r); + ap_rvputs(r, ap_escape_html(r->pool, 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("<table>\n", r); + provs = ap_list_provider_names(r->pool, PROXY_LBMETHOD, "0"); + if (provs) { + ap_rputs("<tr><td>LBmethod:</td>", r); + ap_rputs("<td>\n<select name='b_lbm' id='b_lbm'>", r); + pname = (ap_list_provider_names_t *)provs->elts; + for (i = 0; i < provs->nelts; i++, pname++) { + ap_rvputs(r,"<option value='", pname->provider_name, "'", NULL); + if (strcmp(pname->provider_name, bsel->s->lbpname) == 0) + ap_rputs(" selected ", r); + ap_rvputs(r, ">", pname->provider_name, "\n", NULL); + } + ap_rputs("</select>\n</td></tr>\n", r); + } + ap_rputs("<tr><td>Timeout:</td><td><input name='b_tmo' id='b_tmo' type=text ", r); + ap_rprintf(r, "value='%" APR_TIME_T_FMT "'></td></tr>\n", apr_time_sec(bsel->s->timeout)); + ap_rputs("<tr><td>Failover Attempts:</td><td><input name='b_max' id='b_max' type=text ", r); + ap_rprintf(r, "value='%d'></td></tr>\n", bsel->s->max_attempts); + ap_rputs("<tr><td>Disable Failover:</td>", r); + create_radio("b_sforce", bsel->s->sticky_force, r); + ap_rputs("</tr>\n", r); + ap_rputs("<tr><td>Sticky Session:</td><td><input name='b_ss' id='b_ss' size=64 type=text ", r); + if (strcmp(bsel->s->sticky, bsel->s->sticky_path)) { + ap_rvputs(r, "value =\"", ap_escape_html(r->pool, bsel->s->sticky), " | ", + ap_escape_html(r->pool, bsel->s->sticky_path), NULL); + } + else { + ap_rvputs(r, "value =\"", ap_escape_html(r->pool, bsel->s->sticky), NULL); + } + ap_rputs("\"> (Use '-' to delete)</td></tr>\n", r); + if (storage->num_free_slots(bsel->wslot) != 0) { + ap_rputs("<tr><td>Add New Worker:</td><td><input name='b_nwrkr' id='b_nwrkr' size=32 type=text>" + " Are you sure? <input name='b_wyes' id='b_wyes' type=checkbox value='1'>" + "</td></tr>", 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='b' id='b' ", NULL); + ap_rvputs(r, "value=\"", ap_escape_html(r->pool, bsel->s->name + sizeof(BALANCER_PREFIX) - 1), + "\">\n", NULL); + ap_rvputs(r, "<input type=hidden name='nonce' id='nonce' value='", + bsel->s->nonce, "'>\n", NULL); + ap_rputs("</form>\n", r); + ap_rputs("<hr />\n", r); + } + ap_rputs(ap_psignature("",r), r); + ap_rputs("</body></html>\n", r); + ap_rflush(r); + } +} + +/* Manages the loadfactors and member status + * The balancer, worker and nonce are obtained from + * the request args (?b=...&w=...&nonce=....). + * All other params are pulled from any POST + * data that exists. + * TODO: + * /.../<whatever>/balancer/worker/nonce + */ +static int balancer_handler(request_rec *r) +{ + void *sconf; + proxy_server_conf *conf; + proxy_balancer *balancer, *bsel = NULL; + proxy_worker *wsel = NULL; + apr_table_t *params; + int i; + const char *name, *ref; + apr_status_t rv; + + /* is this for us? */ + if (strcmp(r->handler, "balancer-manager")) { + return DECLINED; + } + + r->allowed = 0 + | (AP_METHOD_BIT << M_GET) + | (AP_METHOD_BIT << M_POST); + if ((r->method_number != M_GET) && (r->method_number != M_POST)) { + return DECLINED; + } + + sconf = r->server->module_config; + conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + params = apr_table_make(r->pool, 10); + + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++, balancer++) { +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01189) + "%s: Lock failed for balancer_handler", + balancer->s->name); + } +#endif + ap_proxy_sync_balancer(balancer, r->server, conf); +#if APR_HAS_THREADS + if ((rv = PROXY_THREAD_UNLOCK(balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01190) + "%s: Unlock failed for balancer_handler", + balancer->s->name); + } +#endif + } + + if (r->args && (r->method_number == M_GET)) { + const char *allowed[] = { "w", "b", "nonce", "xml", NULL }; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01191) "parsing r->args"); + + push2table(r->args, params, allowed, r->pool); + } + if (r->method_number == M_POST) { + apr_bucket_brigade *ib; + apr_size_t len = 1024; + char *buf = apr_pcalloc(r->pool, len+1); + + ib = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc); + rv = ap_get_brigade(r->input_filters, ib, AP_MODE_READBYTES, + APR_BLOCK_READ, len); + if (rv != APR_SUCCESS) { + return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); + } + apr_brigade_flatten(ib, buf, &len); + buf[len] = '\0'; + push2table(buf, params, NULL, r->pool); + } + + /* Ignore parameters if this looks like XSRF */ + ref = apr_table_get(r->headers_in, "Referer"); + if (apr_table_elts(params) + && (!ref || !safe_referer(r, ref))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10187) + "ignoring params in balancer-manager cross-site access %s: %s", ref, ap_get_server_name(r)); + apr_table_clear(params); + } + + /* Process the parameters */ + if ((name = apr_table_get(params, "b"))) + bsel = ap_proxy_get_balancer(r->pool, conf, + apr_pstrcat(r->pool, BALANCER_PREFIX, name, NULL), 0); + + if ((name = apr_table_get(params, "w"))) { + wsel = ap_proxy_get_worker(r->pool, bsel, conf, name); + } + + + /* Check that the supplied nonce matches this server's nonce; + * otherwise ignore all parameters, to prevent a CSRF + * attack. */ + if (bsel + && (*bsel->s->nonce + && ((name = apr_table_get(params, "nonce")) != NULL + && strcmp(bsel->s->nonce, name) == 0))) { + /* Process the parameters and add the worker to the balancer */ + rv = balancer_process_balancer_worker(r, conf, bsel, wsel, params); + if (rv != APR_SUCCESS) { + return HTTP_BAD_REQUEST; + } + } + + /* display the HTML or XML page */ + if (apr_table_get(params, "xml")) { + balancer_display_page(r, conf, bsel, wsel, 1); + } else { + balancer_display_page(r, conf, bsel, wsel, 0); + } + return DONE; +} + +static void balancer_child_init(apr_pool_t *p, server_rec *s) +{ + while (s) { + proxy_balancer *balancer; + int i; + void *sconf = s->module_config; + proxy_server_conf *conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module); + apr_status_t rv; + + if (conf->balancers->nelts) { + apr_size_t size; + unsigned int num; + storage->attach(&(conf->bslot), conf->id, &size, &num, p); + if (!conf->bslot) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(01205) "slotmem_attach failed"); + exit(1); /* Ugly, but what else? */ + } + } + + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++, balancer++) { + rv = ap_proxy_initialize_balancer(balancer, s, p); + + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01206) + "Failed to init balancer %s in child", + balancer->s->name); + exit(1); /* Ugly, but what else? */ + } + init_balancer_members(p, s, balancer); + } + s = s->next; + } + +} + +static void ap_proxy_balancer_register_hook(apr_pool_t *p) +{ + /* Only the mpm_winnt has child init hook handler. + * make sure that we are called after the mpm + * initializes + */ + static const char *const aszPred[] = { "mpm_winnt.c", "mod_slotmem_shm.c", NULL}; + static const char *const aszPred2[] = { "mod_proxy.c", NULL}; + /* manager handler */ + APR_REGISTER_OPTIONAL_FN(balancer_manage); + ap_hook_post_config(balancer_post_config, aszPred2, NULL, APR_HOOK_MIDDLE); + ap_hook_pre_config(balancer_pre_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST); + ap_hook_child_init(balancer_child_init, aszPred, NULL, APR_HOOK_MIDDLE); + proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(proxy_balancer_canon, NULL, NULL, APR_HOOK_FIRST); +} + +AP_DECLARE_MODULE(proxy_balancer) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_balancer_register_hook /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_balancer.dep b/modules/proxy/mod_proxy_balancer.dep new file mode 100644 index 0000000..0902194 --- /dev/null +++ b/modules/proxy/mod_proxy_balancer.dep @@ -0,0 +1,76 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_balancer.mak + +.\mod_proxy_balancer.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_mpm.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\scoreboard.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_balancer.dsp b/modules/proxy/mod_proxy_balancer.dsp new file mode 100644 index 0000000..12168e5 --- /dev/null +++ b/modules/proxy/mod_proxy_balancer.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_balancer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_balancer - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_balancer.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_balancer - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_balancer - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_balancer_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_balancer.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_balancer - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_balancer_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_balancer.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_balancer - Win32 Release" +# Name "mod_proxy_balancer - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_balancer.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_balancer.mak b/modules/proxy/mod_proxy_balancer.mak new file mode 100644 index 0000000..86d7ec5 --- /dev/null +++ b/modules/proxy/mod_proxy_balancer.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_balancer.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_balancer - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_balancer - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_balancer - Win32 Release" && "$(CFG)" != "mod_proxy_balancer - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_balancer - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_balancer - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_balancer.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_balancer.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_balancer.obj" + -@erase "$(INTDIR)\mod_proxy_balancer.res" + -@erase "$(INTDIR)\mod_proxy_balancer_src.idb" + -@erase "$(INTDIR)\mod_proxy_balancer_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_balancer.exp" + -@erase "$(OUTDIR)\mod_proxy_balancer.lib" + -@erase "$(OUTDIR)\mod_proxy_balancer.pdb" + -@erase "$(OUTDIR)\mod_proxy_balancer.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_balancer_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_balancer.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_balancer.pdb" /debug /out:"$(OUTDIR)\mod_proxy_balancer.so" /implib:"$(OUTDIR)\mod_proxy_balancer.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_balancer.obj" \ + "$(INTDIR)\mod_proxy_balancer.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_balancer.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_balancer.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_balancer.so" + if exist .\Release\mod_proxy_balancer.so.manifest mt.exe -manifest .\Release\mod_proxy_balancer.so.manifest -outputresource:.\Release\mod_proxy_balancer.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_balancer - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_balancer.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_balancer.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_balancer.obj" + -@erase "$(INTDIR)\mod_proxy_balancer.res" + -@erase "$(INTDIR)\mod_proxy_balancer_src.idb" + -@erase "$(INTDIR)\mod_proxy_balancer_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_balancer.exp" + -@erase "$(OUTDIR)\mod_proxy_balancer.lib" + -@erase "$(OUTDIR)\mod_proxy_balancer.pdb" + -@erase "$(OUTDIR)\mod_proxy_balancer.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_balancer_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_balancer.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_balancer.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_balancer.pdb" /debug /out:"$(OUTDIR)\mod_proxy_balancer.so" /implib:"$(OUTDIR)\mod_proxy_balancer.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_balancer.obj" \ + "$(INTDIR)\mod_proxy_balancer.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_balancer.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_balancer.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_balancer.so" + if exist .\Debug\mod_proxy_balancer.so.manifest mt.exe -manifest .\Debug\mod_proxy_balancer.so.manifest -outputresource:.\Debug\mod_proxy_balancer.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_balancer.dep") +!INCLUDE "mod_proxy_balancer.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_balancer.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" || "$(CFG)" == "mod_proxy_balancer - Win32 Debug" +SOURCE=.\mod_proxy_balancer.c + +"$(INTDIR)\mod_proxy_balancer.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_balancer - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_balancer - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_balancer - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_balancer - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_balancer - Win32 Release" + + +"$(INTDIR)\mod_proxy_balancer.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_balancer.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_connect.c b/modules/proxy/mod_proxy_connect.c new file mode 100644 index 0000000..5a68135 --- /dev/null +++ b/modules/proxy/mod_proxy_connect.c @@ -0,0 +1,404 @@ +/* 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. + */ + +/* CONNECT method for Apache proxy */ + +#include "mod_proxy.h" +#include "apr_poll.h" + +#define CONN_BLKSZ AP_IOBUFSIZE + +module AP_MODULE_DECLARE_DATA proxy_connect_module; + +/* + * This handles Netscape CONNECT method secure proxy requests. + * A connection is opened to the specified host and data is + * passed through between the WWW site and the browser. + * + * This code is based on the INTERNET-DRAFT document + * "Tunneling SSL Through a WWW Proxy" currently at + * http://www.mcom.com/newsref/std/tunneling_ssl.html. + * + * If proxyhost and proxyport are set, we send a CONNECT to + * the specified proxy.. + * + * FIXME: this doesn't log the number of bytes sent, but + * that may be okay, since the data is supposed to + * be transparent. In fact, this doesn't log at all + * yet. 8^) + * FIXME: doesn't check any headers initially sent from the + * client. + * FIXME: should allow authentication, but hopefully the + * generic proxy authentication is good enough. + * FIXME: no check for r->assbackwards, whatever that is. + */ + +typedef struct { + apr_array_header_t *allowed_connect_ports; +} connect_conf; + +typedef struct { + int first; + int last; +} port_range; + +static void *create_config(apr_pool_t *p, server_rec *s) +{ + connect_conf *c = apr_pcalloc(p, sizeof(connect_conf)); + c->allowed_connect_ports = apr_array_make(p, 10, sizeof(port_range)); + return c; +} + +static void *merge_config(apr_pool_t *p, void *basev, void *overridesv) +{ + connect_conf *c = apr_pcalloc(p, sizeof(connect_conf)); + connect_conf *base = (connect_conf *) basev; + connect_conf *overrides = (connect_conf *) overridesv; + + c->allowed_connect_ports = apr_array_append(p, + base->allowed_connect_ports, + overrides->allowed_connect_ports); + + return c; +} + + +/* + * Set the ports CONNECT can use + */ +static const char * + set_allowed_ports(cmd_parms *parms, void *dummy, const char *arg) +{ + server_rec *s = parms->server; + int first, last; + connect_conf *conf = + ap_get_module_config(s->module_config, &proxy_connect_module); + port_range *New; + char *endptr; + const char *p = arg; + + if (!apr_isdigit(arg[0])) + return "AllowCONNECT: port numbers must be numeric"; + + first = strtol(p, &endptr, 10); + if (*endptr == '-') { + p = endptr + 1; + last = strtol(p, &endptr, 10); + } + else { + last = first; + } + + if (endptr == p || *endptr != '\0') { + return apr_psprintf(parms->temp_pool, + "Cannot parse '%s' as port number", p); + } + + New = apr_array_push(conf->allowed_connect_ports); + New->first = first; + New->last = last; + return NULL; +} + + +static int allowed_port(connect_conf *conf, int port) +{ + int i; + port_range *list = (port_range *) conf->allowed_connect_ports->elts; + + if (apr_is_empty_array(conf->allowed_connect_ports)) { + return port == APR_URI_HTTPS_DEFAULT_PORT + || port == APR_URI_SNEWS_DEFAULT_PORT; + } + + for (i = 0; i < conf->allowed_connect_ports->nelts; i++) { + if (port >= list[i].first && port <= list[i].last) + return 1; + } + return 0; +} + +/* canonicalise CONNECT URLs. */ +static int proxy_connect_canon(request_rec *r, char *url) +{ + + if (r->method_number != M_CONNECT) { + return DECLINED; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url); + + return OK; +} + +/* CONNECT handler */ +static int proxy_connect_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + connect_conf *c_conf = + ap_get_module_config(r->server->module_config, &proxy_connect_module); + + apr_pool_t *p = r->pool; + apr_socket_t *sock; + conn_rec *c = r->connection; + conn_rec *backconn; + + apr_status_t rv; + apr_size_t nbytes; + char buffer[HUGE_STRING_LEN]; + + apr_bucket_brigade *bb; + proxy_tunnel_rec *tunnel; + int failed, rc; + + apr_uri_t uri; + const char *connectname; + apr_port_t connectport = 0; + apr_sockaddr_t *nexthop; + + apr_interval_time_t current_timeout; + + /* is this for us? */ + if (r->method_number != M_CONNECT) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "declining URL %s", url); + return DECLINED; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "serving URL %s", url); + + + /* + * Step One: Determine Who To Connect To + * + * Break up the URL to determine the host to connect to + */ + + /* we break the URL into host, port, uri */ + if (APR_SUCCESS != apr_uri_parse_hostinfo(p, url, &uri)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_pstrcat(p, "URI cannot be parsed: ", url, + NULL)); + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01019) + "connecting %s to %s:%d", url, uri.hostname, uri.port); + + /* Determine host/port of next hop; from request URI or of a proxy. */ + connectname = proxyname ? proxyname : uri.hostname; + connectport = proxyname ? proxyport : uri.port; + + /* Do a DNS lookup for the next hop */ + rv = apr_sockaddr_info_get(&nexthop, connectname, APR_UNSPEC, + connectport, 0, p); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02327) + "failed to resolve hostname '%s'", connectname); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "DNS lookup failure for: ", + connectname, NULL)); + } + + /* Check ProxyBlock directive on the hostname/address. */ + if (ap_proxy_checkproxyblock2(r, conf, uri.hostname, + proxyname ? NULL : nexthop) != OK) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "connecting to remote proxy %s on port %d", + connectname, connectport); + + /* Check if it is an allowed port */ + if (!allowed_port(c_conf, uri.port)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + /* + * Step Two: Make the Connection + * + * We have determined who to connect to. Now make the connection. + */ + + /* + * At this point we have a list of one or more IP addresses of + * the machine to connect to. If configured, reorder this + * list so that the "best candidate" is first try. "best + * candidate" could mean the least loaded server, the fastest + * responding server, whatever. + * + * For now we do nothing, ie we get DNS round robin. + * XXX FIXME + */ + failed = ap_proxy_connect_to_backend(&sock, "CONNECT", nexthop, + connectname, conf, r); + + /* handle a permanent error from the above loop */ + if (failed) { + if (proxyname) { + return DECLINED; + } + else { + return HTTP_SERVICE_UNAVAILABLE; + } + } + + /* + * Step Three: Send the Request + * + * Send the HTTP/1.1 CONNECT request to the remote server + */ + + backconn = ap_run_create_connection(c->pool, r->server, sock, + c->id, c->sbh, c->bucket_alloc); + if (!backconn) { + /* peer reset */ + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01021) + "an error occurred creating a new connection " + "to %pI (%s)", nexthop, connectname); + apr_socket_close(sock); + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_proxy_ssl_engine(backconn, r->per_dir_config, 0); + + /* + * save the timeout of the socket because core_pre_connection + * will set it to base_server->timeout + * (core TimeOut directive). + */ + apr_socket_timeout_get(sock, ¤t_timeout); + rc = ap_run_pre_connection(backconn, sock); + if (rc != OK && rc != DONE) { + backconn->aborted = 1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01022) + "pre_connection setup failed (%d)", rc); + apr_socket_close(sock); + return HTTP_INTERNAL_SERVER_ERROR; + } + apr_socket_timeout_set(sock, current_timeout); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "connection complete to %pI (%s)", + nexthop, connectname); + apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu", + backconn->local_addr->port)); + + bb = apr_brigade_create(p, c->bucket_alloc); + + /* If we are connecting through a remote proxy, we need to pass + * the CONNECT request on to it. + */ + if (proxyport) { + /* FIXME: Error checking ignored. + */ + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "sending the CONNECT request to the remote proxy"); + ap_fprintf(backconn->output_filters, bb, + "CONNECT %s HTTP/1.0" CRLF, r->uri); + ap_fprintf(backconn->output_filters, bb, + "Proxy-agent: %s" CRLF CRLF, ap_get_server_banner()); + ap_fflush(backconn->output_filters, bb); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "Returning 200 OK"); + nbytes = apr_snprintf(buffer, sizeof(buffer), + "HTTP/1.0 200 Connection Established" CRLF); + ap_xlate_proto_to_ascii(buffer, nbytes); + ap_fwrite(c->output_filters, bb, buffer, nbytes); + nbytes = apr_snprintf(buffer, sizeof(buffer), + "Proxy-agent: %s" CRLF CRLF, + ap_get_server_banner()); + ap_xlate_proto_to_ascii(buffer, nbytes); + ap_fwrite(c->output_filters, bb, buffer, nbytes); + ap_fflush(c->output_filters, bb); +#if 0 + /* This is safer code, but it doesn't work yet. I'm leaving it + * here so that I can fix it later. + */ + r->status = HTTP_OK; + r->header_only = 1; + apr_table_set(r->headers_out, "Proxy-agent: %s", ap_get_server_banner()); + ap_rflush(r); +#endif + } + apr_brigade_cleanup(bb); + + /* + * Step Four: Handle Data Transfer + * + * Handle two way transfer of data over the socket (this is a tunnel). + */ + + /* r->sent_bodyct = 1; */ + + rv = ap_proxy_tunnel_create(&tunnel, r, backconn, "CONNECT"); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10208) + "can't create tunnel for %pI (%s)", + nexthop, connectname); + return HTTP_INTERNAL_SERVER_ERROR; + } + + rc = ap_proxy_tunnel_run(tunnel); + if (ap_is_HTTP_ERROR(rc)) { + if (rc == HTTP_GATEWAY_TIME_OUT) { + /* ap_proxy_tunnel_run() didn't log this */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10224) + "tunnel timed out"); + } + /* Don't send an error page if we sent data already */ + if (proxyport && !tunnel->replied) { + return rc; + } + } + + /* + * Step Five: Clean Up + * + * Close the socket and clean up + */ + + if (backconn->aborted) + apr_socket_close(sock); + else + ap_lingering_close(backconn); + + return OK; +} + +static void ap_proxy_connect_register_hook(apr_pool_t *p) +{ + proxy_hook_scheme_handler(proxy_connect_handler, NULL, NULL, APR_HOOK_MIDDLE); + proxy_hook_canon_handler(proxy_connect_canon, NULL, NULL, APR_HOOK_MIDDLE); +} + +static const command_rec cmds[] = +{ + AP_INIT_ITERATE("AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, + "A list of ports or port ranges which CONNECT may connect to"), + {NULL} +}; + +AP_DECLARE_MODULE(proxy_connect) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_config, /* create per-server config structure */ + merge_config, /* merge per-server config structures */ + cmds, /* command apr_table_t */ + ap_proxy_connect_register_hook /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_connect.dep b/modules/proxy/mod_proxy_connect.dep new file mode 100644 index 0000000..926b6e2 --- /dev/null +++ b/modules/proxy/mod_proxy_connect.dep @@ -0,0 +1,73 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_connect.mak + +.\mod_proxy_connect.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_connect.dsp b/modules/proxy/mod_proxy_connect.dsp new file mode 100644 index 0000000..24647bf --- /dev/null +++ b/modules/proxy/mod_proxy_connect.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_connect" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_connect - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_connect.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_connect.mak" CFG="mod_proxy_connect - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_connect - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_connect - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_connect_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_connect.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_connect_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_connect.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_connect - Win32 Release" +# Name "mod_proxy_connect - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_connect.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_connect.mak b/modules/proxy/mod_proxy_connect.mak new file mode 100644 index 0000000..be354db --- /dev/null +++ b/modules/proxy/mod_proxy_connect.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_connect.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_connect - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_connect - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_connect - Win32 Release" && "$(CFG)" != "mod_proxy_connect - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_connect.mak" CFG="mod_proxy_connect - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_connect - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_connect - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_connect.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_connect.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_connect.obj" + -@erase "$(INTDIR)\mod_proxy_connect.res" + -@erase "$(INTDIR)\mod_proxy_connect_src.idb" + -@erase "$(INTDIR)\mod_proxy_connect_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_connect.exp" + -@erase "$(OUTDIR)\mod_proxy_connect.lib" + -@erase "$(OUTDIR)\mod_proxy_connect.pdb" + -@erase "$(OUTDIR)\mod_proxy_connect.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_connect_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_connect.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_connect.pdb" /debug /out:"$(OUTDIR)\mod_proxy_connect.so" /implib:"$(OUTDIR)\mod_proxy_connect.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_connect.obj" \ + "$(INTDIR)\mod_proxy_connect.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_connect.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_connect.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_connect.so" + if exist .\Release\mod_proxy_connect.so.manifest mt.exe -manifest .\Release\mod_proxy_connect.so.manifest -outputresource:.\Release\mod_proxy_connect.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_connect.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_connect.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_connect.obj" + -@erase "$(INTDIR)\mod_proxy_connect.res" + -@erase "$(INTDIR)\mod_proxy_connect_src.idb" + -@erase "$(INTDIR)\mod_proxy_connect_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_connect.exp" + -@erase "$(OUTDIR)\mod_proxy_connect.lib" + -@erase "$(OUTDIR)\mod_proxy_connect.pdb" + -@erase "$(OUTDIR)\mod_proxy_connect.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_connect_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_connect.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_connect.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_connect.pdb" /debug /out:"$(OUTDIR)\mod_proxy_connect.so" /implib:"$(OUTDIR)\mod_proxy_connect.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_connect.obj" \ + "$(INTDIR)\mod_proxy_connect.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_connect.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_connect.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_connect.so" + if exist .\Debug\mod_proxy_connect.so.manifest mt.exe -manifest .\Debug\mod_proxy_connect.so.manifest -outputresource:.\Debug\mod_proxy_connect.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_connect.dep") +!INCLUDE "mod_proxy_connect.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_connect.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" || "$(CFG)" == "mod_proxy_connect - Win32 Debug" +SOURCE=.\mod_proxy_connect.c + +"$(INTDIR)\mod_proxy_connect.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + + +"$(INTDIR)\mod_proxy_connect.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_connect.so" /d LONG_NAME="proxy_connect_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_express.c b/modules/proxy/mod_proxy_express.c new file mode 100644 index 0000000..5d458c4 --- /dev/null +++ b/modules/proxy/mod_proxy_express.c @@ -0,0 +1,250 @@ +/* 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 "apr_dbm.h" + +module AP_MODULE_DECLARE_DATA proxy_express_module; + +#include "apr_version.h" +#if !APR_VERSION_AT_LEAST(2,0,0) +#include "apu_version.h" +#endif + +static int proxy_available = 0; + +typedef struct { + const char *dbmfile; + const char *dbmtype; + int enabled; +} express_server_conf; + +static const char *set_dbmfile(cmd_parms *cmd, + void *dconf, + const char *arg) +{ + express_server_conf *sconf; + sconf = ap_get_module_config(cmd->server->module_config, &proxy_express_module); + + if ((sconf->dbmfile = ap_server_root_relative(cmd->pool, arg)) == NULL) { + return apr_pstrcat(cmd->pool, "ProxyExpressDBMFile: bad path to file: ", + arg, NULL); + } + return NULL; +} + +static const char *set_dbmtype(cmd_parms *cmd, + void *dconf, + const char *arg) +{ + express_server_conf *sconf; + sconf = ap_get_module_config(cmd->server->module_config, &proxy_express_module); + + sconf->dbmtype = arg; + + return NULL; +} + +static const char *set_enabled(cmd_parms *cmd, + void *dconf, + int flag) +{ + express_server_conf *sconf; + sconf = ap_get_module_config(cmd->server->module_config, &proxy_express_module); + + sconf->enabled = flag; + + return NULL; +} + +static void *server_create(apr_pool_t *p, server_rec *s) +{ + express_server_conf *a; + + a = (express_server_conf *)apr_pcalloc(p, sizeof(express_server_conf)); + + a->dbmfile = NULL; + a->dbmtype = "default"; + a->enabled = 0; + + return (void *)a; +} + +static void *server_merge(apr_pool_t *p, void *basev, void *overridesv) +{ + express_server_conf *a, *base, *overrides; + + a = (express_server_conf *)apr_pcalloc(p, + sizeof(express_server_conf)); + base = (express_server_conf *)basev; + overrides = (express_server_conf *)overridesv; + + a->dbmfile = (overrides->dbmfile) ? overrides->dbmfile : base->dbmfile; + a->dbmtype = (overrides->dbmtype) ? overrides->dbmtype : base->dbmtype; + a->enabled = (overrides->enabled) ? overrides->enabled : base->enabled; + + return (void *)a; +} + +static int post_config(apr_pool_t *p, + apr_pool_t *plog, + apr_pool_t *ptemp, + server_rec *s) +{ + proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL); + return OK; +} + + +static int xlate_name(request_rec *r) +{ + int i; + const char *name; + char *backend = NULL; + apr_dbm_t *db; + apr_status_t rv; + apr_datum_t key, val; + struct proxy_alias *ralias; + proxy_dir_conf *dconf; + express_server_conf *sconf; +#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7) + const apr_dbm_driver_t *driver; + const apu_err_t *err; +#endif + + sconf = ap_get_module_config(r->server->module_config, &proxy_express_module); + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + + if (!sconf->enabled) { + return DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01001) "proxy_express: Enabled"); + if (!sconf->dbmfile || (r->filename && strncmp(r->filename, "proxy:", 6) == 0)) { + /* it should be go on as an internal proxy request */ + return DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01002) + "proxy_express: Opening DBM file: %s (%s)", + sconf->dbmfile, sconf->dbmtype); + +#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7) + rv = apr_dbm_get_driver(&driver, sconf->dbmtype, &err, r->pool); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + APLOGNO(10275) "The dbm library '%s' could not be loaded: %s (%s: %d)", + sconf->dbmtype, err->msg, err->reason, err->rc); + return DECLINED; + } + + rv = apr_dbm_open2(&db, driver, sconf->dbmfile, APR_DBM_READONLY, + APR_OS_DEFAULT, r->pool); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + APLOGNO(10276) "The '%s' file '%s' could not be loaded", + sconf->dbmtype, sconf->dbmfile); + return DECLINED; + } +#else + rv = apr_dbm_open_ex(&db, sconf->dbmtype, sconf->dbmfile, APR_DBM_READONLY, + APR_OS_DEFAULT, r->pool); + if (rv != APR_SUCCESS) { + return DECLINED; + } +#endif + + name = ap_get_server_name(r); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01003) + "proxy_express: looking for %s", name); + key.dptr = (char *)name; + key.dsize = strlen(key.dptr); + + rv = apr_dbm_fetch(db, key, &val); + if (rv == APR_SUCCESS) { + backend = apr_pstrmemdup(r->pool, val.dptr, val.dsize); + } + apr_dbm_close(db); + if (rv != APR_SUCCESS || !backend) { + return DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01004) + "proxy_express: found %s -> %s", name, backend); + r->filename = apr_pstrcat(r->pool, "proxy:", backend, r->uri, NULL); + r->handler = "proxy-server"; + r->proxyreq = PROXYREQ_REVERSE; + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01005) + "proxy_express: rewritten as: %s", r->filename); + + ralias = (struct proxy_alias *)dconf->raliases->elts; + /* + * See if we have already added a ProxyPassReverse entry + * for this host... If so, don't do it again. + */ + /* + * NOTE: dconf is process specific so this will only + * work as long as we maintain that this process + * or thread is handling the backend + */ + for (i = 0; i < dconf->raliases->nelts; i++, ralias++) { + if (strcasecmp(backend, ralias->real) == 0) { + ralias = NULL; + break; + } + } + + /* Didn't find one... add it */ + if (!ralias) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01006) + "proxy_express: adding PPR entry"); + ralias = apr_array_push(dconf->raliases); + ralias->fake = "/"; + ralias->real = apr_pstrdup(dconf->raliases->pool, backend); + ralias->flags = 0; + } + return OK; +} + +static const command_rec command_table[] = { + AP_INIT_FLAG("ProxyExpressEnable", set_enabled, NULL, OR_FILEINFO, + "Enable the ProxyExpress functionality"), + AP_INIT_TAKE1("ProxyExpressDBMFile", set_dbmfile, NULL, OR_FILEINFO, + "Location of ProxyExpressDBMFile file"), + AP_INIT_TAKE1("ProxyExpressDBMType", set_dbmtype, NULL, OR_FILEINFO, + "Type of ProxyExpressDBMFile file"), + { NULL } +}; + +static void register_hooks(apr_pool_t *p) +{ + ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_LAST); + ap_hook_translate_name(xlate_name, NULL, NULL, APR_HOOK_FIRST); +} + +/* the main config structure */ + +AP_DECLARE_MODULE(proxy_express) = +{ + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + server_create, /* create per-server config structures */ + server_merge, /* merge per-server config structures */ + command_table, /* table of config file commands */ + register_hooks /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_express.dep b/modules/proxy/mod_proxy_express.dep new file mode 100644 index 0000000..f9e5ffb --- /dev/null +++ b/modules/proxy/mod_proxy_express.dep @@ -0,0 +1,74 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_express.mak + +.\mod_proxy_express.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_dbm.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_express.dsp b/modules/proxy/mod_proxy_express.dsp new file mode 100644 index 0000000..924c676 --- /dev/null +++ b/modules/proxy/mod_proxy_express.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_express" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_express - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_express.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_express.mak" CFG="mod_proxy_express - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_express - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_express - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_express_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_express.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_express.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_express.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_express.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_express.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_express - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_express_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_express.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_express.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_express.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_express.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_express.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_express - Win32 Release" +# Name "mod_proxy_express - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_express.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_express.mak b/modules/proxy/mod_proxy_express.mak new file mode 100644 index 0000000..f656d22 --- /dev/null +++ b/modules/proxy/mod_proxy_express.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_express.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_express - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_express - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_express - Win32 Release" && "$(CFG)" != "mod_proxy_express - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_express.mak" CFG="mod_proxy_express - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_express - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_express - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_express.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_express.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_express.obj" + -@erase "$(INTDIR)\mod_proxy_express.res" + -@erase "$(INTDIR)\mod_proxy_express_src.idb" + -@erase "$(INTDIR)\mod_proxy_express_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_express.exp" + -@erase "$(OUTDIR)\mod_proxy_express.lib" + -@erase "$(OUTDIR)\mod_proxy_express.pdb" + -@erase "$(OUTDIR)\mod_proxy_express.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_express_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_express.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_express.pdb" /debug /out:"$(OUTDIR)\mod_proxy_express.so" /implib:"$(OUTDIR)\mod_proxy_express.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_express.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_express.obj" \ + "$(INTDIR)\mod_proxy_express.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_express.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_express.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_express.so" + if exist .\Release\mod_proxy_express.so.manifest mt.exe -manifest .\Release\mod_proxy_express.so.manifest -outputresource:.\Release\mod_proxy_express.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_express - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_express.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_express.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_express.obj" + -@erase "$(INTDIR)\mod_proxy_express.res" + -@erase "$(INTDIR)\mod_proxy_express_src.idb" + -@erase "$(INTDIR)\mod_proxy_express_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_express.exp" + -@erase "$(OUTDIR)\mod_proxy_express.lib" + -@erase "$(OUTDIR)\mod_proxy_express.pdb" + -@erase "$(OUTDIR)\mod_proxy_express.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_express_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_express.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_express.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_express.pdb" /debug /out:"$(OUTDIR)\mod_proxy_express.so" /implib:"$(OUTDIR)\mod_proxy_express.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_express.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_express.obj" \ + "$(INTDIR)\mod_proxy_express.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_express.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_express.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_express.so" + if exist .\Debug\mod_proxy_express.so.manifest mt.exe -manifest .\Debug\mod_proxy_express.so.manifest -outputresource:.\Debug\mod_proxy_express.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_express.dep") +!INCLUDE "mod_proxy_express.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_express.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" || "$(CFG)" == "mod_proxy_express - Win32 Debug" +SOURCE=.\mod_proxy_express.c + +"$(INTDIR)\mod_proxy_express.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_express - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_express - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_express - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_express - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_express - Win32 Release" + + +"$(INTDIR)\mod_proxy_express.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_express.so" /d LONG_NAME="proxy_balancer_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c new file mode 100644 index 0000000..831bd15 --- /dev/null +++ b/modules/proxy/mod_proxy_fcgi.c @@ -0,0 +1,1345 @@ +/* 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 "util_fcgi.h" +#include "util_script.h" +#include "ap_expr.h" + +module AP_MODULE_DECLARE_DATA proxy_fcgi_module; + +typedef struct { + ap_expr_info_t *cond; + ap_expr_info_t *subst; + const char *envname; +} sei_entry; + +typedef struct { + int need_dirwalk; +} fcgi_req_config_t; + +/* We will assume FPM, but still differentiate */ +typedef enum { + BACKEND_DEFAULT_UNKNOWN = 0, + BACKEND_FPM, + BACKEND_GENERIC, +} fcgi_backend_t; + + +#define FCGI_MAY_BE_FPM(dconf) \ + (dconf && \ + ((dconf->backend_type == BACKEND_DEFAULT_UNKNOWN) || \ + (dconf->backend_type == BACKEND_FPM))) + +typedef struct { + fcgi_backend_t backend_type; + apr_array_header_t *env_fixups; +} fcgi_dirconf_t; + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +static int proxy_fcgi_canon(request_rec *r, char *url) +{ + char *host, sport[7]; + const char *err; + char *path; + apr_port_t port, def_port; + fcgi_req_config_t *rconf = NULL; + const char *pathinfo_type = NULL; + + if (ap_cstr_casecmpn(url, "fcgi:", 5) == 0) { + url += 5; + } + else { + return DECLINED; + } + + port = def_port = ap_proxy_port_of_scheme("fcgi"); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "canonicalising URL %s", url); + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01059) + "error parsing URL %s: %s", url, err); + return HTTP_BAD_REQUEST; + } + + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + if (ap_strchr_c(host, ':')) { + /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + + if (apr_table_get(r->notes, "proxy-nocanon") + || apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the raw/encoded path */ + } + else { + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + } + /* + * If we have a raw control character or a ' ' in nocanon path, + * correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10414) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + r->filename = apr_pstrcat(r->pool, "proxy:fcgi://", host, sport, "/", + path, NULL); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01060) + "set r->filename to %s", r->filename); + + rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module); + if (rconf == NULL) { + rconf = apr_pcalloc(r->pool, sizeof(fcgi_req_config_t)); + ap_set_module_config(r->request_config, &proxy_fcgi_module, rconf); + } + + if (NULL != (pathinfo_type = apr_table_get(r->subprocess_env, "proxy-fcgi-pathinfo"))) { + /* It has to be on disk for this to work */ + if (!strcasecmp(pathinfo_type, "full")) { + rconf->need_dirwalk = 1; + ap_unescape_url_keep2f(path, 0); + } + else if (!strcasecmp(pathinfo_type, "first-dot")) { + char *split = ap_strchr(path, '.'); + if (split) { + char *slash = ap_strchr(split, '/'); + if (slash) { + r->path_info = apr_pstrdup(r->pool, slash); + ap_unescape_url_keep2f(r->path_info, 0); + *slash = '\0'; /* truncate path */ + } + } + } + else if (!strcasecmp(pathinfo_type, "last-dot")) { + char *split = ap_strrchr(path, '.'); + if (split) { + char *slash = ap_strchr(split, '/'); + if (slash) { + r->path_info = apr_pstrdup(r->pool, slash); + ap_unescape_url_keep2f(r->path_info, 0); + *slash = '\0'; /* truncate path */ + } + } + } + else { + /* before proxy-fcgi-pathinfo had multi-values. This requires the + * the FCGI server to fixup PATH_INFO because it's the entire path + */ + r->path_info = apr_pstrcat(r->pool, "/", path, NULL); + if (!strcasecmp(pathinfo_type, "unescape")) { + ap_unescape_url_keep2f(r->path_info, 0); + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01061) + "set r->path_info to %s", r->path_info); + } + } + + return OK; +} + + +/* + ProxyFCGISetEnvIf "reqenv('PATH_INFO') =~ m#/foo(\d+)\.php$#" COVENV1 "$1" + ProxyFCGISetEnvIf "reqenv('PATH_INFO') =~ m#/foo(\d+)\.php$#" PATH_INFO "/foo.php" + ProxyFCGISetEnvIf "reqenv('PATH_TRANSLATED') =~ m#(/.*foo)(\d+)(.*)#" PATH_TRANSLATED "$1$3" +*/ +static apr_status_t fix_cgivars(request_rec *r, fcgi_dirconf_t *dconf) +{ + sei_entry *entries; + const char *err, *src; + int i = 0, rc = 0; + ap_regmatch_t regm[AP_MAX_REG_MATCH]; + + entries = (sei_entry *) dconf->env_fixups->elts; + for (i = 0; i < dconf->env_fixups->nelts; i++) { + sei_entry *entry = &entries[i]; + + rc = ap_expr_exec_re(r, entry->cond, AP_MAX_REG_MATCH, regm, &src, &err); + if (rc < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10241) + "fix_cgivars: Condition eval returned %d: %s", + rc, err); + return APR_EGENERAL; + } + else if (rc == 0) { + continue; /* evaluated false */ + } + + if (entry->envname[0] == '!') { + apr_table_unset(r->subprocess_env, entry->envname+1); + } + else { + const char *val = ap_expr_str_exec_re(r, entry->subst, AP_MAX_REG_MATCH, regm, &src, &err); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(03514) + "Error evaluating expression for replacement of %s: '%s'", + entry->envname, err); + continue; + } + if (APLOGrtrace4(r)) { + const char *oldval = apr_table_get(r->subprocess_env, entry->envname); + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "fix_cgivars: override %s from '%s' to '%s'", + entry->envname, oldval, val); + + } + apr_table_setn(r->subprocess_env, entry->envname, val); + } + } + return APR_SUCCESS; +} + +/* Wrapper for apr_socket_sendv that handles updating the worker stats. */ +static apr_status_t send_data(proxy_conn_rec *conn, + struct iovec *vec, + int nvec, + apr_size_t *len) +{ + apr_status_t rv = APR_SUCCESS; + apr_size_t written = 0, to_write = 0; + int i, offset; + apr_socket_t *s = conn->sock; + + for (i = 0; i < nvec; i++) { + to_write += vec[i].iov_len; + } + + offset = 0; + while (to_write) { + apr_size_t n = 0; + rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n); + if (rv != APR_SUCCESS) { + break; + } + if (n > 0) { + written += n; + if (written >= to_write) + break; /* short circuit out */ + for (i = offset; i < nvec; ) { + if (n >= vec[i].iov_len) { + offset++; + n -= vec[i++].iov_len; + } else { + vec[i].iov_len -= n; + vec[i].iov_base = (char *) vec[i].iov_base + n; + break; + } + } + } + } + + conn->worker->s->transferred += written; + *len = written; + + return rv; +} + +/* Wrapper for apr_socket_recv that handles updating the worker stats. */ +static apr_status_t get_data(proxy_conn_rec *conn, + char *buffer, + apr_size_t *buflen) +{ + apr_status_t rv = apr_socket_recv(conn->sock, buffer, buflen); + + if (rv == APR_SUCCESS) { + conn->worker->s->read += *buflen; + } + + return rv; +} + +static apr_status_t get_data_full(proxy_conn_rec *conn, + char *buffer, + apr_size_t buflen) +{ + apr_size_t readlen; + apr_size_t cumulative_len = 0; + apr_status_t rv; + + do { + readlen = buflen - cumulative_len; + rv = get_data(conn, buffer + cumulative_len, &readlen); + if (rv != APR_SUCCESS) { + return rv; + } + cumulative_len += readlen; + } while (cumulative_len < buflen); + + return APR_SUCCESS; +} + +static apr_status_t send_begin_request(proxy_conn_rec *conn, + apr_uint16_t request_id) +{ + struct iovec vec[2]; + ap_fcgi_header header; + unsigned char farray[AP_FCGI_HEADER_LEN]; + ap_fcgi_begin_request_body brb; + unsigned char abrb[AP_FCGI_HEADER_LEN]; + apr_size_t len; + + ap_fcgi_fill_in_header(&header, AP_FCGI_BEGIN_REQUEST, request_id, + sizeof(abrb), 0); + + ap_fcgi_fill_in_request_body(&brb, AP_FCGI_RESPONDER, + ap_proxy_connection_reusable(conn) + ? AP_FCGI_KEEP_CONN : 0); + + ap_fcgi_header_to_array(&header, farray); + ap_fcgi_begin_request_body_to_array(&brb, abrb); + + vec[0].iov_base = (void *)farray; + vec[0].iov_len = sizeof(farray); + vec[1].iov_base = (void *)abrb; + vec[1].iov_len = sizeof(abrb); + + return send_data(conn, vec, 2, &len); +} + +static apr_status_t send_environment(proxy_conn_rec *conn, request_rec *r, + apr_pool_t *temp_pool, + apr_uint16_t request_id) +{ + const apr_array_header_t *envarr; + const apr_table_entry_t *elts; + struct iovec vec[2]; + ap_fcgi_header header; + unsigned char farray[AP_FCGI_HEADER_LEN]; + char *body; + apr_status_t rv; + apr_size_t avail_len, len, required_len; + int next_elem, starting_elem; + fcgi_req_config_t *rconf = ap_get_module_config(r->request_config, &proxy_fcgi_module); + fcgi_dirconf_t *dconf = ap_get_module_config(r->per_dir_config, &proxy_fcgi_module); + + if (rconf) { + if (rconf->need_dirwalk) { + ap_directory_walk(r); + } + } + + /* Strip proxy: prefixes */ + if (r->filename) { + char *newfname = NULL; + + if (!strncmp(r->filename, "proxy:balancer://", 17)) { + newfname = apr_pstrdup(r->pool, r->filename+17); + } + + if (!FCGI_MAY_BE_FPM(dconf)) { + if (!strncmp(r->filename, "proxy:fcgi://", 13)) { + /* If we strip this under FPM, and any internal redirect occurs + * on PATH_INFO, FPM may use PATH_TRANSLATED instead of + * SCRIPT_FILENAME (a la mod_fastcgi + Action). + */ + newfname = apr_pstrdup(r->pool, r->filename+13); + } + /* Query string in environment only */ + if (newfname && r->args && *r->args) { + char *qs = strrchr(newfname, '?'); + if (qs && !strcmp(qs+1, r->args)) { + *qs = '\0'; + } + } + } + + if (newfname) { + newfname = ap_strchr(newfname, '/'); + r->filename = newfname; + } + } + + ap_add_common_vars(r); + ap_add_cgi_vars(r); + + /* XXX are there any FastCGI specific env vars we need to send? */ + + /* Give admins final option to fine-tune env vars */ + if (APR_SUCCESS != (rv = fix_cgivars(r, dconf))) { + return rv; + } + + /* XXX mod_cgi/mod_cgid use ap_create_environment here, which fills in + * the TZ value specially. We could use that, but it would mean + * parsing the key/value pairs back OUT of the allocated env array, + * not to mention allocating a totally useless array in the first + * place, which would suck. */ + + envarr = apr_table_elts(r->subprocess_env); + elts = (const apr_table_entry_t *) envarr->elts; + + if (APLOGrtrace8(r)) { + int i; + + for (i = 0; i < envarr->nelts; ++i) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(01062) + "sending env var '%s' value '%s'", + elts[i].key, elts[i].val); + } + } + + /* Send envvars over in as many FastCGI records as it takes, */ + next_elem = 0; /* starting with the first one */ + + avail_len = 16 * 1024; /* our limit per record, which could have been up + * to AP_FCGI_MAX_CONTENT_LEN + */ + + while (next_elem < envarr->nelts) { + starting_elem = next_elem; + required_len = ap_fcgi_encoded_env_len(r->subprocess_env, + avail_len, + &next_elem); + + if (!required_len) { + if (next_elem < envarr->nelts) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + APLOGNO(02536) "couldn't encode envvar '%s' in %" + APR_SIZE_T_FMT " bytes", + elts[next_elem].key, avail_len); + /* skip this envvar and continue */ + ++next_elem; + continue; + } + /* only an unused element at the end of the array */ + break; + } + + body = apr_palloc(temp_pool, required_len); + rv = ap_fcgi_encode_env(r, r->subprocess_env, body, required_len, + &starting_elem); + /* we pre-compute, so we can't run out of space */ + ap_assert(rv == APR_SUCCESS); + /* compute and encode must be in sync */ + ap_assert(starting_elem == next_elem); + + ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, + (apr_uint16_t)required_len, 0); + ap_fcgi_header_to_array(&header, farray); + + vec[0].iov_base = (void *)farray; + vec[0].iov_len = sizeof(farray); + vec[1].iov_base = body; + vec[1].iov_len = required_len; + + rv = send_data(conn, vec, 2, &len); + apr_pool_clear(temp_pool); + + if (rv) { + return rv; + } + } + + /* Envvars sent, so say we're done */ + ap_fcgi_fill_in_header(&header, AP_FCGI_PARAMS, request_id, 0, 0); + ap_fcgi_header_to_array(&header, farray); + + vec[0].iov_base = (void *)farray; + vec[0].iov_len = sizeof(farray); + + return send_data(conn, vec, 1, &len); +} + +enum { + HDR_STATE_READING_HEADERS, + HDR_STATE_GOT_CR, + HDR_STATE_GOT_CRLF, + HDR_STATE_GOT_CRLFCR, + HDR_STATE_GOT_LF, + HDR_STATE_DONE_WITH_HEADERS +}; + +/* Try to find the end of the script headers in the response from the back + * end fastcgi server. STATE holds the current header parsing state for this + * request. + * + * Returns 0 if it can't find the end of the headers, and 1 if it found the + * end of the headers. */ +static int handle_headers(request_rec *r, int *state, + const char *readbuf, apr_size_t readlen) +{ + const char *itr = readbuf; + + while (readlen--) { + if (*itr == '\r') { + switch (*state) { + case HDR_STATE_GOT_CRLF: + *state = HDR_STATE_GOT_CRLFCR; + break; + + default: + *state = HDR_STATE_GOT_CR; + break; + } + } + else if (*itr == '\n') { + switch (*state) { + case HDR_STATE_GOT_LF: + *state = HDR_STATE_DONE_WITH_HEADERS; + break; + + case HDR_STATE_GOT_CR: + *state = HDR_STATE_GOT_CRLF; + break; + + case HDR_STATE_GOT_CRLFCR: + *state = HDR_STATE_DONE_WITH_HEADERS; + break; + + default: + *state = HDR_STATE_GOT_LF; + break; + } + } + else { + *state = HDR_STATE_READING_HEADERS; + } + + if (*state == HDR_STATE_DONE_WITH_HEADERS) + break; + + ++itr; + } + + if (*state == HDR_STATE_DONE_WITH_HEADERS) { + return 1; + } + + return 0; +} + +static apr_status_t dispatch(proxy_conn_rec *conn, proxy_dir_conf *conf, + request_rec *r, apr_pool_t *setaside_pool, + apr_uint16_t request_id, const char **err, + int *bad_request, int *has_responded, + apr_bucket_brigade *input_brigade) +{ + apr_bucket_brigade *ib, *ob; + int seen_end_of_headers = 0, done = 0, ignore_body = 0; + apr_status_t rv = APR_SUCCESS; + int script_error_status = HTTP_OK; + conn_rec *c = r->connection; + struct iovec vec[2]; + ap_fcgi_header header; + unsigned char farray[AP_FCGI_HEADER_LEN]; + apr_pollfd_t pfd; + apr_pollfd_t *flushpoll = NULL; + apr_int32_t flushpoll_fd; + int header_state = HDR_STATE_READING_HEADERS; + char stack_iobuf[AP_IOBUFSIZE]; + apr_size_t iobuf_size = AP_IOBUFSIZE; + char *iobuf = stack_iobuf; + + *err = NULL; + if (conn->worker->s->io_buffer_size_set) { + iobuf_size = conn->worker->s->io_buffer_size; + iobuf = apr_palloc(r->pool, iobuf_size); + } + + pfd.desc_type = APR_POLL_SOCKET; + pfd.desc.s = conn->sock; + pfd.p = r->pool; + pfd.reqevents = APR_POLLIN | APR_POLLOUT; + + if (conn->worker->s->flush_packets == flush_auto) { + flushpoll = apr_pcalloc(r->pool, sizeof(apr_pollfd_t)); + flushpoll->reqevents = APR_POLLIN; + flushpoll->desc_type = APR_POLL_SOCKET; + flushpoll->desc.s = conn->sock; + } + + ib = apr_brigade_create(r->pool, c->bucket_alloc); + ob = apr_brigade_create(r->pool, c->bucket_alloc); + + while (! done) { + apr_interval_time_t timeout; + apr_size_t len; + int n; + + /* We need SOME kind of timeout here, or virtually anything will + * cause timeout errors. */ + apr_socket_timeout_get(conn->sock, &timeout); + + rv = apr_poll(&pfd, 1, &n, timeout); + if (rv != APR_SUCCESS) { + if (APR_STATUS_IS_EINTR(rv)) { + continue; + } + *err = "polling"; + break; + } + + if (pfd.rtnevents & APR_POLLOUT) { + apr_size_t to_send, writebuflen; + int last_stdin = 0; + char *iobuf_cursor; + + if (APR_BRIGADE_EMPTY(input_brigade)) { + rv = ap_get_brigade(r->input_filters, ib, + AP_MODE_READBYTES, APR_BLOCK_READ, + iobuf_size); + } + else { + apr_bucket *e; + APR_BRIGADE_CONCAT(ib, input_brigade); + rv = apr_brigade_partition(ib, iobuf_size, &e); + if (rv == APR_SUCCESS) { + while (e != APR_BRIGADE_SENTINEL(ib) + && APR_BUCKET_IS_METADATA(e)) { + e = APR_BUCKET_NEXT(e); + } + apr_brigade_split_ex(ib, e, input_brigade); + } + else if (rv == APR_INCOMPLETE) { + rv = APR_SUCCESS; + } + } + if (rv != APR_SUCCESS) { + *err = "reading input brigade"; + *bad_request = 1; + break; + } + + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(ib))) { + last_stdin = 1; + } + + writebuflen = iobuf_size; + + rv = apr_brigade_flatten(ib, iobuf, &writebuflen); + + apr_brigade_cleanup(ib); + + if (rv != APR_SUCCESS) { + *err = "flattening brigade"; + break; + } + + to_send = writebuflen; + iobuf_cursor = iobuf; + while (to_send > 0) { + int nvec = 0; + apr_size_t write_this_time; + + write_this_time = + to_send < AP_FCGI_MAX_CONTENT_LEN ? to_send : AP_FCGI_MAX_CONTENT_LEN; + + ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, + (apr_uint16_t)write_this_time, 0); + ap_fcgi_header_to_array(&header, farray); + + vec[nvec].iov_base = (void *)farray; + vec[nvec].iov_len = sizeof(farray); + ++nvec; + if (writebuflen) { + vec[nvec].iov_base = iobuf_cursor; + vec[nvec].iov_len = write_this_time; + ++nvec; + } + + rv = send_data(conn, vec, nvec, &len); + if (rv != APR_SUCCESS) { + *err = "sending stdin"; + break; + } + + to_send -= write_this_time; + iobuf_cursor += write_this_time; + } + if (rv != APR_SUCCESS) { + break; + } + + if (last_stdin) { + pfd.reqevents = APR_POLLIN; /* Done with input data */ + + /* signal EOF (empty FCGI_STDIN) */ + ap_fcgi_fill_in_header(&header, AP_FCGI_STDIN, request_id, + 0, 0); + ap_fcgi_header_to_array(&header, farray); + + vec[0].iov_base = (void *)farray; + vec[0].iov_len = sizeof(farray); + + rv = send_data(conn, vec, 1, &len); + if (rv != APR_SUCCESS) { + *err = "sending empty stdin"; + break; + } + } + } + + if (pfd.rtnevents & APR_POLLIN) { + apr_size_t readbuflen; + apr_uint16_t clen, rid; + apr_bucket *b; + unsigned char plen; + unsigned char type, version; + int mayflush = 0; + + /* First, we grab the header... */ + rv = get_data_full(conn, (char *) farray, AP_FCGI_HEADER_LEN); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01067) + "Failed to read FastCGI header"); + break; + } + + ap_log_rdata(APLOG_MARK, APLOG_TRACE8, r, "FastCGI header", + farray, AP_FCGI_HEADER_LEN, 0); + + ap_fcgi_header_fields_from_array(&version, &type, &rid, + &clen, &plen, farray); + + if (version != AP_FCGI_VERSION_1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01068) + "Got bogus version %d", (int)version); + rv = APR_EINVAL; + break; + } + + if (rid != request_id) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01069) + "Got bogus rid %d, expected %d", + rid, request_id); + rv = APR_EINVAL; + break; + } + +recv_again: + if (clen > iobuf_size) { + readbuflen = iobuf_size; + } else { + readbuflen = clen; + } + + /* Now get the actual data. Yes it sucks to do this in a second + * recv call, this will eventually change when we move to real + * nonblocking recv calls. */ + if (readbuflen != 0) { + rv = get_data(conn, iobuf, &readbuflen); + if (rv != APR_SUCCESS) { + *err = "reading response body"; + break; + } + } + + switch (type) { + case AP_FCGI_STDOUT: + if (clen != 0) { + b = apr_bucket_transient_create(iobuf, + readbuflen, + c->bucket_alloc); + + APR_BRIGADE_INSERT_TAIL(ob, b); + + if (! seen_end_of_headers) { + int st = handle_headers(r, &header_state, + iobuf, readbuflen); + + if (st == 1) { + int status; + seen_end_of_headers = 1; + + status = ap_scan_script_header_err_brigade_ex(r, ob, + NULL, APLOG_MODULE_INDEX); + /* suck in all the rest */ + if (status != OK) { + apr_bucket *tmp_b; + apr_brigade_cleanup(ob); + tmp_b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ob, tmp_b); + + *has_responded = 1; + r->status = status; + rv = ap_pass_brigade(r->output_filters, ob); + if (rv != APR_SUCCESS) { + *err = "passing headers brigade to output filters"; + break; + } + else if (status == HTTP_NOT_MODIFIED + || status == HTTP_PRECONDITION_FAILED) { + /* Special 'status' cases handled: + * 1) HTTP 304 response MUST NOT contain + * a message-body, ignore it. + * 2) HTTP 412 response. + * The break is not added since there might + * be more bytes to read from the FCGI + * connection. Even if the message-body is + * ignored (and the EOS bucket has already + * been sent) we want to avoid subsequent + * bogus reads. */ + ignore_body = 1; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01070) + "Error parsing script headers"); + rv = APR_EINVAL; + break; + } + } + + if (ap_proxy_should_override(conf, r->status) && ap_is_initial_req(r)) { + /* + * set script_error_status to discard + * everything after the headers + */ + script_error_status = r->status; + /* + * prevent ap_die() from treating this as a + * recursive error, initially: + */ + r->status = HTTP_OK; + } + + if (script_error_status == HTTP_OK + && !APR_BRIGADE_EMPTY(ob) && !ignore_body) { + /* Send the part of the body that we read while + * reading the headers. + */ + *has_responded = 1; + rv = ap_pass_brigade(r->output_filters, ob); + if (rv != APR_SUCCESS) { + *err = "passing brigade to output filters"; + break; + } + mayflush = 1; + } + apr_brigade_cleanup(ob); + + apr_pool_clear(setaside_pool); + } + else { + /* We're still looking for the end of the + * headers, so this part of the data will need + * to persist. */ + apr_bucket_setaside(b, setaside_pool); + } + } else { + /* we've already passed along the headers, so now pass + * through the content. we could simply continue to + * setaside the content and not pass until we see the + * 0 content-length (below, where we append the EOS), + * but that could be a huge amount of data; so we pass + * along smaller chunks + */ + if (script_error_status == HTTP_OK && !ignore_body) { + *has_responded = 1; + rv = ap_pass_brigade(r->output_filters, ob); + if (rv != APR_SUCCESS) { + *err = "passing brigade to output filters"; + break; + } + mayflush = 1; + } + apr_brigade_cleanup(ob); + } + + /* If we didn't read all the data, go back and get the + * rest of it. */ + if (clen > readbuflen) { + clen -= readbuflen; + goto recv_again; + } + } else { + /* XXX what if we haven't seen end of the headers yet? */ + + if (script_error_status == HTTP_OK) { + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ob, b); + + *has_responded = 1; + rv = ap_pass_brigade(r->output_filters, ob); + if (rv != APR_SUCCESS) { + *err = "passing brigade to output filters"; + break; + } + } + + /* XXX Why don't we cleanup here? (logic from AJP) */ + } + break; + + case AP_FCGI_STDERR: + /* TODO: Should probably clean up this logging a bit... */ + if (clen) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01071) + "Got error '%.*s'", (int)readbuflen, iobuf); + } + + if (clen > readbuflen) { + clen -= readbuflen; + goto recv_again; + } + break; + + case AP_FCGI_END_REQUEST: + done = 1; + break; + + default: + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01072) + "Got bogus record %d", type); + break; + } + /* Leave on above switch's inner error. */ + if (rv != APR_SUCCESS) { + break; + } + + if (plen) { + rv = get_data_full(conn, iobuf, plen); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02537) + "Error occurred reading padding"); + break; + } + } + + if (mayflush && ((conn->worker->s->flush_packets == flush_on) || + ((conn->worker->s->flush_packets == flush_auto) && + (apr_poll(flushpoll, 1, &flushpoll_fd, + conn->worker->s->flush_wait) == APR_TIMEUP)))) { + apr_bucket* flush_b = apr_bucket_flush_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ob, flush_b); + rv = ap_pass_brigade(r->output_filters, ob); + if (rv != APR_SUCCESS) { + *err = "passing headers brigade to output filters"; + break; + } + mayflush = 0; + } + } + } + + apr_brigade_destroy(ib); + apr_brigade_destroy(ob); + + if (script_error_status != HTTP_OK) { + ap_die(script_error_status, r); /* send ErrorDocument */ + *has_responded = 1; + } + + return rv; +} + +/* + * process the request and write the response. + */ +static int fcgi_do_request(apr_pool_t *p, request_rec *r, + proxy_conn_rec *conn, + conn_rec *origin, + proxy_dir_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr, + apr_bucket_brigade *input_brigade) +{ + /* Request IDs are arbitrary numbers that we assign to a + * single request. This would allow multiplex/pipelining of + * multiple requests to the same FastCGI connection, but + * we don't support that, and always use a value of '1' to + * keep things simple. */ + apr_uint16_t request_id = 1; + apr_status_t rv; + apr_pool_t *temp_pool; + const char *err; + int bad_request = 0, + has_responded = 0; + + /* Step 1: Send AP_FCGI_BEGIN_REQUEST */ + rv = send_begin_request(conn, request_id); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01073) + "Failed Writing Request to %s:", server_portstr); + conn->close = 1; + return HTTP_SERVICE_UNAVAILABLE; + } + + apr_pool_create(&temp_pool, r->pool); + apr_pool_tag(temp_pool, "proxy_fcgi_do_request"); + + /* Step 2: Send Environment via FCGI_PARAMS */ + rv = send_environment(conn, r, temp_pool, request_id); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01074) + "Failed writing Environment to %s:", server_portstr); + conn->close = 1; + return HTTP_SERVICE_UNAVAILABLE; + } + + /* Step 3: Read records from the back end server and handle them. */ + rv = dispatch(conn, conf, r, temp_pool, request_id, + &err, &bad_request, &has_responded, + input_brigade); + if (rv != APR_SUCCESS) { + /* If the client aborted the connection during retrieval or (partially) + * sending the response, don't return a HTTP_SERVICE_UNAVAILABLE, since + * this is not a backend problem. */ + if (r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, + "The client aborted the connection."); + conn->close = 1; + return OK; + } + + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01075) + "Error dispatching request to %s: %s%s%s", + server_portstr, + err ? "(" : "", + err ? err : "", + err ? ")" : ""); + conn->close = 1; + if (has_responded) { + return AP_FILTER_ERROR; + } + if (bad_request) { + return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); + } + if (APR_STATUS_IS_TIMEUP(rv)) { + return HTTP_GATEWAY_TIME_OUT; + } + return HTTP_SERVICE_UNAVAILABLE; + } + + return OK; +} + +#define FCGI_SCHEME "FCGI" + +#define MAX_MEM_SPOOL 16384 + +/* + * This handles fcgi:(dest) URLs + */ +static int proxy_fcgi_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + int status; + char server_portstr[32]; + conn_rec *origin = NULL; + proxy_conn_rec *backend = NULL; + apr_bucket_brigade *input_brigade; + apr_off_t input_bytes = 0; + apr_uri_t *uri; + + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_module); + + apr_pool_t *p = r->pool; + + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01076) + "url: %s proxyname: %s proxyport: %d", + url, proxyname, proxyport); + + if (ap_cstr_casecmpn(url, "fcgi:", 5) != 0) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01077) "declining URL %s", url); + return DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01078) "serving URL %s", url); + + /* Create space for state information */ + status = ap_proxy_acquire_connection(FCGI_SCHEME, &backend, worker, + r->server); + if (status != OK) { + if (backend) { + backend->close = 1; + ap_proxy_release_connection(FCGI_SCHEME, backend, r->server); + } + return status; + } + + backend->is_ssl = 0; + + /* 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, + sizeof(server_portstr)); + if (status != OK) { + goto cleanup; + } + + /* We possibly reuse input data prefetched in previous call(s), e.g. for a + * balancer fallback scenario. + */ + apr_pool_userdata_get((void **)&input_brigade, "proxy-fcgi-input", p); + if (input_brigade == NULL) { + const char *old_te = apr_table_get(r->headers_in, "Transfer-Encoding"); + const char *old_cl = NULL; + if (old_te) { + apr_table_unset(r->headers_in, "Content-Length"); + } + else { + old_cl = apr_table_get(r->headers_in, "Content-Length"); + } + + input_brigade = apr_brigade_create(p, r->connection->bucket_alloc); + apr_pool_userdata_setn(input_brigade, "proxy-fcgi-input", NULL, p); + + /* Prefetch (nonlocking) the request body so to increase the chance + * to get the whole (or enough) body and determine Content-Length vs + * chunked or spooled. By doing this before connecting or reusing the + * backend, we want to minimize the delay between this connection is + * considered alive and the first bytes sent (should the client's link + * be slow or some input filter retain the data). This is a best effort + * to prevent the backend from closing (from under us) what it thinks is + * an idle connection, hence to reduce to the minimum the unavoidable + * local is_socket_connected() vs remote keepalive race condition. + */ + status = ap_proxy_prefetch_input(r, backend, input_brigade, + APR_NONBLOCK_READ, &input_bytes, + MAX_MEM_SPOOL); + if (status != OK) { + goto cleanup; + } + + /* + * The request body is streamed by default, using either C-L or + * chunked T-E, like this: + * + * The whole body (including no body) was received on prefetch, i.e. + * the input brigade ends with EOS => C-L = input_bytes. + * + * C-L is known and reliable, i.e. only protocol filters in the input + * chain thus none should change the body => use C-L from client. + * + * The administrator has not "proxy-sendcl" which prevents T-E => use + * T-E and chunks. + * + * Otherwise we need to determine and set a content-length, so spool + * the entire request body to memory/temporary file (MAX_MEM_SPOOL), + * such that we finally know its length => C-L = input_bytes. + */ + if (!APR_BRIGADE_EMPTY(input_brigade) + && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + /* The whole thing fit, so our decision is trivial, use the input + * bytes for the Content-Length. If we expected no body, and read + * no body, do not set the Content-Length. + */ + if (old_cl || old_te || input_bytes) { + apr_table_setn(r->headers_in, "Content-Length", + apr_off_t_toa(p, input_bytes)); + if (old_te) { + apr_table_unset(r->headers_in, "Transfer-Encoding"); + } + } + } + else if (old_cl && r->input_filters == r->proto_input_filters) { + /* Streaming is possible by preserving the existing C-L */ + } + else if (!apr_table_get(r->subprocess_env, "proxy-sendcl")) { + /* Streaming is possible using T-E: chunked */ + } + else { + /* No streaming, C-L is the only option so spool to memory/file */ + apr_bucket_brigade *tmp_bb; + apr_off_t remaining_bytes = 0; + + AP_DEBUG_ASSERT(MAX_MEM_SPOOL >= input_bytes); + tmp_bb = apr_brigade_create(p, r->connection->bucket_alloc); + status = ap_proxy_spool_input(r, backend, tmp_bb, &remaining_bytes, + MAX_MEM_SPOOL - input_bytes); + if (status != OK) { + goto cleanup; + } + + APR_BRIGADE_CONCAT(input_brigade, tmp_bb); + input_bytes += remaining_bytes; + + apr_table_setn(r->headers_in, "Content-Length", + apr_off_t_toa(p, input_bytes)); + if (old_te) { + apr_table_unset(r->headers_in, "Transfer-Encoding"); + } + } + } + + /* This scheme handler does not reuse connections by default, to + * avoid tying up a fastcgi that isn't expecting to work on + * parallel requests. But if the user went out of their way to + * type the default value of disablereuse=off, we'll allow it. + */ + backend->close = 1; + if (worker->s->disablereuse_set && !worker->s->disablereuse) { + backend->close = 0; + } + + /* Step Two: Make the Connection */ + if (ap_proxy_check_connection(FCGI_SCHEME, backend, r->server, 0, + PROXY_CHECK_CONN_EMPTY) + && ap_proxy_connect_backend(FCGI_SCHEME, backend, worker, + r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01079) + "failed to make connection to backend: %s", + backend->hostname); + status = HTTP_SERVICE_UNAVAILABLE; + goto cleanup; + } + + /* Step Three: Process the Request */ + status = fcgi_do_request(p, r, backend, origin, dconf, uri, url, + server_portstr, input_brigade); + +cleanup: + ap_proxy_release_connection(FCGI_SCHEME, backend, r->server); + return status; +} + +static void *fcgi_create_dconf(apr_pool_t *p, char *path) +{ + fcgi_dirconf_t *a; + + a = (fcgi_dirconf_t *)apr_pcalloc(p, sizeof(fcgi_dirconf_t)); + a->backend_type = BACKEND_DEFAULT_UNKNOWN; + a->env_fixups = apr_array_make(p, 20, sizeof(sei_entry)); + + return a; +} + +static void *fcgi_merge_dconf(apr_pool_t *p, void *basev, void *overridesv) +{ + fcgi_dirconf_t *a, *base, *over; + + a = (fcgi_dirconf_t *)apr_pcalloc(p, sizeof(fcgi_dirconf_t)); + base = (fcgi_dirconf_t *)basev; + over = (fcgi_dirconf_t *)overridesv; + + a->backend_type = (over->backend_type != BACKEND_DEFAULT_UNKNOWN) + ? over->backend_type + : base->backend_type; + a->env_fixups = apr_array_append(p, base->env_fixups, over->env_fixups); + return a; +} + +static const char *cmd_servertype(cmd_parms *cmd, void *in_dconf, + const char *val) +{ + fcgi_dirconf_t *dconf = in_dconf; + + if (!strcasecmp(val, "GENERIC")) { + dconf->backend_type = BACKEND_GENERIC; + } + else if (!strcasecmp(val, "FPM")) { + dconf->backend_type = BACKEND_FPM; + } + else { + return "ProxyFCGIBackendType requires one of the following arguments: " + "'GENERIC', 'FPM'"; + } + + return NULL; +} + + +static const char *cmd_setenv(cmd_parms *cmd, void *in_dconf, + const char *arg1, const char *arg2, + const char *arg3) +{ + fcgi_dirconf_t *dconf = in_dconf; + const char *err; + sei_entry *new; + const char *envvar = arg2; + + new = apr_array_push(dconf->env_fixups); + new->cond = ap_expr_parse_cmd(cmd, arg1, 0, &err, NULL); + if (err) { + return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s", + arg1, err); + } + + if (envvar[0] == '!') { + /* Unset mode. */ + if (arg3) { + return apr_psprintf(cmd->pool, "Third argument (\"%s\") is not " + "allowed when using ProxyFCGISetEnvIf's unset " + "mode (%s)", arg3, envvar); + } + else if (!envvar[1]) { + /* i.e. someone tried to give us a name of just "!" */ + return "ProxyFCGISetEnvIf: \"!\" is not a valid variable name"; + } + + new->subst = NULL; + } + else { + /* Set mode. */ + if (!arg3) { + /* A missing expr-value should be treated as empty. */ + arg3 = ""; + } + + new->subst = ap_expr_parse_cmd(cmd, arg3, AP_EXPR_FLAG_STRING_RESULT, &err, NULL); + if (err) { + return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s", + arg3, err); + } + } + + new->envname = envvar; + + return NULL; +} +static void register_hooks(apr_pool_t *p) +{ + proxy_hook_scheme_handler(proxy_fcgi_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(proxy_fcgi_canon, NULL, NULL, APR_HOOK_FIRST); +} + +static const command_rec command_table[] = { + AP_INIT_TAKE1("ProxyFCGIBackendType", cmd_servertype, NULL, OR_FILEINFO, + "Specify the type of FastCGI server: 'Generic', 'FPM'"), + AP_INIT_TAKE23("ProxyFCGISetEnvIf", cmd_setenv, NULL, OR_FILEINFO, + "expr-condition env-name expr-value"), + { NULL } +}; + +AP_DECLARE_MODULE(proxy_fcgi) = { + STANDARD20_MODULE_STUFF, + fcgi_create_dconf, /* create per-directory config structure */ + fcgi_merge_dconf, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + command_table, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_fcgi.dep b/modules/proxy/mod_proxy_fcgi.dep new file mode 100644 index 0000000..a2fc7cb --- /dev/null +++ b/modules/proxy/mod_proxy_fcgi.dep @@ -0,0 +1,75 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_fcgi.mak + +.\mod_proxy_fcgi.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_fcgi.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_fcgi.dsp b/modules/proxy/mod_proxy_fcgi.dsp new file mode 100644 index 0000000..ac778ed --- /dev/null +++ b/modules/proxy/mod_proxy_fcgi.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_fcgi" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_fcgi - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_fcgi.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_fcgi.mak" CFG="mod_proxy_fcgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_fcgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_fcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_fcgi_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_fcgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_fcgi.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_fcgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_fcgi.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_fcgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_fcgi_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_fcgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_fcgi.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_fcgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_fcgi.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_fcgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_fcgi - Win32 Release" +# Name "mod_proxy_fcgi - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_fcgi.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_fcgi.mak b/modules/proxy/mod_proxy_fcgi.mak new file mode 100644 index 0000000..4b15088 --- /dev/null +++ b/modules/proxy/mod_proxy_fcgi.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_fcgi.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_fcgi - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_fcgi - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_fcgi - Win32 Release" && "$(CFG)" != "mod_proxy_fcgi - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_fcgi.mak" CFG="mod_proxy_fcgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_fcgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_fcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_fcgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_fcgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_fcgi.obj" + -@erase "$(INTDIR)\mod_proxy_fcgi.res" + -@erase "$(INTDIR)\mod_proxy_fcgi_src.idb" + -@erase "$(INTDIR)\mod_proxy_fcgi_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_fcgi.exp" + -@erase "$(OUTDIR)\mod_proxy_fcgi.lib" + -@erase "$(OUTDIR)\mod_proxy_fcgi.pdb" + -@erase "$(OUTDIR)\mod_proxy_fcgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_fcgi_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_fcgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_fcgi.pdb" /debug /out:"$(OUTDIR)\mod_proxy_fcgi.so" /implib:"$(OUTDIR)\mod_proxy_fcgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_fcgi.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_fcgi.obj" \ + "$(INTDIR)\mod_proxy_fcgi.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_fcgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_fcgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_fcgi.so" + if exist .\Release\mod_proxy_fcgi.so.manifest mt.exe -manifest .\Release\mod_proxy_fcgi.so.manifest -outputresource:.\Release\mod_proxy_fcgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_fcgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_fcgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_fcgi.obj" + -@erase "$(INTDIR)\mod_proxy_fcgi.res" + -@erase "$(INTDIR)\mod_proxy_fcgi_src.idb" + -@erase "$(INTDIR)\mod_proxy_fcgi_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_fcgi.exp" + -@erase "$(OUTDIR)\mod_proxy_fcgi.lib" + -@erase "$(OUTDIR)\mod_proxy_fcgi.pdb" + -@erase "$(OUTDIR)\mod_proxy_fcgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_fcgi_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_fcgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_fcgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_fcgi.pdb" /debug /out:"$(OUTDIR)\mod_proxy_fcgi.so" /implib:"$(OUTDIR)\mod_proxy_fcgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_fcgi.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_fcgi.obj" \ + "$(INTDIR)\mod_proxy_fcgi.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_fcgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_fcgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_fcgi.so" + if exist .\Debug\mod_proxy_fcgi.so.manifest mt.exe -manifest .\Debug\mod_proxy_fcgi.so.manifest -outputresource:.\Debug\mod_proxy_fcgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_fcgi.dep") +!INCLUDE "mod_proxy_fcgi.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_fcgi.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" || "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" +SOURCE=.\mod_proxy_fcgi.c + +"$(INTDIR)\mod_proxy_fcgi.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_fcgi - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_fcgi - Win32 Release" + + +"$(INTDIR)\mod_proxy_fcgi.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_fcgi.so" /d LONG_NAME="proxy_fcgi_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_fdpass.c b/modules/proxy/mod_proxy_fdpass.c new file mode 100644 index 0000000..8f9893d --- /dev/null +++ b/modules/proxy/mod_proxy_fdpass.c @@ -0,0 +1,241 @@ +/* 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 <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +#ifndef CMSG_DATA +#error This module only works on unix platforms with the correct OS support +#endif + +#include "mod_proxy_fdpass.h" + +module AP_MODULE_DECLARE_DATA proxy_fdpass_module; + +static int proxy_fdpass_canon(request_rec *r, char *url) +{ + const char *path; + + if (ap_cstr_casecmpn(url, "fd://", 5) == 0) { + url += 5; + } + else { + return DECLINED; + } + + path = ap_server_root_relative(r->pool, url); + + r->filename = apr_pstrcat(r->pool, "proxy:fd://", path, NULL); + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01151) + "set r->filename to %s", r->filename); + return OK; +} + +static apr_status_t get_socket_from_path(apr_pool_t *p, + const char* path, + apr_socket_t **out_sock) +{ + apr_socket_t *s; + apr_status_t rv; + *out_sock = NULL; + + rv = apr_socket_create(&s, AF_UNIX, SOCK_STREAM, 0, p); + if (rv != APR_SUCCESS) { + return rv; + } + + rv = ap_proxy_connect_uds(s, path, p); + if (rv != APR_SUCCESS) { + return rv; + } + + *out_sock = s; + + return APR_SUCCESS; +} + +static apr_status_t send_socket(apr_pool_t *p, + apr_socket_t *s, + apr_socket_t *outbound) +{ + apr_status_t rv; + apr_os_sock_t rawsock; + apr_os_sock_t srawsock; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + char b = '\0'; + + rv = apr_os_sock_get(&rawsock, outbound); + if (rv != APR_SUCCESS) { + return rv; + } + + rv = apr_os_sock_get(&srawsock, s); + if (rv != APR_SUCCESS) { + return rv; + } + + memset(&msg, 0, sizeof(msg)); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + iov.iov_base = &b; + iov.iov_len = 1; + + cmsg = apr_palloc(p, sizeof(*cmsg) + sizeof(rawsock)); + cmsg->cmsg_len = sizeof(*cmsg) + sizeof(rawsock); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + memcpy(CMSG_DATA(cmsg), &rawsock, sizeof(rawsock)); + + msg.msg_control = cmsg; + msg.msg_controllen = cmsg->cmsg_len; + + rv = sendmsg(srawsock, &msg, 0); + + if (rv == -1) { + return errno; + } + + return APR_SUCCESS; +} + +static int proxy_fdpass_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + apr_status_t rv; + apr_socket_t *sock; + apr_socket_t *clientsock; + + if (ap_cstr_casecmpn(url, "fd://", 5) == 0) { + url += 5; + } + else { + return DECLINED; + } + + rv = get_socket_from_path(r->pool, url, &sock); + + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01152) + "Failed to connect to '%s'", url); + return HTTP_INTERNAL_SERVER_ERROR; + } + + { + int status; + const char *flush_method = *worker->s->flusher ? worker->s->flusher : "flush"; + + proxy_fdpass_flush *flush = ap_lookup_provider(PROXY_FDPASS_FLUSHER, + flush_method, "0"); + + if (!flush) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01153) + "Unable to find configured flush provider '%s'", + flush_method); + return HTTP_INTERNAL_SERVER_ERROR; + } + + status = flush->flusher(r); + if (status) { + return status; + } + } + + clientsock = ap_get_conn_socket(r->connection); + + rv = send_socket(r->pool, sock, clientsock); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01154) "send_socket failed:"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + { + apr_socket_t *dummy; + /* Create a dummy unconnected socket, and set it as the one we were + * connected to, so that when the core closes it, it doesn't close + * the tcp connection to the client. + */ + rv = apr_socket_create(&dummy, APR_INET, SOCK_STREAM, APR_PROTO_TCP, + r->connection->pool); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01155) + "failed to create dummy socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_set_core_module_config(r->connection->conn_config, dummy); + } + + return OK; +} + +static int standard_flush(request_rec *r) +{ + int status; + apr_bucket_brigade *bb; + apr_bucket *e; + + r->connection->keepalive = AP_CONN_CLOSE; + + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + e = apr_bucket_flush_create(r->connection->bucket_alloc); + + APR_BRIGADE_INSERT_TAIL(bb, e); + + status = ap_pass_brigade(r->output_filters, bb); + + if (status != OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01156) + "ap_pass_brigade failed:"); + return status; + } + + return OK; +} + + +static const proxy_fdpass_flush builtin_flush = +{ + "flush", + &standard_flush, + NULL +}; + +static void register_hooks(apr_pool_t *p) +{ + ap_register_provider(p, PROXY_FDPASS_FLUSHER, "flush", "0", &builtin_flush); + proxy_hook_scheme_handler(proxy_fdpass_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(proxy_fdpass_canon, NULL, NULL, APR_HOOK_FIRST); +} + +AP_DECLARE_MODULE(proxy_fdpass) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_fdpass.h b/modules/proxy/mod_proxy_fdpass.h new file mode 100644 index 0000000..1a13fed --- /dev/null +++ b/modules/proxy/mod_proxy_fdpass.h @@ -0,0 +1,41 @@ +/* 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. + */ + +/** + * @file mod_proxy_fdpass.h + * @brief FD Passing interfaces + * + * @ingroup APACHE_INTERNAL + * @{ + */ + +#include "mod_proxy.h" + +#ifndef _PROXY_FDPASS_H_ +#define _PROXY_FDPASS_H_ + +#define PROXY_FDPASS_FLUSHER "proxy_fdpass_flusher" + +typedef struct proxy_fdpass_flush proxy_fdpass_flush; +struct proxy_fdpass_flush { + const char *name; + int (*flusher)(request_rec *r); + void *context; +}; + +#endif /* _PROXY_FDPASS_H_ */ +/** @} */ + diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c new file mode 100644 index 0000000..a3fb10a --- /dev/null +++ b/modules/proxy/mod_proxy_ftp.c @@ -0,0 +1,2141 @@ +/* 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. + */ + +/* FTP routines for Apache proxy */ + +#define APR_WANT_BYTEFUNC +#include "mod_proxy.h" +#if APR_HAVE_TIME_H +#include <time.h> +#endif +#include "apr_version.h" + +#define AUTODETECT_PWD +/* Automatic timestamping (Last-Modified header) based on MDTM is used if: + * 1) the FTP server supports the MDTM command and + * 2) HAVE_TIMEGM (preferred) or HAVE_GMTOFF is available at compile time + */ +#define USE_MDTM + + +module AP_MODULE_DECLARE_DATA proxy_ftp_module; + +typedef struct { + int ftp_list_on_wildcard; + int ftp_list_on_wildcard_set; + int ftp_escape_wildcards; + int ftp_escape_wildcards_set; + const char *ftp_directory_charset; +} proxy_ftp_dir_conf; + +static void *create_proxy_ftp_dir_config(apr_pool_t *p, char *dummy) +{ + proxy_ftp_dir_conf *new = + (proxy_ftp_dir_conf *) apr_pcalloc(p, sizeof(proxy_ftp_dir_conf)); + + /* Put these in the dir config so they work inside <Location> */ + new->ftp_list_on_wildcard = 1; + new->ftp_escape_wildcards = 1; + + return (void *) new; +} + +static void *merge_proxy_ftp_dir_config(apr_pool_t *p, void *basev, void *addv) +{ + proxy_ftp_dir_conf *new = (proxy_ftp_dir_conf *) apr_pcalloc(p, sizeof(proxy_ftp_dir_conf)); + proxy_ftp_dir_conf *add = (proxy_ftp_dir_conf *) addv; + proxy_ftp_dir_conf *base = (proxy_ftp_dir_conf *) basev; + + /* Put these in the dir config so they work inside <Location> */ + new->ftp_list_on_wildcard = add->ftp_list_on_wildcard_set ? + add->ftp_list_on_wildcard : + base->ftp_list_on_wildcard; + new->ftp_list_on_wildcard_set = add->ftp_list_on_wildcard_set ? + 1 : + base->ftp_list_on_wildcard_set; + new->ftp_escape_wildcards = add->ftp_escape_wildcards_set ? + add->ftp_escape_wildcards : + base->ftp_escape_wildcards; + new->ftp_escape_wildcards_set = add->ftp_escape_wildcards_set ? + 1 : + base->ftp_escape_wildcards_set; + new->ftp_directory_charset = add->ftp_directory_charset ? + add->ftp_directory_charset : + base->ftp_directory_charset; + return new; +} + +static const char *set_ftp_list_on_wildcard(cmd_parms *cmd, void *dconf, + int flag) +{ + proxy_ftp_dir_conf *conf = dconf; + + conf->ftp_list_on_wildcard = flag; + conf->ftp_list_on_wildcard_set = 1; + return NULL; +} + +static const char *set_ftp_escape_wildcards(cmd_parms *cmd, void *dconf, + int flag) +{ + proxy_ftp_dir_conf *conf = dconf; + + conf->ftp_escape_wildcards = flag; + conf->ftp_escape_wildcards_set = 1; + return NULL; +} + +static const char *set_ftp_directory_charset(cmd_parms *cmd, void *dconf, + const char *arg) +{ + proxy_ftp_dir_conf *conf = dconf; + + conf->ftp_directory_charset = arg; + return NULL; +} + +/* + * Decodes a '%' escaped string, and returns the number of characters + */ +static int decodeenc(char *x) +{ + int i, j, ch; + + if (x[0] == '\0') + return 0; /* special case for no characters */ + for (i = 0, j = 0; x[i] != '\0'; i++, j++) { + /* decode it if not already done */ + ch = x[i]; + if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } + x[j] = ch; + } + x[j] = '\0'; + return j; +} + +/* + * Escape the globbing characters in a path used as argument to + * the FTP commands (SIZE, CWD, RETR, MDTM, ...). + * ftpd assumes '\\' as a quoting character to escape special characters. + * Just returns the original string if ProxyFtpEscapeWildcards has been + * configured "off". + * Returns: escaped string + */ +#define FTP_GLOBBING_CHARS "*?[{~" +static const char *ftp_escape_globbingchars(apr_pool_t *p, const char *path, proxy_ftp_dir_conf *dconf) +{ + char *ret; + char *d; + + if (!dconf->ftp_escape_wildcards) { + return path; + } + + ret = apr_palloc(p, 2*strlen(path)+sizeof("")); + for (d = ret; *path; ++path) { + if (strchr(FTP_GLOBBING_CHARS, *path) != NULL) + *d++ = '\\'; + *d++ = *path; + } + *d = '\0'; + return ret; +} + +/* + * Check for globbing characters in a path used as argument to + * the FTP commands (SIZE, CWD, RETR, MDTM, ...). + * ftpd assumes '\\' as a quoting character to escape special characters. + * Returns: 0 (no globbing chars, or all globbing chars escaped), 1 (globbing chars) + */ +static int ftp_check_globbingchars(const char *path) +{ + for ( ; *path; ++path) { + if (*path == '\\') + ++path; + if (*path != '\0' && strchr(FTP_GLOBBING_CHARS, *path) != NULL) + return TRUE; + } + return FALSE; +} + +/* + * checks an encoded ftp string for bad characters, namely, CR, LF or + * non-ascii character + */ +static int ftp_check_string(const char *x) +{ + int i, ch = 0; +#if APR_CHARSET_EBCDIC + char buf[1]; +#endif + + for (i = 0; x[i] != '\0'; i++) { + ch = x[i]; + if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } +#if !APR_CHARSET_EBCDIC + if (ch == '\015' || ch == '\012' || (ch & 0x80)) +#else /* APR_CHARSET_EBCDIC */ + if (ch == '\r' || ch == '\n') + return 0; + buf[0] = ch; + ap_xlate_proto_to_ascii(buf, 1); + if (buf[0] & 0x80) +#endif /* APR_CHARSET_EBCDIC */ + return 0; + } + return 1; +} + +/* + * converts a series of buckets into a string + * XXX: BillS says this function performs essentially the same function as + * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() + * instead? I think ftp_string_read() will not work properly on non ASCII + * (EBCDIC) machines either. + */ +static apr_status_t ftp_string_read(conn_rec *c, apr_bucket_brigade *bb, + char *buff, apr_size_t bufflen, int *eos, apr_size_t *outlen) +{ + apr_bucket *e; + apr_status_t rv; + char *pos = buff; + char *response; + int found = 0; + apr_size_t len; + + /* start with an empty string */ + buff[0] = 0; + *eos = 0; + *outlen = 0; + + /* loop through each brigade */ + while (!found) { + /* get brigade from network one line at a time */ + if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, + AP_MODE_GETLINE, + APR_BLOCK_READ, + 0))) { + return rv; + } + /* loop through each bucket */ + while (!found) { + if (*eos || APR_BRIGADE_EMPTY(bb)) { + /* The connection aborted or timed out */ + return APR_ECONNABORTED; + } + e = APR_BRIGADE_FIRST(bb); + if (APR_BUCKET_IS_EOS(e)) { + *eos = 1; + } + else { + if (APR_SUCCESS != (rv = apr_bucket_read(e, + (const char **)&response, + &len, + APR_BLOCK_READ))) { + return rv; + } + /* + * is string LF terminated? + * XXX: This check can be made more efficient by simply checking + * if the last character in the 'response' buffer is an ASCII_LF. + * See ap_rgetline() for an example. + */ + if (memchr(response, APR_ASCII_LF, len)) { + found = 1; + } + /* concat strings until buff is full - then throw the data away */ + if (len > ((bufflen-1)-(pos-buff))) { + len = (bufflen-1)-(pos-buff); + } + if (len > 0) { + memcpy(pos, response, len); + pos += len; + *outlen += len; + } + } + apr_bucket_delete(e); + } + *pos = '\0'; + } + + return APR_SUCCESS; +} + +/* + * Canonicalise ftp URLs. + */ +static int proxy_ftp_canon(request_rec *r, char *url) +{ + char *user, *password, *host, *path, *parms, *strp, sport[7]; + apr_pool_t *p = r->pool; + const char *err; + apr_port_t port, def_port; + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + /* */ + if (ap_cstr_casecmpn(url, "ftp:", 4) == 0) { + url += 4; + } + else { + return DECLINED; + } + def_port = apr_uri_port_of_scheme("ftp"); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url); + + port = def_port; + err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port); + if (err) + return HTTP_BAD_REQUEST; + if (user != NULL && !ftp_check_string(user)) + return HTTP_BAD_REQUEST; + if (password != NULL && !ftp_check_string(password)) + return HTTP_BAD_REQUEST; + + /* now parse path/parameters args, according to rfc1738 */ + /* + * N.B. if this isn't a true proxy request, then the URL path (but not + * query args) has already been decoded. This gives rise to the problem + * of a ; being decoded into the path. + */ + strp = strchr(url, ';'); + if (strp != NULL) { + *(strp++) = '\0'; + parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, 0, + r->proxyreq); + if (parms == NULL) + return HTTP_BAD_REQUEST; + } + else + parms = ""; + + path = ap_proxy_canonenc_ex(p, url, strlen(url), enc_path, flags, + r->proxyreq); + if (path == NULL) + return HTTP_BAD_REQUEST; + if (!ftp_check_string(path)) + return HTTP_BAD_REQUEST; + + if (r->proxyreq && r->args != NULL) { + if (strp != NULL) { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1, r->proxyreq); + if (strp == NULL) + return HTTP_BAD_REQUEST; + parms = apr_pstrcat(p, parms, "?", strp, NULL); + } + else { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1, r->proxyreq); + if (strp == NULL) + return HTTP_BAD_REQUEST; + path = apr_pstrcat(p, path, "?", strp, NULL); + } + r->args = NULL; + } + +/* now, rebuild URL */ + + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ + host = apr_pstrcat(p, "[", host, "]", NULL); + } + r->filename = apr_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "", + (password != NULL) ? ":" : "", + (password != NULL) ? password : "", + (user != NULL) ? "@" : "", host, sport, "/", path, + (parms[0] != '\0') ? ";" : "", parms, NULL); + + return OK; +} + +/* we chop lines longer than 80 characters */ +#define MAX_LINE_LEN 80 + +/* + * Reads response lines, returns both the ftp status code and + * remembers the response message in the supplied buffer + */ +static int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbuf, int msglen) +{ + int status; + char response[MAX_LINE_LEN]; + char buff[5]; + char *mb = msgbuf, *me = &msgbuf[msglen]; + apr_status_t rv; + apr_size_t nread; + + int eos; + + if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { + return -1; + } +/* + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(03233) + "<%s", response); +*/ + if (nread < 4) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(10229) "Malformed FTP response '%s'", response); + *mb = '\0'; + return -1; + } + + if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) || + !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) + status = 0; + else + status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0'; + + mb = apr_cpystrn(mb, response + 4, me - mb); + + if (response[3] == '-') { /* multi-line reply "123-foo\nbar\n123 baz" */ + memcpy(buff, response, 3); + buff[3] = ' '; + do { + if (APR_SUCCESS != (rv = ftp_string_read(ftp_ctrl, bb, response, sizeof(response), &eos, &nread))) { + return -1; + } + mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb); + } while (memcmp(response, buff, 4) != 0); + } + + return status; +} + +/* this is a filter that turns a raw ASCII directory listing into pretty HTML */ + +/* ideally, mod_proxy should simply send the raw directory list up the filter + * stack to mod_autoindex, which in theory should turn the raw ascii into + * pretty html along with all the bells and whistles it provides... + * + * all in good time...! :) + */ + +typedef struct { + apr_bucket_brigade *in; + char buffer[MAX_STRING_LEN]; + enum { + HEADER, BODY, FOOTER + } state; +} proxy_dir_ctx_t; + +/* fallback regex for ls -s1; ($0..$2) == 3 */ +#define LS_REG_PATTERN "^ *([0-9]+) +([^ ]+)$" +#define LS_REG_MATCH 3 +static ap_regex_t *ls_regex; + +static apr_status_t proxy_send_dir_filter(ap_filter_t *f, + apr_bucket_brigade *in) +{ + request_rec *r = f->r; + conn_rec *c = r->connection; + apr_pool_t *p = r->pool; + apr_bucket_brigade *out = apr_brigade_create(p, c->bucket_alloc); + apr_status_t rv; + + int n; + char *dir, *path, *reldir, *site, *str, *type; + + const char *pwd = apr_table_get(r->notes, "Directory-PWD"); + const char *readme = apr_table_get(r->notes, "Directory-README"); + + proxy_dir_ctx_t *ctx = f->ctx; + + if (!ctx) { + f->ctx = ctx = apr_pcalloc(p, sizeof(*ctx)); + ctx->in = apr_brigade_create(p, c->bucket_alloc); + ctx->buffer[0] = 0; + ctx->state = HEADER; + } + + /* combine the stored and the new */ + APR_BRIGADE_CONCAT(ctx->in, in); + + if (HEADER == ctx->state) { + + /* basedir is either "", or "/%2f" for the "squid %2f hack" */ + const char *basedir = ""; /* By default, path is relative to the $HOME dir */ + char *wildcard = NULL; + const char *escpath; + + /* + * In the reverse proxy case we need to construct our site string + * via ap_construct_url. For non anonymous sites apr_uri_unparse would + * only supply us with 'username@' which leads to the construction of + * an invalid base href later on. Losing the username part of the URL + * is no problem in the reverse proxy case as the browser sents the + * credentials anyway once entered. + */ + if (r->proxyreq == PROXYREQ_REVERSE) { + site = ap_construct_url(p, "", r); + } + else { + /* Save "scheme://site" prefix without password */ + site = apr_uri_unparse(p, &f->r->parsed_uri, + APR_URI_UNP_OMITPASSWORD | + APR_URI_UNP_OMITPATHINFO); + } + + /* ... and path without query args */ + path = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITSITEPART | APR_URI_UNP_OMITQUERY); + + /* If path began with /%2f, change the basedir */ + if (ap_cstr_casecmpn(path, "/%2f", 4) == 0) { + basedir = "/%2f"; + } + + /* Strip off a type qualifier. It is ignored for dir listings */ + if ((type = strstr(path, ";type=")) != NULL) + *type++ = '\0'; + + (void)decodeenc(path); + + while (path[1] == '/') /* collapse multiple leading slashes to one */ + ++path; + + reldir = strrchr(path, '/'); + if (reldir != NULL && ftp_check_globbingchars(reldir)) { + wildcard = &reldir[1]; + reldir[0] = '\0'; /* strip off the wildcard suffix */ + } + + /* Copy path, strip (all except the last) trailing slashes */ + /* (the trailing slash is needed for the dir component loop below) */ + path = dir = apr_pstrcat(p, path, "/", NULL); + for (n = strlen(path); n > 1 && path[n - 1] == '/' && path[n - 2] == '/'; --n) + path[n - 1] = '\0'; + + /* Add a link to the root directory (if %2f hack was used) */ + str = (basedir[0] != '\0') ? "<a href=\"/%2f/\">%2f</a>/" : ""; + + /* print "ftp://host/" */ + escpath = ap_escape_html(p, path); + str = apr_psprintf(p, DOCTYPE_HTML_3_2 + "<html>\n <head>\n <title>%s%s%s</title>\n" + "<base href=\"%s%s%s\">\n" + " </head>\n" + " <body>\n <h2>Directory of " + "<a href=\"/\">%s</a>/%s", + ap_escape_html(p, site), basedir, escpath, + ap_escape_uri(p, site), basedir, escpath, + ap_escape_uri(p, site), str); + + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), + p, c->bucket_alloc)); + + for (dir = path+1; (dir = strchr(dir, '/')) != NULL; ) + { + *dir = '\0'; + if ((reldir = strrchr(path+1, '/'))==NULL) { + reldir = path+1; + } + else + ++reldir; + /* print "path/" component */ + str = apr_psprintf(p, "<a href=\"%s%s/\">%s</a>/", basedir, + ap_escape_uri(p, path), + ap_escape_html(p, reldir)); + *dir = '/'; + while (*dir == '/') + ++dir; + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, + strlen(str), p, + c->bucket_alloc)); + } + if (wildcard != NULL) { + wildcard = ap_escape_html(p, wildcard); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(wildcard, + strlen(wildcard), p, + c->bucket_alloc)); + } + + /* If the caller has determined the current directory, and it differs */ + /* from what the client requested, then show the real name */ + if (pwd == NULL || strncmp(pwd, path, strlen(pwd)) == 0) { + str = apr_psprintf(p, "</h2>\n\n <hr />\n\n<pre>"); + } + else { + str = apr_psprintf(p, "</h2>\n\n(%s)\n\n <hr />\n\n<pre>", + ap_escape_html(p, pwd)); + } + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), + p, c->bucket_alloc)); + + /* print README */ + if (readme) { + str = apr_psprintf(p, "%s\n</pre>\n\n<hr />\n\n<pre>\n", + ap_escape_html(p, readme)); + + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, + strlen(str), p, + c->bucket_alloc)); + } + + /* make sure page intro gets sent out */ + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc)); + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_cleanup(out); + + ctx->state = BODY; + } + + /* loop through each line of directory */ + while (BODY == ctx->state) { + char *filename; + int found = 0; + int eos = 0; + ap_regmatch_t re_result[LS_REG_MATCH]; + + /* get a complete line */ + /* if the buffer overruns - throw data away */ + while (!found && !APR_BRIGADE_EMPTY(ctx->in)) { + char *pos, *response; + apr_size_t len, max; + apr_bucket *e; + + e = APR_BRIGADE_FIRST(ctx->in); + if (APR_BUCKET_IS_EOS(e)) { + eos = 1; + break; + } + if (APR_SUCCESS != (rv = apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ))) { + return rv; + } + pos = memchr(response, APR_ASCII_LF, len); + if (pos != NULL) { + if ((response + len) != (pos + 1)) { + len = pos - response + 1; + apr_bucket_split(e, pos - response + 1); + } + found = 1; + } + max = sizeof(ctx->buffer) - strlen(ctx->buffer) - 1; + if (len > max) { + len = max; + } + + /* len+1 to leave space for the trailing nil char */ + apr_cpystrn(ctx->buffer+strlen(ctx->buffer), response, len+1); + + apr_bucket_delete(e); + } + + /* EOS? jump to footer */ + if (eos) { + ctx->state = FOOTER; + break; + } + + /* not complete? leave and try get some more */ + if (!found) { + return APR_SUCCESS; + } + + { + apr_size_t n = strlen(ctx->buffer); + if (ctx->buffer[n-1] == CRLF[1]) /* strip trailing '\n' */ + ctx->buffer[--n] = '\0'; + if (ctx->buffer[n-1] == CRLF[0]) /* strip trailing '\r' if present */ + ctx->buffer[--n] = '\0'; + } + + /* a symlink? */ + if (ctx->buffer[0] == 'l' && (filename = strstr(ctx->buffer, " -> ")) != NULL) { + char *link_ptr = filename; + + do { + filename--; + } while (filename[0] != ' ' && filename > ctx->buffer); + if (filename > ctx->buffer) + *(filename++) = '\0'; + *(link_ptr++) = '\0'; + str = apr_psprintf(p, "%s <a href=\"%s\">%s %s</a>\n", + ap_escape_html(p, ctx->buffer), + ap_escape_uri(p, filename), + ap_escape_html(p, filename), + ap_escape_html(p, link_ptr)); + } + + /* a directory/file? */ + else if (ctx->buffer[0] == 'd' || ctx->buffer[0] == '-' || ctx->buffer[0] == 'l' || apr_isdigit(ctx->buffer[0])) { + int searchidx = 0; + char *searchptr = NULL; + int firstfile = 1; + if (apr_isdigit(ctx->buffer[0])) { /* handle DOS dir */ + searchptr = strchr(ctx->buffer, '<'); + if (searchptr != NULL) + *searchptr = '['; + searchptr = strchr(ctx->buffer, '>'); + if (searchptr != NULL) + *searchptr = ']'; + } + + filename = strrchr(ctx->buffer, ' '); + if (filename == NULL) { + /* Line is broken. Ignore it. */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01034) + "proxy_ftp: could not parse line %s", + ctx->buffer); + /* erase buffer for next time around */ + ctx->buffer[0] = 0; + continue; /* while state is BODY */ + } + *(filename++) = '\0'; + + /* handle filenames with spaces in 'em */ + if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) { + firstfile = 0; + searchidx = filename - ctx->buffer; + } + else if (searchidx != 0 && ctx->buffer[searchidx] != 0) { + *(--filename) = ' '; + ctx->buffer[searchidx - 1] = '\0'; + filename = &ctx->buffer[searchidx]; + } + + /* Append a slash to the HREF link for directories */ + if (!strcmp(filename, ".") || !strcmp(filename, "..") || ctx->buffer[0] == 'd') { + str = apr_psprintf(p, "%s <a href=\"%s/\">%s</a>\n", + ap_escape_html(p, ctx->buffer), + ap_escape_uri(p, filename), + ap_escape_html(p, filename)); + } + else { + str = apr_psprintf(p, "%s <a href=\"%s\">%s</a>\n", + ap_escape_html(p, ctx->buffer), + ap_escape_uri(p, filename), + ap_escape_html(p, filename)); + } + } + /* Try a fallback for listings in the format of "ls -s1" */ + else if (0 == ap_regexec(ls_regex, ctx->buffer, LS_REG_MATCH, re_result, 0)) { + /* + * We don't need to check for rm_eo == rm_so == -1 here since ls_regex + * is such that $2 cannot be unset if we have a match. + */ + filename = apr_pstrndup(p, &ctx->buffer[re_result[2].rm_so], re_result[2].rm_eo - re_result[2].rm_so); + + str = apr_pstrcat(p, ap_escape_html(p, apr_pstrndup(p, ctx->buffer, re_result[2].rm_so)), + "<a href=\"", ap_escape_uri(p, filename), "\">", + ap_escape_html(p, filename), "</a>\n", NULL); + } + else { + strcat(ctx->buffer, "\n"); /* re-append the newline */ + str = ap_escape_html(p, ctx->buffer); + } + + /* erase buffer for next time around */ + ctx->buffer[0] = 0; + + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p, + c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc)); + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_cleanup(out); + + } + + if (FOOTER == ctx->state) { + str = apr_psprintf(p, "</pre>\n\n <hr />\n\n %s\n\n </body>\n</html>\n", ap_psignature("", r)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p, + c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_eos_create(c->bucket_alloc)); + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_destroy(out); + } + + return APR_SUCCESS; +} + +/* Parse EPSV reply and return port, or zero on error. */ +static apr_port_t parse_epsv_reply(const char *reply) +{ + const char *p; + char *ep; + long port; + + /* Reply syntax per RFC 2428: "229 blah blah (|||port|)" where '|' + * can be any character in ASCII from 33-126, obscurely. Verify + * the syntax. */ + p = ap_strchr_c(reply, '('); + if (p == NULL || !p[1] || p[1] != p[2] || p[1] != p[3] + || p[4] == p[1]) { + return 0; + } + + errno = 0; + port = strtol(p + 4, &ep, 10); + if (errno || port < 1 || port > 65535 || ep[0] != p[1] || ep[1] != ')') { + return 0; + } + + return (apr_port_t)port; +} + +/* + * Generic "send FTP command to server" routine, using the control socket. + * Returns the FTP returncode (3 digit code) + * Allows for tracing the FTP protocol (in LogLevel debug) + */ +static int +proxy_ftp_command(const char *cmd, request_rec *r, conn_rec *ftp_ctrl, + apr_bucket_brigade *bb, char **pmessage) +{ + char *crlf; + int rc; + char message[HUGE_STRING_LEN]; + + /* If cmd == NULL, we retrieve the next ftp response line */ + if (cmd != NULL) { + conn_rec *c = r->connection; + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(cmd, strlen(cmd), r->pool, c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(c->bucket_alloc)); + ap_pass_brigade(ftp_ctrl->output_filters, bb); + + if (APLOGrtrace2(r)) { + /* strip off the CRLF for logging */ + apr_cpystrn(message, cmd, sizeof(message)); + if ((crlf = strchr(message, '\r')) != NULL || + (crlf = strchr(message, '\n')) != NULL) + *crlf = '\0'; + if (strncmp(message,"PASS ", 5) == 0) + strcpy(&message[5], "****"); + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, ">%s", message); + } + } + + rc = ftp_getrc_msg(ftp_ctrl, bb, message, sizeof(message)); + if (rc == -1 || rc == 421) + strcpy(message,"<unable to read result>"); + if ((crlf = strchr(message, '\r')) != NULL || + (crlf = strchr(message, '\n')) != NULL) + *crlf = '\0'; + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "<%3.3u %s", rc, message); + + if (pmessage != NULL) + *pmessage = apr_pstrdup(r->pool, message); + + return rc; +} + +/* Set ftp server to TYPE {A,I,E} before transfer of a directory or file */ +static int ftp_set_TYPE(char xfer_type, request_rec *r, conn_rec *ftp_ctrl, + apr_bucket_brigade *bb, char **pmessage) +{ + char old_type[2] = { 'A', '\0' }; /* After logon, mode is ASCII */ + int ret = HTTP_OK; + int rc; + + /* set desired type */ + old_type[0] = xfer_type; + + rc = proxy_ftp_command(apr_pstrcat(r->pool, "TYPE ", old_type, CRLF, NULL), + r, ftp_ctrl, bb, pmessage); +/* responses: 200, 421, 500, 501, 504, 530 */ + /* 200 Command okay. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 504 Command not implemented for that parameter. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + ret = ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + else if (rc != 200 && rc != 504) { + ret = ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Unable to set transfer type"); + } +/* Allow not implemented */ + else if (rc == 504) { + /* ignore it silently */ + } + + return ret; +} + + +/* Return the current directory which we have selected on the FTP server, or NULL */ +static char *ftp_get_PWD(request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) +{ + char *cwd = NULL; + char *ftpmessage = NULL; + + /* responses: 257, 500, 501, 502, 421, 550 */ + /* 257 "<directory-name>" <commentary> */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 550 Requested action not taken. */ + switch (proxy_ftp_command("PWD" CRLF, r, ftp_ctrl, bb, &ftpmessage)) { + case -1: + case 421: + case 550: + ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Failed to read PWD on ftp server"); + break; + + case 257: { + const char *dirp = ftpmessage; + cwd = ap_getword_conf(r->pool, &dirp); + } + } + return cwd; +} + + +/* Common routine for failed authorization (i.e., missing or wrong password) + * to an ftp service. This causes most browsers to retry the request + * with username and password (which was presumably queried from the user) + * supplied in the Authorization: header. + * Note that we "invent" a realm name which consists of the + * ftp://user@host part of the request (sans password -if supplied but invalid-) + */ +static int ftp_unauthorized(request_rec *r, int log_it) +{ + r->proxyreq = PROXYREQ_NONE; + /* + * Log failed requests if they supplied a password (log username/password + * guessing attempts) + */ + if (log_it) + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01035) + "missing or failed auth to %s", + apr_uri_unparse(r->pool, + &r->parsed_uri, APR_URI_UNP_OMITPATHINFO)); + + apr_table_setn(r->err_headers_out, "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", + apr_uri_unparse(r->pool, &r->parsed_uri, + APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO), + "\"", NULL)); + + return HTTP_UNAUTHORIZED; +} + +static +apr_status_t proxy_ftp_cleanup(request_rec *r, proxy_conn_rec *backend) +{ + + backend->close = 1; + ap_set_module_config(r->connection->conn_config, &proxy_ftp_module, NULL); + ap_proxy_release_connection("FTP", backend, r->server); + + return OK; +} + +static +int ftp_proxyerror(request_rec *r, proxy_conn_rec *conn, int statuscode, const char *message) +{ + proxy_ftp_cleanup(r, conn); + return ap_proxyerror(r, statuscode, message); +} +/* + * Handles direct access of ftp:// URLs + * Original (Non-PASV) version from + * Troy Morrison <spiffnet@zoom.com> + * PASV added by Chuck + * Filters by [Graham Leggett <minfrin@sharp.fm>] + */ +static int proxy_ftp_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, char *url, + const char *proxyhost, apr_port_t proxyport) +{ + apr_pool_t *p = r->pool; + conn_rec *c = r->connection; + proxy_conn_rec *backend; + apr_socket_t *sock, *local_sock, *data_sock = NULL; + apr_sockaddr_t *connect_addr = NULL; + apr_status_t rv; + conn_rec *origin, *data = NULL; + apr_status_t err = APR_SUCCESS; +#if APR_HAS_THREADS + apr_status_t uerr = APR_SUCCESS; +#endif + apr_bucket_brigade *bb; + char *buf, *connectname; + apr_port_t connectport; + char *ftpmessage = NULL; + char *path, *strp, *type_suffix, *cwd = NULL; + apr_uri_t uri; + char *user = NULL; +/* char *account = NULL; how to supply an account in a URL? */ + const char *password = NULL; + int len, rc; + int one = 1; + char *size = NULL; + char xfer_type = 'A'; /* after ftp login, the default is ASCII */ + int dirlisting = 0; +#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) + apr_time_t mtime = 0L; +#endif + proxy_ftp_dir_conf *fdconf = ap_get_module_config(r->per_dir_config, + &proxy_ftp_module); + + /* stuff for PASV mode */ + int connect = 0, use_port = 0; + char dates[APR_RFC822_DATE_LEN]; + int status; + apr_pool_t *address_pool; + + /* is this for us? */ + if (proxyhost) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "declining URL %s - proxyhost %s specified:", url, + proxyhost); + return DECLINED; /* proxy connections are via HTTP */ + } + if (ap_cstr_casecmpn(url, "ftp:", 4)) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "declining URL %s - not ftp:", url); + return DECLINED; /* only interested in FTP */ + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "serving URL %s", url); + + + /* + * I: Who Do I Connect To? ----------------------- + * + * Break up the URL to determine the host to connect to + */ + + /* we only support GET and HEAD */ + if (r->method_number != M_GET) + return HTTP_NOT_IMPLEMENTED; + + /* We break the URL into host, port, path-search */ + if (r->parsed_uri.hostname == NULL) { + if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10189) + "URI cannot be parsed: %s", url); + return ap_proxyerror(r, HTTP_BAD_REQUEST, "URI cannot be parsed"); + } + connectname = uri.hostname; + connectport = uri.port; + path = apr_pstrdup(p, uri.path); + } + else { + connectname = r->parsed_uri.hostname; + connectport = r->parsed_uri.port; + path = apr_pstrdup(p, r->parsed_uri.path); + } + if (connectport == 0) { + connectport = apr_uri_port_of_scheme("ftp"); + } + path = (path != NULL && path[0] != '\0') ? &path[1] : ""; + + type_suffix = strchr(path, ';'); + if (type_suffix != NULL) + *(type_suffix++) = '\0'; + + if (type_suffix != NULL && strncmp(type_suffix, "type=", 5) == 0 + && apr_isalpha(type_suffix[5])) { + /* "type=d" forces a dir listing. + * The other types (i|a|e) are directly used for the ftp TYPE command + */ + if ( ! (dirlisting = (apr_tolower(type_suffix[5]) == 'd'))) + xfer_type = apr_toupper(type_suffix[5]); + + /* Check valid types, rather than ignoring invalid types silently: */ + if (strchr("AEI", xfer_type) == NULL) + return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool, + "ftp proxy supports only types 'a', 'i', or 'e': \"", + type_suffix, "\" is invalid.", NULL)); + } + else { + /* make binary transfers the default */ + xfer_type = 'I'; + } + + + /* + * The "Authorization:" header must be checked first. We allow the user + * to "override" the URL-coded user [ & password ] in the Browsers' + * User&Password Dialog. NOTE that this is only marginally more secure + * than having the password travel in plain as part of the URL, because + * Basic Auth simply uuencodes the plain text password. But chances are + * still smaller that the URL is logged regularly. + */ + if ((password = apr_table_get(r->headers_in, "Authorization")) != NULL + && ap_cstr_casecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 + && (password = ap_pbase64decode(r->pool, password))[0] != ':') { + /* Check the decoded string for special characters. */ + if (!ftp_check_string(password)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + "user credentials contained invalid character"); + } + /* + * Note that this allocation has to be made from r->connection->pool + * because it has the lifetime of the connection. The other + * allocations are temporary and can be tossed away any time. + */ + user = ap_getword_nulls(r->connection->pool, &password, ':'); + r->ap_auth_type = "Basic"; + r->user = r->parsed_uri.user = user; + } + else if ((user = r->parsed_uri.user) != NULL) { + user = apr_pstrdup(p, user); + decodeenc(user); + if ((password = r->parsed_uri.password) != NULL) { + char *tmp = apr_pstrdup(p, password); + decodeenc(tmp); + password = tmp; + } + } + else { + user = "anonymous"; + password = "apache-proxy@"; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01036) + "connecting %s to %s:%d", url, connectname, connectport); + + if (worker->s->is_address_reusable) { + if (!worker->cp->addr) { +#if APR_HAS_THREADS + if ((err = PROXY_THREAD_LOCK(worker->balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(01037) "lock"); + return HTTP_INTERNAL_SERVER_ERROR; + } +#endif + } + connect_addr = AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr); + address_pool = worker->cp->dns_pool; + } + else + address_pool = r->pool; + + /* do a DNS lookup for the destination host */ + if (!connect_addr) + err = apr_sockaddr_info_get(&(connect_addr), + connectname, APR_UNSPEC, + connectport, 0, + address_pool); + if (worker->s->is_address_reusable && !worker->cp->addr) { + worker->cp->addr = connect_addr; +#if APR_HAS_THREADS + if ((uerr = PROXY_THREAD_UNLOCK(worker->balancer)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(01038) "unlock"); + } +#endif + } + /* + * get all the possible IP addresses for the destname and loop through + * them until we get a successful connection + */ + if (APR_SUCCESS != err) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, + "DNS lookup failure for: ", + connectname, NULL)); + } + + /* check if ProxyBlock directive on this host */ + if (OK != ap_proxy_checkproxyblock2(r, conf, connectname, connect_addr)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + /* create space for state information */ + backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, &proxy_ftp_module); + if (!backend) { + status = ap_proxy_acquire_connection("FTP", &backend, worker, r->server); + if (status != OK) { + if (backend) { + backend->close = 1; + ap_proxy_release_connection("FTP", backend, r->server); + } + return status; + } + /* TODO: see if ftp could use determine_connection */ + backend->addr = connect_addr; + ap_set_module_config(c->conn_config, &proxy_ftp_module, backend); + } + + + /* + * II: Make the Connection ----------------------- + * + * We have determined who to connect to. Now make the connection. + */ + + + if (ap_proxy_connect_backend("FTP", backend, worker, r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01039) + "an error occurred creating a new connection to %pI (%s)", + connect_addr, connectname); + proxy_ftp_cleanup(r, backend); + return HTTP_SERVICE_UNAVAILABLE; + } + + status = ap_proxy_connection_create_ex("FTP", backend, r); + if (status != OK) { + proxy_ftp_cleanup(r, backend); + return status; + } + + /* Use old naming */ + origin = backend->connection; + sock = backend->sock; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "control connection complete"); + + + /* + * III: Send Control Request ------------------------- + * + * Log into the ftp server, send the username & password, change to the + * correct directory... + */ + + bb = apr_brigade_create(p, c->bucket_alloc); + + /* possible results: */ + /* 120 Service ready in nnn minutes. */ + /* 220 Service ready for new user. */ + /* 421 Service not available, closing control connection. */ + rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage); + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); + } + if (rc == 120) { + /* + * RFC2616 states: 14.37 Retry-After + * + * The Retry-After response-header field can be used with a 503 (Service + * Unavailable) response to indicate how long the service is expected + * to be unavailable to the requesting client. [...] The value of + * this field can be either an HTTP-date or an integer number of + * seconds (in decimal) after the time of the response. Retry-After + * = "Retry-After" ":" ( HTTP-date | delta-seconds ) + */ + char *secs_str = ftpmessage; + time_t secs; + + /* Look for a number, preceded by whitespace */ + while (*secs_str) + if ((secs_str==ftpmessage || apr_isspace(secs_str[-1])) && + apr_isdigit(secs_str[0])) + break; + if (*secs_str != '\0') { + secs = atol(secs_str); + apr_table_addn(r->headers_out, "Retry-After", + apr_psprintf(p, "%lu", (unsigned long)(60 * secs))); + } + return ftp_proxyerror(r, backend, HTTP_SERVICE_UNAVAILABLE, ftpmessage); + } + if (rc != 220) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + + rc = proxy_ftp_command(apr_pstrcat(p, "USER ", user, CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results; 230, 331, 332, 421, 500, 501, 530 */ + /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ + /* 230 User logged in, proceed. */ + /* 331 User name okay, need password. */ + /* 332 Need account for login. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* (This may include errors such as command line too long.) */ + /* 501 Syntax error in parameters or arguments. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server"); + } + if (rc == 530) { + proxy_ftp_cleanup(r, backend); + return ftp_unauthorized(r, 1); /* log it: user name guessing + * attempt? */ + } + if (rc != 230 && rc != 331) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + + if (rc == 331) { /* send password */ + if (password == NULL) { + proxy_ftp_cleanup(r, backend); + return ftp_unauthorized(r, 0); + } + + rc = proxy_ftp_command(apr_pstrcat(p, "PASS ", password, CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ + /* 230 User logged in, proceed. */ + /* 332 Need account for login. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 503 Bad sequence of commands. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 332) { + return ftp_proxyerror(r, backend, HTTP_UNAUTHORIZED, + apr_pstrcat(p, "Need account for login: ", ftpmessage, NULL)); + } + /* @@@ questionable -- we might as well return a 403 Forbidden here */ + if (rc == 530) { + proxy_ftp_cleanup(r, backend); + return ftp_unauthorized(r, 1); /* log it: passwd guessing + * attempt? */ + } + if (rc != 230 && rc != 202) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + } + apr_table_set(r->notes, "Directory-README", ftpmessage); + + + /* Special handling for leading "%2f": this enforces a "cwd /" + * out of the $HOME directory which was the starting point after login + */ + if (ap_cstr_casecmpn(path, "%2f", 3) == 0) { + path += 3; + while (*path == '/') /* skip leading '/' (after root %2f) */ + ++path; + + rc = proxy_ftp_command("CWD /" CRLF, r, origin, bb, &ftpmessage); + if (rc == -1 || rc == 421) + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + + /* + * set the directory (walk directory component by component): this is + * what we must do if we don't know the OS type of the remote machine + */ + for (;;) { + strp = strchr(path, '/'); + if (strp == NULL) + break; + *strp = '\0'; + + decodeenc(path); /* Note! This decodes a %2f -> "/" */ + + if (strchr(path, '/')) { /* are there now any '/' characters? */ + return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST, + "Use of /%2f is only allowed at the base directory"); + } + + /* NOTE: FTP servers do globbing on the path. + * So we need to escape the URI metacharacters. + * We use a special glob-escaping routine to escape globbing chars. + * We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH + */ + rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", + ftp_escape_globbingchars(p, path, fdconf), CRLF, NULL), + r, origin, bb, &ftpmessage); + *strp = '/'; + /* responses: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage); + } + if (rc != 250) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + + path = strp + 1; + } + + /* + * IV: Make Data Connection? ------------------------- + * + * Try EPSV, if that fails... try PASV, if that fails... try PORT. + */ +/* this temporarily switches off EPSV/PASV */ +/*goto bypass;*/ + + /* set up data connection - EPSV */ + { + apr_port_t data_port; + + /* + * The EPSV command replaces PASV where both IPV4 and IPV6 is + * supported. Only the port is returned, the IP address is always the + * same as that on the control connection. Example: Entering Extended + * Passive Mode (|||6446|) + */ + rc = proxy_ftp_command("EPSV" CRLF, + r, origin, bb, &ftpmessage); + /* possible results: 227, 421, 500, 501, 502, 530 */ + /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 229 && rc != 500 && rc != 501 && rc != 502) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + else if (rc == 229) { + /* Parse the port out of the EPSV reply. */ + data_port = parse_epsv_reply(ftpmessage); + + if (data_port) { + apr_sockaddr_t *remote_addr, epsv_addr; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "EPSV contacting remote host on port %d", data_port); + + /* Retrieve the client's address. */ + rv = apr_socket_addr_get(&remote_addr, APR_REMOTE, sock); + if (rv == APR_SUCCESS) { + /* Take a shallow copy of the server address to + * modify; the _addr_get function gives back a + * pointer to the socket's internal structure. + * This is awkward given current APR network + * interfaces. */ + epsv_addr = *remote_addr; + epsv_addr.port = data_port; +#if APR_HAVE_IPV6 + if (epsv_addr.family == APR_INET6) { + epsv_addr.sa.sin6.sin6_port = htons(data_port); + } + else +#endif + { + epsv_addr.sa.sin.sin_port = htons(data_port); + } + rv = apr_socket_create(&data_sock, epsv_addr.family, SOCK_STREAM, 0, r->pool); + } + + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01040) + "could not establish socket for client data connection"); + proxy_ftp_cleanup(r, backend); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (conf->recv_buffer_size > 0 + && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01041) + "apr_socket_opt_set(SO_RCVBUF): Failed to " + "set ProxyReceiveBufferSize, using default"); + } + + rv = apr_socket_opt_set(data_sock, APR_TCP_NODELAY, 1); + if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01042) + "apr_socket_opt_set(APR_TCP_NODELAY): " + "Failed to set"); + } + + rv = apr_socket_connect(data_sock, &epsv_addr); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01043) + "EPSV attempt to connect to %pI failed - " + "Firewall/NAT?", &epsv_addr); + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, + "EPSV attempt to connect to %pI failed - firewall/NAT?", &epsv_addr)); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "connected data socket to %pI", &epsv_addr); + connect = 1; + } + } + } + } + + /* set up data connection - PASV */ + if (!connect) { + rc = proxy_ftp_command("PASV" CRLF, + r, origin, bb, &ftpmessage); + /* possible results: 227, 421, 500, 501, 502, 530 */ + /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 227 && rc != 502) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + else if (rc == 227) { + unsigned int h0, h1, h2, h3, p0, p1; + char *pstr; + char *tok_cntx; + +/* FIXME: Check PASV against RFC1123 */ + + pstr = ftpmessage; + pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ + if (pstr != NULL) { + if (*(pstr + strlen(pstr) + 1) == '=') { + pstr += strlen(pstr) + 2; + } + else { + pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & + * port params */ + if (pstr != NULL) + pstr = apr_strtok(NULL, ")", &tok_cntx); + } + } + +/* FIXME: Only supports IPV4 - fix in RFC2428 */ + + if (pstr != NULL && (sscanf(pstr, + "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { + + apr_sockaddr_t *pasv_addr; + apr_port_t pasvport = (p1 << 8) + p0; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01044) + "PASV contacting host %d.%d.%d.%d:%d", + h3, h2, h1, h0, pasvport); + + if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01045) + "error creating PASV socket"); + proxy_ftp_cleanup(r, backend); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (conf->recv_buffer_size > 0 + && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01046) + "apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } + + rv = apr_socket_opt_set(data_sock, APR_TCP_NODELAY, 1); + if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01047) + "apr_socket_opt_set(APR_TCP_NODELAY): " + "Failed to set"); + } + + /* make the connection */ + apr_sockaddr_info_get(&pasv_addr, apr_psprintf(p, "%d.%d.%d.%d", h3, h2, h1, h0), connect_addr->family, pasvport, 0, p); + rv = apr_socket_connect(data_sock, pasv_addr); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01048) + "PASV attempt to connect to %pI failed - Firewall/NAT?", pasv_addr); + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, + "PASV attempt to connect to %pI failed - firewall/NAT?", pasv_addr)); + } + else { + connect = 1; + } + } + } + } +/*bypass:*/ + + /* set up data connection - PORT */ + if (!connect) { + apr_sockaddr_t *local_addr; + char *local_ip; + apr_port_t local_port; + unsigned int h0, h1, h2, h3, p0, p1; + + if ((rv = apr_socket_create(&local_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01049) + "error creating local socket"); + proxy_ftp_cleanup(r, backend); + return HTTP_INTERNAL_SERVER_ERROR; + } + apr_socket_addr_get(&local_addr, APR_LOCAL, sock); + local_port = local_addr->port; + apr_sockaddr_ip_get(&local_ip, local_addr); + + if ((rv = apr_socket_opt_set(local_sock, APR_SO_REUSEADDR, one)) + != APR_SUCCESS) { +#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01050) + "error setting reuseaddr option"); + proxy_ftp_cleanup(r, backend); + return HTTP_INTERNAL_SERVER_ERROR; +#endif /* _OSD_POSIX */ + } + + apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool); + + if ((rv = apr_socket_bind(local_sock, local_addr)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01051) + "error binding to ftp data socket %pI", local_addr); + proxy_ftp_cleanup(r, backend); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* only need a short queue */ + if ((rv = apr_socket_listen(local_sock, 2)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01052) + "error listening to ftp data socket %pI", local_addr); + proxy_ftp_cleanup(r, backend); + return HTTP_INTERNAL_SERVER_ERROR; + } + +/* FIXME: Sent PORT here */ + + if (local_ip && (sscanf(local_ip, + "%d.%d.%d.%d", &h3, &h2, &h1, &h0) == 4)) { + p1 = (local_port >> 8); + p0 = (local_port & 0xFF); + + rc = proxy_ftp_command(apr_psprintf(p, "PORT %d,%d,%d,%d,%d,%d" CRLF, h3, h2, h1, h0, p1, p0), + r, origin, bb, &ftpmessage); + /* possible results: 200, 421, 500, 501, 502, 530 */ + /* 200 Command okay. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 200) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + + /* signal that we must use the EPRT/PORT loop */ + use_port = 1; + } + else { +/* IPV6 FIXME: + * The EPRT command replaces PORT where both IPV4 and IPV6 is supported. The first + * number (1,2) indicates the protocol type. Examples: + * EPRT |1|132.235.1.2|6275| + * EPRT |2|1080::8:800:200C:417A|5282| + */ + return ftp_proxyerror(r, backend, HTTP_NOT_IMPLEMENTED, + "Connect to IPV6 ftp server using EPRT not supported. Enable EPSV."); + } + } + + + /* + * V: Set The Headers ------------------- + * + * Get the size of the request, set up the environment for HTTP. + */ + + /* set request; "path" holds last path component */ + len = decodeenc(path); + + if (strchr(path, '/')) { /* are there now any '/' characters? */ + return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST, + "Use of /%2f is only allowed at the base directory"); + } + + /* If len == 0 then it must be a directory (you can't RETR nothing) + * Also, don't allow to RETR by wildcard. Instead, create a dirlisting, + * unless ProxyFtpListOnWildcard is off. + */ + if (len == 0 || (ftp_check_globbingchars(path) && fdconf->ftp_list_on_wildcard)) { + dirlisting = 1; + } + else { + /* (from FreeBSD ftpd): + * SIZE is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return size of file in a format suitable for + * using with RESTART (we just count bytes). + */ + /* from draft-ietf-ftpext-mlst-14.txt: + * This value will + * change depending on the current STRUcture, MODE and TYPE of the data + * connection, or a data connection which would be created were one + * created now. Thus, the result of the SIZE command is dependent on + * the currently established STRU, MODE and TYPE parameters. + */ + /* Therefore: switch to binary if the user did not specify ";type=a" */ + ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage); + rc = proxy_ftp_command(apr_pstrcat(p, "SIZE ", + ftp_escape_globbingchars(p, path, fdconf), CRLF, NULL), + r, origin, bb, &ftpmessage); + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + else if (rc == 213) {/* Size command ok */ + int j; + for (j = 0; apr_isdigit(ftpmessage[j]); j++) + ; + ftpmessage[j] = '\0'; + if (ftpmessage[0] != '\0') + size = ftpmessage; /* already pstrdup'ed: no copy necessary */ + } + else if (rc == 550) { /* Not a regular file */ + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "SIZE shows this is a directory"); + dirlisting = 1; + rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", + ftp_escape_globbingchars(p, path, fdconf), CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage); + } + if (rc != 250) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + path = ""; + len = 0; + } + } + + cwd = ftp_get_PWD(r, origin, bb); + if (cwd != NULL) { + apr_table_set(r->notes, "Directory-PWD", cwd); + } + + if (dirlisting) { + ftp_set_TYPE('A', r, origin, bb, NULL); + /* If the current directory contains no slash, we are talking to + * a non-unix ftp system. Try LIST instead of "LIST -lag", it + * should return a long listing anyway (unlike NLST). + * Some exotic FTP servers might choke on the "-lag" switch. + */ + /* Note that we do not escape the path here, to allow for + * queries like: ftp://user@host/apache/src/server/http_*.c + */ + if (len != 0) + buf = apr_pstrcat(p, "LIST ", path, CRLF, NULL); + else if (cwd == NULL || strchr(cwd, '/') != NULL) + buf = "LIST -lag" CRLF; + else + buf = "LIST" CRLF; + } + else { + /* switch to binary if the user did not specify ";type=a" */ + ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage); +#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) + /* from draft-ietf-ftpext-mlst-14.txt: + * The FTP command, MODIFICATION TIME (MDTM), can be used to determine + * when a file in the server NVFS was last modified. <..> + * The syntax of a time value is: + * time-val = 14DIGIT [ "." 1*DIGIT ] <..> + * Symbolically, a time-val may be viewed as + * YYYYMMDDHHMMSS.sss + * The "." and subsequent digits ("sss") are optional. <..> + * Time values are always represented in UTC (GMT) + */ + rc = proxy_ftp_command(apr_pstrcat(p, "MDTM ", ftp_escape_globbingchars(p, path, fdconf), CRLF, NULL), + r, origin, bb, &ftpmessage); + /* then extract the Last-Modified time from it (YYYYMMDDhhmmss or YYYYMMDDhhmmss.xxx GMT). */ + if (rc == 213) { + struct { + char YYYY[4+1]; + char MM[2+1]; + char DD[2+1]; + char hh[2+1]; + char mm[2+1]; + char ss[2+1]; + } time_val; + if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]", + time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) { + struct tm tms; + memset (&tms, '\0', sizeof tms); + tms.tm_year = atoi(time_val.YYYY) - 1900; + tms.tm_mon = atoi(time_val.MM) - 1; + tms.tm_mday = atoi(time_val.DD); + tms.tm_hour = atoi(time_val.hh); + tms.tm_min = atoi(time_val.mm); + tms.tm_sec = atoi(time_val.ss); +#ifdef HAVE_TIMEGM /* Does system have timegm()? */ + mtime = timegm(&tms); + mtime *= APR_USEC_PER_SEC; +#elif HAVE_GMTOFF /* does struct tm have a member tm_gmtoff? */ + /* mktime will subtract the local timezone, which is not what we want. + * Add it again because the MDTM string is GMT + */ + mtime = mktime(&tms); + mtime += tms.tm_gmtoff; + mtime *= APR_USEC_PER_SEC; +#else + mtime = 0L; +#endif + } + } +#endif /* USE_MDTM */ +/* FIXME: Handle range requests - send REST */ + buf = apr_pstrcat(p, "RETR ", ftp_escape_globbingchars(p, path, fdconf), CRLF, NULL); + } + rc = proxy_ftp_command(buf, r, origin, bb, &ftpmessage); + /* rc is an intermediate response for the LIST or RETR commands */ + + /* + * RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, + * 550 NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, + * 530 + */ + /* 110 Restart marker reply. */ + /* 125 Data connection already open; transfer starting. */ + /* 150 File status okay; about to open data connection. */ + /* 226 Closing data connection. */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 425 Can't open data connection. */ + /* 426 Connection closed; transfer aborted. */ + /* 450 Requested file action not taken. */ + /* 451 Requested action aborted. Local error in processing. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "RETR failed, trying LIST instead"); + + /* Directory Listings should always be fetched in ASCII mode */ + dirlisting = 1; + ftp_set_TYPE('A', r, origin, bb, NULL); + + rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", + ftp_escape_globbingchars(p, path, fdconf), CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage); + } + if (rc != 250) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + + /* Update current directory after CWD */ + cwd = ftp_get_PWD(r, origin, bb); + if (cwd != NULL) { + apr_table_set(r->notes, "Directory-PWD", cwd); + } + + /* See above for the "LIST" vs. "LIST -lag" discussion. */ + rc = proxy_ftp_command((cwd == NULL || strchr(cwd, '/') != NULL) + ? "LIST -lag" CRLF : "LIST" CRLF, + r, origin, bb, &ftpmessage); + + /* rc is an intermediate response for the LIST command (125 transfer starting, 150 opening data connection) */ + if (rc == -1 || rc == 421) + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 125 && rc != 150 && rc != 226 && rc != 250) { + return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage); + } + + r->status = HTTP_OK; + r->status_line = "200 OK"; + + apr_rfc822_date(dates, r->request_time); + apr_table_setn(r->headers_out, "Date", dates); + apr_table_setn(r->headers_out, "Server", ap_get_server_description()); + + /* set content-type */ + if (dirlisting) { + ap_set_content_type(r, apr_pstrcat(p, "text/html;charset=", + fdconf->ftp_directory_charset ? + fdconf->ftp_directory_charset : + "ISO-8859-1", NULL)); + } + else { + if (xfer_type != 'A' && size != NULL) { + /* We "trust" the ftp server to really serve (size) bytes... */ + apr_table_setn(r->headers_out, "Content-Length", size); + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "Content-Length set to %s", size); + } + } + if (r->content_type) { + apr_table_setn(r->headers_out, "Content-Type", r->content_type); + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "Content-Type set to %s", r->content_type); + } + +#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) + if (mtime != 0L) { + char datestr[APR_RFC822_DATE_LEN]; + apr_rfc822_date(datestr, mtime); + apr_table_set(r->headers_out, "Last-Modified", datestr); + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "Last-Modified set to %s", datestr); + } +#endif /* USE_MDTM */ + + /* If an encoding has been set by mistake, delete it. + * @@@ FIXME (e.g., for ftp://user@host/file*.tar.gz, + * @@@ the encoding is currently set to x-gzip) + */ + if (dirlisting && r->content_encoding != NULL) + r->content_encoding = NULL; + + /* set content-encoding (not for dir listings, they are uncompressed)*/ + if (r->content_encoding != NULL && r->content_encoding[0] != '\0') { + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "Content-Encoding set to %s", r->content_encoding); + apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); + } + + /* wait for connection */ + if (use_port) { + for (;;) { + rv = apr_socket_accept(&data_sock, local_sock, r->pool); + if (APR_STATUS_IS_EINTR(rv)) { + continue; + } + else if (rv == APR_SUCCESS) { + break; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01053) + "failed to accept data connection"); + proxy_ftp_cleanup(r, backend); + return HTTP_BAD_GATEWAY; + } + } + } + + /* the transfer socket is now open, create a new connection */ + data = ap_run_create_connection(p, r->server, data_sock, r->connection->id, + r->connection->sbh, c->bucket_alloc); + if (!data) { + /* + * the peer reset the connection already; ap_run_create_connection() closed + * the socket + */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01054) + "an error occurred creating the transfer connection"); + proxy_ftp_cleanup(r, backend); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* + * We do not do SSL over the data connection, even if the virtual host we + * are in might have SSL enabled + */ + ap_proxy_ssl_engine(data, r->per_dir_config, 0); + /* set up the connection filters */ + rc = ap_run_pre_connection(data, data_sock); + if (rc != OK && rc != DONE) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01055) + "pre_connection setup failed (%d)", rc); + data->aborted = 1; + proxy_ftp_cleanup(r, backend); + return rc; + } + + /* + * VI: Receive the Response ------------------------ + * + * Get response from the remote ftp socket, and pass it up the filter chain. + */ + + /* send response */ + r->sent_bodyct = 1; + + if (dirlisting) { + /* insert directory filter */ + ap_add_output_filter("PROXY_SEND_DIR", NULL, r, r->connection); + } + + /* send body */ + if (!r->header_only) { + apr_bucket *e; + int finish = FALSE; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "start body send"); + + /* read the body, pass it to the output filters */ + while (ap_get_brigade(data->input_filters, + bb, + AP_MODE_READBYTES, + APR_BLOCK_READ, + conf->io_buffer_size) == APR_SUCCESS) { +#if DEBUGGING + { + apr_off_t readbytes; + apr_brigade_length(bb, 0, &readbytes); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01056) + "proxy: readbytes: %#x", readbytes); + } +#endif + /* sanity check */ + if (APR_BRIGADE_EMPTY(bb)) { + break; + } + + /* found the last brigade? */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + /* if this is the last brigade, cleanup the + * backend connection first to prevent the + * backend server from hanging around waiting + * for a slow client to eat these bytes + */ + ap_flush_conn(data); + if (data_sock) { + apr_socket_close(data_sock); + } + data_sock = NULL; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01057) + "data connection closed"); + /* signal that we must leave */ + finish = TRUE; + } + + /* if no EOS yet, then we must flush */ + if (FALSE == finish) { + e = apr_bucket_flush_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + + /* try send what we read */ + if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS + || c->aborted) { + /* Ack! Phbtt! Die! User aborted! */ + finish = TRUE; + } + + /* make sure we always clean up after ourselves */ + apr_brigade_cleanup(bb); + + /* if we are done, leave */ + if (TRUE == finish) { + break; + } + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "end body send"); + + } + if (data_sock) { + ap_flush_conn(data); + apr_socket_close(data_sock); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01058) "data connection closed"); + } + + /* Retrieve the final response for the RETR or LIST commands */ + proxy_ftp_command(NULL, r, origin, bb, &ftpmessage); + apr_brigade_cleanup(bb); + + /* + * VII: Clean Up ------------- + * + * If there are no KeepAlives, or if the connection has been signalled to + * close, close the socket and clean up + */ + + /* finish */ + proxy_ftp_command("QUIT" CRLF, r, origin, bb, &ftpmessage); + /* responses: 221, 500 */ + /* 221 Service closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + ap_flush_conn(origin); + proxy_ftp_cleanup(r, backend); + + apr_brigade_destroy(bb); + return OK; +} + +static void ap_proxy_ftp_register_hook(apr_pool_t *p) +{ + /* hooks */ + proxy_hook_scheme_handler(proxy_ftp_handler, NULL, NULL, APR_HOOK_MIDDLE); + proxy_hook_canon_handler(proxy_ftp_canon, NULL, NULL, APR_HOOK_MIDDLE); + /* filters */ + ap_register_output_filter("PROXY_SEND_DIR", proxy_send_dir_filter, + NULL, AP_FTYPE_RESOURCE); + /* Compile the output format of "ls -s1" as a fallback for non-unix ftp listings */ + ls_regex = ap_pregcomp(p, LS_REG_PATTERN, AP_REG_EXTENDED); + ap_assert(ls_regex != NULL); +} + +static const command_rec proxy_ftp_cmds[] = +{ + AP_INIT_FLAG("ProxyFtpListOnWildcard", set_ftp_list_on_wildcard, NULL, + RSRC_CONF|ACCESS_CONF, "Whether wildcard characters in a path cause mod_proxy_ftp to list the files instead of trying to get them. Defaults to on."), + AP_INIT_FLAG("ProxyFtpEscapeWildcards", set_ftp_escape_wildcards, NULL, + RSRC_CONF|ACCESS_CONF, "Whether the proxy should escape wildcards in paths before sending them to the FTP server. Defaults to on, but most FTP servers will need it turned off if you need to manage paths that contain wildcard characters."), + AP_INIT_TAKE1("ProxyFtpDirCharset", set_ftp_directory_charset, NULL, + RSRC_CONF|ACCESS_CONF, "Define the character set for proxied FTP listings"), + {NULL} +}; + + +AP_DECLARE_MODULE(proxy_ftp) = { + STANDARD20_MODULE_STUFF, + create_proxy_ftp_dir_config,/* create per-directory config structure */ + merge_proxy_ftp_dir_config, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + proxy_ftp_cmds, /* command apr_table_t */ + ap_proxy_ftp_register_hook /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_ftp.dep b/modules/proxy/mod_proxy_ftp.dep new file mode 100644 index 0000000..46b78fe --- /dev/null +++ b/modules/proxy/mod_proxy_ftp.dep @@ -0,0 +1,74 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_ftp.mak + +.\mod_proxy_ftp.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_version.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_ftp.dsp b/modules/proxy/mod_proxy_ftp.dsp new file mode 100644 index 0000000..2ad1711 --- /dev/null +++ b/modules/proxy/mod_proxy_ftp.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_ftp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_ftp - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ftp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ftp.mak" CFG="mod_proxy_ftp - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_ftp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_ftp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_ftp_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_ftp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_ftp_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_ftp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_ftp - Win32 Release" +# Name "mod_proxy_ftp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_ftp.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_ftp.mak b/modules/proxy/mod_proxy_ftp.mak new file mode 100644 index 0000000..0b1ca30 --- /dev/null +++ b/modules/proxy/mod_proxy_ftp.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_ftp.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_ftp - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_ftp - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_ftp - Win32 Release" && "$(CFG)" != "mod_proxy_ftp - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ftp.mak" CFG="mod_proxy_ftp - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_ftp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_ftp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_ftp.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_ftp.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_ftp.obj" + -@erase "$(INTDIR)\mod_proxy_ftp.res" + -@erase "$(INTDIR)\mod_proxy_ftp_src.idb" + -@erase "$(INTDIR)\mod_proxy_ftp_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_ftp.exp" + -@erase "$(OUTDIR)\mod_proxy_ftp.lib" + -@erase "$(OUTDIR)\mod_proxy_ftp.pdb" + -@erase "$(OUTDIR)\mod_proxy_ftp.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_ftp_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_ftp.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_ftp.pdb" /debug /out:"$(OUTDIR)\mod_proxy_ftp.so" /implib:"$(OUTDIR)\mod_proxy_ftp.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_ftp.obj" \ + "$(INTDIR)\mod_proxy_ftp.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_ftp.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_ftp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_ftp.so" + if exist .\Release\mod_proxy_ftp.so.manifest mt.exe -manifest .\Release\mod_proxy_ftp.so.manifest -outputresource:.\Release\mod_proxy_ftp.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_ftp.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_ftp.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_ftp.obj" + -@erase "$(INTDIR)\mod_proxy_ftp.res" + -@erase "$(INTDIR)\mod_proxy_ftp_src.idb" + -@erase "$(INTDIR)\mod_proxy_ftp_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_ftp.exp" + -@erase "$(OUTDIR)\mod_proxy_ftp.lib" + -@erase "$(OUTDIR)\mod_proxy_ftp.pdb" + -@erase "$(OUTDIR)\mod_proxy_ftp.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_ftp_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_ftp.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_ftp.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_ftp.pdb" /debug /out:"$(OUTDIR)\mod_proxy_ftp.so" /implib:"$(OUTDIR)\mod_proxy_ftp.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_ftp.obj" \ + "$(INTDIR)\mod_proxy_ftp.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_ftp.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_ftp.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_ftp.so" + if exist .\Debug\mod_proxy_ftp.so.manifest mt.exe -manifest .\Debug\mod_proxy_ftp.so.manifest -outputresource:.\Debug\mod_proxy_ftp.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_ftp.dep") +!INCLUDE "mod_proxy_ftp.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_ftp.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" || "$(CFG)" == "mod_proxy_ftp - Win32 Debug" +SOURCE=.\mod_proxy_ftp.c + +"$(INTDIR)\mod_proxy_ftp.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + + +"$(INTDIR)\mod_proxy_ftp.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_ftp.so" /d LONG_NAME="proxy_ftp_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_hcheck.c b/modules/proxy/mod_proxy_hcheck.c new file mode 100644 index 0000000..d618b4d --- /dev/null +++ b/modules/proxy/mod_proxy_hcheck.c @@ -0,0 +1,1351 @@ +/* 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 +#include "http_ssl.h" + +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 +#ifndef HC_USE_THREADS +#define HC_USE_THREADS 1 +#endif +#else +#define HC_USE_THREADS 0 +#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_array_header_t *templates; + apr_table_t *conditions; + apr_hash_t *hcworkers; + server_rec *s; +} sctx_t; + +/* Used in the HC worker via the context field */ +typedef struct { + const char *path; /* The path of the original worker URL */ + const char *method; /* Method string for the HTTP/AJP request */ + const char *req; /* pre-formatted HTTP/AJP request */ + proxy_worker *w; /* Pointer to the actual worker */ + const char *protocol; /* HTTP 1.0 or 1.1? */ +} wctx_t; + +typedef struct { + apr_pool_t *ptemp; + sctx_t *ctx; + proxy_balancer *balancer; + proxy_worker *worker; + proxy_worker *hc; + apr_time_t *now; +} baton_t; + +static APR_OPTIONAL_FN_TYPE(ajp_handle_cping_cpong) *ajp_handle_cping_cpong = NULL; + +static void *hc_create_config(apr_pool_t *p, server_rec *s) +{ + sctx_t *ctx = apr_pcalloc(p, sizeof(sctx_t)); + ctx->s = s; + apr_pool_create(&ctx->p, p); + apr_pool_tag(ctx->p, "proxy_hcheck"); + ctx->templates = apr_array_make(p, 10, sizeof(hc_template_t)); + ctx->conditions = apr_table_make(p, 10); + ctx->hcworkers = apr_hash_make(p); + return ctx; +} + +static ap_watchdog_t *watchdog; +#if HC_USE_THREADS +static apr_thread_pool_t *hctp; +static int tpsize; +#endif + +/* + * 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()"; + } + if (!ctx) { + ctx = hc_create_config(p, s); + ap_set_module_config(s->module_config, &proxy_hcheck_module, ctx); + } + 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 (!ap_cstr_casecmp(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 (!ap_cstr_casecmp(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")) { + apr_interval_time_t hci; + apr_status_t rv; + rv = ap_timeout_parameter_parse(val, &hci, "s"); + if (rv != APR_SUCCESS) + return "Unparse-able hcinterval setting"; + if (hci < AP_WD_TM_SLICE) + return apr_psprintf(p, "Interval must be a positive value greater than %" + APR_TIME_T_FMT "ms", apr_time_as_msec(AP_WD_TM_SLICE)); + if (worker) { + worker->s->interval = hci; + } else { + temp->interval = hci; + } + } + 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) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err) + return err; + + tpsize = atoi(arg); + if (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 pool for bucket_alloc so that we can simply move + * buckets and use them after the backend connection is released. + */ +static request_rec *create_request_rec(apr_pool_t *p, server_rec *s, + proxy_balancer *balancer, + const char *method, + const char *protocol) +{ + request_rec *r; + + r = apr_pcalloc(p, sizeof(request_rec)); + r->pool = p; + r->server = s; + + r->per_dir_config = r->server->lookup_defaults; + if (balancer->section_config) { + r->per_dir_config = ap_merge_per_dir_configs(r->pool, + r->per_dir_config, + balancer->section_config); + } + + r->proxyreq = PROXYREQ_RESPONSE; + + 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, 1); + r->trailers_in = apr_table_make(r->pool, 1); + 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, 1); + r->notes = apr_table_make(r->pool, 5); + + r->request_config = ap_create_request_config(r->pool); + /* Must be set before we run create request hook */ + + 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->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; + + + /* 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_OPTIONS + || (r->method_number == M_GET && r->method[0] == 'H')) { + r->header_only = 1; + } + else { + r->header_only = 0; + } + r->protocol = "HTTP/1.0"; + r->proto_num = HTTP_VERSION(1, 0); + if ( protocol && (protocol[7] == '1') ) { + r->protocol = "HTTP/1.1"; + r->proto_num = HTTP_VERSION(1, 1); + } + r->hostname = NULL; + + return r; +} + +static void set_request_connection(request_rec *r, conn_rec *conn) +{ + conn->bucket_alloc = apr_bucket_alloc_create(r->pool); + r->connection = conn; + + r->kept_body = apr_brigade_create(r->pool, conn->bucket_alloc); + r->output_filters = r->proto_output_filters = conn->output_filters; + r->input_filters = r->proto_input_filters = conn->input_filters; + + r->useragent_addr = conn->client_addr; + r->useragent_ip = conn->client_ip; +} + +static void create_hcheck_req(wctx_t *wctx, proxy_worker *hc, + apr_pool_t *p) +{ + char *req = NULL; + const char *method = NULL; + const char *protocol = NULL; + + /* TODO: Fold into switch/case below? This seems more obvious */ + if ( (hc->s->method == OPTIONS11) || (hc->s->method == HEAD11) || (hc->s->method == GET11) ) { + protocol = "HTTP/1.1"; + } else { + protocol = "HTTP/1.0"; + } + switch (hc->s->method) { + case OPTIONS: + case OPTIONS11: + method = "OPTIONS"; + req = apr_psprintf(p, + "OPTIONS * %s\r\n" + "Host: %s:%d\r\n" + "\r\n", protocol, + hc->s->hostname_ex, (int)hc->s->port); + break; + + case HEAD: + case HEAD11: + method = "HEAD"; + /* fallthru */ + case GET: + case GET11: + if (!method) { /* did we fall thru? If not, we are GET */ + method = "GET"; + } + req = apr_psprintf(p, + "%s %s%s%s %s\r\n" + "Host: %s:%d\r\n" + "\r\n", + method, + (wctx->path ? wctx->path : ""), + (wctx->path && *hc->s->hcuri ? "/" : "" ), + (*hc->s->hcuri ? hc->s->hcuri : ""), + protocol, + hc->s->hostname_ex, (int)hc->s->port); + break; + + default: + break; + } + wctx->req = req; + wctx->method = method; + wctx->protocol = protocol; +} + +static proxy_worker *hc_get_hcworker(sctx_t *ctx, proxy_worker *worker, + apr_pool_t *p) +{ + proxy_worker *hc = NULL; + apr_port_t port; + + hc = (proxy_worker *)apr_hash_get(ctx->hcworkers, &worker, sizeof worker); + if (!hc) { + apr_uri_t uri; + apr_status_t rv; + const char *url = worker->s->name_ex; + wctx_t *wctx = apr_pcalloc(ctx->p, sizeof(wctx_t)); + + port = (worker->s->port ? worker->s->port + : ap_proxy_port_of_scheme(worker->s->scheme)); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03248) + "Creating hc worker %pp for %s://%s:%d", + worker, worker->s->scheme, worker->s->hostname_ex, + (int)port); + + ap_proxy_define_worker(ctx->p, &hc, NULL, NULL, worker->s->name_ex, 0); + apr_snprintf(hc->s->name, sizeof hc->s->name, "%pp", worker); + apr_snprintf(hc->s->name_ex, sizeof hc->s->name_ex, "%pp", worker); + PROXY_STRNCPY(hc->s->hostname, worker->s->hostname); /* for compatibility */ + PROXY_STRNCPY(hc->s->hostname_ex, worker->s->hostname_ex); + PROXY_STRNCPY(hc->s->scheme, worker->s->scheme); + PROXY_STRNCPY(hc->s->hcuri, worker->s->hcuri); + PROXY_STRNCPY(hc->s->hcexpr, worker->s->hcexpr); + hc->hash.def = hc->s->hash.def = ap_proxy_hashfunc(hc->s->name_ex, + PROXY_HASHFUNC_DEFAULT); + hc->hash.fnv = hc->s->hash.fnv = ap_proxy_hashfunc(hc->s->name_ex, + PROXY_HASHFUNC_FNV); + hc->s->port = port; + hc->s->conn_timeout_set = worker->s->conn_timeout_set; + hc->s->conn_timeout = worker->s->conn_timeout; + hc->s->ping_timeout_set = worker->s->ping_timeout_set; + hc->s->ping_timeout = worker->s->ping_timeout; + hc->s->timeout_set = worker->s->timeout_set; + hc->s->timeout = worker->s->timeout; + /* 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; + create_hcheck_req(wctx, hc, ctx->p); + hc->context = wctx; + apr_hash_set(ctx->hcworkers, &worker, sizeof worker, hc); + } + /* This *could* have changed via the Balancer Manager */ + /* TODO */ + if (hc->s->method != worker->s->method) { + wctx_t *wctx = hc->context; + port = (worker->s->port ? worker->s->port + : ap_proxy_port_of_scheme(worker->s->scheme)); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03311) + "Updating hc worker %pp for %s://%s:%d", + worker, worker->s->scheme, worker->s->hostname_ex, + (int)port); + hc->s->method = worker->s->method; + create_hcheck_req(wctx, hc, ctx->p); + } + return hc; +} + +static int hc_determine_connection(sctx_t *ctx, proxy_worker *worker, + apr_sockaddr_t **addr, apr_pool_t *p) +{ + apr_status_t rv = APR_SUCCESS; + /* + * 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) { + *addr = worker->cp->addr; + } + else { + rv = apr_sockaddr_info_get(addr, worker->s->hostname_ex, + APR_UNSPEC, worker->s->port, 0, 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_ex, (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; + } + if (worker->s->is_address_reusable && !worker->s->disablereuse && + hc_determine_connection(ctx, worker, &worker->cp->addr, + worker->cp->pool) != OK) { + rv = 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_ex); + } + 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, apr_pool_t *ptemp) +{ + int status; + status = ap_proxy_acquire_connection(proxy_function, backend, hc, ctx->s); + if (status == OK) { + (*backend)->addr = hc->cp->addr; + (*backend)->hostname = hc->s->hostname_ex; + if (strcmp(hc->s->scheme, "https") == 0 || strcmp(hc->s->scheme, "wss") == 0 ) { + if (!ap_ssl_has_outgoing_handlers()) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ctx->s, APLOGNO(03252) + "mod_ssl not configured?"); + return !OK; + } + (*backend)->is_ssl = 1; + } + + } + return hc_determine_connection(ctx, hc, &(*backend)->addr, ptemp); +} + +static apr_status_t hc_check_cping(baton_t *baton, apr_thread_t *thread) +{ + int status; + sctx_t *ctx = baton->ctx; + proxy_worker *hc = baton->hc; + proxy_conn_rec *backend = NULL; + apr_pool_t *ptemp = baton->ptemp; + request_rec *r; + apr_interval_time_t timeout; + + if (!ajp_handle_cping_cpong) { + return APR_ENOTIMPL; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, baton->ctx->s, "HCCPING starting"); + if ((status = hc_get_backend("HCCPING", &backend, hc, ctx, baton->ptemp)) != OK) { + return backend_cleanup("HCCPING", backend, ctx->s, status); + } + if ((status = ap_proxy_connect_backend("HCCPING", backend, hc, ctx->s)) != OK) { + return backend_cleanup("HCCPING", backend, ctx->s, status); + } + r = create_request_rec(ptemp, ctx->s, baton->balancer, "CPING", NULL); + if ((status = ap_proxy_connection_create_ex("HCCPING", backend, r)) != OK) { + return backend_cleanup("HCCPING", backend, ctx->s, status); + } + set_request_connection(r, backend->connection); + backend->connection->current_thread = thread; + + if (hc->s->ping_timeout_set) { + timeout = hc->s->ping_timeout; + } else if ( hc->s->conn_timeout_set) { + timeout = hc->s->conn_timeout; + } else if ( hc->s->timeout_set) { + timeout = hc->s->timeout; + } else { + /* default to socket timeout */ + apr_socket_timeout_get(backend->sock, &timeout); + } + status = ajp_handle_cping_cpong(backend->sock, r, timeout); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, baton->ctx->s, "HCCPING done %d", status); + return backend_cleanup("HCCPING", backend, ctx->s, status); +} + +static apr_status_t hc_check_tcp(baton_t *baton) +{ + int status; + sctx_t *ctx = baton->ctx; + proxy_worker *hc = baton->hc; + proxy_conn_rec *backend = NULL; + + status = hc_get_backend("HCTCP", &backend, hc, ctx, baton->ptemp); + if (status == OK) { + 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 int hc_send(request_rec *r, const char *out, apr_bucket_brigade *bb) +{ + apr_status_t rv; + conn_rec *c = r->connection; + apr_bucket_alloc_t *ba = c->bucket_alloc; + ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, r->server, "%s", out); + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(out, strlen(out), + r->pool, ba)); + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(ba)); + rv = ap_pass_brigade(c->output_filters, bb); + apr_brigade_cleanup(bb); + return (rv) ? !OK : OK; +} + +static int hc_read_headers(request_rec *r) +{ + char buffer[HUGE_STRING_LEN]; + int len; + const char *ct; + + len = ap_getline(buffer, sizeof(buffer), r, 1); + if (len <= 0) { + return !OK; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(03254) + "%.*s", len, 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; + ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, r->server, "%.*s", + len, buffer); + if (!(value = strchr(buffer, ':'))) { + return !OK; + } + *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); + } + + /* Set the Content-Type for the request if set */ + if ((ct = apr_table_get(r->headers_out, "Content-Type")) != NULL) + ap_set_content_type(r, ct); + + return OK; +} + +static int hc_read_body(request_rec *r, apr_bucket_brigade *bb) +{ + apr_status_t rv = APR_SUCCESS; + int seen_eos = 0; + + do { + apr_size_t len = HUGE_STRING_LEN; + + apr_brigade_cleanup(bb); + rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_READBYTES, + APR_BLOCK_READ, len); + + if (rv != APR_SUCCESS) { + if (APR_STATUS_IS_EOF(rv)) { + rv = APR_SUCCESS; + break; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server, APLOGNO(03300) + "Error reading response body"); + break; + } + + while (!APR_BRIGADE_EMPTY(bb)) { + apr_bucket *bucket = APR_BRIGADE_FIRST(bb); + if (APR_BUCKET_IS_EOS(bucket)) { + seen_eos = 1; + break; + } + if (APR_BUCKET_IS_FLUSH(bucket)) { + apr_bucket_delete(bucket); + continue; + } + APR_BUCKET_REMOVE(bucket); + APR_BRIGADE_INSERT_TAIL(r->kept_body, bucket); + } + } + while (!seen_eos); + apr_brigade_cleanup(bb); + 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(baton_t *baton, apr_thread_t *thread) +{ + int status; + proxy_conn_rec *backend = NULL; + sctx_t *ctx = baton->ctx; + proxy_worker *hc = baton->hc; + proxy_worker *worker = baton->worker; + apr_pool_t *ptemp = baton->ptemp; + request_rec *r; + wctx_t *wctx; + hc_condition_t *cond; + apr_bucket_brigade *bb; + + wctx = (wctx_t *)hc->context; + if (!wctx->req || !wctx->method) { + return APR_ENOTIMPL; + } + + if ((status = hc_get_backend("HCOH", &backend, hc, ctx, ptemp)) != 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); + } + + r = create_request_rec(ptemp, ctx->s, baton->balancer, wctx->method, wctx->protocol); + if ((status = ap_proxy_connection_create_ex("HCOH", backend, r)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + set_request_connection(r, backend->connection); + backend->connection->current_thread = thread; + + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + + if ((status = hc_send(r, wctx->req, bb)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + if ((status = hc_read_headers(r)) != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + if (!r->header_only) { + apr_table_t *saved_headers_in = r->headers_in; + r->headers_in = apr_table_copy(r->pool, r->headers_out); + ap_proxy_pre_http_request(backend->connection, r); + status = hc_read_body(r, bb); + r->headers_in = saved_headers_in; + if (status != OK) { + return backend_cleanup("HCOH", backend, ctx->s, status); + } + r->trailers_out = apr_table_copy(r->pool, r->trailers_in); + } + + 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) { + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s, + "Condition %s for %s (%s): passed", worker->s->hcexpr, + hc->s->name_ex, worker->s->name_ex); + } else if (ok < 0 || err) { + 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_ex, worker->s->name_ex, err); + status = !OK; + } else { + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s, + "Condition %s for %s (%s) : failed", worker->s->hcexpr, + hc->s->name_ex, worker->s->name_ex); + status = !OK; + } + } else if (r->status < 200 || r->status > 399) { + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s, + "Response status %i for %s (%s): failed", r->status, + hc->s->name_ex, worker->s->name_ex); + status = !OK; + } + return backend_cleanup("HCOH", backend, ctx->s, status); +} + +static void * APR_THREAD_FUNC hc_check(apr_thread_t *thread, void *b) +{ + baton_t *baton = (baton_t *)b; + server_rec *s = baton->ctx->s; + proxy_worker *worker = baton->worker; + proxy_worker *hc = baton->hc; + apr_time_t now; + apr_status_t rv; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03256) + "%sHealth checking %s", (thread ? "Threaded " : ""), + worker->s->name_ex); + + if (hc->s->method == TCP) { + rv = hc_check_tcp(baton); + } + else if (hc->s->method == CPING) { + rv = hc_check_cping(baton, thread); + } + else { + rv = hc_check_http(baton, thread); + } + + now = apr_time_now(); + if (rv == APR_ENOTIMPL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(03257) + "Somehow tried to use unimplemented hcheck method: %d", + (int)hc->s->method); + } + /* what state are we in ? */ + else if (PROXY_WORKER_IS_HCFAILED(worker) || PROXY_WORKER_IS_ERROR(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_ex); + + } + } + } + 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_ex); + } + } + } + if (baton->now) { + *baton->now = now; + } + apr_pool_destroy(baton->ptemp); + worker->s->updated = now; + + return NULL; +} + +static apr_status_t hc_watchdog_callback(int state, void *data, + apr_pool_t *pool) +{ + apr_status_t rv = APR_SUCCESS; + 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 (tpsize && hctp == NULL) { + rv = apr_thread_pool_create(&hctp, tpsize, + 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", + tpsize); + /* we can continue on without the threadpools */ + hctp = NULL; + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03313) + "apr_thread_pool_create() with %d threads succeeded", + tpsize); + } + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03314) + "Skipping apr_thread_pool_create()"); + hctp = NULL; + } +#endif + break; + + case AP_WATCHDOG_STATE_RUNNING: + /* loop thru all workers */ + if (s) { + int i; + conf = (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + balancer = (proxy_balancer *)conf->balancers->elts; + ctx->s = s; + for (i = 0; i < conf->balancers->nelts; i++, balancer++) { + int n; + apr_time_t now; + 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; + now = apr_time_now(); + for (n = 0; n < balancer->workers->nelts; n++) { + worker = *workers; + if (!PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED) && + (worker->s->method != NONE) && + (worker->s->updated != 0) && + (now > worker->s->updated + worker->s->interval)) { + baton_t *baton; + apr_pool_t *ptemp; + + ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s, + "Checking %s worker: %s [%d] (%pp)", balancer->s->name, + worker->s->name_ex, worker->s->method, worker); + + if ((rv = hc_init_worker(ctx, worker)) != APR_SUCCESS) { + worker->s->updated = now; + return rv; + } + worker->s->updated = 0; + + /* This pool has the lifetime of the check */ + apr_pool_create(&ptemp, ctx->p); + apr_pool_tag(ptemp, "hc_request"); + baton = apr_pcalloc(ptemp, sizeof(baton_t)); + baton->ctx = ctx; + baton->balancer = balancer; + baton->worker = worker; + baton->ptemp = ptemp; + baton->hc = hc_get_hcworker(ctx, worker, ptemp); +#if HC_USE_THREADS + if (hctp) { + apr_thread_pool_push(hctp, hc_check, (void *)baton, + APR_THREAD_TASK_PRIORITY_NORMAL, + NULL); + } + else +#endif + { + baton->now = &now; + hc_check(NULL, baton); + } + } + workers++; + } + } + } + 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 + if (hctp) { + rv = apr_thread_pool_destroy(hctp); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03315) + "apr_thread_pool_destroy() failed"); + } + hctp = NULL; + } +#endif + break; + } + return rv; +} +static int hc_pre_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp) +{ +#if HC_USE_THREADS + hctp = NULL; + tpsize = HC_THREADPOOL_SIZE; +#endif + + ajp_handle_cping_cpong = APR_RETRIEVE_OPTIONAL_FN(ajp_handle_cping_cpong); + if (ajp_handle_cping_cpong) { + proxy_hcmethods_t *method = proxy_hcmethods; + for (; method->name; method++) { + if (method->method == CPING) { + method->implemented = 1; + break; + } + } + } + + return OK; +} +static int hc_post_config(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *main_s) +{ + apr_status_t rv; + server_rec *s = main_s; + + APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *hc_watchdog_get_instance; + APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *hc_watchdog_register_callback; + + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + 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; + } + rv = hc_watchdog_get_instance(&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; + } + while (s) { + sctx_t *ctx = ap_get_module_config(s->module_config, + &proxy_hcheck_module); + + if (s != ctx->s) { + ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s, APLOGNO(10019) + "Missing unique per-server context: %s (%pp:%pp) (no hchecks)", + s->server_hostname, s, ctx->s); + s = s->next; + continue; + } + rv = hc_watchdog_register_callback(watchdog, + AP_WD_TM_SLICE, + 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 for %s)", HCHECK_WATHCHDOG_NAME, s->server_hostname); + s = s->next; + } + + 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 (!ctx) + return; + 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 (!ctx) + return; + 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 (!ctx) + return 0; + 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 && ap_cstr_casecmp(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 && ap_cstr_casecmp(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, RSRC_CONF, + "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_pre_config(hc_pre_config, NULL, NULL, APR_HOOK_MIDDLE); + 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_hcheck.dep b/modules/proxy/mod_proxy_hcheck.dep new file mode 100644 index 0000000..008ee64 --- /dev/null +++ b/modules/proxy/mod_proxy_hcheck.dep @@ -0,0 +1,5 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_hcheck.mak + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_hcheck.dsp b/modules/proxy/mod_proxy_hcheck.dsp new file mode 100644 index 0000000..73aaf2b --- /dev/null +++ b/modules/proxy/mod_proxy_hcheck.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_hcheck" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_hcheck - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_hcheck.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_hcheck.mak" CFG="mod_proxy_hcheck - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_hcheck - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_hcheck - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../core" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_hcheck_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_hcheck.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_hcheck.so" /d LONG_NAME="proxy_hcheck_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_hcheck.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_hcheck.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_hcheck.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_hcheck.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_hcheck.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../core" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_hcheck_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_hcheck.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_hcheck.so" /d LONG_NAME="proxy_hcheck_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_hcheck.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_hcheck.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_hcheck.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_hcheck.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_hcheck.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_hcheck - Win32 Release" +# Name "mod_proxy_hcheck - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_hcheck.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_hcheck.mak b/modules/proxy/mod_proxy_hcheck.mak new file mode 100644 index 0000000..dc0d758 --- /dev/null +++ b/modules/proxy/mod_proxy_hcheck.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_hcheck.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_hcheck - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_hcheck - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_hcheck - Win32 Release" && "$(CFG)" != "mod_proxy_hcheck - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_hcheck.mak" CFG="mod_proxy_hcheck - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_hcheck - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_hcheck - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_hcheck.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_hcheck.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_hcheck.obj" + -@erase "$(INTDIR)\mod_proxy_hcheck.res" + -@erase "$(INTDIR)\mod_proxy_hcheck_src.idb" + -@erase "$(INTDIR)\mod_proxy_hcheck_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_hcheck.exp" + -@erase "$(OUTDIR)\mod_proxy_hcheck.lib" + -@erase "$(OUTDIR)\mod_proxy_hcheck.pdb" + -@erase "$(OUTDIR)\mod_proxy_hcheck.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../core" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_hcheck_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_hcheck.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_hcheck.so" /d LONG_NAME="proxy_hcheck_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_hcheck.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_hcheck.pdb" /debug /out:"$(OUTDIR)\mod_proxy_hcheck.so" /implib:"$(OUTDIR)\mod_proxy_hcheck.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_hcheck.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_hcheck.obj" \ + "$(INTDIR)\mod_proxy_hcheck.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_hcheck.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_hcheck.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_hcheck.so" + if exist .\Release\mod_proxy_hcheck.so.manifest mt.exe -manifest .\Release\mod_proxy_hcheck.so.manifest -outputresource:.\Release\mod_proxy_hcheck.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_hcheck.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_hcheck.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_hcheck.obj" + -@erase "$(INTDIR)\mod_proxy_hcheck.res" + -@erase "$(INTDIR)\mod_proxy_hcheck_src.idb" + -@erase "$(INTDIR)\mod_proxy_hcheck_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_hcheck.exp" + -@erase "$(OUTDIR)\mod_proxy_hcheck.lib" + -@erase "$(OUTDIR)\mod_proxy_hcheck.pdb" + -@erase "$(OUTDIR)\mod_proxy_hcheck.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../core" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_hcheck_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_hcheck.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_hcheck.so" /d LONG_NAME="proxy_hcheck_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_hcheck.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_hcheck.pdb" /debug /out:"$(OUTDIR)\mod_proxy_hcheck.so" /implib:"$(OUTDIR)\mod_proxy_hcheck.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_hcheck.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_hcheck.obj" \ + "$(INTDIR)\mod_proxy_hcheck.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_hcheck.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_hcheck.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_hcheck.so" + if exist .\Debug\mod_proxy_hcheck.so.manifest mt.exe -manifest .\Debug\mod_proxy_hcheck.so.manifest -outputresource:.\Debug\mod_proxy_hcheck.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_hcheck.dep") +!INCLUDE "mod_proxy_hcheck.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_hcheck.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" || "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" +SOURCE=.\mod_proxy_hcheck.c + +"$(INTDIR)\mod_proxy_hcheck.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F .\libapr.mak CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F .\libapr.mak CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F .\libapr.mak CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F .\libapr.mak CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F .\libhttpd.mak CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F .\libhttpd.mak CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F .\libhttpd.mak CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F .\libhttpd.mak CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F .\mod_proxy.mak CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F .\mod_proxy.mak CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F .\mod_proxy.mak CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F .\mod_proxy.mak CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_hcheck - Win32 Release" + + +"$(INTDIR)\mod_proxy_hcheck.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_hcheck.res" /i "../../include" /i "../../srclib/apr/include" /i "\build6\hcheck\build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_hcheck.so" /d LONG_NAME="proxy_hcheck_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_proxy_hcheck - Win32 Debug" + + +"$(INTDIR)\mod_proxy_hcheck.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_hcheck.res" /i "../../include" /i "../../srclib/apr/include" /i "\build6\hcheck\build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_hcheck.so" /d LONG_NAME="proxy_hcheck_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c new file mode 100644 index 0000000..1842c49 --- /dev/null +++ b/modules/proxy/mod_proxy_http.c @@ -0,0 +1,2145 @@ +/* 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. + */ + +/* HTTP routines for Apache proxy */ + +#include "mod_proxy.h" +#include "ap_regex.h" + +module AP_MODULE_DECLARE_DATA proxy_http_module; + +static int (*ap_proxy_clear_connection_fn)(request_rec *r, apr_table_t *headers) = + NULL; + +static apr_status_t ap_proxy_http_cleanup(const char *scheme, + request_rec *r, + proxy_conn_rec *backend); + +static apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, + request_rec *r, int flags, int *read); + +static const char *get_url_scheme(const char **url, int *is_ssl) +{ + const char *u = *url; + + switch (u[0]) { + case 'h': + case 'H': + if (strncasecmp(u + 1, "ttp", 3) == 0) { + if (u[4] == ':') { + *is_ssl = 0; + *url = u + 5; + return "http"; + } + if (apr_tolower(u[4]) == 's' && u[5] == ':') { + *is_ssl = 1; + *url = u + 6; + return "https"; + } + } + break; + + case 'w': + case 'W': + if (apr_tolower(u[1]) == 's') { + if (u[2] == ':') { + *is_ssl = 0; + *url = u + 3; + return "ws"; + } + if (apr_tolower(u[2]) == 's' && u[3] == ':') { + *is_ssl = 1; + *url = u + 4; + return "wss"; + } + } + break; + } + + *is_ssl = 0; + return NULL; +} + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + */ +static int proxy_http_canon(request_rec *r, char *url) +{ + const char *base_url = url; + char *host, *path, sport[7]; + char *search = NULL; + const char *err; + const char *scheme; + apr_port_t port, def_port; + int is_ssl = 0; + + scheme = get_url_scheme((const char **)&url, &is_ssl); + if (!scheme) { + return DECLINED; + } + port = def_port = (is_ssl) ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "HTTP: canonicalising URL %s", base_url); + + /* do syntatic check. + * We break the URL into host, port, path, search + */ + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01083) + "error parsing URL %s: %s", base_url, err); + return HTTP_BAD_REQUEST; + } + + /* + * now parse path/search args, according to rfc1738: + * process the path. + * + * In a reverse proxy, our URL has been processed, so canonicalise + * unless proxy-nocanon is set to say it's raw + * In a forward proxy, we have and MUST NOT MANGLE the original. + */ + switch (r->proxyreq) { + default: /* wtf are we doing here? */ + case PROXYREQ_REVERSE: + if (apr_table_get(r->notes, "proxy-nocanon")) { + path = url; /* this is the raw path */ + } + else if (apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the encoded path already */ + search = r->args; + } + else { + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, + flags, r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + search = r->args; + } + break; + case PROXYREQ_PROXY: + path = url; + break; + } + /* + * If we have a raw control character or a ' ' in nocanon path or + * r->args, correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10415) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + if (search && *ap_scan_vchar_obstext(search)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10408) + "To be forwarded query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + + r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, + "/", path, (search) ? "?" : "", search, NULL); + return OK; +} + +/* Clear all connection-based headers from the incoming headers table */ +typedef struct header_dptr { + apr_pool_t *pool; + apr_table_t *table; + apr_time_t time; +} header_dptr; +static ap_regex_t *warn_rx; +static int clean_warning_headers(void *data, const char *key, const char *val) +{ + apr_table_t *headers = ((header_dptr*)data)->table; + apr_pool_t *pool = ((header_dptr*)data)->pool; + char *warning; + char *date; + apr_time_t warn_time; + const int nmatch = 3; + ap_regmatch_t pmatch[3]; + + if (headers == NULL) { + ((header_dptr*)data)->table = headers = apr_table_make(pool, 2); + } +/* + * Parse this, suckers! + * + * Warning = "Warning" ":" 1#warning-value + * + * warning-value = warn-code SP warn-agent SP warn-text + * [SP warn-date] + * + * warn-code = 3DIGIT + * warn-agent = ( host [ ":" port ] ) | pseudonym + * ; the name or pseudonym of the server adding + * ; the Warning header, for use in debugging + * warn-text = quoted-string + * warn-date = <"> HTTP-date <"> + * + * Buggrit, use a bloomin' regexp! + * (\d{3}\s+\S+\s+\".*?\"(\s+\"(.*?)\")?) --> whole in $1, date in $3 + */ + while (!ap_regexec(warn_rx, val, nmatch, pmatch, 0)) { + warning = apr_pstrndup(pool, val+pmatch[0].rm_so, + pmatch[0].rm_eo - pmatch[0].rm_so); + warn_time = 0; + if (pmatch[2].rm_eo > pmatch[2].rm_so) { + /* OK, we have a date here */ + date = apr_pstrndup(pool, val+pmatch[2].rm_so, + pmatch[2].rm_eo - pmatch[2].rm_so); + warn_time = apr_date_parse_http(date); + } + if (!warn_time || (warn_time == ((header_dptr*)data)->time)) { + apr_table_addn(headers, key, warning); + } + val += pmatch[0].rm_eo; + } + return 1; +} +static apr_table_t *ap_proxy_clean_warnings(apr_pool_t *p, apr_table_t *headers) +{ + header_dptr x; + x.pool = p; + x.table = NULL; + x.time = apr_date_parse_http(apr_table_get(headers, "Date")); + apr_table_do(clean_warning_headers, &x, headers, "Warning", NULL); + if (x.table != NULL) { + apr_table_unset(headers, "Warning"); + return apr_table_overlay(p, headers, x.table); + } + else { + return headers; + } +} + +static void add_te_chunked(apr_pool_t *p, + apr_bucket_alloc_t *bucket_alloc, + apr_bucket_brigade *header_brigade) +{ + apr_bucket *e; + char *buf; + const char te_hdr[] = "Transfer-Encoding: chunked" CRLF; + + buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1); + ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1); + + e = apr_bucket_pool_create(buf, sizeof(te_hdr)-1, p, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); +} + +static void add_cl(apr_pool_t *p, + apr_bucket_alloc_t *bucket_alloc, + apr_bucket_brigade *header_brigade, + const char *cl_val) +{ + apr_bucket *e; + char *buf; + + buf = apr_pstrcat(p, "Content-Length: ", + cl_val, + CRLF, + NULL); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); +} + +#ifndef CRLF_ASCII +#define CRLF_ASCII "\015\012" +#endif +#ifndef ZERO_ASCII +#define ZERO_ASCII "\060" +#endif + +#define MAX_MEM_SPOOL 16384 + +typedef enum { + RB_INIT = 0, + RB_STREAM_CL, + RB_STREAM_CHUNKED, + RB_SPOOL_CL +} rb_methods; + +typedef struct { + apr_pool_t *p; + request_rec *r; + const char *proto; + proxy_worker *worker; + proxy_server_conf *sconf; + + char server_portstr[32]; + proxy_conn_rec *backend; + conn_rec *origin; + + apr_bucket_alloc_t *bucket_alloc; + apr_bucket_brigade *header_brigade; + apr_bucket_brigade *input_brigade; + char *old_cl_val, *old_te_val; + apr_off_t cl_val; + + rb_methods rb_method; + + const char *upgrade; + + unsigned int do_100_continue :1, + prefetch_nonblocking :1, + force10 :1; +} proxy_http_req_t; + +static int stream_reqbody(proxy_http_req_t *req) +{ + request_rec *r = req->r; + int seen_eos = 0, rv = OK; + apr_size_t hdr_len; + char chunk_hdr[20]; /* must be here due to transient bucket. */ + conn_rec *origin = req->origin; + proxy_conn_rec *p_conn = req->backend; + apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc; + apr_bucket_brigade *header_brigade = req->header_brigade; + apr_bucket_brigade *input_brigade = req->input_brigade; + rb_methods rb_method = req->rb_method; + apr_off_t bytes, bytes_streamed = 0; + apr_bucket *e; + + do { + if (APR_BRIGADE_EMPTY(input_brigade) + && APR_BRIGADE_EMPTY(header_brigade)) { + rv = ap_proxy_read_input(r, p_conn, input_brigade, + HUGE_STRING_LEN); + if (rv != OK) { + return rv; + } + } + + if (!APR_BRIGADE_EMPTY(input_brigade)) { + /* If this brigade contains EOS, remove it and be done. */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + seen_eos = 1; + + /* We can't pass this EOS to the output_filters. */ + e = APR_BRIGADE_LAST(input_brigade); + apr_bucket_delete(e); + } + + apr_brigade_length(input_brigade, 1, &bytes); + bytes_streamed += bytes; + + if (rb_method == RB_STREAM_CHUNKED) { + if (bytes) { + /* + * Prepend the size of the chunk + */ + hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr), + "%" APR_UINT64_T_HEX_FMT CRLF, + (apr_uint64_t)bytes); + ap_xlate_proto_to_ascii(chunk_hdr, hdr_len); + e = apr_bucket_transient_create(chunk_hdr, hdr_len, + bucket_alloc); + APR_BRIGADE_INSERT_HEAD(input_brigade, e); + + /* + * Append the end-of-chunk CRLF + */ + e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + } + if (seen_eos) { + /* + * Append the tailing 0-size chunk + */ + e = apr_bucket_immortal_create(ZERO_ASCII CRLF_ASCII + /* <trailers> */ + CRLF_ASCII, + 5, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + } + } + else if (rb_method == RB_STREAM_CL + && (bytes_streamed > req->cl_val + || (seen_eos && bytes_streamed < req->cl_val))) { + /* C-L != bytes streamed?!? + * + * Prevent HTTP Request/Response Splitting. + * + * We can't stream more (or less) bytes at the back end since + * they could be interpreted in separate requests (more bytes + * now would start a new request, less bytes would make the + * first bytes of the next request be part of the current one). + * + * It can't happen from the client connection here thanks to + * ap_http_filter(), but some module's filter may be playing + * bad games, hence the HTTP_INTERNAL_SERVER_ERROR. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01086) + "read %s bytes of request body than expected " + "(got %" APR_OFF_T_FMT ", expected " + "%" APR_OFF_T_FMT ")", + bytes_streamed > req->cl_val ? "more" : "less", + bytes_streamed, req->cl_val); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (seen_eos && apr_table_get(r->subprocess_env, + "proxy-sendextracrlf")) { + e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + } + } + + /* If we never sent the header brigade, go ahead and take care of + * that now by prepending it (once only since header_brigade will be + * empty afterward). + */ + APR_BRIGADE_PREPEND(input_brigade, header_brigade); + + /* Flush here on EOS because we won't ap_proxy_read_input() again. */ + rv = ap_proxy_pass_brigade(bucket_alloc, r, p_conn, origin, + input_brigade, seen_eos); + if (rv != OK) { + return rv; + } + } while (!seen_eos); + + return OK; +} + +static void terminate_headers(proxy_http_req_t *req) +{ + apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc; + apr_bucket *e; + char *buf; + + /* + * Handle Connection: header if we do HTTP/1.1 request: + * If we plan to close the backend connection sent Connection: close + * otherwise sent Connection: Keep-Alive. + */ + if (!req->force10) { + if (req->upgrade) { + buf = apr_pstrdup(req->p, "Connection: Upgrade" CRLF); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), req->p, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(req->header_brigade, e); + + /* Tell the backend that it can upgrade the connection. */ + buf = apr_pstrcat(req->p, "Upgrade: ", req->upgrade, CRLF, NULL); + } + else if (ap_proxy_connection_reusable(req->backend)) { + buf = apr_pstrdup(req->p, "Connection: Keep-Alive" CRLF); + } + else { + buf = apr_pstrdup(req->p, "Connection: close" CRLF); + } + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), req->p, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(req->header_brigade, e); + } + + /* add empty line at the end of the headers */ + e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(req->header_brigade, e); +} + +static int ap_proxy_http_prefetch(proxy_http_req_t *req, + apr_uri_t *uri, char *url) +{ + apr_pool_t *p = req->p; + request_rec *r = req->r; + conn_rec *c = r->connection; + proxy_conn_rec *p_conn = req->backend; + apr_bucket_alloc_t *bucket_alloc = req->bucket_alloc; + apr_bucket_brigade *header_brigade = req->header_brigade; + apr_bucket_brigade *input_brigade = req->input_brigade; + apr_bucket *e; + apr_off_t bytes_read = 0; + apr_off_t bytes; + int rv; + + rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, p_conn, + req->worker, req->sconf, + uri, url, req->server_portstr, + &req->old_cl_val, &req->old_te_val); + if (rv != OK) { + return rv; + } + + /* sub-requests never use keepalives, and mustn't pass request bodies. + * Because the new logic looks at input_brigade, we will self-terminate + * input_brigade and jump past all of the request body logic... + * Reading anything with ap_get_brigade is likely to consume the + * main request's body or read beyond EOS - which would be unpleasant. + * + * An exception: when a kept_body is present, then subrequest CAN use + * pass request bodies, and we DONT skip the body. + */ + if (!r->kept_body && r->main) { + /* XXX: Why DON'T sub-requests use keepalives? */ + p_conn->close = 1; + req->old_te_val = NULL; + req->old_cl_val = NULL; + req->rb_method = RB_STREAM_CL; + e = apr_bucket_eos_create(input_brigade->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + goto skip_body; + } + + /* WE only understand chunked. Other modules might inject + * (and therefore, decode) other flavors but we don't know + * that the can and have done so unless they remove + * their decoding from the headers_in T-E list. + * XXX: Make this extensible, but in doing so, presume the + * encoding has been done by the extensions' handler, and + * do not modify add_te_chunked's logic + */ + if (req->old_te_val && ap_cstr_casecmp(req->old_te_val, "chunked") != 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01093) + "%s Transfer-Encoding is not supported", + req->old_te_val); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (req->old_cl_val && req->old_te_val) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01094) + "client %s (%s) requested Transfer-Encoding " + "chunked body with Content-Length (C-L ignored)", + c->client_ip, c->remote_host ? c->remote_host: ""); + req->old_cl_val = NULL; + p_conn->close = 1; + } + + rv = ap_proxy_prefetch_input(r, req->backend, input_brigade, + req->prefetch_nonblocking ? APR_NONBLOCK_READ + : APR_BLOCK_READ, + &bytes_read, MAX_MEM_SPOOL); + if (rv != OK) { + return rv; + } + + /* Use chunked request body encoding or send a content-length body? + * + * Prefer C-L when: + * + * We have no request body (handled by RB_STREAM_CL) + * + * We have a request body length <= MAX_MEM_SPOOL + * + * The administrator has setenv force-proxy-request-1.0 + * + * The client sent a C-L body, and the administrator has + * not setenv proxy-sendchunked or has set setenv proxy-sendcl + * + * The client sent a T-E body, and the administrator has + * setenv proxy-sendcl, and not setenv proxy-sendchunked + * + * If both proxy-sendcl and proxy-sendchunked are set, the + * behavior is the same as if neither were set, large bodies + * that can't be read will be forwarded in their original + * form of C-L, or T-E. + * + * To ensure maximum compatibility, setenv proxy-sendcl + * To reduce server resource use, setenv proxy-sendchunked + * + * Then address specific servers with conditional setenv + * options to restore the default behavior where desirable. + * + * We have to compute content length by reading the entire request + * body; if request body is not small, we'll spool the remaining + * input to a temporary file. Chunked is always preferable. + * + * We can only trust the client-provided C-L if the T-E header + * is absent, and the filters are unchanged (the body won't + * be resized by another content filter). + */ + if (!APR_BRIGADE_EMPTY(input_brigade) + && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + /* The whole thing fit, so our decision is trivial, use + * the filtered bytes read from the client for the request + * body Content-Length. + * + * If we expected no body, and read no body, do not set + * the Content-Length. + */ + if (req->old_cl_val || req->old_te_val || bytes_read) { + req->old_cl_val = apr_off_t_toa(r->pool, bytes_read); + req->cl_val = bytes_read; + } + req->rb_method = RB_STREAM_CL; + } + else if (req->old_te_val) { + if (req->force10 + || (apr_table_get(r->subprocess_env, "proxy-sendcl") + && !apr_table_get(r->subprocess_env, "proxy-sendchunks") + && !apr_table_get(r->subprocess_env, "proxy-sendchunked"))) { + req->rb_method = RB_SPOOL_CL; + } + else { + req->rb_method = RB_STREAM_CHUNKED; + } + } + else if (req->old_cl_val) { + if (r->input_filters == r->proto_input_filters) { + if (!ap_parse_strict_length(&req->cl_val, req->old_cl_val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01085) + "could not parse request Content-Length (%s)", + req->old_cl_val); + return HTTP_BAD_REQUEST; + } + req->rb_method = RB_STREAM_CL; + } + else if (!req->force10 + && (apr_table_get(r->subprocess_env, "proxy-sendchunks") + || apr_table_get(r->subprocess_env, "proxy-sendchunked")) + && !apr_table_get(r->subprocess_env, "proxy-sendcl")) { + req->rb_method = RB_STREAM_CHUNKED; + } + else { + req->rb_method = RB_SPOOL_CL; + } + } + else { + /* This is an appropriate default; very efficient for no-body + * requests, and has the behavior that it will not add any C-L + * when the old_cl_val is NULL. + */ + req->rb_method = RB_SPOOL_CL; + } + + switch (req->rb_method) { + case RB_STREAM_CHUNKED: + add_te_chunked(req->p, bucket_alloc, header_brigade); + break; + + case RB_STREAM_CL: + if (req->old_cl_val) { + add_cl(req->p, bucket_alloc, header_brigade, req->old_cl_val); + } + break; + + default: /* => RB_SPOOL_CL */ + /* If we have to spool the body, do it now, before connecting or + * reusing the backend connection. + */ + rv = ap_proxy_spool_input(r, p_conn, input_brigade, + &bytes, MAX_MEM_SPOOL); + if (rv != OK) { + return rv; + } + if (bytes || req->old_te_val || req->old_cl_val) { + add_cl(p, bucket_alloc, header_brigade, apr_off_t_toa(p, bytes)); + } + } + +/* Yes I hate gotos. This is the subrequest shortcut */ +skip_body: + terminate_headers(req); + + return OK; +} + +static int ap_proxy_http_request(proxy_http_req_t *req) +{ + int rv; + request_rec *r = req->r; + + /* send the request header/body, if any. */ + switch (req->rb_method) { + case RB_SPOOL_CL: + case RB_STREAM_CL: + case RB_STREAM_CHUNKED: + if (req->do_100_continue) { + rv = ap_proxy_pass_brigade(req->bucket_alloc, r, req->backend, + req->origin, req->header_brigade, 1); + } + else { + rv = stream_reqbody(req); + } + break; + + default: + /* shouldn't be possible */ + rv = HTTP_INTERNAL_SERVER_ERROR; + break; + } + + if (rv != OK) { + conn_rec *c = r->connection; + /* apr_status_t value has been logged in lower level method */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01097) + "pass request body failed to %pI (%s) from %s (%s)", + req->backend->addr, + req->backend->hostname ? req->backend->hostname: "", + c->client_ip, c->remote_host ? c->remote_host: ""); + return rv; + } + + return OK; +} + +/* + * If the date is a valid RFC 850 date or asctime() date, then it + * is converted to the RFC 1123 format. + */ +static const char *date_canon(apr_pool_t *p, const char *date) +{ + apr_status_t rv; + char* ndate; + + apr_time_t time = apr_date_parse_http(date); + if (!time) { + return date; + } + + ndate = apr_palloc(p, APR_RFC822_DATE_LEN); + rv = apr_rfc822_date(ndate, time); + if (rv != APR_SUCCESS) { + return date; + } + + return ndate; +} + +static request_rec *make_fake_req(conn_rec *c, request_rec *r) +{ + apr_pool_t *pool; + request_rec *rp; + + apr_pool_create(&pool, c->pool); + apr_pool_tag(pool, "proxy_http_rp"); + + rp = apr_pcalloc(pool, sizeof(*r)); + + rp->pool = pool; + rp->status = HTTP_OK; + + rp->headers_in = apr_table_make(pool, 50); + rp->trailers_in = apr_table_make(pool, 5); + + rp->subprocess_env = apr_table_make(pool, 50); + rp->headers_out = apr_table_make(pool, 12); + rp->trailers_out = apr_table_make(pool, 5); + rp->err_headers_out = apr_table_make(pool, 5); + rp->notes = apr_table_make(pool, 5); + + rp->server = r->server; + rp->log = r->log; + rp->proxyreq = r->proxyreq; + rp->request_time = r->request_time; + rp->connection = c; + rp->output_filters = c->output_filters; + rp->input_filters = c->input_filters; + rp->proto_output_filters = c->output_filters; + rp->proto_input_filters = c->input_filters; + rp->useragent_ip = c->client_ip; + rp->useragent_addr = c->client_addr; + + rp->request_config = ap_create_request_config(pool); + proxy_run_create_req(r, rp); + + return rp; +} + +static void process_proxy_header(request_rec *r, proxy_dir_conf *c, + const char *key, const char *value) +{ + static const char *date_hdrs[] + = { "Date", "Expires", "Last-Modified", NULL }; + static const struct { + const char *name; + ap_proxy_header_reverse_map_fn func; + } transform_hdrs[] = { + { "Location", ap_proxy_location_reverse_map }, + { "Content-Location", ap_proxy_location_reverse_map }, + { "URI", ap_proxy_location_reverse_map }, + { "Destination", ap_proxy_location_reverse_map }, + { "Set-Cookie", ap_proxy_cookie_reverse_map }, + { NULL, NULL } + }; + int i; + for (i = 0; date_hdrs[i]; ++i) { + if (!ap_cstr_casecmp(date_hdrs[i], key)) { + apr_table_add(r->headers_out, key, + date_canon(r->pool, value)); + return; + } + } + for (i = 0; transform_hdrs[i].name; ++i) { + if (!ap_cstr_casecmp(transform_hdrs[i].name, key)) { + apr_table_add(r->headers_out, key, + (*transform_hdrs[i].func)(r, c, value)); + return; + } + } + apr_table_add(r->headers_out, key, value); +} + +/* + * Note: pread_len is the length of the response that we've mistakenly + * read (assuming that we don't consider that an error via + * ProxyBadHeader StartBody). This depends on buffer actually being + * local storage to the calling code in order for pread_len to make + * any sense at all, since we depend on buffer still containing + * what was read by ap_getline() upon return. + */ +static apr_status_t ap_proxy_read_headers(request_rec *r, request_rec *rr, + char *buffer, int size, + conn_rec *c, int *pread_len) +{ + int len; + char *value, *end; + int saw_headers = 0; + void *sconf = r->server->module_config; + proxy_server_conf *psc; + proxy_dir_conf *dconf; + apr_status_t rc; + apr_bucket_brigade *tmp_bb; + + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + r->headers_out = apr_table_make(r->pool, 20); + r->trailers_out = apr_table_make(r->pool, 5); + *pread_len = 0; + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), or we timeout. + */ + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "Headers received from backend:"); + + tmp_bb = apr_brigade_create(r->pool, c->bucket_alloc); + while (1) { + rc = ap_proxygetline(tmp_bb, buffer, size, rr, + AP_GETLINE_FOLD | AP_GETLINE_NOSPC_EOL, &len); + + + if (rc != APR_SUCCESS) { + if (APR_STATUS_IS_ENOSPC(rc)) { + int trunc = (len > 128 ? 128 : len) / 2; + ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124) + "header size is over the limit allowed by " + "ResponseFieldSize (%d bytes). " + "Bad response header: '%.*s[...]%s'", + size, trunc, buffer, buffer + len - trunc); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10404) + "Error reading headers from backend"); + } + r->headers_out = NULL; + return rc; + } + + if (len <= 0) { + break; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "%s", buffer); + } + + if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ + + /* We may encounter invalid headers, usually from buggy + * MS IIS servers, so we need to determine just how to handle + * them. We can either ignore them, assume that they mark the + * start-of-body (eg: a missing CRLF) or (the default) mark + * the headers as totally bogus and return a 500. The sole + * exception is an extra "HTTP/1.0 200, OK" line sprinkled + * in between the usual MIME headers, which is a favorite + * IIS bug. + */ + /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */ + + if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + if (psc->badopt == bad_error) { + /* Nope, it wasn't even an extra HTTP header. Give up. */ + r->headers_out = NULL; + return APR_EINVAL; + } + else if (psc->badopt == bad_body) { + /* if we've already started loading headers_out, then + * return what we've accumulated so far, in the hopes + * that they are useful; also note that we likely pre-read + * the first line of the response. + */ + if (saw_headers) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01098) + "Starting body due to bogus non-header " + "in headers returned by %s (%s)", + r->uri, r->method); + *pread_len = len; + return APR_SUCCESS; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01099) + "No HTTP headers returned by %s (%s)", + r->uri, r->method); + return APR_SUCCESS; + } + } + } + /* this is the psc->badopt == bad_ignore case */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01100) + "Ignoring bogus HTTP header returned by %s (%s)", + r->uri, r->method); + continue; + } + + *value = '\0'; + ++value; + /* XXX: RFC2068 defines only SP and HT as whitespace, this test is + * wrong... and so are many others probably. + */ + while (apr_isspace(*value)) + ++value; /* Skip to start of value */ + + /* should strip trailing whitespace as well */ + for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end) + *end = '\0'; + + /* make sure we add so as not to destroy duplicated headers + * Modify headers requiring canonicalisation and/or affected + * by ProxyPassReverse and family with process_proxy_header + */ + process_proxy_header(r, dconf, buffer, value); + saw_headers = 1; + } + return APR_SUCCESS; +} + + + +static int addit_dammit(void *v, const char *key, const char *val) +{ + apr_table_addn(v, key, val); + return 1; +} + +static apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, + request_rec *r, int flags, int *read) +{ + apr_status_t rv; + apr_size_t len; + + rv = ap_rgetline(&s, n, &len, r, flags, bb); + apr_brigade_cleanup(bb); + + if (rv == APR_SUCCESS || APR_STATUS_IS_ENOSPC(rv)) { + *read = (int)len; + } else { + *read = -1; + } + + return rv; +} + +/* + * Limit the number of interim responses we sent back to the client. Otherwise + * we suffer from a memory build up. Besides there is NO sense in sending back + * an unlimited number of interim responses to the client. Thus if we cross + * this limit send back a 502 (Bad Gateway). + */ +#ifndef AP_MAX_INTERIM_RESPONSES +#define AP_MAX_INTERIM_RESPONSES 10 +#endif + +static int add_trailers(void *data, const char *key, const char *val) +{ + if (val) { + apr_table_add((apr_table_t*)data, key, val); + } + return 1; +} + +static int send_continue_body(proxy_http_req_t *req) +{ + int status; + + /* Send the request body (fully). */ + switch(req->rb_method) { + case RB_SPOOL_CL: + case RB_STREAM_CL: + case RB_STREAM_CHUNKED: + status = stream_reqbody(req); + break; + default: + /* Shouldn't happen */ + status = HTTP_INTERNAL_SERVER_ERROR; + break; + } + if (status != OK) { + conn_rec *c = req->r->connection; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req->r, + APLOGNO(10154) "pass request body failed " + "to %pI (%s) from %s (%s) with status %i", + req->backend->addr, + req->backend->hostname ? req->backend->hostname : "", + c->client_ip, c->remote_host ? c->remote_host : "", + status); + req->backend->close = 1; + } + return status; +} + +static +int ap_proxy_http_process_response(proxy_http_req_t *req) +{ + apr_pool_t *p = req->p; + request_rec *r = req->r; + conn_rec *c = r->connection; + proxy_worker *worker = req->worker; + proxy_conn_rec *backend = req->backend; + conn_rec *origin = req->origin; + int do_100_continue = req->do_100_continue; + int status; + + char *buffer; + char fixed_buffer[HUGE_STRING_LEN]; + const char *buf; + char keepchar; + apr_bucket *e; + apr_bucket_brigade *bb; + apr_bucket_brigade *pass_bb; + int len, backasswards; + int interim_response = 0; /* non-zero whilst interim 1xx responses + * are being read. */ + apr_size_t response_field_size = 0; + int pread_len = 0; + apr_table_t *save_table; + int backend_broke = 0; + static const char *hop_by_hop_hdrs[] = + {"Keep-Alive", "Proxy-Authenticate", "TE", "Trailer", "Upgrade", NULL}; + int i; + const char *te = NULL; + int original_status = r->status; + int proxy_status = OK; + const char *original_status_line = r->status_line; + const char *proxy_status_line = NULL; + apr_interval_time_t old_timeout = 0; + proxy_dir_conf *dconf; + + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + + bb = apr_brigade_create(p, c->bucket_alloc); + pass_bb = apr_brigade_create(p, c->bucket_alloc); + + /* Only use dynamically sized buffer if user specifies ResponseFieldSize */ + if(backend->worker->s->response_field_size_set) { + response_field_size = backend->worker->s->response_field_size; + + if (response_field_size != HUGE_STRING_LEN) + buffer = apr_pcalloc(p, response_field_size); + else + buffer = fixed_buffer; + } + else { + response_field_size = HUGE_STRING_LEN; + buffer = fixed_buffer; + } + + /* Setup for 100-Continue timeout if appropriate */ + if (do_100_continue && worker->s->ping_timeout_set) { + apr_socket_timeout_get(backend->sock, &old_timeout); + if (worker->s->ping_timeout != old_timeout) { + apr_status_t rc; + rc = apr_socket_timeout_set(backend->sock, worker->s->ping_timeout); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(01101) + "could not set 100-Continue timeout"); + } + } + } + + /* Get response from the remote server, and pass it up the + * filter chain + */ + + backend->r = make_fake_req(origin, r); + /* In case anyone needs to know, this is a fake request that is really a + * response. + */ + backend->r->proxyreq = PROXYREQ_RESPONSE; + apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu", + origin->local_addr->port)); + do { + apr_status_t rc; + const char *upgrade = NULL; + int major = 0, minor = 0; + int toclose = 0; + + apr_brigade_cleanup(bb); + + rc = ap_proxygetline(backend->tmp_bb, buffer, response_field_size, + backend->r, 0, &len); + if (len == 0) { + /* handle one potential stray CRLF */ + rc = ap_proxygetline(backend->tmp_bb, buffer, response_field_size, + backend->r, 0, &len); + } + if (len <= 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(01102) + "error reading status line from remote " + "server %s:%d", backend->hostname, backend->port); + if (APR_STATUS_IS_TIMEUP(rc)) { + apr_table_setn(r->notes, "proxy_timedout", "1"); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01103) "read timeout"); + if (do_100_continue) { + return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, + "Timeout on 100-Continue"); + } + } + /* + * If we are a reverse proxy request shutdown the connection + * WITHOUT ANY response to trigger a retry by the client + * if allowed (as for idempotent requests). + * BUT currently we should not do this if the request is the + * first request on a keepalive connection as browsers like + * seamonkey only display an empty page in this case and do + * not do a retry. We should also not do this on a + * connection which times out; instead handle as + * we normally would handle timeouts + */ + if (r->proxyreq == PROXYREQ_REVERSE && c->keepalives && + !APR_STATUS_IS_TIMEUP(rc)) { + apr_bucket *eos; + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01104) + "Closing connection to client because" + " reading from backend server %s:%d failed." + " Number of keepalives %i", backend->hostname, + backend->port, c->keepalives); + ap_proxy_backend_broke(r, bb); + /* + * Add an EOC bucket to signal the ap_http_header_filter + * that it should get out of our way, BUT ensure that the + * EOC bucket is inserted BEFORE an EOS bucket in bb as + * some resource filters like mod_deflate pass everything + * up to the EOS down the chain immediately and sent the + * remainder of the brigade later (or even never). But in + * this case the ap_http_header_filter does not get out of + * our way soon enough. + */ + e = ap_bucket_eoc_create(c->bucket_alloc); + eos = APR_BRIGADE_LAST(bb); + while ((APR_BRIGADE_SENTINEL(bb) != eos) + && !APR_BUCKET_IS_EOS(eos)) { + eos = APR_BUCKET_PREV(eos); + } + if (eos == APR_BRIGADE_SENTINEL(bb)) { + APR_BRIGADE_INSERT_TAIL(bb, e); + } + else { + APR_BUCKET_INSERT_BEFORE(eos, e); + } + ap_pass_brigade(r->output_filters, bb); + /* Mark the backend connection for closing */ + backend->close = 1; + if (origin->keepalives) { + /* We already had a request on this backend connection and + * might just have run into a keepalive race. Hence we + * think positive and assume that the backend is fine and + * we do not need to signal an error on backend side. + */ + return OK; + } + /* + * This happened on our first request on this connection to the + * backend. This indicates something fishy with the backend. + * Return HTTP_INTERNAL_SERVER_ERROR to signal an unrecoverable + * server error. We do not worry about r->status code and a + * possible error response here as the ap_http_outerror_filter + * will fix all of this for us. + */ + return HTTP_INTERNAL_SERVER_ERROR; + } + if (!c->keepalives) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01105) + "NOT Closing connection to client" + " although reading from backend server %s:%d" + " failed.", + backend->hostname, backend->port); + } + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + /* XXX: Is this a real headers length send from remote? */ + backend->worker->s->read += len; + + /* Is it an HTTP/1 response? + * This is buggy if we ever see an HTTP/1.10 + */ + if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + major = buffer[5] - '0'; + minor = buffer[7] - '0'; + + /* If not an HTTP/1 message or + * if the status line was > 8192 bytes + */ + if ((major != 1) || (len >= response_field_size - 1)) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "Corrupt status line returned " + "by remote server: ", buffer, NULL)); + } + backasswards = 0; + + keepchar = buffer[12]; + buffer[12] = '\0'; + proxy_status = atoi(&buffer[9]); + apr_table_setn(r->notes, "proxy-status", + apr_pstrdup(r->pool, &buffer[9])); + + if (keepchar != '\0') { + buffer[12] = keepchar; + } else { + /* 2616 requires the space in Status-Line; the origin + * server may have sent one but ap_rgetline_core will + * have stripped it. */ + buffer[12] = ' '; + buffer[13] = '\0'; + } + proxy_status_line = apr_pstrdup(p, &buffer[9]); + + /* The status out of the front is the same as the status coming in + * from the back, until further notice. + */ + r->status = proxy_status; + r->status_line = proxy_status_line; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "Status from backend: %d", proxy_status); + + /* read the headers. */ + /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/ + /* Also, take care with headers with multiple occurrences. */ + + /* First, tuck away all already existing cookies */ + save_table = apr_table_make(r->pool, 2); + apr_table_do(addit_dammit, save_table, r->headers_out, + "Set-Cookie", NULL); + + /* shove the headers direct into r->headers_out */ + rc = ap_proxy_read_headers(r, backend->r, buffer, response_field_size, + origin, &pread_len); + + if (rc != APR_SUCCESS || r->headers_out == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01106) + "bad HTTP/%d.%d header returned by %s (%s)", + major, minor, r->uri, r->method); + backend->close = 1; + /* + * ap_send_error relies on a headers_out to be present. we + * are in a bad position here.. so force everything we send out + * to have nothing to do with the incoming packet + */ + r->headers_out = apr_table_make(r->pool,1); + r->status = HTTP_BAD_GATEWAY; + r->status_line = "bad gateway"; + return r->status; + } + + /* Now, add in the just read cookies */ + apr_table_do(addit_dammit, save_table, r->headers_out, + "Set-Cookie", NULL); + + /* and now load 'em all in */ + if (!apr_is_empty_table(save_table)) { + apr_table_unset(r->headers_out, "Set-Cookie"); + r->headers_out = apr_table_overlay(r->pool, + r->headers_out, + save_table); + } + + /* + * Save a possible Transfer-Encoding header as we need it later for + * ap_http_filter to know where to end. + */ + te = apr_table_get(r->headers_out, "Transfer-Encoding"); + + /* can't have both Content-Length and Transfer-Encoding */ + if (te && apr_table_get(r->headers_out, "Content-Length")) { + /* + * 2616 section 4.4, point 3: "if both Transfer-Encoding + * and Content-Length are received, the latter MUST be + * ignored"; + * + * To help mitigate HTTP Splitting, unset Content-Length + * and shut down the backend server connection + * XXX: We aught to treat such a response as uncachable + */ + apr_table_unset(r->headers_out, "Content-Length"); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01107) + "server %s:%d returned Transfer-Encoding" + " and Content-Length", + backend->hostname, backend->port); + backend->close = 1; + } + + upgrade = apr_table_get(r->headers_out, "Upgrade"); + if (proxy_status == HTTP_SWITCHING_PROTOCOLS) { + if (!upgrade || !req->upgrade || (strcasecmp(req->upgrade, + upgrade) != 0)) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "Unexpected Upgrade: ", + upgrade ? upgrade : "n/a", + " (expecting ", + req->upgrade ? req->upgrade + : "n/a", ")", + NULL)); + } + backend->close = 1; + } + + /* strip connection listed hop-by-hop headers from response */ + toclose = ap_proxy_clear_connection_fn(r, r->headers_out); + if (toclose) { + backend->close = 1; + if (toclose < 0) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Malformed connection header"); + } + } + + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { + ap_set_content_type(r, apr_pstrdup(p, buf)); + } + if (!ap_is_HTTP_INFO(proxy_status)) { + ap_proxy_pre_http_request(origin, backend->r); + } + + /* Clear hop-by-hop headers */ + for (i=0; hop_by_hop_hdrs[i]; ++i) { + apr_table_unset(r->headers_out, hop_by_hop_hdrs[i]); + } + + /* Delete warnings with wrong date */ + r->headers_out = ap_proxy_clean_warnings(p, r->headers_out); + + /* handle Via header in response */ + if (req->sconf->viaopt != via_off + && req->sconf->viaopt != via_block) { + const char *server_name = ap_get_server_name(r); + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, + * then the server name returned by ap_get_server_name() is the + * origin server name (which does make too much sense with Via: headers) + * so we use the proxy vhost's name instead. + */ + if (server_name == r->hostname) + server_name = r->server->server_hostname; + /* create a "Via:" response header entry and merge it */ + apr_table_addn(r->headers_out, "Via", + (req->sconf->viaopt == via_full) + ? apr_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, + req->server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, + req->server_portstr) + ); + } + + /* cancel keepalive if HTTP/1.0 or less */ + if ((major < 1) || (minor < 1)) { + backend->close = 1; + origin->keepalive = AP_CONN_CLOSE; + } + else { + /* + * Keep track of the number of keepalives we processed on this + * connection. + */ + origin->keepalives++; + } + + } else { + /* an http/0.9 response */ + backasswards = 1; + r->status = proxy_status = 200; + r->status_line = "200 OK"; + backend->close = 1; + } + + if (ap_is_HTTP_INFO(proxy_status)) { + const char *policy = NULL; + + /* RFC2616 tells us to forward this. + * + * OTOH, an interim response here may mean the backend + * is playing sillybuggers. The Client didn't ask for + * it within the defined HTTP/1.1 mechanisms, and if + * it's an extension, it may also be unsupported by us. + * + * There's also the possibility that changing existing + * behaviour here might break something. + * + * So let's make it configurable. + * + * We need to force "r->expecting_100 = 1" for RFC behaviour + * otherwise ap_send_interim_response() does nothing when + * the client did not ask for 100-continue. + * + * 101 Switching Protocol has its own configuration which + * shouldn't be interfered by "proxy-interim-response". + */ + if (proxy_status != HTTP_SWITCHING_PROTOCOLS) { + policy = apr_table_get(r->subprocess_env, + "proxy-interim-response"); + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "HTTP: received interim %d response (policy: %s)", + r->status, policy ? policy : "n/a"); + if (!policy + || (!strcasecmp(policy, "RFC") + && (proxy_status != HTTP_CONTINUE + || (r->expecting_100 = 1)))) { + switch (proxy_status) { + case HTTP_SWITCHING_PROTOCOLS: + AP_DEBUG_ASSERT(upgrade != NULL); + apr_table_setn(r->headers_out, "Connection", "Upgrade"); + apr_table_setn(r->headers_out, "Upgrade", + apr_pstrdup(p, upgrade)); + break; + } + ap_send_interim_response(r, 1); + } + /* FIXME: refine this to be able to specify per-response-status + * policies and maybe also add option to bail out with 502 + */ + else if (strcasecmp(policy, "Suppress")) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01108) + "undefined proxy interim response policy"); + } + interim_response++; + } + else { + interim_response = 0; + } + + /* If we still do 100-continue (end-to-end or ping), either the + * current response is the expected "100 Continue" and we are done + * with this mode, or this is another interim response and we'll wait + * for the next one, or this is a final response and hence the backend + * did not honor our expectation. + */ + if (do_100_continue && (!interim_response + || proxy_status == HTTP_CONTINUE)) { + /* RFC 7231 - Section 5.1.1 - Expect - Requirement for servers + * A server that responds with a final status code before + * reading the entire message body SHOULD indicate in that + * response whether it intends to close the connection or + * continue reading and discarding the request message. + * + * So, if this response is not an interim 100 Continue, we can + * avoid sending the request body if the backend responded with + * "Connection: close" or HTTP < 1.1, and either let the core + * discard it or the caller try another balancer member with the + * same body (given status 503, though not implemented yet). + */ + int do_send_body = (proxy_status == HTTP_CONTINUE + || (!toclose && major > 0 && minor > 0)); + + /* Reset to old timeout iff we've adjusted it. */ + if (worker->s->ping_timeout_set) { + apr_socket_timeout_set(backend->sock, old_timeout); + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10153) + "HTTP: %s100 continue sent by %pI (%s): " + "%ssending body (response: HTTP/%i.%i %s)", + proxy_status != HTTP_CONTINUE ? "no " : "", + backend->addr, + backend->hostname ? backend->hostname : "", + do_send_body ? "" : "not ", + major, minor, proxy_status_line); + + if (do_send_body) { + status = send_continue_body(req); + if (status != OK) { + return status; + } + } + else { + /* If we don't read the client connection any further, since + * there are pending data it should be "Connection: close"d to + * prevent reuse. We don't exactly c->keepalive = AP_CONN_CLOSE + * here though, because error_override or a potential retry on + * another backend could finally read that data and finalize + * the request processing, making keep-alive possible. So what + * we do is leaving r->expecting_100 alone, ap_set_keepalive() + * will do the right thing according to the final response and + * any later update of r->expecting_100. + */ + } + + /* Once only! */ + do_100_continue = 0; + } + + if (proxy_status == HTTP_SWITCHING_PROTOCOLS) { + apr_status_t rv; + proxy_tunnel_rec *tunnel; + apr_interval_time_t client_timeout = -1, + backend_timeout = -1; + + /* If we didn't send the full body yet, do it now */ + if (do_100_continue) { + r->expecting_100 = 0; + status = send_continue_body(req); + if (status != OK) { + return status; + } + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10239) + "HTTP: tunneling protocol %s", upgrade); + + rv = ap_proxy_tunnel_create(&tunnel, r, origin, upgrade); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10240) + "can't create tunnel for %s", upgrade); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Set timeout to the highest configured for client or backend */ + apr_socket_timeout_get(backend->sock, &backend_timeout); + apr_socket_timeout_get(ap_get_conn_socket(c), &client_timeout); + if (backend_timeout >= 0 && backend_timeout > client_timeout) { + tunnel->timeout = backend_timeout; + } + else { + tunnel->timeout = client_timeout; + } + + /* Let proxy tunnel forward everything */ + status = ap_proxy_tunnel_run(tunnel); + + /* We are done with both connections */ + return DONE; + } + + if (interim_response) { + /* Already forwarded above, read next response */ + continue; + } + + /* Moved the fixups of Date headers and those affected by + * ProxyPassReverse/etc from here to ap_proxy_read_headers + */ + + /* PR 41646: get HEAD right with ProxyErrorOverride */ + if (ap_proxy_should_override(dconf, proxy_status)) { + if (proxy_status == HTTP_UNAUTHORIZED) { + const char *buf; + const char *wa = "WWW-Authenticate"; + if ((buf = apr_table_get(r->headers_out, wa))) { + apr_table_set(r->err_headers_out, wa, buf); + } else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01109) + "origin server sent 401 without " + "WWW-Authenticate header"); + } + } + + /* clear r->status for override error, otherwise ErrorDocument + * thinks that this is a recursive error, and doesn't find the + * custom error page + */ + r->status = HTTP_OK; + /* Discard body, if one is expected */ + if (!r->header_only && !AP_STATUS_IS_HEADER_ONLY(proxy_status)) { + const char *tmp; + /* Add minimal headers needed to allow http_in filter + * detecting end of body without waiting for a timeout. */ + if ((tmp = apr_table_get(r->headers_out, "Transfer-Encoding"))) { + apr_table_set(backend->r->headers_in, "Transfer-Encoding", tmp); + } + else if ((tmp = apr_table_get(r->headers_out, "Content-Length"))) { + apr_table_set(backend->r->headers_in, "Content-Length", tmp); + } + else if (te) { + apr_table_set(backend->r->headers_in, "Transfer-Encoding", te); + } + ap_discard_request_body(backend->r); + } + /* + * prevent proxy_handler() from treating this as an + * internal error. + */ + apr_table_setn(r->notes, "proxy-error-override", "1"); + return proxy_status; + } + + /* Forward back Upgrade header if it matches the configured one(s), it + * may be an HTTP_UPGRADE_REQUIRED response or some other status where + * Upgrade makes sense to negotiate the protocol by other means. + */ + if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade, + (*req->proto == 'w') + ? "WebSocket" : NULL)) { + apr_table_setn(r->headers_out, "Connection", "Upgrade"); + apr_table_setn(r->headers_out, "Upgrade", apr_pstrdup(p, upgrade)); + } + + r->sent_bodyct = 1; + /* + * Is it an HTTP/0.9 response or did we maybe preread the 1st line of + * the response? If so, load the extra data. These are 2 mutually + * exclusive possibilities, that just happen to require very + * similar behavior. + */ + if (backasswards || pread_len) { + apr_ssize_t cntr = (apr_ssize_t)pread_len; + if (backasswards) { + /*@@@FIXME: + * At this point in response processing of a 0.9 response, + * we don't know yet whether data is binary or not. + * mod_charset_lite will get control later on, so it cannot + * decide on the conversion of this buffer full of data. + * However, chances are that we are not really talking to an + * HTTP/0.9 server, but to some different protocol, therefore + * the best guess IMHO is to always treat the buffer as "text/x": + */ + ap_xlate_proto_to_ascii(buffer, len); + cntr = (apr_ssize_t)len; + } + e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + + /* send body - but only if a body is expected */ + if ((!r->header_only) && /* not HEAD request */ + (proxy_status != HTTP_NO_CONTENT) && /* not 204 */ + (proxy_status != HTTP_NOT_MODIFIED)) { /* not 304 */ + apr_read_type_e mode; + int finish; + + /* We need to copy the output headers and treat them as input + * headers as well. BUT, we need to do this before we remove + * TE, so that they are preserved accordingly for + * ap_http_filter to know where to end. + */ + backend->r->headers_in = apr_table_clone(backend->r->pool, r->headers_out); + /* + * Restore Transfer-Encoding header from response if we saved + * one before and there is none left. We need it for the + * ap_http_filter. See above. + */ + if (te && !apr_table_get(backend->r->headers_in, "Transfer-Encoding")) { + apr_table_add(backend->r->headers_in, "Transfer-Encoding", te); + } + + apr_table_unset(r->headers_out,"Transfer-Encoding"); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "start body send"); + + /* read the body, pass it to the output filters */ + + /* Handle the case where the error document is itself reverse + * proxied and was successful. We must maintain any previous + * error status so that an underlying error (eg HTTP_NOT_FOUND) + * doesn't become an HTTP_OK. + */ + if (ap_proxy_should_override(dconf, original_status)) { + r->status = original_status; + r->status_line = original_status_line; + } + + mode = APR_NONBLOCK_READ; + finish = FALSE; + do { + apr_off_t readbytes; + apr_status_t rv; + + rv = ap_get_brigade(backend->r->input_filters, bb, + AP_MODE_READBYTES, mode, + req->sconf->io_buffer_size); + + /* ap_get_brigade will return success with an empty brigade + * for a non-blocking read which would block: */ + if (mode == APR_NONBLOCK_READ + && (APR_STATUS_IS_EAGAIN(rv) + || (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)))) { + /* flush to the client and switch to blocking mode */ + e = apr_bucket_flush_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + if (ap_pass_brigade(r->output_filters, bb) + || c->aborted) { + backend->close = 1; + break; + } + apr_brigade_cleanup(bb); + mode = APR_BLOCK_READ; + continue; + } + else if (rv == APR_EOF) { + backend->close = 1; + break; + } + else if (rv != APR_SUCCESS) { + /* In this case, we are in real trouble because + * our backend bailed on us. Pass along a 502 error + * error bucket + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01110) + "error reading response"); + apr_brigade_cleanup(bb); + ap_proxy_backend_broke(r, bb); + ap_pass_brigade(r->output_filters, bb); + backend_broke = 1; + backend->close = 1; + break; + } + /* next time try a non-blocking read */ + mode = APR_NONBLOCK_READ; + + if (!apr_is_empty_table(backend->r->trailers_in)) { + apr_table_do(add_trailers, r->trailers_out, + backend->r->trailers_in, NULL); + apr_table_clear(backend->r->trailers_in); + } + + apr_brigade_length(bb, 0, &readbytes); + backend->worker->s->read += readbytes; +#if DEBUGGING + { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01111) + "readbytes: %#x", readbytes); + } +#endif + /* sanity check */ + if (APR_BRIGADE_EMPTY(bb)) { + break; + } + + /* Switch the allocator lifetime of the buckets */ + ap_proxy_buckets_lifetime_transform(r, bb, pass_bb); + + /* found the last brigade? */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(pass_bb))) { + + /* signal that we must leave */ + finish = TRUE; + + /* the brigade may contain transient buckets that contain + * data that lives only as long as the backend connection. + * Force a setaside so these transient buckets become heap + * buckets that live as long as the request. + */ + for (e = APR_BRIGADE_FIRST(pass_bb); e + != APR_BRIGADE_SENTINEL(pass_bb); e + = APR_BUCKET_NEXT(e)) { + apr_bucket_setaside(e, r->pool); + } + + /* finally it is safe to clean up the brigade from the + * connection pool, as we have forced a setaside on all + * buckets. + */ + apr_brigade_cleanup(bb); + + /* make sure we release the backend connection as soon + * as we know we are done, so that the backend isn't + * left waiting for a slow client to eventually + * acknowledge the data. + */ + ap_proxy_release_connection(backend->worker->s->scheme, + backend, r->server); + /* Ensure that the backend is not reused */ + req->backend = NULL; + + } + + /* try send what we read */ + if (ap_pass_brigade(r->output_filters, pass_bb) != APR_SUCCESS + || c->aborted) { + /* Ack! Phbtt! Die! User aborted! */ + /* Only close backend if we haven't got all from the + * backend. Furthermore if req->backend is NULL it is no + * longer safe to fiddle around with backend as it might + * be already in use by another thread. + */ + if (req->backend) { + /* this causes socket close below */ + req->backend->close = 1; + } + finish = TRUE; + } + + /* make sure we always clean up after ourselves */ + apr_brigade_cleanup(pass_bb); + apr_brigade_cleanup(bb); + + } while (!finish); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "end body send"); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "header only"); + + /* make sure we release the backend connection as soon + * as we know we are done, so that the backend isn't + * left waiting for a slow client to eventually + * acknowledge the data. + */ + ap_proxy_release_connection(backend->worker->s->scheme, + backend, r->server); + /* Ensure that the backend is not reused */ + req->backend = NULL; + + /* Pass EOS bucket down the filter chain. */ + e = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + ap_pass_brigade(r->output_filters, bb); + + apr_brigade_cleanup(bb); + } + } while (interim_response && (interim_response < AP_MAX_INTERIM_RESPONSES)); + + /* We have to cleanup bb brigade, because buckets inserted to it could be + * created from scpool and this pool can be freed before this brigade. */ + apr_brigade_cleanup(bb); + + /* See define of AP_MAX_INTERIM_RESPONSES for why */ + if (interim_response >= AP_MAX_INTERIM_RESPONSES) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_psprintf(p, + "Too many (%d) interim responses from origin server", + interim_response)); + } + + /* If our connection with the client is to be aborted, return DONE. */ + if (c->aborted || backend_broke) { + return DONE; + } + + return OK; +} + +static +apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r, + proxy_conn_rec *backend) +{ + ap_proxy_release_connection(scheme, backend, r->server); + return OK; +} + +/* + * This handles http:// URLs, and other URLs using a remote proxy over http + * If proxyhost is NULL, then contact the server directly, otherwise + * go via the proxy. + * Note that if a proxy is used, then URLs other than http: can be accessed, + * also, if we have trouble which is clearly specific to the proxy, then + * we return DECLINED so that we can try another proxy. (Or the direct + * route.) + */ +static int proxy_http_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + int status; + const char *scheme; + const char *u = url; + proxy_http_req_t *req = NULL; + proxy_conn_rec *backend = NULL; + apr_bucket_brigade *input_brigade = NULL; + int is_ssl = 0; + conn_rec *c = r->connection; + proxy_dir_conf *dconf; + int retry = 0; + char *locurl = url; + int toclose = 0; + /* + * Use a shorter-lived pool to reduce memory usage + * and avoid a memory leak + */ + apr_pool_t *p = r->pool; + apr_uri_t *uri; + + scheme = get_url_scheme(&u, &is_ssl); + if (!scheme && proxyname && strncasecmp(url, "ftp:", 4) == 0) { + u = url + 4; + scheme = "ftp"; + is_ssl = 0; + } + if (!scheme || u[0] != '/' || u[1] != '/' || u[2] == '\0') { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01113) + "HTTP: declining URL %s", url); + return DECLINED; /* only interested in HTTP, WS or FTP via proxy */ + } + if (is_ssl && !ap_ssl_has_outgoing_handlers()) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01112) + "HTTP: declining URL %s (mod_ssl not configured?)", url); + return DECLINED; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "HTTP: serving URL %s", url); + + /* create space for state information */ + if ((status = ap_proxy_acquire_connection(scheme, &backend, + worker, r->server)) != OK) { + return status; + } + + backend->is_ssl = is_ssl; + + req = apr_pcalloc(p, sizeof(*req)); + req->p = p; + req->r = r; + req->sconf = conf; + req->worker = worker; + req->backend = backend; + req->proto = scheme; + req->bucket_alloc = c->bucket_alloc; + req->rb_method = RB_INIT; + + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { + req->force10 = 1; + } + else if (*worker->s->upgrade || *req->proto == 'w') { + /* Forward Upgrade header if it matches the configured one(s), + * the default being "WebSocket" for ws[s] schemes. + */ + const char *upgrade = apr_table_get(r->headers_in, "Upgrade"); + if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade, + (*req->proto == 'w') + ? "WebSocket" : NULL)) { + req->upgrade = upgrade; + } + } + + /* We possibly reuse input data prefetched in previous call(s), e.g. for a + * balancer fallback scenario, and in this case the 100 continue settings + * should be consistent between balancer members. If not, we need to ignore + * Proxy100Continue on=>off once we tried to prefetch already, otherwise + * the HTTP_IN filter won't send 100 Continue for us anymore, and we might + * deadlock with the client waiting for each other. Note that off=>on is + * not an issue because in this case r->expecting_100 is false (the 100 + * Continue is out already), but we make sure that prefetch will be + * nonblocking to avoid passing more time there. + */ + apr_pool_userdata_get((void **)&input_brigade, "proxy-req-input", p); + + /* Should we handle end-to-end or ping 100-continue? */ + if (!req->force10 + && ((r->expecting_100 && (dconf->forward_100_continue || input_brigade)) + || PROXY_SHOULD_PING_100_CONTINUE(worker, r))) { + /* Tell ap_proxy_create_hdrbrgd() to preserve/add the Expect header */ + apr_table_setn(r->notes, "proxy-100-continue", "1"); + req->do_100_continue = 1; + } + + /* Should we block while prefetching the body or try nonblocking and flush + * data to the backend ASAP? + */ + if (input_brigade + || req->do_100_continue + || apr_table_get(r->subprocess_env, + "proxy-prefetch-nonblocking")) { + req->prefetch_nonblocking = 1; + } + + /* + * In the case that we are handling a reverse proxy connection and this + * is not a request that is coming over an already kept alive connection + * with the client, do NOT reuse the connection to the backend, because + * we cannot forward a failure to the client in this case as the client + * does NOT expect this in this situation. + * Yes, this creates a performance penalty. + */ + if ((r->proxyreq == PROXYREQ_REVERSE) && (!c->keepalives) + && (apr_table_get(r->subprocess_env, "proxy-initial-not-pooled"))) { + backend->close = 1; + } + + /* Step One: Determine Who To Connect To */ + uri = apr_palloc(p, sizeof(*uri)); + if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend, + uri, &locurl, proxyname, + proxyport, req->server_portstr, + sizeof(req->server_portstr)))) + goto cleanup; + + /* The header is always (re-)built since it depends on worker settings, + * but the body can be fetched only once (even partially), so it's saved + * in between proxy_http_handler() calls should we come back here. + */ + req->header_brigade = apr_brigade_create(p, req->bucket_alloc); + if (input_brigade == NULL) { + input_brigade = apr_brigade_create(p, req->bucket_alloc); + apr_pool_userdata_setn(input_brigade, "proxy-req-input", NULL, p); + } + req->input_brigade = input_brigade; + + /* Prefetch (nonlocking) the request body so to increase the chance to get + * the whole (or enough) body and determine Content-Length vs chunked or + * spooled. By doing this before connecting or reusing the backend, we want + * to minimize the delay between this connection is considered alive and + * the first bytes sent (should the client's link be slow or some input + * filter retain the data). This is a best effort to prevent the backend + * from closing (from under us) what it thinks is an idle connection, hence + * to reduce to the minimum the unavoidable local is_socket_connected() vs + * remote keepalive race condition. + */ + if ((status = ap_proxy_http_prefetch(req, uri, locurl)) != OK) + goto cleanup; + + /* We need to reset backend->close now, since ap_proxy_http_prefetch() set + * it to disable the reuse of the connection *after* this request (no keep- + * alive), not to close any reusable connection before this request. However + * assure what is expected later by using a local flag and do the right thing + * when ap_proxy_connect_backend() below provides the connection to close. + */ + toclose = backend->close; + backend->close = 0; + + while (retry < 2) { + if (retry) { + char *newurl = url; + + /* Step One (again): (Re)Determine Who To Connect To */ + if ((status = ap_proxy_determine_connection(p, r, conf, worker, + backend, uri, &newurl, proxyname, proxyport, + req->server_portstr, sizeof(req->server_portstr)))) + break; + + /* The code assumes locurl is not changed during the loop, or + * ap_proxy_http_prefetch() would have to be called every time, + * and header_brigade be changed accordingly... + */ + AP_DEBUG_ASSERT(strcmp(newurl, locurl) == 0); + } + + /* Step Two: Make the Connection */ + if (ap_proxy_check_connection(scheme, backend, r->server, 1, + PROXY_CHECK_CONN_EMPTY) + && ap_proxy_connect_backend(scheme, backend, worker, + r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01114) + "HTTP: failed to make connection to backend: %s", + backend->hostname); + status = HTTP_SERVICE_UNAVAILABLE; + break; + } + + /* Step Three: Create conn_rec */ + if ((status = ap_proxy_connection_create_ex(scheme, backend, r)) != OK) + break; + req->origin = backend->connection; + + /* Don't recycle the connection if prefetch (above) told not to do so */ + if (toclose) { + backend->close = 1; + req->origin->keepalive = AP_CONN_CLOSE; + } + + /* Step Four: Send the Request + * On the off-chance that we forced a 100-Continue as a + * kinda HTTP ping test, allow for retries + */ + status = ap_proxy_http_request(req); + if (status != OK) { + if (req->do_100_continue && status == HTTP_SERVICE_UNAVAILABLE) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, status, r, APLOGNO(01115) + "HTTP: 100-Continue failed to %pI (%s:%d)", + worker->cp->addr, worker->s->hostname_ex, + (int)worker->s->port); + backend->close = 1; + retry++; + continue; + } + break; + } + + /* Step Five: Receive the Response... Fall thru to cleanup */ + status = ap_proxy_http_process_response(req); + + break; + } + + /* Step Six: Clean Up */ +cleanup: + if (req->backend) { + if (status != OK) + req->backend->close = 1; + ap_proxy_http_cleanup(scheme, r, req->backend); + } + return status; +} + +/* post_config hook: */ +static int proxy_http_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + + /* proxy_http_post_config() will be called twice during startup. So, don't + * set up the static data the 1st time through. */ + if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { + return OK; + } + + ap_proxy_clear_connection_fn = + APR_RETRIEVE_OPTIONAL_FN(ap_proxy_clear_connection); + if (!ap_proxy_clear_connection_fn) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02477) + "mod_proxy must be loaded for mod_proxy_http"); + return !OK; + } + + return OK; +} + +static void ap_proxy_http_register_hook(apr_pool_t *p) +{ + ap_hook_post_config(proxy_http_post_config, NULL, NULL, APR_HOOK_MIDDLE); + proxy_hook_scheme_handler(proxy_http_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(proxy_http_canon, NULL, NULL, APR_HOOK_FIRST); + warn_rx = ap_pregcomp(p, "[0-9]{3}[ \t]+[^ \t]+[ \t]+\"[^\"]*\"([ \t]+\"([^\"]+)\")?", 0); +} + +AP_DECLARE_MODULE(proxy_http) = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_http_register_hook/* register hooks */ +}; + diff --git a/modules/proxy/mod_proxy_http.dep b/modules/proxy/mod_proxy_http.dep new file mode 100644 index 0000000..35c91b7 --- /dev/null +++ b/modules/proxy/mod_proxy_http.dep @@ -0,0 +1,73 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_http.mak + +.\mod_proxy_http.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_http.dsp b/modules/proxy/mod_proxy_http.dsp new file mode 100644 index 0000000..7d48815 --- /dev/null +++ b/modules/proxy/mod_proxy_http.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_http - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_http.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_http.mak" CFG="mod_proxy_http - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_http - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_http - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_http_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_http.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_http_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_http.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_http - Win32 Release" +# Name "mod_proxy_http - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_http.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_http.mak b/modules/proxy/mod_proxy_http.mak new file mode 100644 index 0000000..c381187 --- /dev/null +++ b/modules/proxy/mod_proxy_http.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_http.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_http - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_http - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_http - Win32 Release" && "$(CFG)" != "mod_proxy_http - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_http.mak" CFG="mod_proxy_http - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_http - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_http - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_http.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_http.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_http.obj" + -@erase "$(INTDIR)\mod_proxy_http.res" + -@erase "$(INTDIR)\mod_proxy_http_src.idb" + -@erase "$(INTDIR)\mod_proxy_http_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_http.exp" + -@erase "$(OUTDIR)\mod_proxy_http.lib" + -@erase "$(OUTDIR)\mod_proxy_http.pdb" + -@erase "$(OUTDIR)\mod_proxy_http.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_http_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_http.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_http.pdb" /debug /out:"$(OUTDIR)\mod_proxy_http.so" /implib:"$(OUTDIR)\mod_proxy_http.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_http.obj" \ + "$(INTDIR)\mod_proxy_http.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_http.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_http.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_http.so" + if exist .\Release\mod_proxy_http.so.manifest mt.exe -manifest .\Release\mod_proxy_http.so.manifest -outputresource:.\Release\mod_proxy_http.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_http.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_http.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_http.obj" + -@erase "$(INTDIR)\mod_proxy_http.res" + -@erase "$(INTDIR)\mod_proxy_http_src.idb" + -@erase "$(INTDIR)\mod_proxy_http_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_http.exp" + -@erase "$(OUTDIR)\mod_proxy_http.lib" + -@erase "$(OUTDIR)\mod_proxy_http.pdb" + -@erase "$(OUTDIR)\mod_proxy_http.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_http_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_http.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_http.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_http.pdb" /debug /out:"$(OUTDIR)\mod_proxy_http.so" /implib:"$(OUTDIR)\mod_proxy_http.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_http.obj" \ + "$(INTDIR)\mod_proxy_http.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_http.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_http.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_http.so" + if exist .\Debug\mod_proxy_http.so.manifest mt.exe -manifest .\Debug\mod_proxy_http.so.manifest -outputresource:.\Debug\mod_proxy_http.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_http.dep") +!INCLUDE "mod_proxy_http.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_http.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" || "$(CFG)" == "mod_proxy_http - Win32 Debug" +SOURCE=.\mod_proxy_http.c + +"$(INTDIR)\mod_proxy_http.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + + +"$(INTDIR)\mod_proxy_http.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_http.so" /d LONG_NAME="proxy_http_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_scgi.c b/modules/proxy/mod_proxy_scgi.c new file mode 100644 index 0000000..5444a5c --- /dev/null +++ b/modules/proxy/mod_proxy_scgi.c @@ -0,0 +1,676 @@ +/* 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. + */ + +/* + * mod_proxy_scgi.c + * Proxy backend module for the SCGI protocol + * (http://python.ca/scgi/protocol.txt) + * + * Andr� Malo (nd/perlig.de), August 2007 + */ + +#define APR_WANT_MEMFUNC +#define APR_WANT_STRFUNC +#include "apr_strings.h" +#include "ap_hooks.h" +#include "apr_optional_hooks.h" +#include "apr_buckets.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" +#include "util_script.h" + +#include "mod_proxy.h" +#include "scgi.h" + + +#define SCHEME "scgi" +#define PROXY_FUNCTION "SCGI" +#define SCGI_MAGIC "SCGI" +#define SCGI_PROTOCOL_VERSION "1" + +/* just protect from typos */ +#define CONTENT_LENGTH "CONTENT_LENGTH" +#define GATEWAY_INTERFACE "GATEWAY_INTERFACE" + +module AP_MODULE_DECLARE_DATA proxy_scgi_module; + + +typedef enum { + scgi_internal_redirect, + scgi_sendfile +} scgi_request_type; + +typedef struct { + const char *location; /* target URL */ + scgi_request_type type; /* type of request */ +} scgi_request_config; + +const char *scgi_sendfile_off = "off"; +const char *scgi_sendfile_on = "X-Sendfile"; +const char *scgi_internal_redirect_off = "off"; +const char *scgi_internal_redirect_on = "Location"; + +typedef struct { + const char *sendfile; + const char *internal_redirect; +} scgi_config; + + +/* + * We create our own bucket type, which is actually derived (c&p) from the + * socket bucket. + * Maybe some time this should be made more abstract (like passing an + * interception function to read or something) and go into the ap_ or + * even apr_ namespace. + */ + +typedef struct { + apr_socket_t *sock; + apr_off_t *counter; +} socket_ex_data; + +static apr_bucket *bucket_socket_ex_create(socket_ex_data *data, + apr_bucket_alloc_t *list); + + +static apr_status_t bucket_socket_ex_read(apr_bucket *a, const char **str, + apr_size_t *len, + apr_read_type_e block) +{ + socket_ex_data *data = a->data; + apr_socket_t *p = data->sock; + char *buf; + apr_status_t rv; + apr_interval_time_t timeout; + + if (block == APR_NONBLOCK_READ) { + apr_socket_timeout_get(p, &timeout); + apr_socket_timeout_set(p, 0); + } + + *str = NULL; + *len = APR_BUCKET_BUFF_SIZE; + buf = apr_bucket_alloc(*len, a->list); + + rv = apr_socket_recv(p, buf, len); + + if (block == APR_NONBLOCK_READ) { + apr_socket_timeout_set(p, timeout); + } + + if (rv != APR_SUCCESS && rv != APR_EOF) { + apr_bucket_free(buf); + return rv; + } + + if (*len > 0) { + apr_bucket_heap *h; + + /* count for stats */ + *data->counter += *len; + + /* Change the current bucket to refer to what we read */ + a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); + h = a->data; + h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ + *str = buf; + APR_BUCKET_INSERT_AFTER(a, bucket_socket_ex_create(data, a->list)); + } + else { + apr_bucket_free(buf); + a = apr_bucket_immortal_make(a, "", 0); + *str = a->data; + } + return APR_SUCCESS; +} + +static const apr_bucket_type_t bucket_type_socket_ex = { + "SOCKET_EX", 5, APR_BUCKET_DATA, + apr_bucket_destroy_noop, + bucket_socket_ex_read, + apr_bucket_setaside_notimpl, + apr_bucket_split_notimpl, + apr_bucket_copy_notimpl +}; + +static apr_bucket *bucket_socket_ex_make(apr_bucket *b, socket_ex_data *data) +{ + b->type = &bucket_type_socket_ex; + b->length = (apr_size_t)(-1); + b->start = -1; + b->data = data; + return b; +} + +static apr_bucket *bucket_socket_ex_create(socket_ex_data *data, + apr_bucket_alloc_t *list) +{ + apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); + + APR_BUCKET_INIT(b); + b->free = apr_bucket_free; + b->list = list; + return bucket_socket_ex_make(b, data); +} + + +/* + * Canonicalize scgi-like URLs. + */ +static int scgi_canon(request_rec *r, char *url) +{ + char *host, sport[sizeof(":65535")]; + const char *err, *path; + apr_port_t port, def_port; + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + if (ap_cstr_casecmpn(url, SCHEME "://", sizeof(SCHEME) + 2)) { + return DECLINED; + } + url += sizeof(SCHEME); /* Keep slashes */ + + port = def_port = SCGI_DEF_PORT; + + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00857) + "error parsing URL %s: %s", url, err); + return HTTP_BAD_REQUEST; + } + + if (port != def_port) { + apr_snprintf(sport, sizeof(sport), ":%u", port); + } + else { + sport[0] = '\0'; + } + + if (ap_strchr(host, ':')) { /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + + r->filename = apr_pstrcat(r->pool, "proxy:" SCHEME "://", host, sport, "/", + path, NULL); + + if (apr_table_get(r->subprocess_env, "proxy-scgi-pathinfo")) { + r->path_info = apr_pstrcat(r->pool, "/", path, NULL); + } + + return OK; +} + + +/* + * Send a block of data, ensure, everything is sent + */ +static int sendall(proxy_conn_rec *conn, const char *buf, apr_size_t length, + request_rec *r) +{ + apr_status_t rv; + apr_size_t written; + + while (length > 0) { + written = length; + if ((rv = apr_socket_send(conn->sock, buf, &written)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00858) + "sending data to %s:%u failed", + conn->hostname, conn->port); + return HTTP_SERVICE_UNAVAILABLE; + } + + /* count for stats */ + conn->worker->s->transferred += written; + buf += written; + length -= written; + } + + return OK; +} + + +/* + * Send SCGI header block + */ +static int send_headers(request_rec *r, proxy_conn_rec *conn) +{ + char *buf, *cp, *bodylen; + const char *ns_len; + const apr_array_header_t *env_table; + const apr_table_entry_t *env; + int j; + apr_size_t len, bodylen_size; + apr_size_t headerlen = sizeof(CONTENT_LENGTH) + + sizeof(SCGI_MAGIC) + + sizeof(SCGI_PROTOCOL_VERSION); + + ap_add_common_vars(r); + ap_add_cgi_vars(r); + + /* + * The header blob basically takes the environment and concatenates + * keys and values using 0 bytes. There are special treatments here: + * - GATEWAY_INTERFACE and SCGI_MAGIC are dropped + * - CONTENT_LENGTH is always set and must be sent as the very first + * variable + * + * Additionally it's wrapped into a so-called netstring (see SCGI spec) + */ + env_table = apr_table_elts(r->subprocess_env); + env = (apr_table_entry_t *)env_table->elts; + for (j = 0; j < env_table->nelts; ++j) { + if ( (!strcmp(env[j].key, GATEWAY_INTERFACE)) + || (!strcmp(env[j].key, CONTENT_LENGTH)) + || (!strcmp(env[j].key, SCGI_MAGIC))) { + continue; + } + headerlen += strlen(env[j].key) + strlen(env[j].val) + 2; + } + bodylen = apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->remaining); + bodylen_size = strlen(bodylen) + 1; + headerlen += bodylen_size; + + ns_len = apr_psprintf(r->pool, "%" APR_SIZE_T_FMT ":", headerlen); + len = strlen(ns_len); + headerlen += len + 1; /* 1 == , */ + cp = buf = apr_palloc(r->pool, headerlen); + memcpy(cp, ns_len, len); + cp += len; + + memcpy(cp, CONTENT_LENGTH, sizeof(CONTENT_LENGTH)); + cp += sizeof(CONTENT_LENGTH); + memcpy(cp, bodylen, bodylen_size); + cp += bodylen_size; + memcpy(cp, SCGI_MAGIC, sizeof(SCGI_MAGIC)); + cp += sizeof(SCGI_MAGIC); + memcpy(cp, SCGI_PROTOCOL_VERSION, sizeof(SCGI_PROTOCOL_VERSION)); + cp += sizeof(SCGI_PROTOCOL_VERSION); + + for (j = 0; j < env_table->nelts; ++j) { + if ( (!strcmp(env[j].key, GATEWAY_INTERFACE)) + || (!strcmp(env[j].key, CONTENT_LENGTH)) + || (!strcmp(env[j].key, SCGI_MAGIC))) { + continue; + } + len = strlen(env[j].key) + 1; + memcpy(cp, env[j].key, len); + cp += len; + len = strlen(env[j].val) + 1; + memcpy(cp, env[j].val, len); + cp += len; + } + *cp++ = ','; + + return sendall(conn, buf, headerlen, r); +} + + +/* + * Send request body (if any) + */ +static int send_request_body(request_rec *r, proxy_conn_rec *conn) +{ + if (ap_should_client_block(r)) { + char *buf = apr_palloc(r->pool, AP_IOBUFSIZE); + int status; + long readlen; + + readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE); + while (readlen > 0) { + status = sendall(conn, buf, (apr_size_t)readlen, r); + if (status != OK) { + return HTTP_SERVICE_UNAVAILABLE; + } + readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE); + } + if (readlen == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00859) + "receiving request body failed"); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + return OK; +} + + +/* + * Fetch response from backend and pass back to the front + */ +static int pass_response(request_rec *r, proxy_conn_rec *conn) +{ + apr_bucket_brigade *bb; + apr_bucket *b; + const char *location; + scgi_config *conf; + socket_ex_data *sock_data; + int status; + + sock_data = apr_palloc(r->pool, sizeof(*sock_data)); + sock_data->sock = conn->sock; + sock_data->counter = &conn->worker->s->read; + + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + b = bucket_socket_ex_create(sock_data, r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + b = apr_bucket_eos_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + + status = ap_scan_script_header_err_brigade_ex(r, bb, NULL, + APLOG_MODULE_INDEX); + if (status != OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00860) + "error reading response headers from %s:%u", + conn->hostname, conn->port); + r->status_line = NULL; + apr_brigade_destroy(bb); + return status; + } + + conf = ap_get_module_config(r->per_dir_config, &proxy_scgi_module); + if (conf->sendfile && conf->sendfile != scgi_sendfile_off) { + short err = 1; + + location = apr_table_get(r->err_headers_out, conf->sendfile); + if (!location) { + err = 0; + location = apr_table_get(r->headers_out, conf->sendfile); + } + if (location) { + scgi_request_config *req_conf = apr_palloc(r->pool, + sizeof(*req_conf)); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00861) + "Found %s: %s - preparing subrequest.", + conf->sendfile, location); + + if (err) { + apr_table_unset(r->err_headers_out, conf->sendfile); + } + else { + apr_table_unset(r->headers_out, conf->sendfile); + } + req_conf->location = location; + req_conf->type = scgi_sendfile; + ap_set_module_config(r->request_config, &proxy_scgi_module, + req_conf); + apr_brigade_destroy(bb); + return OK; + } + } + + if (r->status == HTTP_OK + && (!conf->internal_redirect /* default === On */ + || conf->internal_redirect != scgi_internal_redirect_off)) { + short err = 1; + const char *location_header = conf->internal_redirect ? + conf->internal_redirect : scgi_internal_redirect_on; + + location = apr_table_get(r->err_headers_out, location_header); + if (!location) { + err = 0; + location = apr_table_get(r->headers_out, location_header); + } + if (location && *location == '/') { + scgi_request_config *req_conf = apr_palloc(r->pool, + sizeof(*req_conf)); + if (ap_cstr_casecmp(location_header, "Location")) { + if (err) { + apr_table_unset(r->err_headers_out, location_header); + } + else { + apr_table_unset(r->headers_out, location_header); + } + } + req_conf->location = location; + req_conf->type = scgi_internal_redirect; + ap_set_module_config(r->request_config, &proxy_scgi_module, + req_conf); + apr_brigade_destroy(bb); + return OK; + } + } + + if (ap_pass_brigade(r->output_filters, bb)) { + return AP_FILTER_ERROR; + } + + return OK; +} + +/* + * Internal redirect / subrequest handler, working on request_status hook + */ +static int scgi_request_status(int *status, request_rec *r) +{ + scgi_request_config *req_conf; + + if ( (*status == OK) + && (req_conf = ap_get_module_config(r->request_config, + &proxy_scgi_module))) { + switch (req_conf->type) { + case scgi_internal_redirect: + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00862) + "Internal redirect to %s", req_conf->location); + + r->status_line = NULL; + if (r->method_number != M_GET) { + /* keep HEAD, which is passed around as M_GET, too */ + r->method = "GET"; + r->method_number = M_GET; + } + apr_table_unset(r->headers_in, "Content-Length"); + ap_internal_redirect_handler(req_conf->location, r); + return OK; + /* break; */ + + case scgi_sendfile: + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00863) + "File subrequest to %s", req_conf->location); + do { + request_rec *rr; + + rr = ap_sub_req_lookup_file(req_conf->location, r, + r->output_filters); + if (rr->status == HTTP_OK && rr->finfo.filetype != APR_NOFILE) { + /* + * We don't touch Content-Length here. It might be + * borked (there's plenty of room for a race condition). + * Either the backend sets it or it's gonna be chunked. + */ + ap_run_sub_req(rr); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00864) + "Subrequest to file '%s' not possible. " + "(rr->status=%d, rr->finfo.filetype=%d)", + req_conf->location, rr->status, + rr->finfo.filetype); + *status = HTTP_INTERNAL_SERVER_ERROR; + return *status; + } + } while (0); + + return OK; + /* break; */ + } + } + + return DECLINED; +} + + +/* + * This handles scgi:(dest) URLs + */ +static int scgi_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, char *url, + const char *proxyname, apr_port_t proxyport) +{ + int status; + proxy_conn_rec *backend = NULL; + apr_pool_t *p = r->pool; + apr_uri_t *uri; + char dummy; + + if (ap_cstr_casecmpn(url, SCHEME "://", sizeof(SCHEME) + 2)) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00865) + "declining URL %s", url); + return DECLINED; + } + + /* Create space for state information */ + status = ap_proxy_acquire_connection(PROXY_FUNCTION, &backend, worker, + r->server); + if (status != OK) { + goto cleanup; + } + 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); + if (status != OK) { + goto cleanup; + } + + /* Step Two: Make the Connection */ + if (ap_proxy_connect_backend(PROXY_FUNCTION, backend, worker, r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00866) + "failed to make connection to backend: %s:%u", + backend->hostname, backend->port); + status = HTTP_SERVICE_UNAVAILABLE; + goto cleanup; + } + + /* Step Three: Process the Request */ + if ( ((status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK) + || ((status = send_headers(r, backend)) != OK) + || ((status = send_request_body(r, backend)) != OK) + || ((status = pass_response(r, backend)) != OK)) { + goto cleanup; + } + +cleanup: + if (backend) { + backend->close = 1; /* always close the socket */ + ap_proxy_release_connection(PROXY_FUNCTION, backend, r->server); + } + return status; +} + + +static void *create_scgi_config(apr_pool_t *p, char *dummy) +{ + scgi_config *conf=apr_palloc(p, sizeof(*conf)); + + conf->sendfile = NULL; /* === default (off) */ + conf->internal_redirect = NULL; /* === default (on) */ + + return conf; +} + + +static void *merge_scgi_config(apr_pool_t *p, void *base_, void *add_) +{ + scgi_config *base=base_, *add=add_, *conf=apr_palloc(p, sizeof(*conf)); + + conf->sendfile = add->sendfile ? add->sendfile: base->sendfile; + conf->internal_redirect = add->internal_redirect + ? add->internal_redirect + : base->internal_redirect; + return conf; +} + + +static const char *scgi_set_send_file(cmd_parms *cmd, void *mconfig, + const char *arg) +{ + scgi_config *conf=mconfig; + + if (!strcasecmp(arg, "Off")) { + conf->sendfile = scgi_sendfile_off; + } + else if (!strcasecmp(arg, "On")) { + conf->sendfile = scgi_sendfile_on; + } + else { + conf->sendfile = arg; + } + return NULL; +} + + +static const char *scgi_set_internal_redirect(cmd_parms *cmd, void *mconfig, + const char *arg) +{ + scgi_config *conf = mconfig; + + if (!strcasecmp(arg, "Off")) { + conf->internal_redirect = scgi_internal_redirect_off; + } + else if (!strcasecmp(arg, "On")) { + conf->internal_redirect = scgi_internal_redirect_on; + } + else { + conf->internal_redirect = arg; + } + return NULL; +} + + +static const command_rec scgi_cmds[] = +{ + AP_INIT_TAKE1("ProxySCGISendfile", scgi_set_send_file, NULL, + RSRC_CONF|ACCESS_CONF, + "The name of the X-Sendfile pseudo response header or " + "On or Off"), + AP_INIT_TAKE1("ProxySCGIInternalRedirect", scgi_set_internal_redirect, NULL, + RSRC_CONF|ACCESS_CONF, + "The name of the pseudo response header or On or Off"), + {NULL} +}; + + +static void register_hooks(apr_pool_t *p) +{ + proxy_hook_scheme_handler(scgi_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(scgi_canon, NULL, NULL, APR_HOOK_FIRST); + APR_OPTIONAL_HOOK(proxy, request_status, scgi_request_status, NULL, NULL, + APR_HOOK_MIDDLE); +} + + +AP_DECLARE_MODULE(proxy_scgi) = { + STANDARD20_MODULE_STUFF, + create_scgi_config, /* create per-directory config structure */ + merge_scgi_config, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + scgi_cmds, /* command table */ + register_hooks /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_scgi.dep b/modules/proxy/mod_proxy_scgi.dep new file mode 100644 index 0000000..1b20db8 --- /dev/null +++ b/modules/proxy/mod_proxy_scgi.dep @@ -0,0 +1,75 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_scgi.mak + +.\mod_proxy_scgi.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + ".\scgi.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_scgi.dsp b/modules/proxy/mod_proxy_scgi.dsp new file mode 100644 index 0000000..8aee15e --- /dev/null +++ b/modules/proxy/mod_proxy_scgi.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_scgi" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_scgi - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_scgi.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_scgi.mak" CFG="mod_proxy_scgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_scgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_scgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_scgi_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_scgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_scgi.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_scgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_scgi.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_scgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_scgi - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_scgi_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_scgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_scgi.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_scgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_scgi.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_scgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_scgi - Win32 Release" +# Name "mod_proxy_scgi - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_scgi.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_scgi.mak b/modules/proxy/mod_proxy_scgi.mak new file mode 100644 index 0000000..7ffb248 --- /dev/null +++ b/modules/proxy/mod_proxy_scgi.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_scgi.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_scgi - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_scgi - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_scgi - Win32 Release" && "$(CFG)" != "mod_proxy_scgi - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_scgi.mak" CFG="mod_proxy_scgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_scgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_scgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_scgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_scgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_scgi.obj" + -@erase "$(INTDIR)\mod_proxy_scgi.res" + -@erase "$(INTDIR)\mod_proxy_scgi_src.idb" + -@erase "$(INTDIR)\mod_proxy_scgi_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_scgi.exp" + -@erase "$(OUTDIR)\mod_proxy_scgi.lib" + -@erase "$(OUTDIR)\mod_proxy_scgi.pdb" + -@erase "$(OUTDIR)\mod_proxy_scgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_scgi_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_scgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_scgi.pdb" /debug /out:"$(OUTDIR)\mod_proxy_scgi.so" /implib:"$(OUTDIR)\mod_proxy_scgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_scgi.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_scgi.obj" \ + "$(INTDIR)\mod_proxy_scgi.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_scgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_scgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_scgi.so" + if exist .\Release\mod_proxy_scgi.so.manifest mt.exe -manifest .\Release\mod_proxy_scgi.so.manifest -outputresource:.\Release\mod_proxy_scgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_scgi - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_scgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_scgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_scgi.obj" + -@erase "$(INTDIR)\mod_proxy_scgi.res" + -@erase "$(INTDIR)\mod_proxy_scgi_src.idb" + -@erase "$(INTDIR)\mod_proxy_scgi_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_scgi.exp" + -@erase "$(OUTDIR)\mod_proxy_scgi.lib" + -@erase "$(OUTDIR)\mod_proxy_scgi.pdb" + -@erase "$(OUTDIR)\mod_proxy_scgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_scgi_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_scgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_scgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_scgi.pdb" /debug /out:"$(OUTDIR)\mod_proxy_scgi.so" /implib:"$(OUTDIR)\mod_proxy_scgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_scgi.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_scgi.obj" \ + "$(INTDIR)\mod_proxy_scgi.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_scgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_scgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_scgi.so" + if exist .\Debug\mod_proxy_scgi.so.manifest mt.exe -manifest .\Debug\mod_proxy_scgi.so.manifest -outputresource:.\Debug\mod_proxy_scgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_scgi.dep") +!INCLUDE "mod_proxy_scgi.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_scgi.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" || "$(CFG)" == "mod_proxy_scgi - Win32 Debug" +SOURCE=.\mod_proxy_scgi.c + +"$(INTDIR)\mod_proxy_scgi.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_scgi - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_scgi - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_scgi - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_scgi - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_scgi - Win32 Release" + + +"$(INTDIR)\mod_proxy_scgi.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_scgi.so" /d LONG_NAME="proxy_scgi_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c new file mode 100644 index 0000000..fd76c95 --- /dev/null +++ b/modules/proxy/mod_proxy_uwsgi.c @@ -0,0 +1,595 @@ +/* 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. + */ + +/* + +*** mod_proxy_uwsgi *** + +Copyright 2009-2017 Unbit S.a.s. <info@unbit.it> + +Licensed 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. + +*/ + +#define APR_WANT_MEMFUNC +#define APR_WANT_STRFUNC +#include "apr_strings.h" +#include "apr_hooks.h" +#include "apr_optional_hooks.h" +#include "apr_buckets.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" +#include "util_script.h" + +#include "mod_proxy.h" + + +#define UWSGI_SCHEME "uwsgi" +#define UWSGI_DEFAULT_PORT 3031 + +module AP_MODULE_DECLARE_DATA proxy_uwsgi_module; + + +static int uwsgi_canon(request_rec *r, char *url) +{ + char *host, sport[sizeof(":65535")]; + const char *err, *path; + apr_port_t port = UWSGI_DEFAULT_PORT; + + if (ap_cstr_casecmpn(url, UWSGI_SCHEME "://", sizeof(UWSGI_SCHEME) + 2)) { + return DECLINED; + } + url += sizeof(UWSGI_SCHEME); /* Keep slashes */ + + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10097) + "error parsing URL %s: %s", url, err); + return HTTP_BAD_REQUEST; + } + + if (port != UWSGI_DEFAULT_PORT) + apr_snprintf(sport, sizeof(sport), ":%u", port); + else + sport[0] = '\0'; + + if (ap_strchr(host, ':')) { /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + + if (apr_table_get(r->notes, "proxy-nocanon") + || apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the raw/encoded path */ + } + else { + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + } + /* + * If we have a raw control character or a ' ' in nocanon path, + * correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10417) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + r->filename = + apr_pstrcat(r->pool, "proxy:" UWSGI_SCHEME "://", host, sport, "/", + path, NULL); + + return OK; +} + + +static int uwsgi_send(proxy_conn_rec * conn, const char *buf, + apr_size_t length, request_rec *r) +{ + apr_status_t rv; + apr_size_t written; + + while (length > 0) { + written = length; + if ((rv = apr_socket_send(conn->sock, buf, &written)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10098) + "sending data to %s:%u failed", + conn->hostname, conn->port); + return HTTP_SERVICE_UNAVAILABLE; + } + + /* count for stats */ + conn->worker->s->transferred += written; + buf += written; + length -= written; + } + + return OK; +} + + +/* + * Send uwsgi header block + */ +static int uwsgi_send_headers(request_rec *r, proxy_conn_rec * conn) +{ + char *buf, *ptr; + + const apr_array_header_t *env_table; + const apr_table_entry_t *env; + + int j; + + apr_size_t headerlen = 4; + apr_size_t pktsize, keylen, vallen; + const char *script_name; + const char *path_info; + const char *auth; + + ap_add_common_vars(r); + ap_add_cgi_vars(r); + + /* + this is not a security problem (in Linux) as uWSGI destroy the env memory area readable in /proc + and generally if you host untrusted apps in your server and allows them to read others uid /proc/<pid> + files you have higher problems... + */ + auth = apr_table_get(r->headers_in, "Authorization"); + if (auth) { + apr_table_setn(r->subprocess_env, "HTTP_AUTHORIZATION", auth); + } + + script_name = apr_table_get(r->subprocess_env, "SCRIPT_NAME"); + path_info = apr_table_get(r->subprocess_env, "PATH_INFO"); + + if (script_name && path_info) { + if (strcmp(path_info, "/")) { + apr_table_set(r->subprocess_env, "SCRIPT_NAME", + apr_pstrndup(r->pool, script_name, + strlen(script_name) - + strlen(path_info))); + } + else { + if (!strcmp(script_name, "/")) { + apr_table_setn(r->subprocess_env, "SCRIPT_NAME", ""); + } + } + } + + env_table = apr_table_elts(r->subprocess_env); + env = (apr_table_entry_t *) env_table->elts; + + for (j = 0; j < env_table->nelts; ++j) { + headerlen += 2 + strlen(env[j].key) + 2 + (env[j].val ? strlen(env[j].val) : 0); + } + + pktsize = headerlen - 4; + if (pktsize > APR_UINT16_MAX) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10259) + "can't send headers to %s:%u: packet size too " + "large (%" APR_SIZE_T_FMT ")", + conn->hostname, conn->port, pktsize); + return HTTP_INTERNAL_SERVER_ERROR; + } + + ptr = buf = apr_palloc(r->pool, headerlen); + + ptr += 4; + + for (j = 0; j < env_table->nelts; ++j) { + keylen = strlen(env[j].key); + *ptr++ = (apr_byte_t) (keylen & 0xff); + *ptr++ = (apr_byte_t) ((keylen >> 8) & 0xff); + memcpy(ptr, env[j].key, keylen); + ptr += keylen; + + vallen = env[j].val ? strlen(env[j].val) : 0; + *ptr++ = (apr_byte_t) (vallen & 0xff); + *ptr++ = (apr_byte_t) ((vallen >> 8) & 0xff); + if (env[j].val) { + memcpy(ptr, env[j].val, vallen); + } + ptr += vallen; + } + + buf[0] = 0; + buf[1] = (apr_byte_t) (pktsize & 0xff); + buf[2] = (apr_byte_t) ((pktsize >> 8) & 0xff); + buf[3] = 0; + + return uwsgi_send(conn, buf, headerlen, r); +} + + +static int uwsgi_send_body(request_rec *r, proxy_conn_rec * conn) +{ + if (ap_should_client_block(r)) { + char *buf = apr_palloc(r->pool, AP_IOBUFSIZE); + int status; + long readlen; + + readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE); + while (readlen > 0) { + status = uwsgi_send(conn, buf, (apr_size_t)readlen, r); + if (status != OK) { + return HTTP_SERVICE_UNAVAILABLE; + } + readlen = ap_get_client_block(r, buf, AP_IOBUFSIZE); + } + if (readlen == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10099) + "receiving request body failed"); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + return OK; +} + +static request_rec *make_fake_req(conn_rec *c, request_rec *r) +{ + apr_pool_t *pool; + request_rec *rp; + + apr_pool_create(&pool, c->pool); + apr_pool_tag(pool, "proxy_uwsgi_rp"); + + rp = apr_pcalloc(pool, sizeof(*r)); + + rp->pool = pool; + rp->status = HTTP_OK; + + rp->headers_in = apr_table_make(pool, 50); + rp->subprocess_env = apr_table_make(pool, 50); + rp->headers_out = apr_table_make(pool, 12); + rp->err_headers_out = apr_table_make(pool, 5); + rp->notes = apr_table_make(pool, 5); + + rp->server = r->server; + rp->log = r->log; + rp->proxyreq = r->proxyreq; + rp->request_time = r->request_time; + rp->connection = c; + rp->output_filters = c->output_filters; + rp->input_filters = c->input_filters; + rp->proto_output_filters = c->output_filters; + rp->proto_input_filters = c->input_filters; + rp->useragent_ip = c->client_ip; + rp->useragent_addr = c->client_addr; + + rp->request_config = ap_create_request_config(pool); + proxy_run_create_req(r, rp); + + return rp; +} + +static int uwsgi_response(request_rec *r, proxy_conn_rec * backend, + proxy_server_conf * conf) +{ + + char buffer[HUGE_STRING_LEN]; + const char *buf; + char *value, *end; + char keepchar; + int len; + int backend_broke = 0; + int status_start; + int status_end; + int finish = 0; + conn_rec *c = r->connection; + apr_off_t readbytes; + apr_status_t rv; + apr_bucket *e; + apr_read_type_e mode = APR_NONBLOCK_READ; + apr_bucket_brigade *pass_bb; + apr_bucket_brigade *bb; + proxy_dir_conf *dconf; + + request_rec *rp = make_fake_req(backend->connection, r); + rp->proxyreq = PROXYREQ_RESPONSE; + + bb = apr_brigade_create(r->pool, c->bucket_alloc); + pass_bb = apr_brigade_create(r->pool, c->bucket_alloc); + + len = ap_getline(buffer, sizeof(buffer), rp, 1); + if (len <= 0) { + /* invalid or empty */ + return HTTP_INTERNAL_SERVER_ERROR; + } + backend->worker->s->read += len; + if ((apr_size_t)len >= sizeof(buffer)) { + /* too long */ + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Position of http status code */ + if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + status_start = 9; + } + else if (apr_date_checkmask(buffer, "HTTP/# ###*")) { + status_start = 7; + } + else { + /* not HTTP */ + return HTTP_BAD_GATEWAY; + } + status_end = status_start + 3; + + keepchar = buffer[status_end]; + buffer[status_end] = '\0'; + r->status = atoi(&buffer[status_start]); + + if (keepchar != '\0') { + buffer[status_end] = keepchar; + } + else { + /* 2616 requires the space in Status-Line; the origin + * server may have sent one but ap_rgetline_core will + * have stripped it. */ + buffer[status_end] = ' '; + buffer[status_end + 1] = '\0'; + } + r->status_line = apr_pstrdup(r->pool, &buffer[status_start]); + + /* parse headers */ + while ((len = ap_getline(buffer, sizeof(buffer), rp, 1)) > 0) { + if ((apr_size_t)len >= sizeof(buffer)) { + /* too long */ + len = -1; + break; + } + value = strchr(buffer, ':'); + if (!value) { + /* invalid header */ + len = -1; + break; + } + *value++ = '\0'; + if (*ap_scan_http_token(buffer)) { + /* invalid name */ + len = -1; + break; + } + while (apr_isspace(*value)) + ++value; + for (end = &value[strlen(value) - 1]; + end > value && apr_isspace(*end); --end) + *end = '\0'; + if (*ap_scan_http_field_content(value)) { + /* invalid value */ + len = -1; + break; + } + apr_table_add(r->headers_out, buffer, value); + } + if (len < 0) { + /* Reset headers, but not to NULL because things below the chain expect + * this to be non NULL e.g. the ap_content_length_filter. + */ + r->headers_out = apr_table_make(r->pool, 1); + return HTTP_BAD_GATEWAY; + } + + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { + ap_set_content_type(r, apr_pstrdup(r->pool, buf)); + } + + /* honor ProxyErrorOverride and ErrorDocument */ +#if AP_MODULE_MAGIC_AT_LEAST(20101106,0) + dconf = + ap_get_module_config(r->per_dir_config, &proxy_module); + if (ap_proxy_should_override(dconf, r->status)) { +#else + if (ap_proxy_should_override(conf, r->status)) { +#endif + int status = r->status; + r->status = HTTP_OK; + r->status_line = NULL; + + apr_brigade_cleanup(bb); + apr_brigade_cleanup(pass_bb); + + return status; + } + + while (!finish) { + rv = ap_get_brigade(rp->input_filters, bb, + AP_MODE_READBYTES, mode, conf->io_buffer_size); + if (APR_STATUS_IS_EAGAIN(rv) + || (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb))) { + e = apr_bucket_flush_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + if (ap_pass_brigade(r->output_filters, bb) || c->aborted) { + break; + } + apr_brigade_cleanup(bb); + mode = APR_BLOCK_READ; + continue; + } + else if (rv == APR_EOF) { + break; + } + else if (rv != APR_SUCCESS) { + ap_proxy_backend_broke(r, bb); + ap_pass_brigade(r->output_filters, bb); + backend_broke = 1; + break; + } + + mode = APR_NONBLOCK_READ; + apr_brigade_length(bb, 0, &readbytes); + backend->worker->s->read += readbytes; + + if (APR_BRIGADE_EMPTY(bb)) { + apr_brigade_cleanup(bb); + break; + } + + ap_proxy_buckets_lifetime_transform(r, bb, pass_bb); + + /* found the last brigade? */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) + finish = 1; + + /* do not pass chunk if it is zero_sized */ + apr_brigade_length(pass_bb, 0, &readbytes); + + if ((readbytes > 0 + && ap_pass_brigade(r->output_filters, pass_bb) != APR_SUCCESS) + || c->aborted) { + finish = 1; + } + + apr_brigade_cleanup(bb); + apr_brigade_cleanup(pass_bb); + } + + e = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + ap_pass_brigade(r->output_filters, bb); + + apr_brigade_cleanup(bb); + + if (c->aborted || backend_broke) { + return DONE; + } + + return OK; +} + +static int uwsgi_handler(request_rec *r, proxy_worker * worker, + proxy_server_conf * conf, char *url, + const char *proxyname, apr_port_t proxyport) +{ + int status; + proxy_conn_rec *backend = NULL; + apr_pool_t *p = r->pool; + char server_portstr[32]; + char *u_path_info; + apr_uri_t *uri; + + if (ap_cstr_casecmpn(url, UWSGI_SCHEME "://", sizeof(UWSGI_SCHEME) + 2)) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "declining URL %s", url); + return DECLINED; + } + + uri = apr_palloc(r->pool, sizeof(*uri)); + + /* ADD PATH_INFO (unescaped) */ + u_path_info = ap_strchr(url + sizeof(UWSGI_SCHEME) + 2, '/'); + if (!u_path_info) { + u_path_info = apr_pstrdup(r->pool, "/"); + } + else if (ap_unescape_url(u_path_info) != OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10100) + "unable to decode uwsgi uri: %s", url); + return HTTP_INTERNAL_SERVER_ERROR; + } + else { + /* Remove duplicate slashes at the beginning of PATH_INFO */ + while (u_path_info[1] == '/') { + u_path_info++; + } + } + apr_table_add(r->subprocess_env, "PATH_INFO", u_path_info); + + /* Create space for state information */ + status = ap_proxy_acquire_connection(UWSGI_SCHEME, &backend, worker, + r->server); + if (status != OK) { + goto cleanup; + } + backend->is_ssl = 0; + + /* Step One: Determine Who To Connect To */ + status = ap_proxy_determine_connection(p, r, conf, worker, backend, + uri, &url, proxyname, proxyport, + server_portstr, + sizeof(server_portstr)); + if (status != OK) { + goto cleanup; + } + + + /* Step Two: Make the Connection */ + if (ap_proxy_connect_backend(UWSGI_SCHEME, backend, worker, r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10101) + "failed to make connection to backend: %s:%u", + backend->hostname, backend->port); + status = HTTP_SERVICE_UNAVAILABLE; + goto cleanup; + } + + /* Step Three: Create conn_rec */ + if ((status = ap_proxy_connection_create(UWSGI_SCHEME, backend, + r->connection, + r->server)) != OK) + goto cleanup; + + /* Step Four: Process the Request */ + if (((status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) != OK) + || ((status = uwsgi_send_headers(r, backend)) != OK) + || ((status = uwsgi_send_body(r, backend)) != OK) + || ((status = uwsgi_response(r, backend, conf)) != OK)) { + goto cleanup; + } + + cleanup: + if (backend) { + backend->close = 1; /* always close the socket */ + ap_proxy_release_connection(UWSGI_SCHEME, backend, r->server); + } + return status; +} + + +static void register_hooks(apr_pool_t * p) +{ + proxy_hook_scheme_handler(uwsgi_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(uwsgi_canon, NULL, NULL, APR_HOOK_FIRST); +} + + +module AP_MODULE_DECLARE_DATA proxy_uwsgi_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command table */ + register_hooks /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_uwsgi.dep b/modules/proxy/mod_proxy_uwsgi.dep new file mode 100644 index 0000000..6513378 --- /dev/null +++ b/modules/proxy/mod_proxy_uwsgi.dep @@ -0,0 +1,75 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_scgi.mak + +.\mod_proxy_uwsgi.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\include\util_script.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + ".\scgi.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_uwsgi.dsp b/modules/proxy/mod_proxy_uwsgi.dsp new file mode 100644 index 0000000..00c3034 --- /dev/null +++ b/modules/proxy/mod_proxy_uwsgi.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_uwsgi" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_uwsgi - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_uwsgi.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_uwsgi.mak" CFG="mod_proxy_uwsgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_uwsgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_uwsgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_uwsgi_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_uwsgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_uwsgi.so" /d LONG_NAME="proxy_uwsgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_uwsgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_uwsgi.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_uwsgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_uwsgi.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_uwsgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_uwsgi_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_uwsgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_uwsgi.so" /d LONG_NAME="proxy_uwsgi_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_uwsgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_uwsgi.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_uwsgi.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_uwsgi.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_uwsgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_uwsgi - Win32 Release" +# Name "mod_proxy_uwsgi - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_uwsgi.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_uwsgi.mak b/modules/proxy/mod_proxy_uwsgi.mak new file mode 100644 index 0000000..6db95e5 --- /dev/null +++ b/modules/proxy/mod_proxy_uwsgi.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_uwsgi.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_uwsgi - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_uwsgi - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_uwsgi - Win32 Release" && "$(CFG)" != "mod_proxy_uwsgi - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_uwsgi.mak" CFG="mod_proxy_uwsgi - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_uwsgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_uwsgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_uwsgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_uwsgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_uwsgi.obj" + -@erase "$(INTDIR)\mod_proxy_uwsgi.res" + -@erase "$(INTDIR)\mod_proxy_uwsgi_src.idb" + -@erase "$(INTDIR)\mod_proxy_uwsgi_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.exp" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.lib" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.pdb" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_uwsgi_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_uwsgi.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_uwsgi.so" /d LONG_NAME="proxy_uwsgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_uwsgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_uwsgi.pdb" /debug /out:"$(OUTDIR)\mod_proxy_uwsgi.so" /implib:"$(OUTDIR)\mod_proxy_uwsgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_uwsgi.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_uwsgi.obj" \ + "$(INTDIR)\mod_proxy_uwsgi.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_uwsgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_uwsgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_uwsgi.so" + if exist .\Release\mod_proxy_uwsgi.so.manifest mt.exe -manifest .\Release\mod_proxy_uwsgi.so.manifest -outputresource:.\Release\mod_proxy_uwsgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_uwsgi.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_uwsgi.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_uwsgi.obj" + -@erase "$(INTDIR)\mod_proxy_uwsgi.res" + -@erase "$(INTDIR)\mod_proxy_uwsgi_src.idb" + -@erase "$(INTDIR)\mod_proxy_uwsgi_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.exp" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.lib" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.pdb" + -@erase "$(OUTDIR)\mod_proxy_uwsgi.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_uwsgi_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_uwsgi.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_uwsgi.so" /d LONG_NAME="proxy_uwsgi_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_uwsgi.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_uwsgi.pdb" /debug /out:"$(OUTDIR)\mod_proxy_uwsgi.so" /implib:"$(OUTDIR)\mod_proxy_uwsgi.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_uwsgi.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_uwsgi.obj" \ + "$(INTDIR)\mod_proxy_uwsgi.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_uwsgi.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_uwsgi.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_uwsgi.so" + if exist .\Debug\mod_proxy_uwsgi.so.manifest mt.exe -manifest .\Debug\mod_proxy_uwsgi.so.manifest -outputresource:.\Debug\mod_proxy_uwsgi.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_uwsgi.dep") +!INCLUDE "mod_proxy_uwsgi.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_uwsgi.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" || "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" +SOURCE=.\mod_proxy_uwsgi.c + +"$(INTDIR)\mod_proxy_uwsgi.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_uwsgi - Win32 Release" + + +"$(INTDIR)\mod_proxy_uwsgi.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_uwsgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "NDEBUG" /d BIN_NAME="mod_proxy_uwsgi.so" /d LONG_NAME="proxy_uwsgi_module for Apache" $(SOURCE) + + +!ELSEIF "$(CFG)" == "mod_proxy_uwsgi - Win32 Debug" + + +"$(INTDIR)\mod_proxy_uwsgi.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x409 /fo"$(INTDIR)\mod_proxy_uwsgi.res" /i "../../include" /i "../../srclib/apr/include" /i "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_uwsgi.so" /d LONG_NAME="proxy_uwsgi_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c new file mode 100644 index 0000000..30ba1b4 --- /dev/null +++ b/modules/proxy/mod_proxy_wstunnel.c @@ -0,0 +1,513 @@ +/* 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 "http_config.h" + +module AP_MODULE_DECLARE_DATA proxy_wstunnel_module; + +typedef struct { + unsigned int fallback_to_proxy_http :1, + fallback_to_proxy_http_set :1; +} proxyws_dir_conf; + +static int can_fallback_to_proxy_http; + +static int proxy_wstunnel_check_trans(request_rec *r, const char *url) +{ + proxyws_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_wstunnel_module); + + if (can_fallback_to_proxy_http && dconf->fallback_to_proxy_http) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "check_trans fallback"); + return DECLINED; + } + + if (ap_cstr_casecmpn(url, "ws:", 3) != 0 + && ap_cstr_casecmpn(url, "wss:", 4) != 0) { + return DECLINED; + } + + if (!apr_table_get(r->headers_in, "Upgrade")) { + /* No Upgrade, let mod_proxy_http handle it (for instance). + * Note: anything but OK/DECLINED will do (i.e. bypass wstunnel w/o + * aborting the request), HTTP_UPGRADE_REQUIRED is documentary... + */ + return HTTP_UPGRADE_REQUIRED; + } + + return OK; +} + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +static int proxy_wstunnel_canon(request_rec *r, char *url) +{ + proxyws_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_wstunnel_module); + char *host, *path, sport[7]; + char *search = NULL; + const char *err; + char *scheme; + apr_port_t port, def_port; + + if (can_fallback_to_proxy_http && dconf->fallback_to_proxy_http) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "canon fallback"); + return DECLINED; + } + + /* ap_port_of_scheme() */ + if (ap_cstr_casecmpn(url, "ws:", 3) == 0) { + url += 3; + scheme = "ws:"; + def_port = apr_uri_port_of_scheme("http"); + } + else if (ap_cstr_casecmpn(url, "wss:", 4) == 0) { + url += 4; + scheme = "wss:"; + def_port = apr_uri_port_of_scheme("https"); + } + else { + return DECLINED; + } + + port = def_port; + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url); + + /* + * do syntactic check. + * We break the URL into host, port, path, search + */ + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02439) "error parsing URL %s: %s", + url, err); + return HTTP_BAD_REQUEST; + } + + /* + * now parse path/search args, according to rfc1738: + * process the path. With proxy-nocanon set (by + * mod_proxy) we use the raw, unparsed uri + */ + if (apr_table_get(r->notes, "proxy-nocanon")) { + path = url; /* this is the raw path */ + } + else if (apr_table_get(r->notes, "proxy-noencode")) { + path = url; /* this is the encoded path already */ + search = r->args; + } + else { + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); + if (!path) { + return HTTP_BAD_REQUEST; + } + search = r->args; + } + /* + * If we have a raw control character or a ' ' in nocanon path or + * r->args, correct encoding was missed. + */ + if (path == url && *ap_scan_vchar_obstext(path)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10419) + "To be forwarded path contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + if (search && *ap_scan_vchar_obstext(search)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10409) + "To be forwarded query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } + + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + if (ap_strchr_c(host, ':')) { + /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "//", host, sport, + "/", path, (search) ? "?" : "", + (search) ? search : "", NULL); + return OK; +} + +/* + * process the request and write the response. + */ +static int proxy_wstunnel_request(apr_pool_t *p, request_rec *r, + proxy_conn_rec *conn, + proxy_worker *worker, + proxy_server_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr) +{ + apr_status_t rv; + apr_pollset_t *pollset; + apr_pollfd_t pollfd; + const apr_pollfd_t *signalled; + apr_int32_t pollcnt, pi; + apr_int16_t pollevent; + conn_rec *c = r->connection; + apr_socket_t *sock = conn->sock; + conn_rec *backconn = conn->connection; + char *buf; + apr_bucket_brigade *header_brigade; + apr_bucket *e; + char *old_cl_val = NULL; + char *old_te_val = NULL; + apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc); + apr_socket_t *client_socket = ap_get_conn_socket(c); + int done = 0, replied = 0; + const char *upgrade_method = *worker->s->upgrade ? worker->s->upgrade : "WebSocket"; + + header_brigade = apr_brigade_create(p, backconn->bucket_alloc); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "sending request"); + + rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, conn, + worker, conf, uri, url, server_portstr, + &old_cl_val, &old_te_val); + if (rv != OK) { + return rv; + } + + if (ap_cstr_casecmp(upgrade_method, "NONE") == 0) { + buf = apr_pstrdup(p, "Upgrade: WebSocket" CRLF "Connection: Upgrade" CRLF CRLF); + } else if (ap_cstr_casecmp(upgrade_method, "ANY") == 0) { + const char *upgrade; + upgrade = apr_table_get(r->headers_in, "Upgrade"); + buf = apr_pstrcat(p, "Upgrade: ", upgrade, CRLF "Connection: Upgrade" CRLF CRLF, NULL); + } else { + buf = apr_pstrcat(p, "Upgrade: ", upgrade_method, CRLF "Connection: Upgrade" CRLF CRLF, NULL); + } + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + + if ((rv = ap_proxy_pass_brigade(backconn->bucket_alloc, r, conn, backconn, + header_brigade, 1)) != OK) + return rv; + + apr_brigade_cleanup(header_brigade); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "setting up poll()"); + + if ((rv = apr_pollset_create(&pollset, 2, p, 0)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02443) + "error apr_pollset_create()"); + return HTTP_INTERNAL_SERVER_ERROR; + } + +#if 0 + apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1); + apr_socket_opt_set(sock, APR_SO_KEEPALIVE, 1); + apr_socket_opt_set(client_socket, APR_SO_NONBLOCK, 1); + apr_socket_opt_set(client_socket, APR_SO_KEEPALIVE, 1); +#endif + + pollfd.p = p; + pollfd.desc_type = APR_POLL_SOCKET; + pollfd.reqevents = APR_POLLIN | APR_POLLHUP; + pollfd.desc.s = sock; + pollfd.client_data = NULL; + apr_pollset_add(pollset, &pollfd); + + pollfd.desc.s = client_socket; + apr_pollset_add(pollset, &pollfd); + + ap_remove_input_filter_byhandle(c->input_filters, "reqtimeout"); + + r->output_filters = c->output_filters; + r->proto_output_filters = c->output_filters; + r->input_filters = c->input_filters; + r->proto_input_filters = c->input_filters; + + /* This handler should take care of the entire connection; make it so that + * nothing else is attempted on the connection after returning. */ + c->keepalive = AP_CONN_CLOSE; + + do { /* Loop until done (one side closes the connection, or an error) */ + rv = apr_pollset_poll(pollset, -1, &pollcnt, &signalled); + if (rv != APR_SUCCESS) { + if (APR_STATUS_IS_EINTR(rv)) { + continue; + } + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02444) "error apr_poll()"); + return HTTP_INTERNAL_SERVER_ERROR; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02445) + "woke from poll(), i=%d", pollcnt); + + for (pi = 0; pi < pollcnt; pi++) { + const apr_pollfd_t *cur = &signalled[pi]; + + if (cur->desc.s == sock) { + pollevent = cur->rtnevents; + if (pollevent & (APR_POLLIN | APR_POLLHUP)) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02446) + "sock was readable"); + done |= ap_proxy_transfer_between_connections(r, backconn, + c, + header_brigade, + bb, "sock", + &replied, + AP_IOBUFSIZE, + 0) + != APR_SUCCESS; + } + else if (pollevent & APR_POLLERR) { + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447) + "error on backconn"); + backconn->aborted = 1; + done = 1; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02605) + "unknown event on backconn %d", pollevent); + done = 1; + } + } + else if (cur->desc.s == client_socket) { + pollevent = cur->rtnevents; + if (pollevent & (APR_POLLIN | APR_POLLHUP)) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02448) + "client was readable"); + done |= ap_proxy_transfer_between_connections(r, c, + backconn, bb, + header_brigade, + "client", + NULL, + AP_IOBUFSIZE, + 0) + != APR_SUCCESS; + } + else if (pollevent & APR_POLLERR) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02607) + "error on client conn"); + c->aborted = 1; + done = 1; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02606) + "unknown event on client conn %d", pollevent); + done = 1; + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02449) + "unknown socket in pollset"); + done = 1; + } + + } + } while (!done); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "finished with poll() - cleaning up"); + + if (!replied) { + return HTTP_BAD_GATEWAY; + } + else { + return OK; + } + + return OK; +} + +/* + */ +static int proxy_wstunnel_handler(request_rec *r, proxy_worker *worker, + proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + proxyws_dir_conf *dconf = ap_get_module_config(r->per_dir_config, + &proxy_wstunnel_module); + int status; + char server_portstr[32]; + proxy_conn_rec *backend = NULL; + const char *upgrade; + char *scheme; + apr_pool_t *p = r->pool; + char *locurl = url; + apr_uri_t *uri; + int is_ssl = 0; + + if (can_fallback_to_proxy_http && dconf->fallback_to_proxy_http) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "handler fallback"); + return DECLINED; + } + + if (ap_cstr_casecmpn(url, "wss:", 4) == 0) { + scheme = "WSS"; + is_ssl = 1; + } + else if (ap_cstr_casecmpn(url, "ws:", 3) == 0) { + scheme = "WS"; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02450) + "declining URL %s", url); + return DECLINED; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "serving URL %s", url); + + upgrade = apr_table_get(r->headers_in, "Upgrade"); + if (!upgrade || !ap_proxy_worker_can_upgrade(p, worker, upgrade, + "WebSocket")) { + const char *worker_upgrade = *worker->s->upgrade ? worker->s->upgrade + : "WebSocket"; + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02900) + "require upgrade for URL %s " + "(Upgrade header is %s, expecting %s)", + url, upgrade ? upgrade : "missing", worker_upgrade); + apr_table_setn(r->err_headers_out, "Connection", "Upgrade"); + apr_table_setn(r->err_headers_out, "Upgrade", worker_upgrade); + return HTTP_UPGRADE_REQUIRED; + } + + uri = apr_palloc(p, sizeof(*uri)); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02451) "serving URL %s", url); + + /* create space for state information */ + status = ap_proxy_acquire_connection(scheme, &backend, worker, r->server); + if (status != OK) { + goto cleanup; + } + + backend->is_ssl = is_ssl; + backend->close = 0; + + /* Step One: Determine Who To Connect To */ + status = ap_proxy_determine_connection(p, r, conf, worker, backend, + uri, &locurl, proxyname, proxyport, + server_portstr, + sizeof(server_portstr)); + if (status != OK) { + goto cleanup; + } + + /* Step Two: Make the Connection */ + if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02452) + "failed to make connection to backend: %s", + backend->hostname); + status = HTTP_SERVICE_UNAVAILABLE; + goto cleanup; + } + + /* Step Three: Create conn_rec */ + status = ap_proxy_connection_create_ex(scheme, backend, r); + if (status != OK) { + goto cleanup; + } + + /* Step Four: Process the Request */ + status = proxy_wstunnel_request(p, r, backend, worker, conf, uri, locurl, + server_portstr); + +cleanup: + /* Do not close the socket */ + if (backend) { + backend->close = 1; + ap_proxy_release_connection(scheme, backend, r->server); + } + return status; +} + +static void *create_proxyws_dir_config(apr_pool_t *p, char *dummy) +{ + proxyws_dir_conf *new = + (proxyws_dir_conf *) apr_pcalloc(p, sizeof(proxyws_dir_conf)); + + new->fallback_to_proxy_http = 1; + + return (void *) new; +} + +static void *merge_proxyws_dir_config(apr_pool_t *p, void *vbase, void *vadd) +{ + proxyws_dir_conf *new = apr_pcalloc(p, sizeof(proxyws_dir_conf)), + *add = vadd, *base = vbase; + + new->fallback_to_proxy_http = (add->fallback_to_proxy_http_set) + ? add->fallback_to_proxy_http + : base->fallback_to_proxy_http; + new->fallback_to_proxy_http_set = (add->fallback_to_proxy_http_set + || base->fallback_to_proxy_http_set); + + return new; +} + +static const char * proxyws_fallback_to_proxy_http(cmd_parms *cmd, void *conf, int arg) +{ + proxyws_dir_conf *dconf = conf; + dconf->fallback_to_proxy_http = !!arg; + dconf->fallback_to_proxy_http_set = 1; + return NULL; +} + +static int proxy_wstunnel_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + can_fallback_to_proxy_http = + (ap_find_linked_module("mod_proxy_http.c") != NULL); + + return OK; +} + +static const command_rec ws_proxy_cmds[] = +{ + AP_INIT_FLAG("ProxyWebsocketFallbackToProxyHttp", + proxyws_fallback_to_proxy_http, NULL, RSRC_CONF|ACCESS_CONF, + "whether to let mod_proxy_http handle the upgrade and tunneling, " + "On by default"), + + {NULL} +}; + +static void ws_proxy_hooks(apr_pool_t *p) +{ + static const char * const aszSucc[] = { "mod_proxy_http.c", NULL}; + ap_hook_post_config(proxy_wstunnel_post_config, NULL, NULL, APR_HOOK_MIDDLE); + proxy_hook_scheme_handler(proxy_wstunnel_handler, NULL, aszSucc, APR_HOOK_FIRST); + proxy_hook_check_trans(proxy_wstunnel_check_trans, NULL, aszSucc, APR_HOOK_MIDDLE); + proxy_hook_canon_handler(proxy_wstunnel_canon, NULL, aszSucc, APR_HOOK_FIRST); +} + +AP_DECLARE_MODULE(proxy_wstunnel) = { + STANDARD20_MODULE_STUFF, + create_proxyws_dir_config, /* create per-directory config structure */ + merge_proxyws_dir_config, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + ws_proxy_cmds, /* command apr_table_t */ + ws_proxy_hooks /* register hooks */ +}; diff --git a/modules/proxy/mod_proxy_wstunnel.dep b/modules/proxy/mod_proxy_wstunnel.dep new file mode 100644 index 0000000..5692e63 --- /dev/null +++ b/modules/proxy/mod_proxy_wstunnel.dep @@ -0,0 +1,73 @@ +# Microsoft Developer Studio Generated Dependency File, included by mod_proxy_wstunnel.mak + +.\mod_proxy_wstunnel.c : \ + "..\..\include\ap_config.h"\ + "..\..\include\ap_config_layout.h"\ + "..\..\include\ap_expr.h"\ + "..\..\include\ap_hooks.h"\ + "..\..\include\ap_mmn.h"\ + "..\..\include\ap_provider.h"\ + "..\..\include\ap_regex.h"\ + "..\..\include\ap_release.h"\ + "..\..\include\ap_slotmem.h"\ + "..\..\include\apache_noprobes.h"\ + "..\..\include\http_config.h"\ + "..\..\include\http_connection.h"\ + "..\..\include\http_core.h"\ + "..\..\include\http_log.h"\ + "..\..\include\http_main.h"\ + "..\..\include\http_protocol.h"\ + "..\..\include\http_request.h"\ + "..\..\include\http_vhost.h"\ + "..\..\include\httpd.h"\ + "..\..\include\os.h"\ + "..\..\include\util_cfgtree.h"\ + "..\..\include\util_charset.h"\ + "..\..\include\util_ebcdic.h"\ + "..\..\include\util_filter.h"\ + "..\..\include\util_mutex.h"\ + "..\..\srclib\apr-util\include\apr_buckets.h"\ + "..\..\srclib\apr-util\include\apr_date.h"\ + "..\..\srclib\apr-util\include\apr_hooks.h"\ + "..\..\srclib\apr-util\include\apr_md5.h"\ + "..\..\srclib\apr-util\include\apr_optional.h"\ + "..\..\srclib\apr-util\include\apr_optional_hooks.h"\ + "..\..\srclib\apr-util\include\apr_reslist.h"\ + "..\..\srclib\apr-util\include\apr_strmatch.h"\ + "..\..\srclib\apr-util\include\apr_uri.h"\ + "..\..\srclib\apr-util\include\apr_uuid.h"\ + "..\..\srclib\apr-util\include\apr_xlate.h"\ + "..\..\srclib\apr-util\include\apu.h"\ + "..\..\srclib\apr\include\apr.h"\ + "..\..\srclib\apr\include\apr_allocator.h"\ + "..\..\srclib\apr\include\apr_dso.h"\ + "..\..\srclib\apr\include\apr_errno.h"\ + "..\..\srclib\apr\include\apr_file_info.h"\ + "..\..\srclib\apr\include\apr_file_io.h"\ + "..\..\srclib\apr\include\apr_fnmatch.h"\ + "..\..\srclib\apr\include\apr_general.h"\ + "..\..\srclib\apr\include\apr_global_mutex.h"\ + "..\..\srclib\apr\include\apr_hash.h"\ + "..\..\srclib\apr\include\apr_inherit.h"\ + "..\..\srclib\apr\include\apr_lib.h"\ + "..\..\srclib\apr\include\apr_mmap.h"\ + "..\..\srclib\apr\include\apr_network_io.h"\ + "..\..\srclib\apr\include\apr_poll.h"\ + "..\..\srclib\apr\include\apr_pools.h"\ + "..\..\srclib\apr\include\apr_portable.h"\ + "..\..\srclib\apr\include\apr_proc_mutex.h"\ + "..\..\srclib\apr\include\apr_ring.h"\ + "..\..\srclib\apr\include\apr_shm.h"\ + "..\..\srclib\apr\include\apr_strings.h"\ + "..\..\srclib\apr\include\apr_tables.h"\ + "..\..\srclib\apr\include\apr_thread_mutex.h"\ + "..\..\srclib\apr\include\apr_thread_proc.h"\ + "..\..\srclib\apr\include\apr_time.h"\ + "..\..\srclib\apr\include\apr_user.h"\ + "..\..\srclib\apr\include\apr_want.h"\ + ".\mod_proxy.h"\ + + +..\..\build\win32\httpd.rc : \ + "..\..\include\ap_release.h"\ + diff --git a/modules/proxy/mod_proxy_wstunnel.dsp b/modules/proxy/mod_proxy_wstunnel.dsp new file mode 100644 index 0000000..60fd062 --- /dev/null +++ b/modules/proxy/mod_proxy_wstunnel.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_wstunnel" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_wstunnel - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_wstunnel.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_wstunnel.mak" CFG="mod_proxy_wstunnel - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_wstunnel - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_wstunnel - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_wstunnel_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x409 /fo"Release/mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:".\Release\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so /opt:ref +# Begin Special Build Tool +TargetPath=.\Release\mod_proxy_wstunnel.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_wstunnel_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x409 /fo"Debug/mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_proxy_wstunnel.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so +# Begin Special Build Tool +TargetPath=.\Debug\mod_proxy_wstunnel.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "mod_proxy_wstunnel - Win32 Release" +# Name "mod_proxy_wstunnel - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy_wstunnel.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\httpd.rc +# End Source File +# End Target +# End Project diff --git a/modules/proxy/mod_proxy_wstunnel.mak b/modules/proxy/mod_proxy_wstunnel.mak new file mode 100644 index 0000000..530715f --- /dev/null +++ b/modules/proxy/mod_proxy_wstunnel.mak @@ -0,0 +1,380 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on mod_proxy_wstunnel.dsp +!IF "$(CFG)" == "" +CFG=mod_proxy_wstunnel - Win32 Release +!MESSAGE No configuration specified. Defaulting to mod_proxy_wstunnel - Win32 Release. +!ENDIF + +!IF "$(CFG)" != "mod_proxy_wstunnel - Win32 Release" && "$(CFG)" != "mod_proxy_wstunnel - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_wstunnel.mak" CFG="mod_proxy_wstunnel - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_wstunnel - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_wstunnel - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_wstunnel.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Release" "libhttpd - Win32 Release" "libaprutil - Win32 Release" "libapr - Win32 Release" "$(OUTDIR)\mod_proxy_wstunnel.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 ReleaseCLEAN" "libaprutil - Win32 ReleaseCLEAN" "libhttpd - Win32 ReleaseCLEAN" "mod_proxy - Win32 ReleaseCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_wstunnel.obj" + -@erase "$(INTDIR)\mod_proxy_wstunnel.res" + -@erase "$(INTDIR)\mod_proxy_wstunnel_src.idb" + -@erase "$(INTDIR)\mod_proxy_wstunnel_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.exp" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.lib" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.pdb" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_wstunnel_src" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_wstunnel.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_wstunnel.pdb" /debug /out:"$(OUTDIR)\mod_proxy_wstunnel.so" /implib:"$(OUTDIR)\mod_proxy_wstunnel.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so /opt:ref +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_wstunnel.obj" \ + "$(INTDIR)\mod_proxy_wstunnel.res" \ + "..\..\srclib\apr\Release\libapr-1.lib" \ + "..\..\srclib\apr-util\Release\libaprutil-1.lib" \ + "..\..\Release\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_wstunnel.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Release\mod_proxy_wstunnel.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_wstunnel.so" + if exist .\Release\mod_proxy_wstunnel.so.manifest mt.exe -manifest .\Release\mod_proxy_wstunnel.so.manifest -outputresource:.\Release\mod_proxy_wstunnel.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +!IF "$(RECURSE)" == "0" + +ALL : "$(OUTDIR)\mod_proxy_wstunnel.so" "$(DS_POSTBUILD_DEP)" + +!ELSE + +ALL : "mod_proxy - Win32 Debug" "libhttpd - Win32 Debug" "libaprutil - Win32 Debug" "libapr - Win32 Debug" "$(OUTDIR)\mod_proxy_wstunnel.so" "$(DS_POSTBUILD_DEP)" + +!ENDIF + +!IF "$(RECURSE)" == "1" +CLEAN :"libapr - Win32 DebugCLEAN" "libaprutil - Win32 DebugCLEAN" "libhttpd - Win32 DebugCLEAN" "mod_proxy - Win32 DebugCLEAN" +!ELSE +CLEAN : +!ENDIF + -@erase "$(INTDIR)\mod_proxy_wstunnel.obj" + -@erase "$(INTDIR)\mod_proxy_wstunnel.res" + -@erase "$(INTDIR)\mod_proxy_wstunnel_src.idb" + -@erase "$(INTDIR)\mod_proxy_wstunnel_src.pdb" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.exp" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.lib" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.pdb" + -@erase "$(OUTDIR)\mod_proxy_wstunnel.so" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_proxy_wstunnel_src" /FD /EHsc /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_proxy_wstunnel.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_proxy_wstunnel.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_proxy_wstunnel.pdb" /debug /out:"$(OUTDIR)\mod_proxy_wstunnel.so" /implib:"$(OUTDIR)\mod_proxy_wstunnel.lib" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_wstunnel.so +LINK32_OBJS= \ + "$(INTDIR)\mod_proxy_wstunnel.obj" \ + "$(INTDIR)\mod_proxy_wstunnel.res" \ + "..\..\srclib\apr\Debug\libapr-1.lib" \ + "..\..\srclib\apr-util\Debug\libaprutil-1.lib" \ + "..\..\Debug\libhttpd.lib" \ + "$(OUTDIR)\mod_proxy.lib" + +"$(OUTDIR)\mod_proxy_wstunnel.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +TargetPath=.\Debug\mod_proxy_wstunnel.so +SOURCE="$(InputPath)" +PostBuild_Desc=Embed .manifest +DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep + +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +"$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_proxy_wstunnel.so" + if exist .\Debug\mod_proxy_wstunnel.so.manifest mt.exe -manifest .\Debug\mod_proxy_wstunnel.so.manifest -outputresource:.\Debug\mod_proxy_wstunnel.so;2 + echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("mod_proxy_wstunnel.dep") +!INCLUDE "mod_proxy_wstunnel.dep" +!ELSE +!MESSAGE Warning: cannot find "mod_proxy_wstunnel.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" || "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" +SOURCE=.\mod_proxy_wstunnel.c + +"$(INTDIR)\mod_proxy_wstunnel.obj" : $(SOURCE) "$(INTDIR)" + + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" + +"libapr - Win32 Release" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" + cd "..\..\modules\proxy" + +"libapr - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" + +"libapr - Win32 Debug" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" + cd "..\..\modules\proxy" + +"libapr - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr" + $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" + +"libaprutil - Win32 Release" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" + cd "..\..\modules\proxy" + +"libaprutil - Win32 ReleaseCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Release" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" + +"libaprutil - Win32 Debug" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" + cd "..\..\modules\proxy" + +"libaprutil - Win32 DebugCLEAN" : + cd ".\..\..\srclib\apr-util" + $(MAKE) /$(MAKEFLAGS) /F ".\libaprutil.mak" CFG="libaprutil - Win32 Debug" RECURSE=1 CLEAN + cd "..\..\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" + +"libhttpd - Win32 Release" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" + cd ".\modules\proxy" + +"libhttpd - Win32 ReleaseCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Release" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" + +"libhttpd - Win32 Debug" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" + cd ".\modules\proxy" + +"libhttpd - Win32 DebugCLEAN" : + cd ".\..\.." + $(MAKE) /$(MAKEFLAGS) /F ".\libhttpd.mak" CFG="libhttpd - Win32 Debug" RECURSE=1 CLEAN + cd ".\modules\proxy" + +!ENDIF + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" + +"mod_proxy - Win32 Release" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" + cd "." + +"mod_proxy - Win32 ReleaseCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Release" RECURSE=1 CLEAN + cd "." + +!ELSEIF "$(CFG)" == "mod_proxy_wstunnel - Win32 Debug" + +"mod_proxy - Win32 Debug" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" + cd "." + +"mod_proxy - Win32 DebugCLEAN" : + cd "." + $(MAKE) /$(MAKEFLAGS) /F ".\mod_proxy.mak" CFG="mod_proxy - Win32 Debug" RECURSE=1 CLEAN + cd "." + +!ENDIF + +SOURCE=..\..\build\win32\httpd.rc + +!IF "$(CFG)" == "mod_proxy_wstunnel - Win32 Release" + + +"$(INTDIR)\mod_proxy_wstunnel.res" : $(SOURCE) "$(INTDIR)" + $(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 "../../build\win32" /d "_DEBUG" /d BIN_NAME="mod_proxy_wstunnel.so" /d LONG_NAME="proxy_wstunnel_module for Apache" $(SOURCE) + + +!ENDIF + + +!ENDIF + diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c new file mode 100644 index 0000000..caafde0 --- /dev/null +++ b/modules/proxy/proxy_util.c @@ -0,0 +1,5110 @@ +/* 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. + */ + +/* Utility routines for Apache proxy */ +#include "mod_proxy.h" +#include "ap_mpm.h" +#include "scoreboard.h" +#include "apr_version.h" +#include "apr_strings.h" +#include "apr_hash.h" +#include "proxy_util.h" +#include "ajp.h" +#include "scgi.h" + +#include "mod_http2.h" /* for http2_get_num_workers() */ + +#if APR_HAVE_UNISTD_H +#include <unistd.h> /* for getpid() */ +#endif + +#if APR_HAVE_SYS_UN_H +#include <sys/un.h> +#endif +#if (APR_MAJOR_VERSION < 2) +#include "apr_support.h" /* for apr_wait_for_io_or_timeout() */ +#endif + +APLOG_USE_MODULE(proxy); + +/* + * Opaque structure containing target server info when + * using a forward proxy. + * Up to now only used in combination with HTTP CONNECT. + */ +typedef struct { + int use_http_connect; /* Use SSL Tunneling via HTTP CONNECT */ + const char *target_host; /* Target hostname */ + apr_port_t target_port; /* Target port */ + const char *proxy_auth; /* Proxy authorization */ +} forward_info; + +/* Global balancer counter */ +int PROXY_DECLARE_DATA proxy_lb_workers = 0; +static int lb_workers_limit = 0; +const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_path; +const apr_strmatch_pattern PROXY_DECLARE_DATA *ap_proxy_strmatch_domain; + +extern apr_global_mutex_t *proxy_mutex; + +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_word(struct dirconn_entry *This, request_rec *r); +static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, server_rec *s); +static proxy_worker *proxy_balancer_get_best_worker(proxy_balancer *balancer, + request_rec *r, + proxy_is_best_callback_fn_t *is_best, + void *baton); + +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, + (request_rec *r, request_rec *pr), (r, pr), + OK, DECLINED) + +PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src, + apr_size_t dlen) +{ + char *thenil; + apr_size_t thelen; + + /* special case handling */ + if (!dlen) { + /* XXX: APR_ENOSPACE would be better */ + return APR_EGENERAL; + } + if (!src) { + *dst = '\0'; + return APR_SUCCESS; + } + thenil = apr_cpystrn(dst, src, dlen); + thelen = thenil - dst; + if (src[thelen] == '\0') { + return APR_SUCCESS; + } + return APR_EGENERAL; +} + +/* already called in the knowledge that the characters are hex digits */ +PROXY_DECLARE(int) ap_proxy_hex2c(const char *x) +{ + int i; + +#if !APR_CHARSET_EBCDIC + int ch = x[0]; + + if (apr_isdigit(ch)) { + i = ch - '0'; + } + else if (apr_isupper(ch)) { + i = ch - ('A' - 10); + } + else { + i = ch - ('a' - 10); + } + i <<= 4; + + ch = x[1]; + if (apr_isdigit(ch)) { + i += ch - '0'; + } + else if (apr_isupper(ch)) { + i += ch - ('A' - 10); + } + else { + i += ch - ('a' - 10); + } + return i; +#else /*APR_CHARSET_EBCDIC*/ + /* + * we assume that the hex value refers to an ASCII character + * so convert to EBCDIC so that it makes sense locally; + * + * example: + * + * client specifies %20 in URL to refer to a space char; + * at this point we're called with EBCDIC "20"; after turning + * EBCDIC "20" into binary 0x20, we then need to assume that 0x20 + * represents an ASCII char and convert 0x20 to EBCDIC, yielding + * 0x40 + */ + char buf[1]; + + if (1 == sscanf(x, "%2x", &i)) { + buf[0] = i & 0xFF; + ap_xlate_proto_from_ascii(buf, 1); + return buf[0]; + } + else { + return 0; + } +#endif /*APR_CHARSET_EBCDIC*/ +} + +PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x) +{ +#if !APR_CHARSET_EBCDIC + int i; + + x[0] = '%'; + i = (ch & 0xF0) >> 4; + if (i >= 10) { + x[1] = ('A' - 10) + i; + } + else { + x[1] = '0' + i; + } + + i = ch & 0x0F; + if (i >= 10) { + x[2] = ('A' - 10) + i; + } + else { + x[2] = '0' + i; + } +#else /*APR_CHARSET_EBCDIC*/ + static const char ntoa[] = { "0123456789ABCDEF" }; + char buf[1]; + + ch &= 0xFF; + + buf[0] = ch; + ap_xlate_proto_to_ascii(buf, 1); + + x[0] = '%'; + x[1] = ntoa[(buf[0] >> 4) & 0x0F]; + x[2] = ntoa[buf[0] & 0x0F]; + x[3] = '\0'; +#endif /*APR_CHARSET_EBCDIC*/ +} + +/* + * canonicalise a URL-encoded string + */ + +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +PROXY_DECLARE(char *)ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len, + enum enctype t, int flags, + int proxyreq) +{ + int i, j, ch; + char *y; + char *allowed; /* characters which should not be encoded */ + char *reserved; /* characters which much not be en/de-coded */ + int forcedec = flags & PROXY_CANONENC_FORCEDEC; + int noencslashesenc = flags & PROXY_CANONENC_NOENCODEDSLASHENCODING; + +/* + * N.B. in addition to :@&=, this allows ';' in an http path + * and '?' in an ftp path -- this may be revised + * + * Also, it makes a '+' character in a search string reserved, as + * it may be form-encoded. (Although RFC 1738 doesn't allow this - + * it only permits ; / ? : @ = & as reserved chars.) + */ + if (t == enc_path) { + allowed = "~$-_.+!*'(),;:@&="; + } + else if (t == enc_search) { + allowed = "$-_.!*'(),;:@&="; + } + else if (t == enc_user) { + allowed = "$-_.+!*'(),;@&="; + } + else if (t == enc_fpath) { + allowed = "$-_.+!*'(),?:@&="; + } + else { /* if (t == enc_parm) */ + allowed = "$-_.+!*'(),?/:@&="; + } + + if (t == enc_path) { + reserved = "/"; + } + else if (t == enc_search) { + reserved = "+"; + } + else { + reserved = ""; + } + + y = apr_palloc(p, 3 * len + 1); + + for (i = 0, j = 0; i < len; i++, j++) { +/* always handle '/' first */ + ch = x[i]; + if (strchr(reserved, ch)) { + y[j] = ch; + continue; + } +/* + * decode it if not already done. do not decode reverse proxied URLs + * unless specifically forced + */ + if ((forcedec || noencslashesenc + || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') { + if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) { + return NULL; + } + ch = ap_proxy_hex2c(&x[i + 1]); + if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ + y[j++] = x[i++]; + y[j++] = x[i++]; + y[j] = x[i]; + continue; + } + if (noencslashesenc && !forcedec && (proxyreq == PROXYREQ_REVERSE)) { + /* + * In the reverse proxy case when we only want to keep encoded + * slashes untouched revert back to '%' which will cause + * '%' to be encoded in the following. + */ + ch = '%'; + } + else { + i += 2; + } + } +/* recode it, if necessary */ + if (!apr_isalnum(ch) && !strchr(allowed, ch)) { + ap_proxy_c2hex(ch, &y[j]); + j += 2; + } + else { + y[j] = ch; + } + } + y[j] = '\0'; + return y; +} + +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, + enum enctype t, int forcedec, + int proxyreq) +{ + int flags; + + flags = forcedec ? PROXY_CANONENC_FORCEDEC : 0; + return ap_proxy_canonenc_ex(p, x, len, t, flags, proxyreq); +} + +/* + * Parses network-location. + * urlp on input the URL; on output the path, after the leading / + * user NULL if no user/password permitted + * password holder for password + * host holder for host + * port port number; only set if one is supplied. + * + * Returns an error string. + */ +PROXY_DECLARE(char *) + ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, + char **passwordp, char **hostp, apr_port_t *port) +{ + char *addr, *scope_id, *strp, *host, *url = *urlp; + char *user = NULL, *password = NULL; + apr_port_t tmp_port; + apr_status_t rv; + + if (url[0] != '/' || url[1] != '/') { + return "Malformed URL"; + } + host = url + 2; + url = strchr(host, '/'); + if (url == NULL) { + url = ""; + } + else { + *(url++) = '\0'; /* skip separating '/' */ + } + + /* find _last_ '@' since it might occur in user/password part */ + strp = strrchr(host, '@'); + + if (strp != NULL) { + *strp = '\0'; + user = host; + host = strp + 1; + +/* find password */ + strp = strchr(user, ':'); + if (strp != NULL) { + *strp = '\0'; + password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0); + if (password == NULL) { + return "Bad %-escape in URL (password)"; + } + } + + user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0); + if (user == NULL) { + return "Bad %-escape in URL (username)"; + } + } + if (userp != NULL) { + *userp = user; + } + if (passwordp != NULL) { + *passwordp = password; + } + + /* + * Parse the host string to separate host portion from optional port. + * Perform range checking on port. + */ + rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p); + if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) { + return "Invalid host/port"; + } + if (tmp_port != 0) { /* only update caller's port if port was specified */ + *port = tmp_port; + } + + ap_str_tolower(addr); /* DNS names are case-insensitive */ + + *urlp = url; + *hostp = addr; + + return NULL; +} + +PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message) +{ + apr_table_setn(r->notes, "error-notes", + apr_pstrcat(r->pool, + "The proxy server could not handle the request<p>" + "Reason: <strong>", ap_escape_html(r->pool, message), + "</strong></p>", + NULL)); + + /* Allow "error-notes" string to be printed by ap_send_error_response() */ + apr_table_setn(r->notes, "verbose-error-to", "*"); + + r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00898) "%s returned by %s", message, + r->uri); + return statuscode; +} + +static const char * + proxy_get_host_of_request(request_rec *r) +{ + char *url, *user = NULL, *password = NULL, *err, *host = NULL; + apr_port_t port; + + if (r->hostname != NULL) { + return r->hostname; + } + + /* Set url to the first char after "scheme://" */ + if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') { + return NULL; + } + + url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ + + err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); + + if (err != NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00899) "%s", err); + } + + r->hostname = host; + + return host; /* ought to return the port, too */ +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p) +{ + const char *addr = This->name; + long ip_addr[4]; + int i, quads; + long bits; + + /* + * if the address is given with an explicit netmask, use that + * Due to a deficiency in apr_inet_addr(), it is impossible to parse + * "partial" addresses (with less than 4 quads) correctly, i.e. + * 192.168.123 is parsed as 192.168.0.123, which is not what I want. + * I therefore have to parse the IP address manually: + * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) + * addr and mask were set by proxy_readmask() + * return 1; + */ + + /* + * Parse IP addr manually, optionally allowing + * abbreviated net addresses like 192.168. + */ + + /* Iterate over up to 4 (dotted) quads. */ + for (quads = 0; quads < 4 && *addr != '\0'; ++quads) { + char *tmp; + + if (*addr == '/' && quads > 0) { /* netmask starts here. */ + break; + } + + if (!apr_isdigit(*addr)) { + return 0; /* no digit at start of quad */ + } + + ip_addr[quads] = strtol(addr, &tmp, 0); + + if (tmp == addr) { /* expected a digit, found something else */ + return 0; + } + + if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { + /* invalid octet */ + return 0; + } + + addr = tmp; + + if (*addr == '.' && quads != 3) { + ++addr; /* after the 4th quad, a dot would be illegal */ + } + } + + for (This->addr.s_addr = 0, i = 0; i < quads; ++i) { + This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + } + + if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */ + char *tmp; + + ++addr; + + bits = strtol(addr, &tmp, 0); + + if (tmp == addr) { /* expected a digit, found something else */ + return 0; + } + + addr = tmp; + + if (bits < 0 || bits > 32) { /* netmask must be between 0 and 32 */ + return 0; + } + + } + else { + /* + * Determine (i.e., "guess") netmask by counting the + * number of trailing .0's; reduce #quads appropriately + * (so that 192.168.0.0 is equivalent to 192.168.) + */ + while (quads > 0 && ip_addr[quads - 1] == 0) { + --quads; + } + + /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */ + if (quads < 1) { + return 0; + } + + /* every zero-byte counts as 8 zero-bits */ + bits = 8 * quads; + + if (bits != 32) { /* no warning for fully qualified IP address */ + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00900) + "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld", + inet_ntoa(This->addr), bits); + } + } + + This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits)); + + if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00901) + "Warning: NetMask and IP-Addr disagree in %s/%ld", + inet_ntoa(This->addr), bits); + This->addr.s_addr &= This->mask.s_addr; + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00902) + " Set to %s/%ld", inet_ntoa(This->addr), bits); + } + + if (*addr == '\0') { + This->matcher = proxy_match_ipaddr; + return 1; + } + else { + return (*addr == '\0'); /* okay iff we've parsed the whole string */ + } +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) +{ + int i, ip_addr[4]; + struct in_addr addr, *ip; + const char *host = proxy_get_host_of_request(r); + + if (host == NULL) { /* oops! */ + return 0; + } + + memset(&addr, '\0', sizeof addr); + memset(ip_addr, '\0', sizeof ip_addr); + + if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { + for (addr.s_addr = 0, i = 0; i < 4; ++i) { + /* ap_proxy_is_ipaddr() already confirmed that we have + * a valid octet in ip_addr[i] + */ + addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + } + + if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00903) + "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00904) + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00905) + "%s", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00906) + "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00907) + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00908) + "%s", inet_ntoa(This->mask)); + } +#endif + } + else { + struct apr_sockaddr_t *reqaddr; + + if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool) + != APR_SUCCESS) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00909) + "2)IP-NoMatch: hostname=%s msg=Host not found", host); +#endif + return 0; + } + + /* Try to deal with multiple IP addr's for a host */ + /* FIXME: This needs to be able to deal with IPv6 */ + while (reqaddr) { + ip = (struct in_addr *) reqaddr->ipaddr_ptr; + if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00910) + "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00911) + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00912) + "%s", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00913) + "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00914) + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00915) + "%s", inet_ntoa(This->mask)); + } +#endif + reqaddr = reqaddr->next; + } + } + + return 0; +} + +/* Return TRUE if addr represents a domain name */ +PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p) +{ + char *addr = This->name; + int i; + + /* Domain name must start with a '.' */ + if (addr[0] != '.') { + return 0; + } + + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) { + continue; + } + +#if 0 + if (addr[i] == ':') { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(03234) + "@@@@ handle optional port in proxy_is_domainname()"); + /* @@@@ handle optional port */ + } +#endif + + if (addr[i] != '\0') { + return 0; + } + + /* Strip trailing dots */ + for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) { + addr[i] = '\0'; + } + + This->matcher = proxy_match_domainname; + return 1; +} + +/* Return TRUE if host "host" is in domain "domain" */ +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + int d_len = strlen(This->name), h_len; + + if (host == NULL) { /* some error was logged already */ + return 0; + } + + h_len = strlen(host); + + /* @@@ do this within the setup? */ + /* Ignore trailing dots in domain comparison: */ + while (d_len > 0 && This->name[d_len - 1] == '.') { + --d_len; + } + while (h_len > 0 && host[h_len - 1] == '.') { + --h_len; + } + return h_len > d_len + && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; +} + +/* Return TRUE if host represents a host name */ +PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p) +{ + struct apr_sockaddr_t *addr; + char *host = This->name; + int i; + + /* Host names must not start with a '.' */ + if (host[0] == '.') { + return 0; + } + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i); + + if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) { + return 0; + } + + This->hostaddr = addr; + + /* Strip trailing dots */ + for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) { + host[i] = '\0'; + } + + This->matcher = proxy_match_hostname; + return 1; +} + +/* Return TRUE if host "host" is equal to host2 "host2" */ +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) +{ + char *host = This->name; + const char *host2 = proxy_get_host_of_request(r); + int h2_len; + int h1_len; + + if (host == NULL || host2 == NULL) { + return 0; /* oops! */ + } + + h2_len = strlen(host2); + h1_len = strlen(host); + +#if 0 + struct apr_sockaddr_t *addr = *This->hostaddr; + + /* Try to deal with multiple IP addr's for a host */ + while (addr) { + if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?) + return 1; + addr = addr->next; + } +#endif + + /* Ignore trailing dots in host2 comparison: */ + while (h2_len > 0 && host2[h2_len - 1] == '.') { + --h2_len; + } + while (h1_len > 0 && host[h1_len - 1] == '.') { + --h1_len; + } + return h1_len == h2_len + && strncasecmp(host, host2, h1_len) == 0; +} + +/* Return TRUE if addr is to be matched as a word */ +PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p) +{ + This->matcher = proxy_match_word; + return 1; +} + +/* Return TRUE if string "str2" occurs literally in "str1" */ +static int proxy_match_word(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + return host != NULL && ap_strstr_c(host, This->name) != NULL; +} + +/* Backwards-compatible interface. */ +PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, + apr_sockaddr_t *uri_addr) +{ + return ap_proxy_checkproxyblock2(r, conf, uri_addr->hostname, uri_addr); +} + +#define MAX_IP_STR_LEN (46) + +PROXY_DECLARE(int) ap_proxy_checkproxyblock2(request_rec *r, proxy_server_conf *conf, + const char *hostname, apr_sockaddr_t *addr) +{ + int j; + + /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ + for (j = 0; j < conf->noproxies->nelts; j++) { + struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; + struct apr_sockaddr_t *conf_addr; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "checking remote machine [%s] against [%s]", + hostname, npent[j].name); + if (ap_strstr_c(hostname, npent[j].name) || npent[j].name[0] == '*') { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00916) + "connect to remote machine %s blocked: name %s " + "matched", hostname, npent[j].name); + return HTTP_FORBIDDEN; + } + + /* No IP address checks if no IP address was passed in, + * i.e. the forward address proxy case, where this server does + * not resolve the hostname. */ + if (!addr) + continue; + + for (conf_addr = npent[j].addr; conf_addr; conf_addr = conf_addr->next) { + char caddr[MAX_IP_STR_LEN], uaddr[MAX_IP_STR_LEN]; + apr_sockaddr_t *uri_addr; + + if (apr_sockaddr_ip_getbuf(caddr, sizeof caddr, conf_addr)) + continue; + + for (uri_addr = addr; uri_addr; uri_addr = uri_addr->next) { + if (apr_sockaddr_ip_getbuf(uaddr, sizeof uaddr, uri_addr)) + continue; + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "ProxyBlock comparing %s and %s", caddr, uaddr); + if (!strcmp(caddr, uaddr)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00917) + "connect to remote machine %s blocked: " + "IP %s matched", hostname, caddr); + return HTTP_FORBIDDEN; + } + } + } + } + + return OK; +} + +/* set up the minimal filter set */ +PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r) +{ + ap_add_input_filter("HTTP_IN", NULL, r, c); + return OK; +} + +PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r, + proxy_dir_conf *conf, const char *url) +{ + proxy_req_conf *rconf; + struct proxy_alias *ent; + int i, l1, l1_orig, l2; + char *u; + + /* + * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT> + * after the hostname + * XXX FIXME: Ensure the /uri component is a case sensitive match + */ + if (r->proxyreq != PROXYREQ_REVERSE) { + return url; + } + + l1_orig = strlen(url); + if (conf->interpolate_env == 1) { + rconf = ap_get_module_config(r->request_config, &proxy_module); + ent = (struct proxy_alias *)rconf->raliases->elts; + } + else { + ent = (struct proxy_alias *)conf->raliases->elts; + } + for (i = 0; i < conf->raliases->nelts; i++) { + proxy_server_conf *sconf = (proxy_server_conf *) + ap_get_module_config(r->server->module_config, &proxy_module); + proxy_balancer *balancer; + const char *real = ent[i].real; + + /* Restore the url length, if it had been changed by the code below */ + l1 = l1_orig; + + /* + * First check if mapping against a balancer and see + * if we have such a entity. If so, then we need to + * find the particulars of the actual worker which may + * or may not be the right one... basically, we need + * to find which member actually handled this request. + */ + if (ap_proxy_valid_balancer_name((char *)real, 0) && + (balancer = ap_proxy_get_balancer(r->pool, sconf, real, 1))) { + int n, l3 = 0; + proxy_worker **worker = (proxy_worker **)balancer->workers->elts; + const char *urlpart = ap_strchr_c(real + sizeof(BALANCER_PREFIX) - 1, '/'); + if (urlpart) { + if (!urlpart[1]) + urlpart = NULL; + else + l3 = strlen(urlpart); + } + /* The balancer comparison is a bit trickier. Given the context + * BalancerMember balancer://alias http://example.com/foo + * ProxyPassReverse /bash balancer://alias/bar + * translate url http://example.com/foo/bar/that to /bash/that + */ + for (n = 0; n < balancer->workers->nelts; n++) { + l2 = strlen((*worker)->s->name_ex); + if (urlpart) { + /* urlpart (l3) assuredly starts with its own '/' */ + if ((*worker)->s->name_ex[l2 - 1] == '/') + --l2; + if (l1 >= l2 + l3 + && strncasecmp((*worker)->s->name_ex, url, l2) == 0 + && strncmp(urlpart, url + l2, l3) == 0) { + u = apr_pstrcat(r->pool, ent[i].fake, &url[l2 + l3], + NULL); + return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r); + } + } + else if (l1 >= l2 && strncasecmp((*worker)->s->name_ex, url, l2) == 0) { + /* edge case where fake is just "/"... avoid double slash */ + if ((ent[i].fake[0] == '/') && (ent[i].fake[1] == 0) && (url[l2] == '/')) { + u = apr_pstrdup(r->pool, &url[l2]); + } else { + u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL); + } + return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r); + } + worker++; + } + } + else { + const char *part = url; + l2 = strlen(real); + if (real[0] == '/') { + part = ap_strstr_c(url, "://"); + if (part) { + part = ap_strchr_c(part+3, '/'); + if (part) { + l1 = strlen(part); + } + else { + part = url; + } + } + else { + part = url; + } + } + if (l2 > 0 && l1 >= l2 && strncasecmp(real, part, l2) == 0) { + u = apr_pstrcat(r->pool, ent[i].fake, &part[l2], NULL); + return ap_is_url(u) ? u : ap_construct_url(r->pool, u, r); + } + } + } + + return url; +} + +/* + * Cookies are a bit trickier to match: we've got two substrings to worry + * about, and we can't just find them with strstr 'cos of case. Regexp + * matching would be an easy fix, but for better consistency with all the + * other matches we'll refrain and use apr_strmatch to find path=/domain= + * and stick to plain strings for the config values. + */ +PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r, + proxy_dir_conf *conf, const char *str) +{ + proxy_req_conf *rconf = ap_get_module_config(r->request_config, + &proxy_module); + struct proxy_alias *ent; + apr_size_t len = strlen(str); + const char *newpath = NULL; + const char *newdomain = NULL; + const char *pathp; + const char *domainp; + const char *pathe = NULL; + const char *domaine = NULL; + apr_size_t l1, l2, poffs = 0, doffs = 0; + int i; + int ddiff = 0; + int pdiff = 0; + char *tmpstr, *tmpstr_orig, *token, *last, *ret; + + if (r->proxyreq != PROXYREQ_REVERSE) { + return str; + } + + /* + * Find the match and replacement, but save replacing until we've done + * both path and domain so we know the new strlen + */ + tmpstr_orig = tmpstr = apr_pstrdup(r->pool, str); + while ((token = apr_strtok(tmpstr, ";", &last))) { + /* skip leading spaces */ + while (apr_isspace(*token)) { + ++token; + } + + if (ap_cstr_casecmpn("path=", token, 5) == 0) { + pathp = token + 5; + poffs = pathp - tmpstr_orig; + l1 = strlen(pathp); + pathe = str + poffs + l1; + if (conf->interpolate_env == 1) { + ent = (struct proxy_alias *)rconf->cookie_paths->elts; + } + else { + ent = (struct proxy_alias *)conf->cookie_paths->elts; + } + for (i = 0; i < conf->cookie_paths->nelts; i++) { + l2 = strlen(ent[i].fake); + if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) { + newpath = ent[i].real; + pdiff = strlen(newpath) - l1; + break; + } + } + } + else if (ap_cstr_casecmpn("domain=", token, 7) == 0) { + domainp = token + 7; + doffs = domainp - tmpstr_orig; + l1 = strlen(domainp); + domaine = str + doffs + l1; + if (conf->interpolate_env == 1) { + ent = (struct proxy_alias *)rconf->cookie_domains->elts; + } + else { + ent = (struct proxy_alias *)conf->cookie_domains->elts; + } + for (i = 0; i < conf->cookie_domains->nelts; i++) { + l2 = strlen(ent[i].fake); + if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) { + newdomain = ent[i].real; + ddiff = strlen(newdomain) - l1; + break; + } + } + } + + /* Iterate the remaining tokens using apr_strtok(NULL, ...) */ + tmpstr = NULL; + } + + if (newpath) { + ret = apr_palloc(r->pool, len + pdiff + ddiff + 1); + l1 = strlen(newpath); + if (newdomain) { + l2 = strlen(newdomain); + if (doffs > poffs) { + memcpy(ret, str, poffs); + memcpy(ret + poffs, newpath, l1); + memcpy(ret + poffs + l1, pathe, str + doffs - pathe); + memcpy(ret + doffs + pdiff, newdomain, l2); + strcpy(ret + doffs + pdiff + l2, domaine); + } + else { + memcpy(ret, str, doffs) ; + memcpy(ret + doffs, newdomain, l2); + memcpy(ret + doffs + l2, domaine, str + poffs - domaine); + memcpy(ret + poffs + ddiff, newpath, l1); + strcpy(ret + poffs + ddiff + l1, pathe); + } + } + else { + memcpy(ret, str, poffs); + memcpy(ret + poffs, newpath, l1); + strcpy(ret + poffs + l1, pathe); + } + } + else if (newdomain) { + ret = apr_palloc(r->pool, len + ddiff + 1); + l2 = strlen(newdomain); + memcpy(ret, str, doffs); + memcpy(ret + doffs, newdomain, l2); + strcpy(ret + doffs + l2, domaine); + } + else { + ret = (char *)str; /* no change */ + } + + return ret; +} + +/* + * BALANCER related... + */ + +/* + * verifies that the balancer name conforms to standards. + */ +PROXY_DECLARE(int) ap_proxy_valid_balancer_name(char *name, int i) +{ + if (!i) + i = sizeof(BALANCER_PREFIX)-1; + return (!ap_cstr_casecmpn(name, BALANCER_PREFIX, i)); +} + + +PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p, + proxy_server_conf *conf, + const char *url, + int care) +{ + proxy_balancer *balancer; + char *c, *uri = apr_pstrdup(p, url); + int i; + proxy_hashes hash; + + c = strchr(uri, ':'); + if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { + return NULL; + } + /* remove path from uri */ + if ((c = strchr(c + 3, '/'))) { + *c = '\0'; + } + ap_str_tolower(uri); + hash.def = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_DEFAULT); + hash.fnv = ap_proxy_hashfunc(uri, PROXY_HASHFUNC_FNV); + balancer = (proxy_balancer *)conf->balancers->elts; + for (i = 0; i < conf->balancers->nelts; i++) { + if (balancer->hash.def == hash.def && balancer->hash.fnv == hash.fnv) { + if (!care || !balancer->s->inactive) { + return balancer; + } + } + balancer++; + } + return NULL; +} + + +PROXY_DECLARE(char *) ap_proxy_update_balancer(apr_pool_t *p, + proxy_balancer *balancer, + const char *url) +{ + apr_uri_t puri; + if (!url) { + return NULL; + } + if (apr_uri_parse(p, url, &puri) != APR_SUCCESS) { + return apr_psprintf(p, "unable to parse: %s", url); + } + if (puri.path && PROXY_STRNCPY(balancer->s->vpath, puri.path) != APR_SUCCESS) { + return apr_psprintf(p, "balancer %s front-end virtual-path (%s) too long", + balancer->s->name, puri.path); + } + if (puri.hostname && PROXY_STRNCPY(balancer->s->vhost, puri.hostname) != APR_SUCCESS) { + return apr_psprintf(p, "balancer %s front-end vhost name (%s) too long", + balancer->s->name, puri.hostname); + } + return NULL; +} + +#define PROXY_UNSET_NONCE '\n' + +PROXY_DECLARE(char *) ap_proxy_define_balancer(apr_pool_t *p, + proxy_balancer **balancer, + proxy_server_conf *conf, + const char *url, + const char *alias, + int do_malloc) +{ + proxy_balancer_method *lbmethod; + proxy_balancer_shared *bshared; + char *c, *q, *uri = apr_pstrdup(p, url); + const char *sname; + + /* We should never get here without a valid BALANCER_PREFIX... */ + + c = strchr(uri, ':'); + if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') + return apr_psprintf(p, "Bad syntax for a balancer name (%s)", uri); + /* remove path from uri */ + if ((q = strchr(c + 3, '/'))) + *q = '\0'; + + ap_str_tolower(uri); + *balancer = apr_array_push(conf->balancers); + memset(*balancer, 0, sizeof(proxy_balancer)); + + /* + * NOTE: The default method is byrequests - if it doesn't + * exist, that's OK at this time. We check when we share and sync + */ + lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0"); + (*balancer)->lbmethod = lbmethod; + + (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker *)); +#if APR_HAS_THREADS + (*balancer)->gmutex = NULL; + (*balancer)->tmutex = NULL; +#endif + + if (do_malloc) + bshared = ap_malloc(sizeof(proxy_balancer_shared)); + else + bshared = apr_palloc(p, sizeof(proxy_balancer_shared)); + + memset(bshared, 0, sizeof(proxy_balancer_shared)); + + bshared->was_malloced = (do_malloc != 0); + PROXY_STRNCPY(bshared->lbpname, "byrequests"); + if (PROXY_STRNCPY(bshared->name, uri) != APR_SUCCESS) { + if (do_malloc) free(bshared); + return apr_psprintf(p, "balancer name (%s) too long", uri); + } + (*balancer)->lbmethod_set = 1; + + /* + * We do the below for verification. The real sname will be + * done post_config + */ + ap_pstr2_alnum(p, bshared->name + sizeof(BALANCER_PREFIX) - 1, + &sname); + sname = apr_pstrcat(p, conf->id, "_", sname, NULL); + if (PROXY_STRNCPY(bshared->sname, sname) != APR_SUCCESS) { + if (do_malloc) free(bshared); + return apr_psprintf(p, "balancer safe-name (%s) too long", sname); + } + bshared->hash.def = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_DEFAULT); + bshared->hash.fnv = ap_proxy_hashfunc(bshared->name, PROXY_HASHFUNC_FNV); + (*balancer)->hash = bshared->hash; + + bshared->forcerecovery = 1; + bshared->sticky_separator = '.'; + *bshared->nonce = PROXY_UNSET_NONCE; /* impossible valid input */ + + (*balancer)->s = bshared; + (*balancer)->sconf = conf; + + return ap_proxy_update_balancer(p, *balancer, alias); +} + +/* + * Create an already defined balancer and free up memory. + */ +PROXY_DECLARE(apr_status_t) ap_proxy_share_balancer(proxy_balancer *balancer, + proxy_balancer_shared *shm, + int i) +{ + apr_status_t rv = APR_SUCCESS; + proxy_balancer_method *lbmethod; + char *action = "copying"; + if (!shm || !balancer->s) + return APR_EINVAL; + + if ((balancer->s->hash.def != shm->hash.def) || + (balancer->s->hash.fnv != shm->hash.fnv)) { + memcpy(shm, balancer->s, sizeof(proxy_balancer_shared)); + if (balancer->s->was_malloced) + free(balancer->s); + } else { + action = "re-using"; + } + balancer->s = shm; + balancer->s->index = i; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02337) + "%s shm[%d] (0x%pp) for %s", action, i, (void *)shm, + balancer->s->name); + /* the below should always succeed */ + lbmethod = ap_lookup_provider(PROXY_LBMETHOD, balancer->s->lbpname, "0"); + if (lbmethod) { + balancer->lbmethod = lbmethod; + balancer->lbmethod_set = 1; + } else { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(02432) + "Cannot find LB Method: %s", balancer->s->lbpname); + return APR_EINVAL; + } + if (*balancer->s->nonce == PROXY_UNSET_NONCE) { + char nonce[APR_UUID_FORMATTED_LENGTH + 1]; + apr_uuid_t uuid; + + /* Generate a pseudo-UUID from the PRNG to use as a nonce for + * the lifetime of the process. uuid.data is a char array so + * this is an adequate substitute for apr_uuid_get(). */ + ap_random_insecure_bytes(uuid.data, sizeof uuid.data); + apr_uuid_format(nonce, &uuid); + rv = PROXY_STRNCPY(balancer->s->nonce, nonce); + } + return rv; +} + +PROXY_DECLARE(apr_status_t) ap_proxy_initialize_balancer(proxy_balancer *balancer, server_rec *s, apr_pool_t *p) +{ +#if APR_HAS_THREADS + apr_status_t rv = APR_SUCCESS; +#endif + ap_slotmem_provider_t *storage = balancer->storage; + apr_size_t size; + unsigned int num; + + if (!storage) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00918) + "no provider for %s", balancer->s->name); + return APR_EGENERAL; + } + /* + * for each balancer we need to init the global + * mutex and then attach to the shared worker shm + */ + if (!balancer->gmutex) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00919) + "no mutex %s", balancer->s->name); + return APR_EGENERAL; + } + + /* Re-open the mutex for the child. */ + rv = apr_global_mutex_child_init(&(balancer->gmutex), + apr_global_mutex_lockfile(balancer->gmutex), + p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00920) + "Failed to reopen mutex %s in child", + balancer->s->name); + return rv; + } + + /* now attach */ + storage->attach(&(balancer->wslot), balancer->s->sname, &size, &num, p); + if (!balancer->wslot) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00921) "slotmem_attach failed"); + return APR_EGENERAL; + } + if (balancer->lbmethod && balancer->lbmethod->reset) + balancer->lbmethod->reset(balancer, s); + +#if APR_HAS_THREADS + if (balancer->tmutex == NULL) { + rv = apr_thread_mutex_create(&(balancer->tmutex), APR_THREAD_MUTEX_DEFAULT, p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(00922) + "can not create balancer thread mutex"); + return rv; + } + } +#endif + return APR_SUCCESS; +} + +static proxy_worker *proxy_balancer_get_best_worker(proxy_balancer *balancer, + request_rec *r, + proxy_is_best_callback_fn_t *is_best, + void *baton) +{ + int i = 0; + int cur_lbset = 0; + int max_lbset = 0; + int unusable_workers = 0; + apr_pool_t *tpool = NULL; + apr_array_header_t *spares = NULL; + apr_array_header_t *standbys = NULL; + proxy_worker *worker = NULL; + proxy_worker *best_worker = NULL; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(10122) + "proxy: Entering %s for BALANCER (%s)", + balancer->lbmethod->name, balancer->s->name); + + apr_pool_create(&tpool, r->pool); + apr_pool_tag(tpool, "proxy_lb_best"); + + spares = apr_array_make(tpool, 1, sizeof(proxy_worker*)); + standbys = apr_array_make(tpool, 1, sizeof(proxy_worker*)); + + /* Process lbsets in order, only replacing unusable workers in a given lbset + * with available spares from the same lbset. Hot standbys will be used as a + * last resort when all other workers and spares are unavailable. + */ + for (cur_lbset = 0; !best_worker && (cur_lbset <= max_lbset); cur_lbset++) { + unusable_workers = 0; + apr_array_clear(spares); + apr_array_clear(standbys); + + for (i = 0; i < balancer->workers->nelts; i++) { + worker = APR_ARRAY_IDX(balancer->workers, i, proxy_worker *); + + if (worker->s->lbset > max_lbset) { + max_lbset = worker->s->lbset; + } + + if (worker->s->lbset != cur_lbset) { + continue; + } + + /* A draining worker that is neither a spare nor a standby should be + * considered unusable to be replaced by spares. + */ + if (PROXY_WORKER_IS_DRAINING(worker)) { + if (!PROXY_WORKER_IS_SPARE(worker) && !PROXY_WORKER_IS_STANDBY(worker)) { + unusable_workers++; + } + + continue; + } + + /* If the worker is in error state run retry on that worker. It will + * be marked as operational if the retry timeout is elapsed. The + * worker might still be unusable, but we try anyway. + */ + if (!PROXY_WORKER_IS_USABLE(worker)) { + ap_proxy_retry_worker("BALANCER", worker, r->server); + } + + if (PROXY_WORKER_IS_SPARE(worker)) { + if (PROXY_WORKER_IS_USABLE(worker)) { + APR_ARRAY_PUSH(spares, proxy_worker *) = worker; + } + } + else if (PROXY_WORKER_IS_STANDBY(worker)) { + if (PROXY_WORKER_IS_USABLE(worker)) { + APR_ARRAY_PUSH(standbys, proxy_worker *) = worker; + } + } + else if (PROXY_WORKER_IS_USABLE(worker)) { + if (is_best(worker, best_worker, baton)) { + best_worker = worker; + } + } + else { + unusable_workers++; + } + } + + /* Check if any spares are best. */ + for (i = 0; (i < spares->nelts) && (i < unusable_workers); i++) { + worker = APR_ARRAY_IDX(spares, i, proxy_worker *); + + if (is_best(worker, best_worker, baton)) { + best_worker = worker; + } + } + + /* If no workers are available, use the standbys. */ + if (!best_worker) { + for (i = 0; i < standbys->nelts; i++) { + worker = APR_ARRAY_IDX(standbys, i, proxy_worker *); + + if (is_best(worker, best_worker, baton)) { + best_worker = worker; + } + } + } + } + + apr_pool_destroy(tpool); + + if (best_worker) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(10123) + "proxy: %s selected worker \"%s\" : busy %" APR_SIZE_T_FMT " : lbstatus %d", + balancer->lbmethod->name, best_worker->s->name_ex, + best_worker->s->busy, best_worker->s->lbstatus); + } + + return best_worker; +} + +PROXY_DECLARE(proxy_worker *) ap_proxy_balancer_get_best_worker(proxy_balancer *balancer, + request_rec *r, + proxy_is_best_callback_fn_t *is_best, + void *baton) +{ + return proxy_balancer_get_best_worker(balancer, r, is_best, baton); +} + +/* + * CONNECTION related... + */ + +static void socket_cleanup(proxy_conn_rec *conn) +{ + conn->sock = NULL; + conn->tmp_bb = NULL; + conn->connection = NULL; + conn->ssl_hostname = NULL; + apr_pool_clear(conn->scpool); +} + +static apr_status_t conn_pool_cleanup(void *theworker) +{ + ((proxy_worker *)theworker)->cp = NULL; + return APR_SUCCESS; +} + +static void init_conn_pool(apr_pool_t *p, proxy_worker *worker) +{ + apr_pool_t *pool; + apr_pool_t *dns_pool; + proxy_conn_pool *cp; + + /* + * Create a connection pool's subpool. + * This pool is used for connection recycling. + * Once the worker is added it is never removed but + * it can be disabled. + */ + apr_pool_create(&pool, p); + apr_pool_tag(pool, "proxy_worker_cp"); + /* + * Create a subpool of the connection pool for worker + * scoped DNS resolutions. This is needed to avoid race + * conditions in using the connection pool by multiple + * threads during ramp up. + */ + apr_pool_create(&dns_pool, pool); + apr_pool_tag(dns_pool, "proxy_worker_dns"); + /* + * Alloc from the same pool as worker. + * proxy_conn_pool is permanently attached to the worker. + */ + cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool)); + cp->pool = pool; + cp->dns_pool = dns_pool; + worker->cp = cp; + + apr_pool_pre_cleanup_register(p, worker, conn_pool_cleanup); +} + +PROXY_DECLARE(int) ap_proxy_connection_reusable(proxy_conn_rec *conn) +{ + proxy_worker *worker = conn->worker; + + return ! (conn->close || !worker->s->is_address_reusable || worker->s->disablereuse); +} + +static apr_status_t connection_cleanup(void *theconn) +{ + proxy_conn_rec *conn = (proxy_conn_rec *)theconn; + proxy_worker *worker = conn->worker; + + if (conn->r) { + apr_pool_destroy(conn->r->pool); + conn->r = NULL; + } + + /* Sanity check: Did we already return the pooled connection? */ + if (conn->inreslist) { + ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool, APLOGNO(00923) + "Pooled connection 0x%pp for worker %s has been" + " already returned to the connection pool.", conn, + ap_proxy_worker_name(conn->pool, worker)); + return APR_SUCCESS; + } + + /* determine if the connection need to be closed */ + if (!worker->s->is_address_reusable || worker->s->disablereuse) { + apr_pool_t *p = conn->pool; + apr_pool_clear(p); + conn = apr_pcalloc(p, sizeof(proxy_conn_rec)); + conn->pool = p; + conn->worker = worker; + apr_pool_create(&(conn->scpool), p); + apr_pool_tag(conn->scpool, "proxy_conn_scpool"); + } + else if (conn->close + || (conn->connection + && conn->connection->keepalive == AP_CONN_CLOSE)) { + socket_cleanup(conn); + conn->close = 0; + } + else if (conn->is_ssl) { + /* Unbind/reset the SSL connection dir config (sslconn->dc) from + * r->per_dir_config, r will likely get destroyed before this proxy + * conn is reused. + */ + ap_proxy_ssl_engine(conn->connection, worker->section_config, 1); + } + + if (worker->s->hmax && worker->cp->res) { + conn->inreslist = 1; + apr_reslist_release(worker->cp->res, (void *)conn); + } + else + { + worker->cp->conn = conn; + } + + /* Always return the SUCCESS */ + return APR_SUCCESS; +} + +/* DEPRECATED */ +PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn, + request_rec *r) +{ + apr_status_t rv; + + /* + * If we have an existing SSL connection it might be possible that the + * server sent some SSL message we have not read so far (e.g. an SSL + * shutdown message if the server closed the keepalive connection while + * the connection was held unused in our pool). + * So ensure that if present (=> APR_NONBLOCK_READ) it is read and + * processed. We don't expect any data to be in the returned brigade. + */ + if (conn->sock && conn->connection) { + rv = ap_get_brigade(conn->connection->input_filters, conn->tmp_bb, + AP_MODE_READBYTES, APR_NONBLOCK_READ, + HUGE_STRING_LEN); + if (!APR_BRIGADE_EMPTY(conn->tmp_bb)) { + apr_off_t len; + + rv = apr_brigade_length(conn->tmp_bb, 0, &len); + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, rv, r, + "SSL cleanup brigade contained %" + APR_OFF_T_FMT " bytes of data.", len); + apr_brigade_cleanup(conn->tmp_bb); + } + if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) { + socket_cleanup(conn); + } + } + return APR_SUCCESS; +} + +/* reslist constructor */ +static apr_status_t connection_constructor(void **resource, void *params, + apr_pool_t *pool) +{ + apr_pool_t *ctx; + apr_pool_t *scpool; + proxy_conn_rec *conn; + proxy_worker *worker = (proxy_worker *)params; + + /* + * Create the subpool for each connection + * This keeps the memory consumption constant + * when disconnecting from backend. + */ + apr_pool_create(&ctx, pool); + apr_pool_tag(ctx, "proxy_conn_pool"); + /* + * Create another subpool that manages the data for the + * socket and the connection member of the proxy_conn_rec struct as we + * destroy this data more frequently than other data in the proxy_conn_rec + * struct like hostname and addr (at least in the case where we have + * keepalive connections that timed out). + */ + apr_pool_create(&scpool, ctx); + apr_pool_tag(scpool, "proxy_conn_scpool"); + conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec)); + + conn->pool = ctx; + conn->scpool = scpool; + conn->worker = worker; + conn->inreslist = 1; + *resource = conn; + + return APR_SUCCESS; +} + +/* reslist destructor */ +static apr_status_t connection_destructor(void *resource, void *params, + apr_pool_t *pool) +{ + proxy_worker *worker = params; + + /* Destroy the pool only if not called from reslist_destroy */ + if (worker->cp) { + proxy_conn_rec *conn = resource; + apr_pool_destroy(conn->pool); + } + + return APR_SUCCESS; +} + +/* + * WORKER related... + */ + +PROXY_DECLARE(char *) ap_proxy_worker_name(apr_pool_t *p, + proxy_worker *worker) +{ + if (!(*worker->s->uds_path) || !p) { + /* just in case */ + return worker->s->name_ex; + } + return apr_pstrcat(p, "unix:", worker->s->uds_path, "|", worker->s->name_ex, NULL); +} + +PROXY_DECLARE(int) ap_proxy_worker_can_upgrade(apr_pool_t *p, + const proxy_worker *worker, + const char *upgrade, + const char *dflt) +{ + /* Find in worker->s->upgrade list (if any) */ + const char *worker_upgrade = worker->s->upgrade; + if (*worker_upgrade) { + return (strcmp(worker_upgrade, "*") == 0 + || ap_cstr_casecmp(worker_upgrade, upgrade) == 0 + || ap_find_token(p, worker_upgrade, upgrade)); + } + + /* Compare to the provided default (if any) */ + return (dflt && ap_cstr_casecmp(dflt, upgrade) == 0); +} + +/* + * Taken from ap_strcmp_match() : + * Match = 0, NoMatch = 1, Abort = -1, Inval = -2 + * Based loosely on sections of wildmat.c by Rich Salz + * Hmmm... shouldn't this really go component by component? + * + * Adds handling of the "\<any>" => "<any>" unescaping. + */ +static int ap_proxy_strcmp_ematch(const char *str, const char *expected) +{ + apr_size_t x, y; + + for (x = 0, y = 0; expected[y]; ++y, ++x) { + if (expected[y] == '$' && apr_isdigit(expected[y + 1])) { + do { + y += 2; + } while (expected[y] == '$' && apr_isdigit(expected[y + 1])); + if (!expected[y]) + return 0; + while (str[x]) { + int ret; + if ((ret = ap_proxy_strcmp_ematch(&str[x++], &expected[y])) != 1) + return ret; + } + return -1; + } + else if (!str[x]) { + return -1; + } + else if (expected[y] == '\\' && !expected[++y]) { + /* NUL is an invalid char! */ + return -2; + } + if (str[x] != expected[y]) + return 1; + } + /* We got all the way through the worker path without a difference */ + return 0; +} + +PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker_ex(apr_pool_t *p, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + unsigned int mask) +{ + proxy_worker *worker; + proxy_worker *max_worker = NULL; + int max_match = 0; + int url_length; + int min_match; + int worker_name_length; + const char *c; + char *url_copy; + int i; + + if (!url) { + return NULL; + } + + if (!(mask & AP_PROXY_WORKER_NO_UDS)) { + url = ap_proxy_de_socketfy(p, url); + if (!url) { + return NULL; + } + } + + c = ap_strchr_c(url, ':'); + if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') { + return NULL; + } + + url_length = strlen(url); + url_copy = apr_pstrmemdup(p, url, url_length); + + /* Default to lookup for both _PREFIX and _MATCH workers */ + if (!(mask & (AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH))) { + mask |= AP_PROXY_WORKER_IS_PREFIX | AP_PROXY_WORKER_IS_MATCH; + } + + /* + * We need to find the start of the path and + * therefore we know the length of the scheme://hostname/ + * part to we can force-lowercase everything up to + * the start of the path. + */ + c = ap_strchr_c(c+3, '/'); + if (c) { + char *pathstart; + pathstart = url_copy + (c - url); + *pathstart = '\0'; + ap_str_tolower(url_copy); + min_match = strlen(url_copy); + *pathstart = '/'; + } + else { + ap_str_tolower(url_copy); + min_match = strlen(url_copy); + } + /* + * Do a "longest match" on the worker name to find the worker that + * fits best to the URL, but keep in mind that we must have at least + * a minimum matching of length min_match such that + * scheme://hostname[:port] matches between worker and url. + */ + + if (balancer) { + proxy_worker **workers = (proxy_worker **)balancer->workers->elts; + for (i = 0; i < balancer->workers->nelts; i++, workers++) { + worker = *workers; + if ( ((worker_name_length = strlen(worker->s->name_ex)) <= url_length) + && (worker_name_length >= min_match) + && (worker_name_length > max_match) + && (worker->s->is_name_matchable + || ((mask & AP_PROXY_WORKER_IS_PREFIX) + && strncmp(url_copy, worker->s->name_ex, + worker_name_length) == 0)) + && (!worker->s->is_name_matchable + || ((mask & AP_PROXY_WORKER_IS_MATCH) + && ap_proxy_strcmp_ematch(url_copy, + worker->s->name_ex) == 0)) ) { + max_worker = worker; + max_match = worker_name_length; + } + } + } else { + worker = (proxy_worker *)conf->workers->elts; + for (i = 0; i < conf->workers->nelts; i++, worker++) { + if ( ((worker_name_length = strlen(worker->s->name_ex)) <= url_length) + && (worker_name_length >= min_match) + && (worker_name_length > max_match) + && (worker->s->is_name_matchable + || ((mask & AP_PROXY_WORKER_IS_PREFIX) + && strncmp(url_copy, worker->s->name_ex, + worker_name_length) == 0)) + && (!worker->s->is_name_matchable + || ((mask & AP_PROXY_WORKER_IS_MATCH) + && ap_proxy_strcmp_ematch(url_copy, + worker->s->name_ex) == 0)) ) { + max_worker = worker; + max_match = worker_name_length; + } + } + } + + return max_worker; +} + +PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url) +{ + return ap_proxy_get_worker_ex(p, balancer, conf, url, 0); +} + +/* + * To create a worker from scratch first we define the + * specifics of the worker; this is all local data. + * We then allocate space for it if data needs to be + * shared. This allows for dynamic addition during + * config and runtime. + */ +PROXY_DECLARE(char *) ap_proxy_define_worker_ex(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + unsigned int mask) +{ + apr_status_t rv; + proxy_worker_shared *wshared; + const char *ptr = NULL, *sockpath = NULL, *pdollars = NULL; + apr_port_t port_of_scheme; + apr_uri_t uri; + + /* + * Look to see if we are using UDS: + * require format: unix:/path/foo/bar.sock|http://ignored/path2/ + * This results in talking http to the socket at /path/foo/bar.sock + */ + if (!ap_cstr_casecmpn(url, "unix:", 5) + && (ptr = ap_strchr_c(url + 5, '|'))) { + rv = apr_uri_parse(p, apr_pstrmemdup(p, url, ptr - url), &uri); + if (rv == APR_SUCCESS) { + sockpath = ap_runtime_dir_relative(p, uri.path);; + ptr++; /* so we get the scheme for the uds */ + } + else { + ptr = url; + } + } + else { + ptr = url; + } + + if (mask & AP_PROXY_WORKER_IS_MATCH) { + /* apr_uri_parse() will accept the '$' sign anywhere in the URL but + * in the :port part, and we don't want scheme://host:port$1$2/path + * to fail (e.g. "ProxyPassMatch ^/(a|b)(/.*)? http://host:port$2"). + * So we trim all the $n from the :port and prepend them in uri.path + * afterward for apr_uri_unparse() to restore the original URL below. + */ +#define IS_REF(x) (x[0] == '$' && apr_isdigit(x[1])) + const char *pos = ap_strstr_c(ptr, "://"); + if (pos) { + pos += 3; + while (*pos && *pos != ':' && *pos != '/') { + pos++; + } + if (*pos == ':') { + pos++; + while (*pos && !IS_REF(pos) && *pos != '/') { + pos++; + } + if (IS_REF(pos)) { + struct iovec vec[2]; + const char *path = pos + 2; + while (*path && *path != '/') { + path++; + } + pdollars = apr_pstrmemdup(p, pos, path - pos); + vec[0].iov_base = (void *)ptr; + vec[0].iov_len = pos - ptr; + vec[1].iov_base = (void *)path; + vec[1].iov_len = strlen(path); + ptr = apr_pstrcatv(p, vec, 2, NULL); + } + } + } +#undef IS_REF + } + + /* Normalize the url (worker name) */ + rv = apr_uri_parse(p, ptr, &uri); + if (rv != APR_SUCCESS) { + return apr_pstrcat(p, "Unable to parse URL: ", url, NULL); + } + if (!uri.scheme) { + return apr_pstrcat(p, "URL must be absolute!: ", url, NULL); + } + if (!uri.hostname) { + if (sockpath) { + /* allow for unix:/path|http: */ + uri.hostname = "localhost"; + } + else { + return apr_pstrcat(p, "URL must be absolute!: ", url, NULL); + } + } + else { + ap_str_tolower(uri.hostname); + } + ap_str_tolower(uri.scheme); + port_of_scheme = ap_proxy_port_of_scheme(uri.scheme); + if (uri.port && uri.port == port_of_scheme) { + uri.port = 0; + } + if (pdollars) { + /* Restore/prepend pdollars into the path. */ + uri.path = apr_pstrcat(p, pdollars, uri.path, NULL); + } + ptr = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD); + + /* + * Workers can be associated w/ balancers or on their + * own; ie: the generic reverse-proxy or a worker + * in a simple ProxyPass statement. eg: + * + * ProxyPass / http://www.example.com + * + * in which case the worker goes in the conf slot. + */ + if (balancer) { + proxy_worker **runtime; + /* recall that we get a ptr to the ptr here */ + runtime = apr_array_push(balancer->workers); + *worker = *runtime = apr_palloc(p, sizeof(proxy_worker)); /* right to left baby */ + /* we've updated the list of workers associated with + * this balancer *locally* */ + balancer->wupdated = apr_time_now(); + } else if (conf) { + *worker = apr_array_push(conf->workers); + } else { + /* we need to allocate space here */ + *worker = apr_palloc(p, sizeof(proxy_worker)); + } + memset(*worker, 0, sizeof(proxy_worker)); + + /* right here we just want to tuck away the worker info. + * if called during config, we don't have shm setup yet, + * so just note the info for later. */ + if (mask & AP_PROXY_WORKER_IS_MALLOCED) + wshared = ap_malloc(sizeof(proxy_worker_shared)); /* will be freed ap_proxy_share_worker */ + else + wshared = apr_palloc(p, sizeof(proxy_worker_shared)); + memset(wshared, 0, sizeof(proxy_worker_shared)); + + if (PROXY_STRNCPY(wshared->name_ex, ptr) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(10366) + "Alert! worker name (%s) too long; truncated to: %s", ptr, wshared->name_ex); + } + if (PROXY_STRNCPY(wshared->name, ptr) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(010118) + "worker name (%s) too long; truncated for legacy modules that do not use " + "proxy_worker_shared->name_ex: %s", ptr, wshared->name); + } + if (PROXY_STRNCPY(wshared->scheme, uri.scheme) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(010117) + "Alert! worker scheme (%s) too long; truncated to: %s", uri.scheme, wshared->scheme); + } + if (PROXY_STRNCPY(wshared->hostname_ex, uri.hostname) != APR_SUCCESS) { + return apr_psprintf(p, "worker hostname (%s) too long", uri.hostname); + } + if (PROXY_STRNCPY(wshared->hostname, uri.hostname) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(010118) + "worker hostname (%s) too long; truncated for legacy modules that do not use " + "proxy_worker_shared->hostname_ex: %s", uri.hostname, wshared->hostname); + } + wshared->port = (uri.port) ? uri.port : port_of_scheme; + wshared->flush_packets = flush_off; + wshared->flush_wait = PROXY_FLUSH_WAIT; + wshared->is_address_reusable = 1; + wshared->lbfactor = 100; + 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_ex, PROXY_HASHFUNC_DEFAULT); + wshared->hash.fnv = ap_proxy_hashfunc(wshared->name_ex, PROXY_HASHFUNC_FNV); + wshared->was_malloced = (mask & AP_PROXY_WORKER_IS_MALLOCED) != 0; + wshared->is_name_matchable = 0; + if (sockpath) { + if (PROXY_STRNCPY(wshared->uds_path, sockpath) != APR_SUCCESS) { + return apr_psprintf(p, "worker uds path (%s) too long", sockpath); + } + + } + else { + *wshared->uds_path = '\0'; + } + if (!balancer) { + wshared->status |= PROXY_WORKER_IGNORE_ERRORS; + } + + (*worker)->hash = wshared->hash; + (*worker)->context = NULL; + (*worker)->cp = NULL; + (*worker)->balancer = balancer; + (*worker)->s = wshared; + + if (mask & AP_PROXY_WORKER_IS_MATCH) { + (*worker)->s->is_name_matchable = 1; + if (ap_strchr_c((*worker)->s->name_ex, '$')) { + /* Before AP_PROXY_WORKER_IS_MATCH (< 2.4.47), a regex worker + * with dollar substitution was never matched against the actual + * URL thus the request fell through the generic worker. To avoid + * dns and connection reuse compat issues, let's disable connection + * reuse by default, it can still be overwritten by an explicit + * enablereuse=on. + */ + (*worker)->s->disablereuse = 1; + } + } + + return NULL; +} + +PROXY_DECLARE(char *) ap_proxy_define_worker(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + int do_malloc) +{ + return ap_proxy_define_worker_ex(p, worker, balancer, conf, url, + AP_PROXY_WORKER_IS_PREFIX | + (do_malloc ? AP_PROXY_WORKER_IS_MALLOCED + : 0)); +} + +/* DEPRECATED */ +PROXY_DECLARE(char *) ap_proxy_define_match_worker(apr_pool_t *p, + proxy_worker **worker, + proxy_balancer *balancer, + proxy_server_conf *conf, + const char *url, + int do_malloc) +{ + return ap_proxy_define_worker_ex(p, worker, balancer, conf, url, + AP_PROXY_WORKER_IS_MATCH | + (do_malloc ? AP_PROXY_WORKER_IS_MALLOCED + : 0)); +} + +/* + * Create an already defined worker and free up memory + */ +PROXY_DECLARE(apr_status_t) ap_proxy_share_worker(proxy_worker *worker, proxy_worker_shared *shm, + int i) +{ + char *action = "copying"; + if (!shm || !worker->s) + return APR_EINVAL; + + if ((worker->s->hash.def != shm->hash.def) || + (worker->s->hash.fnv != shm->hash.fnv)) { + memcpy(shm, worker->s, sizeof(proxy_worker_shared)); + if (worker->s->was_malloced) + free(worker->s); /* was malloced in ap_proxy_define_worker */ + } else { + action = "re-using"; + } + worker->s = shm; + worker->s->index = i; + + if (APLOGdebug(ap_server_conf)) { + apr_pool_t *pool; + apr_pool_create(&pool, ap_server_conf->process->pool); + apr_pool_tag(pool, "proxy_worker_name"); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(02338) + "%s shm[%d] (0x%pp) for worker: %s", action, i, (void *)shm, + ap_proxy_worker_name(pool, worker)); + if (pool) { + apr_pool_destroy(pool); + } + } + return APR_SUCCESS; +} + +PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s, apr_pool_t *p) +{ + APR_OPTIONAL_FN_TYPE(http2_get_num_workers) *get_h2_num_workers; + apr_status_t rv = APR_SUCCESS; + int max_threads, minw, maxw; + + if (worker->s->status & PROXY_WORKER_INITIALIZED) { + /* The worker is already initialized */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00924) + "worker %s shared already initialized", + ap_proxy_worker_name(p, worker)); + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00925) + "initializing worker %s shared", + ap_proxy_worker_name(p, worker)); + /* Set default parameters */ + if (!worker->s->retry_set) { + worker->s->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY); + } + /* By default address is reusable unless DisableReuse is set */ + if (worker->s->disablereuse) { + worker->s->is_address_reusable = 0; + } + else { + worker->s->is_address_reusable = 1; + } + + /* + * When mod_http2 is loaded we might have more threads since it has + * its own pool of processing threads. + */ + ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads); + get_h2_num_workers = APR_RETRIEVE_OPTIONAL_FN(http2_get_num_workers); + if (get_h2_num_workers) { + get_h2_num_workers(s, &minw, &maxw); + /* So now the max is: + * max_threads-1 threads for HTTP/1 each requiring one connection + * + one thread for HTTP/2 requiring maxw connections + */ + max_threads = max_threads - 1 + maxw; + } + if (max_threads > 1) { + /* Default hmax is max_threads to scale with the load and never + * wait for an idle connection to proceed. + */ + if (worker->s->hmax == 0) { + worker->s->hmax = max_threads; + } + if (worker->s->smax == -1 || worker->s->smax > worker->s->hmax) { + worker->s->smax = worker->s->hmax; + } + /* Set min to be lower than smax */ + if (worker->s->min > worker->s->smax) { + worker->s->min = worker->s->smax; + } + } + else { + /* This will suppress the apr_reslist creation */ + worker->s->min = worker->s->smax = worker->s->hmax = 0; + } + } + + /* What if local is init'ed and shm isn't?? Even possible? */ + if (worker->local_status & PROXY_WORKER_INITIALIZED) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00926) + "worker %s local already initialized", + ap_proxy_worker_name(p, worker)); + } + else { + apr_global_mutex_lock(proxy_mutex); + /* Check again after we got the lock if we are still uninitialized */ + if (!(AP_VOLATILIZE_T(unsigned int, worker->local_status) & PROXY_WORKER_INITIALIZED)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00927) + "initializing worker %s local", + ap_proxy_worker_name(p, worker)); + /* Now init local worker data */ +#if APR_HAS_THREADS + if (worker->tmutex == NULL) { + rv = apr_thread_mutex_create(&(worker->tmutex), APR_THREAD_MUTEX_DEFAULT, p); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00928) + "can not create worker thread mutex"); + apr_global_mutex_unlock(proxy_mutex); + return rv; + } + } +#endif + if (worker->cp == NULL) + init_conn_pool(p, worker); + if (worker->cp == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00929) + "can not create connection pool"); + apr_global_mutex_unlock(proxy_mutex); + return APR_EGENERAL; + } + + if (worker->s->hmax) { + rv = apr_reslist_create(&(worker->cp->res), + worker->s->min, worker->s->smax, + worker->s->hmax, worker->s->ttl, + connection_constructor, connection_destructor, + worker, worker->cp->pool); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00930) + "initialized pool in child %" APR_PID_T_FMT " for (%s:%d) min=%d max=%d smax=%d", + getpid(), worker->s->hostname_ex, (int)worker->s->port, + worker->s->min, worker->s->hmax, worker->s->smax); + + /* Set the acquire timeout */ + if (rv == APR_SUCCESS && worker->s->acquire_set) { + apr_reslist_timeout_set(worker->cp->res, worker->s->acquire); + } + + } + else { + void *conn; + + rv = connection_constructor(&conn, worker, worker->cp->pool); + worker->cp->conn = conn; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00931) + "initialized single connection worker in child %" APR_PID_T_FMT " for (%s:%d)", + getpid(), worker->s->hostname_ex, + (int)worker->s->port); + } + if (rv == APR_SUCCESS) { + worker->local_status |= (PROXY_WORKER_INITIALIZED); + } + } + apr_global_mutex_unlock(proxy_mutex); + + } + if (rv == APR_SUCCESS) { + worker->s->status |= (PROXY_WORKER_INITIALIZED); + } + return rv; +} + +static int ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, + server_rec *s) +{ + if (worker->s->status & PROXY_WORKER_IN_ERROR) { + 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:%d): stopped", + proxy_function, worker->s->hostname_ex, + (int)worker->s->port); + 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) + "%s: worker for (%s:%d) has been marked for retry", + proxy_function, worker->s->hostname_ex, + (int)worker->s->port); + return OK; + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00933) + "%s: too soon to retry worker for (%s:%d)", + proxy_function, worker->s->hostname_ex, + (int)worker->s->port); + return DECLINED; + } + } + else { + return OK; + } +} + +/* + * In the case of the reverse proxy, we need to see if we + * were passed a UDS url (eg: from mod_proxy) and adjust uds_path + * as required. + */ +static int fix_uds_filename(request_rec *r, char **url) +{ + char *uds_url = r->filename + 6, *origin_url; + + if (!strncmp(r->filename, "proxy:", 6) && + !ap_cstr_casecmpn(uds_url, "unix:", 5) && + (origin_url = ap_strchr(uds_url + 5, '|'))) { + char *uds_path = NULL; + apr_size_t url_len; + apr_uri_t urisock; + apr_status_t rv; + + *origin_url = '\0'; + rv = apr_uri_parse(r->pool, uds_url, &urisock); + *origin_url++ = '|'; + + if (rv == APR_SUCCESS && urisock.path && (!urisock.hostname + || !urisock.hostname[0])) { + uds_path = ap_runtime_dir_relative(r->pool, urisock.path); + } + if (!uds_path) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10292) + "Invalid proxy UDS filename (%s)", r->filename); + return 0; + } + apr_table_setn(r->notes, "uds_path", uds_path); + + /* Remove the UDS path from *url and r->filename */ + url_len = strlen(origin_url); + *url = apr_pstrmemdup(r->pool, origin_url, url_len); + memcpy(uds_url, *url, url_len + 1); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "*: rewrite of url due to UDS(%s): %s (%s)", + uds_path, *url, r->filename); + } + return 1; +} + +PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, + proxy_balancer **balancer, + request_rec *r, + proxy_server_conf *conf, char **url) +{ + int access_status; + + access_status = proxy_run_pre_request(worker, balancer, r, conf, url); + if (access_status == DECLINED && *balancer == NULL) { + const int forward = (r->proxyreq == PROXYREQ_PROXY); + *worker = ap_proxy_get_worker_ex(r->pool, NULL, conf, *url, + forward ? AP_PROXY_WORKER_NO_UDS : 0); + if (*worker) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "%s: found worker %s for %s", + (*worker)->s->scheme, (*worker)->s->name_ex, *url); + if (!forward && !fix_uds_filename(r, url)) { + return HTTP_INTERNAL_SERVER_ERROR; + } + access_status = OK; + } + else if (forward) { + if (conf->forward) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "*: found forward proxy worker for %s", *url); + *worker = conf->forward; + access_status = OK; + /* + * The forward worker does not keep connections alive, so + * ensure that mod_proxy_http does the correct thing + * regarding the Connection header in the request. + */ + apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); + } + } + else if (r->proxyreq == PROXYREQ_REVERSE) { + if (conf->reverse) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "*: using default reverse proxy worker for %s " + "(no keepalive)", *url); + *worker = conf->reverse; + access_status = OK; + /* + * The reverse worker does not keep connections alive, so + * ensure that mod_proxy_http does the correct thing + * regarding the Connection header in the request. + */ + apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); + if (!fix_uds_filename(r, url)) { + return HTTP_INTERNAL_SERVER_ERROR; + } + } + } + } + else if (access_status == DECLINED && *balancer != NULL) { + /* All the workers are busy */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00934) + "all workers are busy. Unable to serve %s", *url); + access_status = HTTP_SERVICE_UNAVAILABLE; + } + return access_status; +} + +PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker, + proxy_balancer *balancer, + request_rec *r, + proxy_server_conf *conf) +{ + int access_status = OK; + if (balancer) { + access_status = proxy_run_post_request(worker, balancer, r, conf); + if (access_status == DECLINED) { + access_status = OK; /* no post_request handler available */ + /* TODO: recycle direct worker */ + } + } + + return access_status; +} + +/* DEPRECATED */ +PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock, + const char *proxy_function, + apr_sockaddr_t *backend_addr, + const char *backend_name, + proxy_server_conf *conf, + request_rec *r) +{ + apr_status_t rv; + int connected = 0; + int loglevel; + + while (backend_addr && !connected) { + if ((rv = apr_socket_create(newsock, backend_addr->family, + SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) { + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00935) + "%s: error creating fam %d socket for target %s", + proxy_function, backend_addr->family, backend_name); + /* + * this could be an IPv6 address from the DNS but the + * local machine won't give us an IPv6 socket; hopefully the + * DNS returned an additional address to try + */ + backend_addr = backend_addr->next; + continue; + } + + if (conf->recv_buffer_size > 0 && + (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00936) + "apr_socket_opt_set(SO_RCVBUF): Failed to set " + "ProxyReceiveBufferSize, using default"); + } + + rv = apr_socket_opt_set(*newsock, APR_TCP_NODELAY, 1); + if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00937) + "apr_socket_opt_set(APR_TCP_NODELAY): " + "Failed to set"); + } + + /* Set a timeout on the socket */ + if (conf->timeout_set) { + apr_socket_timeout_set(*newsock, conf->timeout); + } + else { + apr_socket_timeout_set(*newsock, r->server->timeout); + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "%s: fam %d socket created to connect to %s", + proxy_function, backend_addr->family, backend_name); + + if (conf->source_address) { + apr_sockaddr_t *local_addr; + /* Make a copy since apr_socket_bind() could change + * conf->source_address, which we don't want. + */ + local_addr = apr_pmemdup(r->pool, conf->source_address, + sizeof(apr_sockaddr_t)); + local_addr->pool = r->pool; + rv = apr_socket_bind(*newsock, local_addr); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00938) + "%s: failed to bind socket to local address", + proxy_function); + } + } + + /* make the connection out of the socket */ + rv = apr_socket_connect(*newsock, backend_addr); + + /* if an error occurred, loop round and try again */ + if (rv != APR_SUCCESS) { + apr_socket_close(*newsock); + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00939) + "%s: attempt to connect to %pI (%s) failed", + proxy_function, backend_addr, backend_name); + backend_addr = backend_addr->next; + continue; + } + connected = 1; + } + return connected ? 0 : 1; +} + +PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, + proxy_conn_rec **conn, + proxy_worker *worker, + server_rec *s) +{ + apr_status_t rv; + + if (!PROXY_WORKER_IS_USABLE(worker)) { + /* Retry the worker */ + ap_proxy_retry_worker(proxy_function, worker, s); + + if (!PROXY_WORKER_IS_USABLE(worker)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00940) + "%s: disabled connection for (%s:%d)", + proxy_function, worker->s->hostname_ex, + (int)worker->s->port); + return HTTP_SERVICE_UNAVAILABLE; + } + } + + if (worker->s->hmax && worker->cp->res) { + rv = apr_reslist_acquire(worker->cp->res, (void **)conn); + } + else { + /* create the new connection if the previous was destroyed */ + if (!worker->cp->conn) { + rv = connection_constructor((void **)conn, worker, worker->cp->pool); + } + else { + *conn = worker->cp->conn; + worker->cp->conn = NULL; + rv = APR_SUCCESS; + } + } + + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00941) + "%s: failed to acquire connection for (%s:%d)", + proxy_function, worker->s->hostname_ex, + (int)worker->s->port); + return HTTP_SERVICE_UNAVAILABLE; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00942) + "%s: has acquired connection for (%s:%d)", + proxy_function, worker->s->hostname_ex, + (int)worker->s->port); + + (*conn)->worker = worker; + (*conn)->close = 0; + (*conn)->inreslist = 0; + + return OK; +} + +PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, + proxy_conn_rec *conn, + server_rec *s) +{ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00943) + "%s: has released connection for (%s:%d)", + proxy_function, conn->worker->s->hostname_ex, + (int)conn->worker->s->port); + connection_cleanup(conn); + + return OK; +} + +PROXY_DECLARE(int) +ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, + proxy_server_conf *conf, + proxy_worker *worker, + proxy_conn_rec *conn, + apr_uri_t *uri, + char **url, + const char *proxyname, + apr_port_t proxyport, + char *server_portstr, + int server_portstr_size) +{ + int server_port; + apr_status_t err = APR_SUCCESS; +#if APR_HAS_THREADS + apr_status_t uerr = APR_SUCCESS; +#endif + const char *uds_path; + + /* + * Break up the URL to determine the host to connect to + */ + + /* we break the URL into host, port, uri */ + if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_pstrcat(p,"URI cannot be parsed: ", *url, + NULL)); + } + if (!uri->port) { + uri->port = ap_proxy_port_of_scheme(uri->scheme); + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00944) + "connecting %s to %s:%d", *url, uri->hostname, uri->port); + + /* + * allocate these out of the specified connection pool + * The scheme handler decides if this is permanent or + * short living pool. + */ + /* Unless we are connecting the backend via a (forward Proxy)Remote, we + * have to use the original form of the URI (non absolute), but this is + * also the case via a remote proxy using the CONNECT method since the + * original request (and URI) is to be embedded in the body. + */ + if (!proxyname || conn->is_ssl) { + *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "", + uri->query ? uri->query : "", + uri->fragment ? "#" : "", + uri->fragment ? uri->fragment : "", NULL); + } + /* + * Figure out if our passed in proxy_conn_rec has a usable + * address cached. + * + * TODO: Handle this much better... + * + * XXX: If generic workers are ever address-reusable, we need + * to check host and port on the conn and be careful about + * spilling the cached addr from the worker. + */ + uds_path = (*worker->s->uds_path ? worker->s->uds_path : apr_table_get(r->notes, "uds_path")); + if (uds_path) { + if (conn->uds_path == NULL) { + /* use (*conn)->pool instead of worker->cp->pool to match lifetime */ + conn->uds_path = apr_pstrdup(conn->pool, uds_path); + } + if (conn->uds_path) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02545) + "%s: has determined UDS as %s", + uri->scheme, conn->uds_path); + } + else { + /* should never happen */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02546) + "%s: cannot determine UDS (%s)", + uri->scheme, uds_path); + + } + /* + * In UDS cases, some structs are NULL. Protect from de-refs + * and provide info for logging at the same time. + */ + if (!conn->addr) { + apr_sockaddr_t *sa; + apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 0, 0, conn->pool); + conn->addr = sa; + } + conn->hostname = "httpd-UDS"; + conn->port = 0; + } + else { + int will_reuse = worker->s->is_address_reusable && !worker->s->disablereuse; + if (!conn->hostname || !will_reuse) { + if (proxyname) { + conn->hostname = apr_pstrdup(conn->pool, proxyname); + conn->port = proxyport; + /* + * If we have a forward proxy and the protocol is HTTPS, + * then we need to prepend a HTTP CONNECT request before + * sending our actual HTTPS requests. + * Save our real backend data for using it later during HTTP CONNECT. + */ + if (conn->is_ssl) { + const char *proxy_auth; + + forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info)); + conn->forward = forward; + forward->use_http_connect = 1; + forward->target_host = apr_pstrdup(conn->pool, uri->hostname); + forward->target_port = uri->port; + /* Do we want to pass Proxy-Authorization along? + * If we haven't used it, then YES + * If we have used it then MAYBE: RFC2616 says we MAY propagate it. + * So let's make it configurable by env. + * The logic here is the same used in mod_proxy_http. + */ + proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization"); + if (proxy_auth != NULL && + proxy_auth[0] != '\0' && + r->user == NULL && /* we haven't yet authenticated */ + apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { + forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth); + } + } + } + else { + conn->hostname = apr_pstrdup(conn->pool, uri->hostname); + conn->port = uri->port; + } + if (!will_reuse) { + /* + * Only do a lookup if we should not reuse the backend address. + * Otherwise we will look it up once for the worker. + */ + err = apr_sockaddr_info_get(&(conn->addr), + conn->hostname, APR_UNSPEC, + conn->port, 0, + conn->pool); + } + socket_cleanup(conn); + conn->close = 0; + } + if (will_reuse) { + /* + * Looking up the backend address for the worker only makes sense if + * we can reuse the address. + */ + if (!worker->cp->addr) { +#if APR_HAS_THREADS + if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock"); + return HTTP_INTERNAL_SERVER_ERROR; + } +#endif + + /* + * Recheck addr after we got the lock. This may have changed + * while waiting for the lock. + */ + if (!AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr)) { + + apr_sockaddr_t *addr; + + /* + * Worker can have the single constant backend address. + * The single DNS lookup is used once per worker. + * If dynamic change is needed then set the addr to NULL + * inside dynamic config to force the lookup. + */ + err = apr_sockaddr_info_get(&addr, + conn->hostname, APR_UNSPEC, + conn->port, 0, + worker->cp->dns_pool); + worker->cp->addr = addr; + } + conn->addr = worker->cp->addr; +#if APR_HAS_THREADS + if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock"); + } +#endif + } + else { + conn->addr = worker->cp->addr; + } + } + } + /* Close a possible existing socket if we are told to do so */ + if (conn->close) { + socket_cleanup(conn); + conn->close = 0; + } + + if (err != APR_SUCCESS) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "DNS lookup failure for: ", + conn->hostname, NULL)); + } + + /* Get the server port for the Via headers */ + server_port = ap_get_server_port(r); + AP_DEBUG_ASSERT(server_portstr_size > 0); + if (ap_is_default_port(server_port, r)) { + server_portstr[0] = '\0'; + } + else { + apr_snprintf(server_portstr, server_portstr_size, ":%d", + server_port); + } + + /* check if ProxyBlock directive on this host */ + if (OK != ap_proxy_checkproxyblock2(r, conf, uri->hostname, + proxyname ? NULL : conn->addr)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + /* + * When SSL is configured, determine the hostname (SNI) for the request + * and save it in conn->ssl_hostname. Close any reused connection whose + * SNI differs. + */ + if (conn->is_ssl) { + proxy_dir_conf *dconf; + const char *ssl_hostname; + /* + * In the case of ProxyPreserveHost on use the hostname of + * the request if present otherwise use the one from the + * backend request URI. + */ + dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + if (dconf->preserve_host) { + ssl_hostname = r->hostname; + } + else if (conn->forward + && ((forward_info *)(conn->forward))->use_http_connect) { + ssl_hostname = ((forward_info *)conn->forward)->target_host; + } + else { + ssl_hostname = conn->hostname; + } + /* + * Close if a SNI is in use but this request requires no or + * a different one, or no SNI is in use but one is required. + */ + if ((conn->ssl_hostname && (!ssl_hostname || + strcasecmp(conn->ssl_hostname, + ssl_hostname) != 0)) || + (!conn->ssl_hostname && ssl_hostname && conn->sock)) { + socket_cleanup(conn); + } + if (conn->ssl_hostname == NULL) { + conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname); + } + } + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00947) + "connected %s to %s:%d", *url, conn->hostname, conn->port); + return OK; +} + +#define USE_ALTERNATE_IS_CONNECTED 1 + +#if !defined(APR_MSG_PEEK) && defined(MSG_PEEK) +#define APR_MSG_PEEK MSG_PEEK +#endif + +#if USE_ALTERNATE_IS_CONNECTED && defined(APR_MSG_PEEK) +PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *socket) +{ + apr_pollfd_t pfds[1]; + apr_status_t status; + apr_int32_t nfds; + + pfds[0].reqevents = APR_POLLIN; + pfds[0].desc_type = APR_POLL_SOCKET; + pfds[0].desc.s = socket; + + do { + status = apr_poll(&pfds[0], 1, &nfds, 0); + } while (APR_STATUS_IS_EINTR(status)); + + if (status == APR_SUCCESS && nfds == 1 && + pfds[0].rtnevents == APR_POLLIN) { + apr_sockaddr_t unused; + apr_size_t len = 1; + char buf[1]; + /* The socket might be closed in which case + * the poll will return POLLIN. + * If there is no data available the socket + * is closed. + */ + status = apr_socket_recvfrom(&unused, socket, APR_MSG_PEEK, + &buf[0], &len); + if (status == APR_SUCCESS && len) + return 1; + else + return 0; + } + else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { + return 1; + } + return 0; + +} +#else +PROXY_DECLARE(int) ap_proxy_is_socket_connected(apr_socket_t *sock) + +{ + apr_size_t buffer_len = 1; + char test_buffer[1]; + apr_status_t socket_status; + apr_interval_time_t current_timeout; + + /* save timeout */ + apr_socket_timeout_get(sock, ¤t_timeout); + /* set no timeout */ + apr_socket_timeout_set(sock, 0); + socket_status = apr_socket_recv(sock, test_buffer, &buffer_len); + /* put back old timeout */ + apr_socket_timeout_set(sock, current_timeout); + if (APR_STATUS_IS_EOF(socket_status) + || APR_STATUS_IS_ECONNRESET(socket_status)) { + return 0; + } + else { + return 1; + } +} +#endif /* USE_ALTERNATE_IS_CONNECTED */ + + +/* + * Send a HTTP CONNECT request to a forward proxy. + * The proxy is given by "backend", the target server + * is contained in the "forward" member of "backend". + */ +static apr_status_t send_http_connect(proxy_conn_rec *backend, + server_rec *s) +{ + int status; + apr_size_t nbytes; + apr_size_t left; + int complete = 0; + char buffer[HUGE_STRING_LEN]; + char drain_buffer[HUGE_STRING_LEN]; + forward_info *forward = (forward_info *)backend->forward; + int len = 0; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00948) + "CONNECT: sending the CONNECT request for %s:%d " + "to the remote proxy %pI (%s)", + forward->target_host, forward->target_port, + backend->addr, backend->hostname); + /* Create the CONNECT request */ + nbytes = apr_snprintf(buffer, sizeof(buffer), + "CONNECT %s:%d HTTP/1.0" CRLF, + forward->target_host, forward->target_port); + /* Add proxy authorization from the initial request if necessary */ + if (forward->proxy_auth != NULL) { + nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, + "Proxy-Authorization: %s" CRLF, + forward->proxy_auth); + } + /* Set a reasonable agent and send everything */ + nbytes += apr_snprintf(buffer + nbytes, sizeof(buffer) - nbytes, + "Proxy-agent: %s" CRLF CRLF, + ap_get_server_banner()); + ap_xlate_proto_to_ascii(buffer, nbytes); + apr_socket_send(backend->sock, buffer, &nbytes); + + /* Receive the whole CONNECT response */ + left = sizeof(buffer) - 1; + /* Read until we find the end of the headers or run out of buffer */ + do { + nbytes = left; + status = apr_socket_recv(backend->sock, buffer + len, &nbytes); + len += nbytes; + left -= nbytes; + buffer[len] = '\0'; + if (strstr(buffer + len - nbytes, CRLF_ASCII CRLF_ASCII) != NULL) { + ap_xlate_proto_from_ascii(buffer, len); + complete = 1; + break; + } + } while (status == APR_SUCCESS && left > 0); + /* Drain what's left */ + if (!complete) { + nbytes = sizeof(drain_buffer) - 1; + while (status == APR_SUCCESS && nbytes) { + status = apr_socket_recv(backend->sock, drain_buffer, &nbytes); + drain_buffer[nbytes] = '\0'; + nbytes = sizeof(drain_buffer) - 1; + if (strstr(drain_buffer, CRLF_ASCII CRLF_ASCII) != NULL) { + break; + } + } + } + + /* Check for HTTP_OK response status */ + if (status == APR_SUCCESS) { + unsigned int major, minor; + /* Only scan for three character status code */ + char code_str[4]; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00949) + "send_http_connect: response from the forward proxy: %s", + buffer); + + /* Extract the returned code */ + if (sscanf(buffer, "HTTP/%u.%u %3s", &major, &minor, code_str) == 3) { + status = atoi(code_str); + if (status == HTTP_OK) { + status = APR_SUCCESS; + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00950) + "send_http_connect: the forward proxy returned code is '%s'", + code_str); + status = APR_INCOMPLETE; + } + } + } + + return(status); +} + + +/* 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; + struct sockaddr_un *sa; + apr_socklen_t addrlen, pathlen; + + rv = apr_os_sock_get(&rawsock, sock); + if (rv != APR_SUCCESS) { + return rv; + } + + rv = apr_socket_timeout_get(sock, &t); + if (rv != APR_SUCCESS) { + return rv; + } + + pathlen = strlen(uds_path); + /* copy the UDS path (including NUL) to the sockaddr_un */ + addrlen = APR_OFFSETOF(struct sockaddr_un, sun_path) + pathlen; + sa = (struct sockaddr_un *)apr_palloc(p, addrlen + 1); + memcpy(sa->sun_path, uds_path, pathlen + 1); + sa->sun_family = AF_UNIX; + + do { + rv = connect(rawsock, (struct sockaddr*)sa, addrlen); + } while (rv == -1 && (rv = errno) == EINTR); + + if (rv && rv != EISCONN) { + if ((rv == EINPROGRESS || rv == EALREADY) && (t > 0)) { +#if APR_MAJOR_VERSION < 2 + rv = apr_wait_for_io_or_timeout(NULL, sock, 0); +#else + rv = apr_socket_wait(sock, APR_WAIT_WRITE); +#endif + } + if (rv != APR_SUCCESS) { + return rv; + } + } + + return APR_SUCCESS; +#else + return APR_ENOTIMPL; +#endif +} + +PROXY_DECLARE(apr_status_t) ap_proxy_check_connection(const char *scheme, + proxy_conn_rec *conn, + server_rec *server, + unsigned max_blank_lines, + int flags) +{ + apr_status_t rv = APR_SUCCESS; + proxy_worker *worker = conn->worker; + + if (!PROXY_WORKER_IS_USABLE(worker)) { + /* + * The worker is in error likely done by a different thread / process + * e.g. for a timeout or bad status. We should respect this and should + * not continue with a connection via this worker even if we got one. + */ + rv = APR_EINVAL; + } + else if (conn->connection) { + /* We have a conn_rec, check the full filter stack for things like + * SSL alert/shutdown, filters aside data... + */ + rv = ap_check_pipeline(conn->connection, conn->tmp_bb, + max_blank_lines); + apr_brigade_cleanup(conn->tmp_bb); + if (rv == APR_SUCCESS) { + /* Some data available, the caller might not want them. */ + if (flags & PROXY_CHECK_CONN_EMPTY) { + rv = APR_ENOTEMPTY; + } + } + else if (APR_STATUS_IS_EAGAIN(rv)) { + /* Filter chain is OK and empty, yet we can't determine from + * ap_check_pipeline (actually ap_core_input_filter) whether + * an empty non-blocking read is EAGAIN or EOF on the socket + * side (it's always SUCCESS), so check it explicitly here. + */ + if (ap_proxy_is_socket_connected(conn->sock)) { + rv = APR_SUCCESS; + } + else { + rv = APR_EPIPE; + } + } + } + else if (conn->sock) { + /* For modules working with sockets directly, check it. */ + if (!ap_proxy_is_socket_connected(conn->sock)) { + rv = APR_EPIPE; + } + } + else { + rv = APR_ENOSOCKET; + } + + if (rv == APR_SUCCESS) { + if (APLOGtrace2(server)) { + apr_sockaddr_t *local_addr = NULL; + apr_socket_addr_get(&local_addr, APR_LOCAL, conn->sock); + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, server, + "%s: reusing backend connection %pI<>%pI", + scheme, local_addr, conn->addr); + } + } + else if (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(). + */ + char ssl_hostname[PROXY_WORKER_RFC1035_NAME_SIZE]; + if (rv == APR_EINVAL + || !conn->ssl_hostname + || PROXY_STRNCPY(ssl_hostname, conn->ssl_hostname)) { + ssl_hostname[0] = '\0'; + } + + socket_cleanup(conn); + if (rv != APR_ENOTEMPTY) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, APLOGNO(00951) + "%s: backend socket is disconnected.", scheme); + } + else { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, server, APLOGNO(03408) + "%s: reusable backend connection is not empty: " + "forcibly closed", scheme); + } + + if (ssl_hostname[0]) { + conn->ssl_hostname = apr_pstrdup(conn->scpool, ssl_hostname); + } + } + + return rv; +} + +PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, + proxy_conn_rec *conn, + proxy_worker *worker, + server_rec *s) +{ + apr_status_t rv; + int loglevel; + apr_sockaddr_t *backend_addr = conn->addr; + /* the local address to use for the outgoing connection */ + apr_sockaddr_t *local_addr; + apr_socket_t *newsock; + void *sconf = s->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + rv = ap_proxy_check_connection(proxy_function, conn, s, 0, 0); + if (rv == APR_EINVAL) { + return DECLINED; + } + + while (rv != APR_SUCCESS && (backend_addr || conn->uds_path)) { +#if APR_HAVE_SYS_UN_H + if (conn->uds_path) + { + rv = apr_socket_create(&newsock, AF_UNIX, SOCK_STREAM, 0, + conn->scpool); + if (rv != APR_SUCCESS) { + loglevel = APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(02453) + "%s: error creating Unix domain socket for " + "target %s:%d", + proxy_function, + worker->s->hostname_ex, + (int)worker->s->port); + break; + } + conn->connection = NULL; + + rv = ap_proxy_connect_uds(newsock, conn->uds_path, conn->scpool); + if (rv != APR_SUCCESS) { + apr_socket_close(newsock); + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(02454) + "%s: attempt to connect to Unix domain socket " + "%s (%s:%d) failed", + proxy_function, + conn->uds_path, + worker->s->hostname_ex, + (int)worker->s->port); + break; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02823) + "%s: connection established with Unix domain socket " + "%s (%s:%d)", + proxy_function, + conn->uds_path, + worker->s->hostname_ex, + (int)worker->s->port); + } + else +#endif + { + if ((rv = apr_socket_create(&newsock, backend_addr->family, + SOCK_STREAM, APR_PROTO_TCP, + conn->scpool)) != APR_SUCCESS) { + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952) + "%s: error creating fam %d socket for " + "target %s:%d", + proxy_function, + backend_addr->family, + worker->s->hostname_ex, + (int)worker->s->port); + /* + * this could be an IPv6 address from the DNS but the + * local machine won't give us an IPv6 socket; hopefully the + * DNS returned an additional address to try + */ + backend_addr = backend_addr->next; + continue; + } + conn->connection = NULL; + + if (worker->s->recv_buffer_size > 0 && + (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF, + worker->s->recv_buffer_size))) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00953) + "apr_socket_opt_set(SO_RCVBUF): Failed to set " + "ProxyReceiveBufferSize, using default"); + } + + rv = apr_socket_opt_set(newsock, APR_TCP_NODELAY, 1); + if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00954) + "apr_socket_opt_set(APR_TCP_NODELAY): " + "Failed to set"); + } + + /* Set a timeout for connecting to the backend on the socket */ + if (worker->s->conn_timeout_set) { + apr_socket_timeout_set(newsock, worker->s->conn_timeout); + } + else if (worker->s->timeout_set) { + apr_socket_timeout_set(newsock, worker->s->timeout); + } + else if (conf->timeout_set) { + apr_socket_timeout_set(newsock, conf->timeout); + } + else { + apr_socket_timeout_set(newsock, s->timeout); + } + /* Set a keepalive option */ + if (worker->s->keepalive) { + if ((rv = apr_socket_opt_set(newsock, + APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00955) + "apr_socket_opt_set(SO_KEEPALIVE): Failed to set" + " Keepalive"); + } + } + ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s, + "%s: fam %d socket created to connect to %s:%d", + proxy_function, backend_addr->family, + worker->s->hostname_ex, (int)worker->s->port); + + if (conf->source_address_set) { + local_addr = apr_pmemdup(conn->scpool, conf->source_address, + sizeof(apr_sockaddr_t)); + local_addr->pool = conn->scpool; + rv = apr_socket_bind(newsock, local_addr); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00956) + "%s: failed to bind socket to local address", + proxy_function); + } + } + + /* make the connection out of the socket */ + rv = apr_socket_connect(newsock, backend_addr); + + /* if an error occurred, loop round and try again */ + if (rv != APR_SUCCESS) { + apr_socket_close(newsock); + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00957) + "%s: attempt to connect to %pI (%s:%d) failed", + proxy_function, + backend_addr, + worker->s->hostname_ex, + (int)worker->s->port); + backend_addr = backend_addr->next; + continue; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02824) + "%s: connection established with %pI (%s:%d)", + proxy_function, + backend_addr, + worker->s->hostname_ex, + (int)worker->s->port); + } + + /* Set a timeout on the socket */ + if (worker->s->timeout_set) { + apr_socket_timeout_set(newsock, worker->s->timeout); + } + else if (conf->timeout_set) { + apr_socket_timeout_set(newsock, conf->timeout); + } + else { + apr_socket_timeout_set(newsock, s->timeout); + } + + conn->sock = newsock; + + if (!conn->uds_path && conn->forward) { + forward_info *forward = (forward_info *)conn->forward; + /* + * For HTTP CONNECT we need to prepend CONNECT request before + * sending our actual HTTPS requests. + */ + if (forward->use_http_connect) { + rv = send_http_connect(conn, s); + /* If an error occurred, loop round and try again */ + if (rv != APR_SUCCESS) { + conn->sock = NULL; + apr_socket_close(newsock); + loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR; + ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00958) + "%s: attempt to connect to %s:%d " + "via http CONNECT through %pI (%s:%d) failed", + proxy_function, + forward->target_host, forward->target_port, + backend_addr, worker->s->hostname_ex, + (int)worker->s->port); + backend_addr = backend_addr->next; + continue; + } + } + } + } + + if (PROXY_WORKER_IS_USABLE(worker)) { + /* + * Put the entire worker to error state if + * the PROXY_WORKER_IGNORE_ERRORS flag is not set. + * Although some connections may be alive + * no further connections to the worker could be made + */ + if (rv != APR_SUCCESS) { + if (!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) { + worker->s->error_time = apr_time_now(); + worker->s->status |= PROXY_WORKER_IN_ERROR; + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00959) + "ap_proxy_connect_backend disabling worker for (%s:%d) for %" + APR_TIME_T_FMT "s", + worker->s->hostname_ex, (int)worker->s->port, + apr_time_sec(worker->s->retry)); + } + } + else { + if (worker->s->retries) { + /* + * A worker came back. So here is where we need to + * either reset all params to initial conditions or + * apply some sort of aging + */ + } + worker->s->error_time = 0; + worker->s->retries = 0; + } + } + else { + /* + * The worker is in error likely done by a different thread / process + * e.g. for a timeout or bad status. We should respect this and should + * not continue with a connection via this worker even if we got one. + */ + if (rv == APR_SUCCESS) { + socket_cleanup(conn); + } + rv = APR_EINVAL; + } + + return rv == APR_SUCCESS ? OK : DECLINED; +} + +static apr_status_t connection_shutdown(void *theconn) +{ + proxy_conn_rec *conn = (proxy_conn_rec *)theconn; + conn_rec *c = conn->connection; + if (c) { + if (!c->aborted) { + apr_interval_time_t saved_timeout = 0; + apr_socket_timeout_get(conn->sock, &saved_timeout); + if (saved_timeout) { + apr_socket_timeout_set(conn->sock, 0); + } + + (void)ap_shutdown_conn(c, 0); + c->aborted = 1; + + if (saved_timeout) { + apr_socket_timeout_set(conn->sock, saved_timeout); + } + } + + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02642) + "proxy: connection shutdown"); + } + return APR_SUCCESS; +} + + +static int proxy_connection_create(const char *proxy_function, + proxy_conn_rec *conn, + request_rec *r, server_rec *s) +{ + ap_conf_vector_t *per_dir_config = (r) ? r->per_dir_config + : conn->worker->section_config; + apr_sockaddr_t *backend_addr = conn->addr; + int rc; + apr_interval_time_t current_timeout; + apr_bucket_alloc_t *bucket_alloc; + + if (conn->connection) { + if (conn->is_ssl) { + /* on reuse, reinit the SSL connection dir config with the current + * r->per_dir_config, the previous one was reset on release. + */ + ap_proxy_ssl_engine(conn->connection, per_dir_config, 1); + } + return OK; + } + + bucket_alloc = apr_bucket_alloc_create(conn->scpool); + conn->tmp_bb = apr_brigade_create(conn->scpool, bucket_alloc); + /* + * The socket is now open, create a new backend server connection + */ + conn->connection = ap_run_create_connection(conn->scpool, s, conn->sock, + 0, NULL, + bucket_alloc); + + if (!conn->connection) { + /* + * the peer reset the connection already; ap_run_create_connection() + * closed the socket + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + s, APLOGNO(00960) "%s: an error occurred creating a " + "new connection to %pI (%s)", proxy_function, + backend_addr, conn->hostname); + /* XXX: Will be closed when proxy_conn is closed */ + socket_cleanup(conn); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* For ssl connection to backend */ + if (conn->is_ssl) { + if (!ap_proxy_ssl_engine(conn->connection, per_dir_config, 1)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, + s, APLOGNO(00961) "%s: failed to enable ssl support " + "for %pI (%s)", proxy_function, + backend_addr, conn->hostname); + return HTTP_INTERNAL_SERVER_ERROR; + } + if (conn->ssl_hostname) { + /* Set a note on the connection about what CN is requested, + * such that mod_ssl can check if it is requested to do so. + */ + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, conn->connection, + "%s: set SNI to %s for (%s)", proxy_function, + conn->ssl_hostname, conn->hostname); + apr_table_setn(conn->connection->notes, "proxy-request-hostname", + conn->ssl_hostname); + } + } + else { + /* TODO: See if this will break FTP */ + ap_proxy_ssl_engine(conn->connection, per_dir_config, 0); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00962) + "%s: connection complete to %pI (%s)", + proxy_function, backend_addr, conn->hostname); + + /* + * save the timeout of the socket because core_pre_connection + * will set it to base_server->timeout + * (core TimeOut directive). + */ + apr_socket_timeout_get(conn->sock, ¤t_timeout); + /* set up the connection filters */ + rc = ap_run_pre_connection(conn->connection, conn->sock); + if (rc != OK && rc != DONE) { + conn->connection->aborted = 1; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(00963) + "%s: pre_connection setup failed (%d)", + proxy_function, rc); + return rc; + } + apr_socket_timeout_set(conn->sock, current_timeout); + + /* Shutdown the connection before closing it (eg. SSL connections + * need to be close-notify-ed). + */ + apr_pool_pre_cleanup_register(conn->scpool, conn, connection_shutdown); + + return OK; +} + +PROXY_DECLARE(int) ap_proxy_connection_create_ex(const char *proxy_function, + proxy_conn_rec *conn, + request_rec *r) +{ + return proxy_connection_create(proxy_function, conn, r, r->server); +} + +PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function, + proxy_conn_rec *conn, + conn_rec *c, server_rec *s) +{ + (void) c; /* unused */ + return proxy_connection_create(proxy_function, conn, NULL, s); +} + +int ap_proxy_lb_workers(void) +{ + /* + * Since we can't resize the scoreboard when reconfiguring, we + * have to impose a limit on the number of workers, we are + * able to reconfigure to. + */ + if (!lb_workers_limit) + lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT; + return lb_workers_limit; +} + +static APR_INLINE int error_code_overridden(const int *elts, int nelts, + int code) +{ + int min = 0; + int max = nelts - 1; + AP_DEBUG_ASSERT(max >= 0); + + while (min < max) { + int mid = (min + max) / 2; + int val = elts[mid]; + + if (val < code) { + min = mid + 1; + } + else if (val > code) { + max = mid - 1; + } + else { + return 1; + } + } + + return elts[min] == code; +} + +PROXY_DECLARE(int) ap_proxy_should_override(proxy_dir_conf *conf, int code) +{ + if (!conf->error_override) + return 0; + + if (apr_is_empty_array(conf->error_override_codes)) + return ap_is_HTTP_ERROR(code); + + /* Since error_override_codes is sorted, apply binary search. */ + return error_code_overridden((int *)conf->error_override_codes->elts, + conf->error_override_codes->nelts, + code); +} + +PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r, + apr_bucket_brigade *brigade) +{ + apr_bucket *e; + conn_rec *c = r->connection; + + r->no_cache = 1; + /* + * If this is a subrequest, then prevent also caching of the main + * request. + */ + if (r->main) + r->main->no_cache = 1; + e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, c->pool, + c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(brigade, e); + e = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(brigade, e); +} + +/* + * Provide a string hashing function for the proxy. + * We offer 2 methods: one is the APR model but we + * also provide our own, based on either FNV or SDBM. + * The reason is in case we want to use both to ensure no + * collisions. + */ +PROXY_DECLARE(unsigned int) +ap_proxy_hashfunc(const char *str, proxy_hash_t method) +{ + if (method == PROXY_HASHFUNC_APR) { + apr_ssize_t slen = strlen(str); + return apr_hashfunc_default(str, &slen); + } + else if (method == PROXY_HASHFUNC_FNV) { + /* FNV model */ + unsigned int hash; + const unsigned int fnv_prime = 0x811C9DC5; + for (hash = 0; *str; str++) { + hash *= fnv_prime; + hash ^= (*str); + } + return hash; + } + else { /* method == PROXY_HASHFUNC_DEFAULT */ + /* SDBM model */ + unsigned int hash; + for (hash = 0; *str; str++) { + hash = (*str) + (hash << 6) + (hash << 16) - hash; + } + return hash; + } +} + +PROXY_DECLARE(apr_status_t) ap_proxy_set_wstatus(char c, int set, proxy_worker *w) +{ + unsigned int *status = &w->s->status; + char flag = toupper(c); + proxy_wstat_t *pwt = proxy_wstat_tbl; + while (pwt->bit) { + if (flag == pwt->flag) { + if (set) + *status |= pwt->bit; + else + *status &= ~(pwt->bit); + return APR_SUCCESS; + } + pwt++; + } + return APR_EINVAL; +} + +PROXY_DECLARE(char *) ap_proxy_parse_wstatus(apr_pool_t *p, proxy_worker *w) +{ + char *ret = ""; + unsigned int status = w->s->status; + 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; +} + +PROXY_DECLARE(apr_status_t) ap_proxy_sync_balancer(proxy_balancer *b, server_rec *s, + proxy_server_conf *conf) +{ + proxy_worker **workers; + int i; + int index; + proxy_worker_shared *shm; + proxy_balancer_method *lbmethod; + ap_slotmem_provider_t *storage = b->storage; + + if (b->s->wupdated <= b->wupdated) + return APR_SUCCESS; + /* balancer sync */ + lbmethod = ap_lookup_provider(PROXY_LBMETHOD, b->s->lbpname, "0"); + if (lbmethod) { + b->lbmethod = lbmethod; + } else { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(02433) + "Cannot find LB Method: %s", b->s->lbpname); + return APR_EINVAL; + } + + /* worker sync */ + + /* + * Look thru the list of workers in shm + * and see which one(s) we are lacking... + * again, the cast to unsigned int is safe + * since our upper limit is always max_workers + * which is int. + */ + for (index = 0; index < b->max_workers; index++) { + int found; + apr_status_t rv; + if ((rv = storage->dptr(b->wslot, (unsigned int)index, (void *)&shm)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00965) "worker slotmem_dptr failed"); + return APR_EGENERAL; + } + /* account for possible "holes" in the slotmem + * (eg: slots 0-2 are used, but 3 isn't, but 4-5 is) + */ + if (!shm->hash.def || !shm->hash.fnv) + continue; + found = 0; + workers = (proxy_worker **)b->workers->elts; + for (i = 0; i < b->workers->nelts; i++, workers++) { + proxy_worker *worker = *workers; + if (worker->hash.def == shm->hash.def && worker->hash.fnv == shm->hash.fnv) { + found = 1; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02402) + "re-grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm, + ap_proxy_worker_name(conf->pool, worker)); + break; + } + } + if (!found) { + proxy_worker **runtime; + /* XXX: a thread mutex is maybe enough here */ + apr_global_mutex_lock(proxy_mutex); + runtime = apr_array_push(b->workers); + *runtime = apr_pcalloc(conf->pool, sizeof(proxy_worker)); + apr_global_mutex_unlock(proxy_mutex); + (*runtime)->hash = shm->hash; + (*runtime)->balancer = b; + (*runtime)->s = shm; + + rv = ap_proxy_initialize_worker(*runtime, s, conf->pool); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00966) "Cannot init worker"); + return rv; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02403) + "grabbing shm[%d] (0x%pp) for worker: %s", i, (void *)shm, + (*runtime)->s->name_ex); + } + } + if (b->s->need_reset) { + if (b->lbmethod && b->lbmethod->reset) + b->lbmethod->reset(b, s); + b->s->need_reset = 0; + } + b->wupdated = b->s->wupdated; + return APR_SUCCESS; +} + +PROXY_DECLARE(proxy_worker_shared *) ap_proxy_find_workershm(ap_slotmem_provider_t *storage, + ap_slotmem_instance_t *slot, + proxy_worker *worker, + unsigned int *index) +{ + proxy_worker_shared *shm; + unsigned int i, limit; + limit = storage->num_slots(slot); + for (i = 0; i < limit; i++) { + if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) { + return NULL; + } + if ((worker->s->hash.def == shm->hash.def) && + (worker->s->hash.fnv == shm->hash.fnv)) { + *index = i; + return shm; + } + } + return NULL; +} + +PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_provider_t *storage, + ap_slotmem_instance_t *slot, + proxy_balancer *balancer, + unsigned int *index) +{ + proxy_balancer_shared *shm; + unsigned int i, limit; + limit = storage->num_slots(slot); + for (i = 0; i < limit; i++) { + if (storage->dptr(slot, i, (void *)&shm) != APR_SUCCESS) { + return NULL; + } + if ((balancer->s->hash.def == shm->hash.def) && + (balancer->s->hash.fnv == shm->hash.fnv)) { + *index = i; + return shm; + } + } + return NULL; +} + +typedef struct header_connection { + apr_pool_t *pool; + apr_array_header_t *array; + const char *first; + unsigned int closed:1; +} header_connection; + +static int find_conn_headers(void *data, const char *key, const char *val) +{ + header_connection *x = data; + const char *name; + + do { + while (*val == ',' || *val == ';') { + val++; + } + name = ap_get_token(x->pool, &val, 0); + if (!strcasecmp(name, "close")) { + x->closed = 1; + } + if (!x->first) { + x->first = name; + } + else { + const char **elt; + if (!x->array) { + x->array = apr_array_make(x->pool, 4, sizeof(char *)); + } + elt = apr_array_push(x->array); + *elt = name; + } + } while (*val); + + return 1; +} + +/** + * Remove all headers referred to by the Connection header. + */ +static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers) +{ + const char **name; + header_connection x; + + x.pool = r->pool; + x.array = NULL; + x.first = NULL; + x.closed = 0; + + apr_table_unset(headers, "Proxy-Connection"); + + apr_table_do(find_conn_headers, &x, headers, "Connection", NULL); + if (x.first) { + /* fast path - no memory allocated for one header */ + apr_table_unset(headers, "Connection"); + apr_table_unset(headers, x.first); + } + if (x.array) { + /* two or more headers */ + while ((name = apr_array_pop(x.array))) { + apr_table_unset(headers, *name); + } + } + + return x.closed; +} + +PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, + apr_bucket_brigade *header_brigade, + request_rec *r, + proxy_conn_rec *p_conn, + proxy_worker *worker, + proxy_server_conf *conf, + apr_uri_t *uri, + char *url, char *server_portstr, + char **old_cl_val, + char **old_te_val) +{ + int rc = OK; + conn_rec *c = r->connection; + int counter; + char *buf; + apr_table_t *saved_headers_in = r->headers_in; + const char *saved_host = apr_table_get(saved_headers_in, "Host"); + const apr_array_header_t *headers_in_array; + const apr_table_entry_t *headers_in; + apr_bucket *e; + int force10 = 0, do_100_continue = 0; + conn_rec *origin = p_conn->connection; + const char *host, *val; + proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module); + + /* + * HTTP "Ping" test? Easiest is 100-Continue. However: + * To be compliant, we only use 100-Continue for requests with bodies. + * We also make sure we won't be talking HTTP/1.0 as well. + */ + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { + force10 = 1; + } + else if (apr_table_get(r->notes, "proxy-100-continue") + || PROXY_SHOULD_PING_100_CONTINUE(worker, r)) { + do_100_continue = 1; + } + if (force10 || apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { + if (origin) { + origin->keepalive = AP_CONN_CLOSE; + } + p_conn->close = 1; + } + + if (force10) { + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL); + } + else { + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); + } + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + + /* + * Make a copy on r->headers_in for the request we make to the backend, + * modify the copy in place according to our configuration and connection + * handling, use it to fill in the forwarded headers' brigade, and finally + * restore the saved/original ones in r->headers_in. + * + * Note: We need to take r->pool for apr_table_copy as the key / value + * pairs in r->headers_in have been created out of r->pool and + * p might be (and actually is) a longer living pool. + * This would trigger the bad pool ancestry abort in apr_table_copy if + * apr is compiled with APR_POOL_DEBUG. + * + * icing: if p indeed lives longer than r->pool, we should allocate + * all new header values from r->pool as well and avoid leakage. + */ + r->headers_in = apr_table_copy(r->pool, saved_headers_in); + + /* Return the original Transfer-Encoding and/or Content-Length values + * then drop the headers, they must be set by the proxy handler based + * on the actual body being forwarded. + */ + if ((*old_te_val = (char *)apr_table_get(r->headers_in, + "Transfer-Encoding"))) { + apr_table_unset(r->headers_in, "Transfer-Encoding"); + } + if ((*old_cl_val = (char *)apr_table_get(r->headers_in, + "Content-Length"))) { + apr_table_unset(r->headers_in, "Content-Length"); + } + + /* Clear out hop-by-hop request headers not to forward */ + if (ap_proxy_clear_connection(r, r->headers_in) < 0) { + rc = HTTP_BAD_REQUEST; + goto cleanup; + } + + /* RFC2616 13.5.1 says we should strip these */ + apr_table_unset(r->headers_in, "Keep-Alive"); + apr_table_unset(r->headers_in, "Upgrade"); + apr_table_unset(r->headers_in, "Trailer"); + apr_table_unset(r->headers_in, "TE"); + + /* Compute Host header */ + if (dconf->preserve_host == 0) { + if (ap_strchr_c(uri->hostname, ':')) { /* if literal IPv6 address */ + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { + host = apr_pstrcat(r->pool, "[", uri->hostname, "]:", + uri->port_str, NULL); + } else { + host = apr_pstrcat(r->pool, "[", uri->hostname, "]", NULL); + } + } else { + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { + host = apr_pstrcat(r->pool, uri->hostname, ":", + uri->port_str, NULL); + } else { + host = uri->hostname; + } + } + apr_table_setn(r->headers_in, "Host", host); + } + else { + /* don't want to use r->hostname as the incoming header might have a + * port attached, let's use the original header. + */ + host = saved_host; + if (!host) { + host = r->server->server_hostname; + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01092) + "no HTTP 0.9 request (with no host line) " + "on incoming request and preserve host set " + "forcing hostname to be %s for uri %s", + host, r->uri); + apr_table_setn(r->headers_in, "Host", host); + } + } + + /* handle Via */ + if (conf->viaopt == via_block) { + /* Block all outgoing Via: headers */ + apr_table_unset(r->headers_in, "Via"); + } else if (conf->viaopt != via_off) { + const char *server_name = ap_get_server_name(r); + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, + * then the server name returned by ap_get_server_name() is the + * origin server name (which does make too much sense with Via: headers) + * so we use the proxy vhost's name instead. + */ + if (server_name == r->hostname) + server_name = r->server->server_hostname; + /* Create a "Via:" request header entry and merge it */ + /* Generate outgoing Via: header with/without server comment: */ + apr_table_mergen(r->headers_in, "Via", + (conf->viaopt == via_full) + ? apr_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, server_portstr) + ); + } + + /* Use HTTP/1.1 100-Continue as quick "HTTP ping" test + * to backend + */ + if (do_100_continue) { + /* Add the Expect header if not already there. */ + if (!(val = apr_table_get(r->headers_in, "Expect")) + || (ap_cstr_casecmp(val, "100-Continue") != 0 /* fast path */ + && !ap_find_token(r->pool, val, "100-Continue"))) { + apr_table_mergen(r->headers_in, "Expect", "100-Continue"); + } + } + else { + /* XXX: we should strip the 100-continue token only from the + * Expect header, but are there others actually used anywhere? + */ + apr_table_unset(r->headers_in, "Expect"); + } + + /* X-Forwarded-*: handling + * + * XXX Privacy Note: + * ----------------- + * + * These request headers are only really useful when the mod_proxy + * is used in a reverse proxy configuration, so that useful info + * about the client can be passed through the reverse proxy and on + * to the backend server, which may require the information to + * function properly. + * + * In a forward proxy situation, these options are a potential + * privacy violation, as information about clients behind the proxy + * are revealed to arbitrary servers out there on the internet. + * + * The HTTP/1.1 Via: header is designed for passing client + * information through proxies to a server, and should be used in + * a forward proxy configuration instead of X-Forwarded-*. See the + * ProxyVia option for details. + */ + if (dconf->add_forwarded_headers) { + if (PROXYREQ_REVERSE == r->proxyreq) { + /* Add X-Forwarded-For: so that the upstream has a chance to + * determine, where the original request came from. + */ + apr_table_mergen(r->headers_in, "X-Forwarded-For", + r->useragent_ip); + + /* Add X-Forwarded-Host: so that upstream knows what the + * original request hostname was. + */ + if (saved_host) { + apr_table_mergen(r->headers_in, "X-Forwarded-Host", + saved_host); + } + + /* Add X-Forwarded-Server: so that upstream knows what the + * name of this proxy server is (if there are more than one) + * XXX: This duplicates Via: - do we strictly need it? + */ + apr_table_mergen(r->headers_in, "X-Forwarded-Server", + r->server->server_hostname); + } + } + + /* Do we want to strip Proxy-Authorization ? + * If we haven't used it, then NO + * If we have used it then MAYBE: RFC2616 says we MAY propagate it. + * So let's make it configurable by env. + */ + if (r->user != NULL /* we've authenticated */ + && !apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) { + apr_table_unset(r->headers_in, "Proxy-Authorization"); + } + + /* for sub-requests, ignore freshness/expiry headers */ + if (r->main) { + apr_table_unset(r->headers_in, "If-Match"); + apr_table_unset(r->headers_in, "If-Modified-Since"); + apr_table_unset(r->headers_in, "If-Range"); + apr_table_unset(r->headers_in, "If-Unmodified-Since"); + apr_table_unset(r->headers_in, "If-None-Match"); + } + + /* run hook to fixup the request we are about to send */ + proxy_run_fixups(r); + + /* We used to send `Host: ` always first, so let's keep it that + * way. No telling which legacy backend is relying on this. + * If proxy_run_fixups() changed the value, use it (though removal + * is ignored). + */ + val = apr_table_get(r->headers_in, "Host"); + if (val) { + apr_table_unset(r->headers_in, "Host"); + host = val; + } + buf = apr_pstrcat(p, "Host: ", host, CRLF, NULL); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + + /* Append the (remaining) headers to the brigade */ + headers_in_array = apr_table_elts(r->headers_in); + headers_in = (const apr_table_entry_t *) headers_in_array->elts; + for (counter = 0; counter < headers_in_array->nelts; counter++) { + if (headers_in[counter].key == NULL + || headers_in[counter].val == NULL) { + continue; + } + + buf = apr_pstrcat(p, headers_in[counter].key, ": ", + headers_in[counter].val, CRLF, + NULL); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + } + +cleanup: + r->headers_in = saved_headers_in; + return rc; +} + +PROXY_DECLARE(int) ap_proxy_prefetch_input(request_rec *r, + proxy_conn_rec *backend, + apr_bucket_brigade *input_brigade, + apr_read_type_e block, + apr_off_t *bytes_read, + apr_off_t max_read) +{ + apr_pool_t *p = r->pool; + conn_rec *c = r->connection; + apr_bucket_brigade *temp_brigade; + apr_status_t status; + apr_off_t bytes; + + *bytes_read = 0; + if (max_read < APR_BUCKET_BUFF_SIZE) { + max_read = APR_BUCKET_BUFF_SIZE; + } + + /* Prefetch max_read bytes + * + * This helps us avoid any election of C-L v.s. T-E + * request bodies, since we are willing to keep in + * memory this much data, in any case. This gives + * us an instant C-L election if the body is of some + * reasonable size. + */ + temp_brigade = apr_brigade_create(p, input_brigade->bucket_alloc); + + /* Account for saved input, if any. */ + apr_brigade_length(input_brigade, 0, bytes_read); + + /* Ensure we don't hit a wall where we have a buffer too small for + * ap_get_brigade's filters to fetch us another bucket, surrender + * once we hit 80 bytes (an arbitrary value) less than max_read. + */ + while (*bytes_read < max_read - 80 + && (APR_BRIGADE_EMPTY(input_brigade) + || !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)))) { + status = ap_get_brigade(r->input_filters, temp_brigade, + AP_MODE_READBYTES, block, + max_read - *bytes_read); + /* ap_get_brigade may return success with an empty brigade + * for a non-blocking read which would block + */ + if (block == APR_NONBLOCK_READ + && ((status == APR_SUCCESS && APR_BRIGADE_EMPTY(temp_brigade)) + || APR_STATUS_IS_EAGAIN(status))) { + break; + } + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01095) + "prefetch request body failed to %pI (%s)" + " from %s (%s)", backend->addr, + backend->hostname ? backend->hostname : "", + c->client_ip, c->remote_host ? c->remote_host : ""); + return ap_map_http_request_error(status, HTTP_BAD_REQUEST); + } + + apr_brigade_length(temp_brigade, 1, &bytes); + *bytes_read += bytes; + + /* + * Save temp_brigade in input_brigade. (At least) in the SSL case + * temp_brigade contains transient buckets whose data would get + * overwritten during the next call of ap_get_brigade in the loop. + * ap_save_brigade ensures these buckets to be set aside. + * Calling ap_save_brigade with NULL as filter is OK, because + * input_brigade already has been created and does not need to get + * created by ap_save_brigade. + */ + status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01096) + "processing prefetched request body failed" + " to %pI (%s) from %s (%s)", backend->addr, + backend->hostname ? backend->hostname : "", + c->client_ip, c->remote_host ? c->remote_host : ""); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + return OK; +} + +PROXY_DECLARE(int) ap_proxy_read_input(request_rec *r, + proxy_conn_rec *backend, + apr_bucket_brigade *bb, + apr_off_t max_read) +{ + apr_bucket_alloc_t *bucket_alloc = bb->bucket_alloc; + apr_read_type_e block = (backend->connection) ? APR_NONBLOCK_READ + : APR_BLOCK_READ; + apr_status_t status; + int rv; + + for (;;) { + apr_brigade_cleanup(bb); + status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, + block, max_read); + if (block == APR_BLOCK_READ + || (!(status == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) + && !APR_STATUS_IS_EAGAIN(status))) { + break; + } + + /* Flush and retry (blocking) */ + apr_brigade_cleanup(bb); + rv = ap_proxy_pass_brigade(bucket_alloc, r, backend, + backend->connection, bb, 1); + if (rv != OK) { + return rv; + } + block = APR_BLOCK_READ; + } + + if (status != APR_SUCCESS) { + conn_rec *c = r->connection; + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02608) + "read request body failed to %pI (%s)" + " from %s (%s)", backend->addr, + backend->hostname ? backend->hostname : "", + c->client_ip, c->remote_host ? c->remote_host : ""); + return ap_map_http_request_error(status, HTTP_BAD_REQUEST); + } + + return OK; +} + +PROXY_DECLARE(int) ap_proxy_spool_input(request_rec *r, + proxy_conn_rec *backend, + apr_bucket_brigade *input_brigade, + apr_off_t *bytes_spooled, + apr_off_t max_mem_spool) +{ + apr_pool_t *p = r->pool; + int seen_eos = 0, rv = OK; + apr_status_t status = APR_SUCCESS; + apr_bucket_alloc_t *bucket_alloc = input_brigade->bucket_alloc; + apr_bucket_brigade *body_brigade; + apr_bucket *e; + apr_off_t bytes, fsize = 0; + apr_file_t *tmpfile = NULL; + + *bytes_spooled = 0; + body_brigade = apr_brigade_create(p, bucket_alloc); + + do { + if (APR_BRIGADE_EMPTY(input_brigade)) { + rv = ap_proxy_read_input(r, backend, input_brigade, + HUGE_STRING_LEN); + if (rv != OK) { + return rv; + } + } + + /* If this brigade contains EOS, either stop or remove it. */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + seen_eos = 1; + } + + apr_brigade_length(input_brigade, 1, &bytes); + + if (*bytes_spooled + bytes > max_mem_spool) { + /* can't spool any more in memory; write latest brigade to disk */ + if (tmpfile == NULL) { + const char *temp_dir; + char *template; + + status = apr_temp_dir_get(&temp_dir, p); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01089) + "search for temporary directory failed"); + return HTTP_INTERNAL_SERVER_ERROR; + } + apr_filepath_merge(&template, temp_dir, + "modproxy.tmp.XXXXXX", + APR_FILEPATH_NATIVE, p); + status = apr_file_mktemp(&tmpfile, template, 0, p); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01090) + "creation of temporary file in directory " + "%s failed", temp_dir); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + for (e = APR_BRIGADE_FIRST(input_brigade); + e != APR_BRIGADE_SENTINEL(input_brigade); + e = APR_BUCKET_NEXT(e)) { + const char *data; + apr_size_t bytes_read, bytes_written; + + apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ); + status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written); + if (status != APR_SUCCESS) { + const char *tmpfile_name; + + if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) { + tmpfile_name = "(unknown)"; + } + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01091) + "write to temporary file %s failed", + tmpfile_name); + return HTTP_INTERNAL_SERVER_ERROR; + } + AP_DEBUG_ASSERT(bytes_read == bytes_written); + fsize += bytes_written; + } + apr_brigade_cleanup(input_brigade); + } + else { + + /* + * Save input_brigade in body_brigade. (At least) in the SSL case + * input_brigade contains transient buckets whose data would get + * overwritten during the next call of ap_get_brigade in the loop. + * ap_save_brigade ensures these buckets to be set aside. + * Calling ap_save_brigade with NULL as filter is OK, because + * body_brigade already has been created and does not need to get + * created by ap_save_brigade. + */ + status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p); + if (status != APR_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + } + + *bytes_spooled += bytes; + } while (!seen_eos); + + APR_BRIGADE_CONCAT(input_brigade, body_brigade); + if (tmpfile) { + apr_brigade_insert_file(input_brigade, tmpfile, 0, fsize, p); + } + if (apr_table_get(r->subprocess_env, "proxy-sendextracrlf")) { + e = apr_bucket_immortal_create(CRLF_ASCII, 2, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + } + if (tmpfile) { + /* We dropped metadata buckets when spooling to tmpfile, + * terminate with EOS to allow for flushing in a one go. + */ + e = apr_bucket_eos_create(bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + } + return OK; +} + +PROXY_DECLARE(int) ap_proxy_pass_brigade(apr_bucket_alloc_t *bucket_alloc, + request_rec *r, proxy_conn_rec *p_conn, + conn_rec *origin, apr_bucket_brigade *bb, + int flush) +{ + apr_status_t status; + apr_off_t transferred; + + if (flush) { + apr_bucket *e = apr_bucket_flush_create(bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + apr_brigade_length(bb, 0, &transferred); + if (transferred != -1) + p_conn->worker->s->transferred += transferred; + status = ap_pass_brigade(origin->output_filters, bb); + /* Cleanup the brigade now to avoid buckets lifetime + * issues in case of error returned below. */ + apr_brigade_cleanup(bb); + if (status != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01084) + "pass request body failed to %pI (%s)", + p_conn->addr, p_conn->hostname); + if (origin->aborted) { + const char *ssl_note; + + if (((ssl_note = apr_table_get(origin->notes, "SSL_connect_rv")) + != NULL) && (strcmp(ssl_note, "err") == 0)) { + return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, + "Error during SSL Handshake with" + " remote server"); + } + return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY; + } + else { + return HTTP_BAD_REQUEST; + } + } + return OK; +} + +/* Fill in unknown schemes from apr_uri_port_of_scheme() */ + +typedef struct proxy_schemes_t { + const char *name; + apr_port_t default_port; +} proxy_schemes_t ; + +static proxy_schemes_t pschemes[] = +{ + {"fcgi", 8000}, + {"ajp", AJP13_DEF_PORT}, + {"scgi", SCGI_DEF_PORT}, + {"h2c", DEFAULT_HTTP_PORT}, + {"h2", DEFAULT_HTTPS_PORT}, + {"ws", DEFAULT_HTTP_PORT}, + {"wss", DEFAULT_HTTPS_PORT}, + { NULL, 0xFFFF } /* unknown port */ +}; + +PROXY_DECLARE(apr_port_t) ap_proxy_port_of_scheme(const char *scheme) +{ + if (scheme) { + apr_port_t port; + if ((port = apr_uri_port_of_scheme(scheme)) != 0) { + return port; + } else { + proxy_schemes_t *pscheme; + for (pscheme = pschemes; pscheme->name != NULL; ++pscheme) { + if (ap_cstr_casecmp(scheme, pscheme->name) == 0) { + return pscheme->default_port; + } + } + } + } + return 0; +} + +static APR_INLINE int ap_filter_should_yield(ap_filter_t *f) +{ + return f->c->data_in_output_filters; +} + +static APR_INLINE int ap_filter_output_pending(conn_rec *c) +{ + ap_filter_t *f = c->output_filters; + while (f->next) { + f = f->next; + } + if (f->frec->filter_func.out_func(f, NULL)) { + return AP_FILTER_ERROR; + } + return c->data_in_output_filters ? OK : DECLINED; +} + +PROXY_DECLARE(apr_status_t) ap_proxy_buckets_lifetime_transform(request_rec *r, + apr_bucket_brigade *from, + apr_bucket_brigade *to) +{ + apr_bucket *e; + apr_bucket *new; + const char *data; + apr_size_t bytes; + apr_status_t rv = APR_SUCCESS; + apr_bucket_alloc_t *bucket_alloc = to->bucket_alloc; + + apr_brigade_cleanup(to); + for (e = APR_BRIGADE_FIRST(from); + e != APR_BRIGADE_SENTINEL(from); + e = APR_BUCKET_NEXT(e)) { + if (!APR_BUCKET_IS_METADATA(e)) { + apr_bucket_read(e, &data, &bytes, APR_BLOCK_READ); + new = apr_bucket_transient_create(data, bytes, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(to, new); + } + else if (APR_BUCKET_IS_FLUSH(e)) { + new = apr_bucket_flush_create(bucket_alloc); + APR_BRIGADE_INSERT_TAIL(to, new); + } + else if (APR_BUCKET_IS_EOS(e)) { + new = apr_bucket_eos_create(bucket_alloc); + APR_BRIGADE_INSERT_TAIL(to, new); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03304) + "Unhandled bucket type of type %s in" + " ap_proxy_buckets_lifetime_transform", e->type->name); + rv = APR_EGENERAL; + } + } + return rv; +} + +/* An arbitrary large value to address pathological case where we keep + * reading from one side only, without scheduling the other direction for + * too long. This can happen with large MTU and small read buffers, like + * micro-benchmarking huge files bidirectional transfer with client, proxy + * and backend on localhost for instance. Though we could just ignore the + * case and let the sender stop by itself at some point when/if it needs to + * receive data, or the receiver stop when/if it needs to send... + */ +#define PROXY_TRANSFER_MAX_READS 10000 + +PROXY_DECLARE(apr_status_t) ap_proxy_transfer_between_connections( + request_rec *r, + conn_rec *c_i, + conn_rec *c_o, + apr_bucket_brigade *bb_i, + apr_bucket_brigade *bb_o, + const char *name, + int *sent, + apr_off_t bsize, + int flags) +{ + apr_status_t rv; + int flush_each = 0; + unsigned int num_reads = 0; +#ifdef DEBUGGING + apr_off_t len; +#endif + + /* + * Compat: since FLUSH_EACH is default (and zero) for legacy reasons, we + * pretend it's no FLUSH_AFTER nor YIELD_PENDING flags, the latter because + * flushing would defeat the purpose of checking for pending data (hence + * determine whether or not the output chain/stack is full for stopping). + */ + if (!(flags & (AP_PROXY_TRANSFER_FLUSH_AFTER | + AP_PROXY_TRANSFER_YIELD_PENDING))) { + flush_each = 1; + } + + for (;;) { + apr_brigade_cleanup(bb_i); + rv = ap_get_brigade(c_i->input_filters, bb_i, AP_MODE_READBYTES, + APR_NONBLOCK_READ, bsize); + if (rv != APR_SUCCESS) { + if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(03308) + "ap_proxy_transfer_between_connections: " + "error on %s - ap_get_brigade", + name); + if (rv == APR_INCOMPLETE) { + /* Don't return APR_INCOMPLETE, it'd mean "should yield" + * for the caller, while it means "incomplete body" here + * from ap_http_filter(), which is an error. + */ + rv = APR_EGENERAL; + } + } + break; + } + + if (c_o->aborted) { + apr_brigade_cleanup(bb_i); + flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER; + rv = APR_EPIPE; + break; + } + if (APR_BRIGADE_EMPTY(bb_i)) { + break; + } +#ifdef DEBUGGING + len = -1; + apr_brigade_length(bb_i, 0, &len); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03306) + "ap_proxy_transfer_between_connections: " + "read %" APR_OFF_T_FMT + " bytes from %s", len, name); +#endif + if (sent) { + *sent = 1; + } + ap_proxy_buckets_lifetime_transform(r, bb_i, bb_o); + if (flush_each) { + apr_bucket *b; + /* + * Do not use ap_fflush here since this would cause the flush + * bucket to be sent in a separate brigade afterwards which + * causes some filters to set aside the buckets from the first + * brigade and process them when FLUSH arrives in the second + * brigade. As set asides of our transformed buckets involve + * memory copying we try to avoid this. If we have the flush + * bucket in the first brigade they directly process the + * buckets without setting them aside. + */ + b = apr_bucket_flush_create(bb_o->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb_o, b); + } + rv = ap_pass_brigade(c_o->output_filters, bb_o); + apr_brigade_cleanup(bb_o); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(03307) + "ap_proxy_transfer_between_connections: " + "error on %s - ap_pass_brigade", + name); + flags &= ~AP_PROXY_TRANSFER_FLUSH_AFTER; + break; + } + + /* Yield if the output filters stack is full? This is to avoid + * blocking and give the caller a chance to POLLOUT async. + */ + if ((flags & AP_PROXY_TRANSFER_YIELD_PENDING) + && ap_filter_should_yield(c_o->output_filters)) { + int rc = ap_filter_output_pending(c_o); + if (rc == OK) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "ap_proxy_transfer_between_connections: " + "yield (output pending)"); + rv = APR_INCOMPLETE; + break; + } + if (rc != DECLINED) { + rv = AP_FILTER_ERROR; + break; + } + } + + /* Yield if we keep hold of the thread for too long? This gives + * the caller a chance to schedule the other direction too. + */ + if ((flags & AP_PROXY_TRANSFER_YIELD_MAX_READS) + && ++num_reads > PROXY_TRANSFER_MAX_READS) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, + "ap_proxy_transfer_between_connections: " + "yield (max reads)"); + rv = APR_SUCCESS; + break; + } + } + + if (flags & AP_PROXY_TRANSFER_FLUSH_AFTER) { + ap_fflush(c_o->output_filters, bb_o); + apr_brigade_cleanup(bb_o); + } + apr_brigade_cleanup(bb_i); + + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, rv, r, + "ap_proxy_transfer_between_connections complete (%s %pI)", + (c_i == r->connection) ? "to" : "from", + (c_i == r->connection) ? c_o->client_addr + : c_i->client_addr); + + if (APR_STATUS_IS_EAGAIN(rv)) { + rv = APR_SUCCESS; + } + return rv; +} + +struct proxy_tunnel_conn { + /* the other side of the tunnel */ + struct proxy_tunnel_conn *other; + + conn_rec *c; + const char *name; + + apr_pollfd_t *pfd; + apr_bucket_brigade *bb; + + unsigned int down_in:1, + down_out:1; +}; + +PROXY_DECLARE(apr_status_t) ap_proxy_tunnel_create(proxy_tunnel_rec **ptunnel, + request_rec *r, conn_rec *c_o, + const char *scheme) +{ + apr_status_t rv; + conn_rec *c_i = r->connection; + apr_interval_time_t timeout = -1; + proxy_tunnel_rec *tunnel; + + *ptunnel = NULL; + + tunnel = apr_pcalloc(r->pool, sizeof(*tunnel)); + + rv = apr_pollset_create(&tunnel->pollset, 2, r->pool, APR_POLLSET_NOCOPY); + if (rv != APR_SUCCESS) { + return rv; + } + + tunnel->r = r; + tunnel->scheme = apr_pstrdup(r->pool, scheme); + tunnel->client = apr_pcalloc(r->pool, sizeof(struct proxy_tunnel_conn)); + tunnel->origin = apr_pcalloc(r->pool, sizeof(struct proxy_tunnel_conn)); + tunnel->pfds = apr_array_make(r->pool, 2, sizeof(apr_pollfd_t)); + tunnel->read_buf_size = ap_get_read_buf_size(r); + tunnel->client->other = tunnel->origin; + tunnel->origin->other = tunnel->client; + tunnel->timeout = -1; + + tunnel->client->c = c_i; + tunnel->client->name = "client"; + tunnel->client->bb = apr_brigade_create(c_i->pool, c_i->bucket_alloc); + tunnel->client->pfd = &APR_ARRAY_PUSH(tunnel->pfds, apr_pollfd_t); + tunnel->client->pfd->p = r->pool; + tunnel->client->pfd->desc_type = APR_POLL_SOCKET; + tunnel->client->pfd->desc.s = ap_get_conn_socket(c_i); + tunnel->client->pfd->client_data = tunnel->client; + + tunnel->origin->c = c_o; + tunnel->origin->name = "origin"; + tunnel->origin->bb = apr_brigade_create(c_o->pool, c_o->bucket_alloc); + tunnel->origin->pfd = &APR_ARRAY_PUSH(tunnel->pfds, apr_pollfd_t); + tunnel->origin->pfd->p = r->pool; + tunnel->origin->pfd->desc_type = APR_POLL_SOCKET; + tunnel->origin->pfd->desc.s = ap_get_conn_socket(c_o); + tunnel->origin->pfd->client_data = tunnel->origin; + + /* Defaults to the biggest timeout of both connections */ + apr_socket_timeout_get(tunnel->client->pfd->desc.s, &timeout); + apr_socket_timeout_get(tunnel->origin->pfd->desc.s, &tunnel->timeout); + if (timeout >= 0 && (tunnel->timeout < 0 || tunnel->timeout < timeout)) { + tunnel->timeout = timeout; + } + + /* We should be nonblocking from now on the sockets */ + apr_socket_opt_set(tunnel->client->pfd->desc.s, APR_SO_NONBLOCK, 1); + apr_socket_opt_set(tunnel->origin->pfd->desc.s, APR_SO_NONBLOCK, 1); + + /* No coalescing filters */ + ap_remove_output_filter_byhandle(c_i->output_filters, + "SSL/TLS Coalescing Filter"); + ap_remove_output_filter_byhandle(c_o->output_filters, + "SSL/TLS Coalescing Filter"); + + /* Bidirectional non-HTTP stream will confuse mod_reqtimeoout */ + ap_remove_input_filter_byhandle(c_i->input_filters, "reqtimeout"); + + /* The input/output filter stacks should contain connection filters only */ + r->input_filters = r->proto_input_filters = c_i->input_filters; + r->output_filters = r->proto_output_filters = c_i->output_filters; + + /* Won't be reused after tunneling */ + c_i->keepalive = AP_CONN_CLOSE; + c_o->keepalive = AP_CONN_CLOSE; + + /* Disable half-close forwarding for this request? */ + if (apr_table_get(r->subprocess_env, "proxy-nohalfclose")) { + tunnel->nohalfclose = 1; + } + + /* Start with POLLOUT and let ap_proxy_tunnel_run() schedule both + * directions when there are no output data pending (anymore). + */ + tunnel->client->pfd->reqevents = APR_POLLOUT | APR_POLLERR; + tunnel->origin->pfd->reqevents = APR_POLLOUT | APR_POLLERR; + if ((rv = apr_pollset_add(tunnel->pollset, tunnel->client->pfd)) + || (rv = apr_pollset_add(tunnel->pollset, tunnel->origin->pfd))) { + return rv; + } + + *ptunnel = tunnel; + return APR_SUCCESS; +} + +static void add_pollset(apr_pollset_t *pollset, apr_pollfd_t *pfd, + apr_int16_t events) +{ + apr_status_t rv; + + AP_DEBUG_ASSERT((pfd->reqevents & events) == 0); + + if (pfd->reqevents) { + rv = apr_pollset_remove(pollset, pfd); + if (rv != APR_SUCCESS) { + AP_DEBUG_ASSERT(1); + } + } + + if (events & APR_POLLIN) { + events |= APR_POLLHUP; + } + pfd->reqevents |= events | APR_POLLERR; + rv = apr_pollset_add(pollset, pfd); + if (rv != APR_SUCCESS) { + AP_DEBUG_ASSERT(1); + } +} + +static void del_pollset(apr_pollset_t *pollset, apr_pollfd_t *pfd, + apr_int16_t events) +{ + apr_status_t rv; + + AP_DEBUG_ASSERT((pfd->reqevents & events) != 0); + + rv = apr_pollset_remove(pollset, pfd); + if (rv != APR_SUCCESS) { + AP_DEBUG_ASSERT(0); + return; + } + + if (events & APR_POLLIN) { + events |= APR_POLLHUP; + } + if (pfd->reqevents & ~(events | APR_POLLERR)) { + pfd->reqevents &= ~events; + rv = apr_pollset_add(pollset, pfd); + if (rv != APR_SUCCESS) { + AP_DEBUG_ASSERT(0); + return; + } + } + else { + pfd->reqevents = 0; + } +} + +static int proxy_tunnel_forward(proxy_tunnel_rec *tunnel, + struct proxy_tunnel_conn *in) +{ + struct proxy_tunnel_conn *out = in->other; + apr_status_t rv; + int sent = 0; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, tunnel->r, + "proxy: %s: %s input ready", + tunnel->scheme, in->name); + + rv = ap_proxy_transfer_between_connections(tunnel->r, + in->c, out->c, + in->bb, out->bb, + in->name, &sent, + tunnel->read_buf_size, + AP_PROXY_TRANSFER_YIELD_PENDING | + AP_PROXY_TRANSFER_YIELD_MAX_READS); + if (sent && out == tunnel->client) { + tunnel->replied = 1; + } + if (rv != APR_SUCCESS) { + if (APR_STATUS_IS_INCOMPLETE(rv)) { + /* Pause POLLIN while waiting for POLLOUT on the other + * side, hence avoid filling the output filters even + * more to avoid blocking there. + */ + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, tunnel->r, + "proxy: %s: %s wait writable", + tunnel->scheme, out->name); + } + else if (APR_STATUS_IS_EOF(rv)) { + /* Stop POLLIN and wait for POLLOUT (flush) on the + * other side to shut it down. + */ + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, tunnel->r, + "proxy: %s: %s read shutdown", + tunnel->scheme, in->name); + if (tunnel->nohalfclose) { + /* No half-close forwarding, we are done both ways as + * soon as one side shuts down. + */ + return DONE; + } + in->down_in = 1; + } + else { + /* Real failure, bail out */ + return HTTP_INTERNAL_SERVER_ERROR; + } + + del_pollset(tunnel->pollset, in->pfd, APR_POLLIN); + add_pollset(tunnel->pollset, out->pfd, APR_POLLOUT); + } + + return OK; +} + +PROXY_DECLARE(int) ap_proxy_tunnel_run(proxy_tunnel_rec *tunnel) +{ + int status = OK, rc; + request_rec *r = tunnel->r; + apr_pollset_t *pollset = tunnel->pollset; + struct proxy_tunnel_conn *client = tunnel->client, + *origin = tunnel->origin; + apr_interval_time_t timeout = tunnel->timeout >= 0 ? tunnel->timeout : -1; + const char *scheme = tunnel->scheme; + apr_status_t rv; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10212) + "proxy: %s: tunnel running (timeout %lf)", + scheme, timeout >= 0 ? (double)timeout / APR_USEC_PER_SEC + : (double)-1.0); + + /* Loop until both directions of the connection are closed, + * or a failure occurs. + */ + do { + const apr_pollfd_t *results; + apr_int32_t nresults, i; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, + "proxy: %s: polling (client=%hx, origin=%hx)", + scheme, client->pfd->reqevents, origin->pfd->reqevents); + do { + rv = apr_pollset_poll(pollset, timeout, &nresults, &results); + } while (APR_STATUS_IS_EINTR(rv)); + + if (rv != APR_SUCCESS) { + if (APR_STATUS_IS_TIMEUP(rv)) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(10213) + "proxy: %s: polling timed out " + "(client=%hx, origin=%hx)", + scheme, client->pfd->reqevents, + origin->pfd->reqevents); + status = HTTP_GATEWAY_TIME_OUT; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10214) + "proxy: %s: polling failed", scheme); + status = HTTP_INTERNAL_SERVER_ERROR; + } + goto done; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(10215) + "proxy: %s: woken up, %i result(s)", scheme, nresults); + + for (i = 0; i < nresults; i++) { + const apr_pollfd_t *pfd = &results[i]; + struct proxy_tunnel_conn *tc = pfd->client_data; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, + "proxy: %s: #%i: %s: %hx/%hx", scheme, i, + tc->name, pfd->rtnevents, tc->pfd->reqevents); + + /* sanity check */ + if (pfd->desc.s != client->pfd->desc.s + && pfd->desc.s != origin->pfd->desc.s) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10222) + "proxy: %s: unknown socket in pollset", scheme); + status = HTTP_INTERNAL_SERVER_ERROR; + goto done; + } + + if (!(pfd->rtnevents & (APR_POLLIN | APR_POLLOUT | + APR_POLLHUP | APR_POLLERR))) { + /* this catches POLLNVAL etc.. */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10220) + "proxy: %s: polling events error (%x)", + scheme, pfd->rtnevents); + status = HTTP_INTERNAL_SERVER_ERROR; + goto done; + } + + /* We want to write if we asked for POLLOUT and got: + * - POLLOUT: the socket is ready for write; + * - !POLLIN: the socket is in error state (POLLERR) so we let + * the user know by failing the write and log, OR the socket + * is shutdown for read already (POLLHUP) so we have to + * shutdown for write. + */ + if ((tc->pfd->reqevents & APR_POLLOUT) + && ((pfd->rtnevents & APR_POLLOUT) + || !(tc->pfd->reqevents & APR_POLLIN) + || !(pfd->rtnevents & (APR_POLLIN | APR_POLLHUP)))) { + struct proxy_tunnel_conn *out = tc, *in = tc->other; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, + "proxy: %s: %s output ready", + scheme, out->name); + + rc = ap_filter_output_pending(out->c); + if (rc == OK) { + /* Keep polling out (only) */ + continue; + } + if (rc != DECLINED) { + /* Real failure, bail out */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10221) + "proxy: %s: %s flushing failed (%i)", + scheme, out->name, rc); + status = rc; + goto done; + } + + /* No more pending data. If the other side is not readable + * anymore it's time to shutdown for write (this direction + * is over). Otherwise back to normal business. + */ + del_pollset(pollset, out->pfd, APR_POLLOUT); + if (in->down_in) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, + "proxy: %s: %s write shutdown", + scheme, out->name); + apr_socket_shutdown(out->pfd->desc.s, 1); + out->down_out = 1; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "proxy: %s: %s resume writable", + scheme, out->name); + add_pollset(pollset, in->pfd, APR_POLLIN); + + /* Flush any pending input data now, we don't know when + * the next POLLIN will trigger and retaining data might + * deadlock the underlying protocol. We don't check for + * pending data first with ap_filter_input_pending() since + * the read from proxy_tunnel_forward() is nonblocking + * anyway and returning OK if there's no data. + */ + rc = proxy_tunnel_forward(tunnel, in); + if (rc != OK) { + status = rc; + goto done; + } + } + } + + /* We want to read if we asked for POLLIN|HUP and got: + * - POLLIN|HUP: the socket is ready for read or EOF (POLLHUP); + * - !POLLOUT: the socket is in error state (POLLERR) so we let + * the user know by failing the read and log. + */ + if ((tc->pfd->reqevents & APR_POLLIN) + && ((pfd->rtnevents & (APR_POLLIN | APR_POLLHUP)) + || !(pfd->rtnevents & APR_POLLOUT))) { + rc = proxy_tunnel_forward(tunnel, tc); + if (rc != OK) { + status = rc; + goto done; + } + } + } + } while (!client->down_out || !origin->down_out); + +done: + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(10223) + "proxy: %s: tunneling returns (%i)", scheme, status); + if (status == DONE) { + status = OK; + } + return status; +} + +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); + APR_REGISTER_OPTIONAL_FN(ap_proxy_clear_connection); + APR_REGISTER_OPTIONAL_FN(proxy_balancer_get_best_worker); +} diff --git a/modules/proxy/proxy_util.h b/modules/proxy/proxy_util.h new file mode 100644 index 0000000..bc131da --- /dev/null +++ b/modules/proxy/proxy_util.h @@ -0,0 +1,45 @@ +/* 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. + */ + +#ifndef PROXY_UTIL_H_ +#define PROXY_UTIL_H_ + +/** + * @file proxy_util.h + * @brief Internal interfaces private to mod_proxy. + * + * @defgroup MOD_PROXY_PRIVATE Private + * @ingroup MOD_PROXY + * @{ + */ + +PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p); +PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p); +PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p); +PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p); + +extern PROXY_DECLARE_DATA int proxy_lb_workers; +extern PROXY_DECLARE_DATA const apr_strmatch_pattern *ap_proxy_strmatch_path; +extern PROXY_DECLARE_DATA const apr_strmatch_pattern *ap_proxy_strmatch_domain; + +/** + * Register optional functions declared within proxy_util.c. + */ +void proxy_util_register_hooks(apr_pool_t *p); + +/** @} */ + +#endif /* PROXY_UTIL_H_ */ diff --git a/modules/proxy/scgi.h b/modules/proxy/scgi.h new file mode 100644 index 0000000..a2e5e96 --- /dev/null +++ b/modules/proxy/scgi.h @@ -0,0 +1,36 @@ +/* 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. + */ + +/** + * @file scgi.h + * @brief Shared SCGI-related definitions + * + * @ingroup APACHE_INTERNAL + * @{ + */ + +#ifndef SCGI_H +#define SCGI_H + +/* This is not defined by the protocol. It is a convention + * of mod_proxy_scgi, and mod_proxy utility routines must + * use the same value as mod_proxy_scgi. + */ +#define SCGI_DEF_PORT 4000 + +/** @} */ + +#endif /* SCGI_H */ |