JBoss Native SVN: r2305 - trunk/mod_cluster/native/mod_proxy.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-19 12:30:33 -0500 (Thu, 19 Feb 2009)
New Revision: 2305
Modified:
trunk/mod_cluster/native/mod_proxy/Makefile.in
trunk/mod_cluster/native/mod_proxy/configure.in
Log:
Use apxs.
Modified: trunk/mod_cluster/native/mod_proxy/Makefile.in
===================================================================
--- trunk/mod_cluster/native/mod_proxy/Makefile.in 2009-02-19 17:26:28 UTC (rev 2304)
+++ trunk/mod_cluster/native/mod_proxy/Makefile.in 2009-02-19 17:30:33 UTC (rev 2305)
@@ -1,7 +1,7 @@
# Makefile.in for mod_proxy_cluster
# copy the source in the httpd Apache source tree
APACHE_BASE = @APACHE_BASE@
-top_builddir = @APACHE_BASE@
+top_builddir = @APACHE_BUILDDIR@
# For .deps.
builddir = @CLUSTER_BASE@
# For the apache includes
@@ -14,17 +14,17 @@
proxy_ajp_objs=mod_proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo ajp_utils.lo
proxy_ajp_objs_slo=mod_proxy_ajp.slo ajp_header.slo ajp_link.slo ajp_msg.slo ajp_utils.slo
-include $(APACHE_BASE)/build/rules.mk
+include $(top_builddir)/build/rules.mk
SH_COMPILE = $(LIBTOOL) --mode=compile $(BASE_CC) -I../include -prefer-pic -c $< && touch $@
all: mod_proxy.so mod_proxy_http.so mod_proxy_ajp.so
mod_proxy.so: mod_proxy.la
- $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy.la `pwd`
+ $(top_builddir)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy.la `pwd`
mod_proxy_http.so: mod_proxy_http.la
- $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy_http.la `pwd`
+ $(top_builddir)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy_http.la `pwd`
mod_proxy_ajp.so: mod_proxy_ajp.la
- $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy_ajp.la `pwd`
+ $(top_builddir)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy_ajp.la `pwd`
mod_proxy.la: $(proxy_objs_slo)
$(SH_LINK) -rpath $(libexecdir) -module -avoid-version $(proxy_objs)
Modified: trunk/mod_cluster/native/mod_proxy/configure.in
===================================================================
--- trunk/mod_cluster/native/mod_proxy/configure.in 2009-02-19 17:26:28 UTC (rev 2304)
+++ trunk/mod_cluster/native/mod_proxy/configure.in 2009-02-19 17:30:33 UTC (rev 2305)
@@ -4,21 +4,30 @@
AC_INIT(mod_proxy.c)
AC_MSG_CHECKING(for Apache httpd installation)
-AC_ARG_WITH(apache,
-[ --with-apache[=DIR] DIR is the apache base installation
+AC_ARG_WITH(apxs,
+[ --with-apxs[=FILE] Build shared Apache module using apxs.
+ FILE is the apache apxs tool
],
-[ if test "$withval" = "yes"; then
- withval=/usr/local/etc/httpd
- fi
+[ apxs_support=true
if test "$withval" != "no"; then
- APACHE_BASE=$withval
+ APXS=$withval
+ AC_MSG_RESULT([APXS is ${APXS}])
+ ${APXS} -q PREFIX >/dev/null 2>/dev/null || apxs_support=false
else
- AC_MSG_ERROR(mod_proxy need a valid apache location)
+ apxs_support=false
fi
+ AC_MSG_RESULT([apxs_support is ${apxs_support}])
+ if test "${apxs_support}" = "false" ; then
+ AC_MSG_ERROR(mod_cluster module need a valid apxs)
+ fi
+ APACHE_BASE=`$APXS -q PREFIX`
+ APACHE_BUILDDIR=`$APXS -q installbuilddir`
+ APACHE_BUILDDIR=`(cd $APACHE_BUILDDIR/..; pwd)`
],
-[ AC_MSG_ERROR(Please use --with-apache[=DIR])])
+[ AC_MSG_ERROR(Please use --with-apxs[=FILE])])
CLUSTER_BASE=`pwd`
AC_SUBST(APACHE_BASE)
AC_SUBST(CLUSTER_BASE)
+AC_SUBST(APACHE_BUILDDIR)
AC_OUTPUT(Makefile)
15 years, 11 months
JBoss Native SVN: r2304 - trunk/mod_cluster/native/mod_manager.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-19 12:26:28 -0500 (Thu, 19 Feb 2009)
New Revision: 2304
Modified:
trunk/mod_cluster/native/mod_manager/Makefile.in
trunk/mod_cluster/native/mod_manager/configure.in
Log:
Use apxs.
Modified: trunk/mod_cluster/native/mod_manager/Makefile.in
===================================================================
--- trunk/mod_cluster/native/mod_manager/Makefile.in 2009-02-19 17:22:04 UTC (rev 2303)
+++ trunk/mod_cluster/native/mod_manager/Makefile.in 2009-02-19 17:26:28 UTC (rev 2304)
@@ -1,19 +1,19 @@
# Makefile.in for mod_proxy_cluster
# copy the source in the httpd Apache source tree
APACHE_BASE = @APACHE_BASE@
-top_builddir = @APACHE_BASE@
+top_builddir = @APACHE_BUILDDIR@
# For .deps.
builddir = @CLUSTER_BASE@
# For the apache includes
top_srcdir = @APACHE_BASE@
-include $(APACHE_BASE)/build/rules.mk
+include $(top_builddir)/build/rules.mk
SH_COMPILE = $(LIBTOOL) --mode=compile $(BASE_CC) -I../include -prefer-pic -c $< && touch $@
all: mod_manager.so
mod_manager.so: mod_manager.la
- $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_manager.la `pwd`
+ $(top_builddir)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_manager.la `pwd`
mod_manager.la: mod_manager.slo node.slo context.slo host.slo balancer.slo sessionid.slo
$(SH_LINK) -rpath $(libexecdir) -module -avoid-version mod_manager.lo node.lo context.lo host.lo balancer.lo sessionid.lo
Modified: trunk/mod_cluster/native/mod_manager/configure.in
===================================================================
--- trunk/mod_cluster/native/mod_manager/configure.in 2009-02-19 17:22:04 UTC (rev 2303)
+++ trunk/mod_cluster/native/mod_manager/configure.in 2009-02-19 17:26:28 UTC (rev 2304)
@@ -4,21 +4,30 @@
AC_INIT(mod_manager.c)
AC_MSG_CHECKING(for Apache httpd installation)
-AC_ARG_WITH(apache,
-[ --with-apache[=DIR] DIR is the apache base installation
+AC_ARG_WITH(apxs,
+[ --with-apxs[=FILE] Build shared Apache module using apxs.
+ FILE is the apache apxs tool
],
-[ if test "$withval" = "yes"; then
- withval=/usr/local/etc/httpd
- fi
+[ apxs_support=true
if test "$withval" != "no"; then
- APACHE_BASE=$withval
+ APXS=$withval
+ AC_MSG_RESULT([APXS is ${APXS}])
+ ${APXS} -q PREFIX >/dev/null 2>/dev/null || apxs_support=false
else
- AC_MSG_ERROR(mod_manager need a valid apache location)
+ apxs_support=false
fi
+ AC_MSG_RESULT([apxs_support is ${apxs_support}])
+ if test "${apxs_support}" = "false" ; then
+ AC_MSG_ERROR(mod_cluster module need a valid apxs)
+ fi
+ APACHE_BASE=`$APXS -q PREFIX`
+ APACHE_BUILDDIR=`$APXS -q installbuilddir`
+ APACHE_BUILDDIR=`(cd $APACHE_BUILDDIR/..; pwd)`
],
-[ AC_MSG_ERROR(Please use --with-apache[=DIR])])
+[ AC_MSG_ERROR(Please use --with-apxs[=FILE])])
CLUSTER_BASE=`pwd`
AC_SUBST(APACHE_BASE)
AC_SUBST(CLUSTER_BASE)
+AC_SUBST(APACHE_BUILDDIR)
AC_OUTPUT(Makefile)
15 years, 11 months
JBoss Native SVN: r2303 - trunk/mod_cluster/native/advertise.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-19 12:22:04 -0500 (Thu, 19 Feb 2009)
New Revision: 2303
Modified:
trunk/mod_cluster/native/advertise/Makefile.in
trunk/mod_cluster/native/advertise/configure.in
Log:
Use apxs.
Modified: trunk/mod_cluster/native/advertise/Makefile.in
===================================================================
--- trunk/mod_cluster/native/advertise/Makefile.in 2009-02-19 15:35:46 UTC (rev 2302)
+++ trunk/mod_cluster/native/advertise/Makefile.in 2009-02-19 17:22:04 UTC (rev 2303)
@@ -1,19 +1,19 @@
# Makefile.in for mod_proxy_cluster
# copy the source in the httpd Apache source tree
APACHE_BASE = @APACHE_BASE@
-top_builddir = @APACHE_BASE@
+top_builddir = @APACHE_BUILDDIR@
# For .deps.
builddir = @CLUSTER_BASE@
# For the apache includes
top_srcdir = @APACHE_BASE@
-include $(APACHE_BASE)/build/rules.mk
+include $(top_builddir)/build/rules.mk
SH_COMPILE = $(LIBTOOL) --mode=compile $(BASE_CC) -I../include -prefer-pic -c $< && touch $@
all: mod_advertise.so
mod_advertise.so: mod_advertise.la
- $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_advertise.la `pwd`
+ $(top_builddir)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_advertise.la `pwd`
mod_advertise.la: mod_advertise.slo
$(SH_LINK) -rpath $(libexecdir) -module -avoid-version mod_advertise.lo
Modified: trunk/mod_cluster/native/advertise/configure.in
===================================================================
--- trunk/mod_cluster/native/advertise/configure.in 2009-02-19 15:35:46 UTC (rev 2302)
+++ trunk/mod_cluster/native/advertise/configure.in 2009-02-19 17:22:04 UTC (rev 2303)
@@ -4,21 +4,30 @@
AC_INIT(mod_advertise.c)
AC_MSG_CHECKING(for Apache httpd installation)
-AC_ARG_WITH(apache,
-[ --with-apache[=DIR] DIR is the apache base installation
+AC_ARG_WITH(apxs,
+[ --with-apxs[=FILE] Build shared Apache module using apxs.
+ FILE is the apache apxs tool
],
-[ if test "$withval" = "yes"; then
- withval=/usr/local/etc/httpd
- fi
+[ apxs_support=true
if test "$withval" != "no"; then
- APACHE_BASE=$withval
+ APXS=$withval
+ AC_MSG_RESULT([APXS is ${APXS}])
+ ${APXS} -q PREFIX >/dev/null 2>/dev/null || apxs_support=false
else
- AC_MSG_ERROR(mod_manager need a valid apache location)
+ apxs_support=false
fi
+ AC_MSG_RESULT([apxs_support is ${apxs_support}])
+ if test "${apxs_support}" = "false" ; then
+ AC_MSG_ERROR(mod_cluster module need a valid apxs)
+ fi
+ APACHE_BASE=`$APXS -q PREFIX`
+ APACHE_BUILDDIR=`$APXS -q installbuilddir`
+ APACHE_BUILDDIR=`(cd $APACHE_BUILDDIR/..; pwd)`
],
-[ AC_MSG_ERROR(Please use --with-apache[=DIR])])
+[ AC_MSG_ERROR(Please use --with-apxs[=FILE])])
CLUSTER_BASE=`pwd`
AC_SUBST(APACHE_BASE)
AC_SUBST(CLUSTER_BASE)
+AC_SUBST(APACHE_BUILDDIR)
AC_OUTPUT(Makefile)
15 years, 11 months
JBoss Native SVN: r2302 - trunk/mod_cluster.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-19 10:35:46 -0500 (Thu, 19 Feb 2009)
New Revision: 2302
Modified:
trunk/mod_cluster/README.txt
Log:
Add the tools that are needed to build mod-cluster native components.
Modified: trunk/mod_cluster/README.txt
===================================================================
--- trunk/mod_cluster/README.txt 2009-02-19 13:56:31 UTC (rev 2301)
+++ trunk/mod_cluster/README.txt 2009-02-19 15:35:46 UTC (rev 2302)
@@ -28,6 +28,19 @@
1.
+Native:
+To build the native component you need a C compiler and the following tools:
+m4, perl, autoconf, automake, libtool, make and patch.
+Of course the make and the patch must be gnu ones.
+For example on Solaris you need:
+SMCm4 (requires libsigsegv and libgcc34).
+SMCperl
+SMCautoc
+SMCautom
+SMClibt
+SMCmake
+SMCpatch
+All those can be downloaded from http://www.sunfreeware.com/
This software is distributed under the terms of the FSF Lesser Gnu
-Public License (see lgpl.txt).
\ No newline at end of file
+Public License (see lgpl.txt).
15 years, 11 months
JBoss Native SVN: r2301 - trunk/mod_cluster/test/java.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-19 08:56:31 -0500 (Thu, 19 Feb 2009)
New Revision: 2301
Modified:
trunk/mod_cluster/test/java/installhttpd.sh
Log:
Arrange the installation to allow apxs to work.
Modified: trunk/mod_cluster/test/java/installhttpd.sh
===================================================================
--- trunk/mod_cluster/test/java/installhttpd.sh 2009-02-19 12:14:53 UTC (rev 2300)
+++ trunk/mod_cluster/test/java/installhttpd.sh 2009-02-19 13:56:31 UTC (rev 2301)
@@ -77,6 +77,7 @@
BASEHTTPD=opt/jboss/httpd
BASEHTTPDCONF=opt/jboss/httpd/httpd/conf
BASEHTTPDSBIN=opt/jboss/httpd/sbin
+BASEHTTPDBUILD=opt/jboss/httpd/htdocs/build
ADDMODULES=false
case $BUILD_TAG in
*hpux-parisc2*)
@@ -178,7 +179,7 @@
case ${EXT} in
tar.gz)
# Arrange the installed files
- files="${BASEHTTPDSBIN}/apachectl ${BASEHTTPDCONF}/httpd.conf ${BASEHTTPDSBIN}/envvars"
+ files="${BASEHTTPDSBIN}/apachectl ${BASEHTTPDCONF}/httpd.conf ${BASEHTTPDSBIN}/envvars ${BASEHTTPDSBIN}/apxs ${BASEHTTPDBUILD}/config_vars.mk"
for FILE in `echo $files`
do
file=${BASELOC}/$FILE
15 years, 11 months
JBoss Native SVN: r2300 - in trunk/mod_cluster/native: mod_proxy and 1 other directory.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-19 07:14:53 -0500 (Thu, 19 Feb 2009)
New Revision: 2300
Added:
trunk/mod_cluster/native/mod_proxy/
trunk/mod_cluster/native/mod_proxy/.deps
trunk/mod_cluster/native/mod_proxy/Makefile.in
trunk/mod_cluster/native/mod_proxy/README
trunk/mod_cluster/native/mod_proxy/ajp.h
trunk/mod_cluster/native/mod_proxy/ajp_header.c
trunk/mod_cluster/native/mod_proxy/ajp_header.h
trunk/mod_cluster/native/mod_proxy/ajp_link.c
trunk/mod_cluster/native/mod_proxy/ajp_msg.c
trunk/mod_cluster/native/mod_proxy/ajp_utils.c
trunk/mod_cluster/native/mod_proxy/buildconf
trunk/mod_cluster/native/mod_proxy/cluster_util.c
trunk/mod_cluster/native/mod_proxy/configure.in
trunk/mod_cluster/native/mod_proxy/mod_proxy.c
trunk/mod_cluster/native/mod_proxy/mod_proxy.h
trunk/mod_cluster/native/mod_proxy/mod_proxy_ajp.c
trunk/mod_cluster/native/mod_proxy/mod_proxy_http.c
trunk/mod_cluster/native/mod_proxy/proxy_util.c
Log:
Add mod_proxy source to allow packaging of .so file only and for any httpd-2.2.x version.
Based on ASF httpd-2.2.11 proxy code.
Added: trunk/mod_cluster/native/mod_proxy/.deps
===================================================================
Added: trunk/mod_cluster/native/mod_proxy/Makefile.in
===================================================================
--- trunk/mod_cluster/native/mod_proxy/Makefile.in (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/Makefile.in 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,38 @@
+# Makefile.in for mod_proxy_cluster
+# copy the source in the httpd Apache source tree
+APACHE_BASE = @APACHE_BASE@
+top_builddir = @APACHE_BASE@
+# For .deps.
+builddir = @CLUSTER_BASE@
+# For the apache includes
+top_srcdir = @APACHE_BASE@
+# For obj list
+proxy_objs=mod_proxy.lo proxy_util.lo cluster_util.lo
+proxy_objs_slo=mod_proxy.slo proxy_util.slo cluster_util.slo
+proxy_http_objs=mod_proxy_http.lo
+proxy_http_objs_slo=mod_proxy_http.slo
+proxy_ajp_objs=mod_proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo ajp_utils.lo
+proxy_ajp_objs_slo=mod_proxy_ajp.slo ajp_header.slo ajp_link.slo ajp_msg.slo ajp_utils.slo
+
+include $(APACHE_BASE)/build/rules.mk
+SH_COMPILE = $(LIBTOOL) --mode=compile $(BASE_CC) -I../include -prefer-pic -c $< && touch $@
+
+all: mod_proxy.so mod_proxy_http.so mod_proxy_ajp.so
+
+mod_proxy.so: mod_proxy.la
+ $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy.la `pwd`
+mod_proxy_http.so: mod_proxy_http.la
+ $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy_http.la `pwd`
+mod_proxy_ajp.so: mod_proxy_ajp.la
+ $(APACHE_BASE)/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_proxy_ajp.la `pwd`
+
+mod_proxy.la: $(proxy_objs_slo)
+ $(SH_LINK) -rpath $(libexecdir) -module -avoid-version $(proxy_objs)
+mod_proxy_http.la: $(proxy_http_objs_slo)
+ $(SH_LINK) -rpath $(libexecdir) -module -avoid-version $(proxy_http_objs)
+mod_proxy_ajp.la: $(proxy_ajp_objs_slo)
+ $(SH_LINK) -rpath $(libexecdir) -module -avoid-version $(proxy_ajp_objs)
+
+clean:
+ rm -f *.o *.lo *.slo *.so
+ rm -rf .libs
Added: trunk/mod_cluster/native/mod_proxy/README
===================================================================
--- trunk/mod_cluster/native/mod_proxy/README (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/README 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1 @@
+That is mod_proxy code httpd-2.2.11
Added: trunk/mod_cluster/native/mod_proxy/ajp.h
===================================================================
--- trunk/mod_cluster/native/mod_proxy/ajp.h (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/ajp.h 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,498 @@
+/* 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 AJP definitions
+ * @ingroup MOD_PROXY
+ * @{
+ */
+
+#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_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"
+
+#if APR_CHARSET_EBCDIC
+
+#define USE_CHARSET_EBCDIC
+#define ajp_xlate_to_ascii(b, l) ap_xlate_proto_to_ascii(b, l)
+#define ajp_xlate_from_ascii(b, l) ap_xlate_proto_from_ascii(b, l)
+
+#else /* APR_CHARSET_EBCDIC */
+
+#define ajp_xlate_to_ascii(b, l)
+#define ajp_xlate_from_ascii(b, l)
+
+#endif
+
+#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"
+
+
+/** 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;
+ /** The size of the buffer */
+ apr_size_t max_size;
+ /** Flag indicating the origing of the message */
+ int server_side;
+};
+
+/**
+ * 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 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);
+
+/**
+ * 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 smsg 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 smsg 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
+ * @return dump message
+ */
+char * ajp_msg_dump(apr_pool_t *pool, ajp_msg_t *msg, char *err);
+
+/**
+ * Send an AJP message to backend
+ *
+ * @param soct backend socket
+ * @param smsg 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 smsg 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.
+ * @uri uri requested uri
+ * @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);
+
+/**
+ * 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 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);
+
+
+/**
+ * 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);
+/** @} */
+
+#endif /* AJP_H */
+
Added: trunk/mod_cluster/native/mod_proxy/ajp_header.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/ajp_header.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/ajp_header.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,778 @@
+/* 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"
+
+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;
+
+ 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_DELETE */
+ 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)
+{
+ 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Into ajp_marshal_into_msgb");
+
+ if ((method = sc_for_req_method_by_id(r)) == UNKNOWN_METHOD) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_marshal_into_msgb - No such method %s",
+ r->method);
+ return AJP_EBAD_METHOD;
+ }
+
+ 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_remote_host(r->connection, r->per_dir_config, REMOTE_HOST, NULL);
+
+ ajp_msg_reset(msg);
+
+ if (ajp_msg_append_uint8(msg, CMD_AJP13_FORWARD_REQUEST) ||
+ ajp_msg_append_uint8(msg, method) ||
+ ajp_msg_append_string(msg, r->protocol) ||
+ ajp_msg_append_string(msg, uri->path) ||
+ ajp_msg_append_string(msg, r->connection->remote_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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_marshal_into_msgb: "
+ "Error appending the message begining");
+ 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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_marshal_into_msgb: "
+ "Error appending the header name");
+ return AJP_EOVERFLOW;
+ }
+ }
+ else {
+ if (ajp_msg_append_string(msg, elts[i].key)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_marshal_into_msgb: "
+ "Error appending the header name");
+ return AJP_EOVERFLOW;
+ }
+ }
+
+ if (ajp_msg_append_string(msg, elts[i].val)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_marshal_into_msgb: "
+ "Error appending the header value");
+ return AJP_EOVERFLOW;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ajp_marshal_into_msgb: Header[%d] [%s] = [%s]",
+ i, elts[i].key, elts[i].val);
+ }
+
+/* XXXX need to figure out how to do this
+ if (s->secret) {
+ if (ajp_msg_append_uint8(msg, SC_A_SECRET) ||
+ ajp_msg_append_string(msg, s->secret)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "Error 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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "Error ajp_marshal_into_msgb - "
+ "Error appending the SSL key size");
+ return APR_EGENERAL;
+ }
+ }
+ }
+ /* 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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_marshal_into_msgb: "
+ "Error appending the message end");
+ return AJP_EOVERFLOW;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_unmarshal_response: Null status");
+ return rc;
+ }
+ r->status = status;
+
+ rc = ajp_msg_get_string(msg, &ptr);
+ if (rc == APR_SUCCESS) {
+#if defined(AS400) || defined(_OSD_POSIX) /* EBCDIC platforms */
+ 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "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 {
+ r->headers_out = NULL;
+ num_headers = 0;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_unmarshal_response: "
+ "Null header name");
+ return rc;
+ }
+#if defined(AS400) || defined(_OSD_POSIX)
+ ap_xlate_proto_from_ascii(stringname, strlen(stringname));
+#endif
+ }
+
+ rc = ajp_msg_get_string(msg, &value);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_unmarshal_response: "
+ "Null header value");
+ return rc;
+ }
+
+ /* Set-Cookie need additional processing */
+ if (!strcasecmp(stringname, "Set-Cookie")) {
+ value = ap_proxy_cookie_reverse_map(r, dconf, value);
+ }
+ /* Location, Content-Location, URI and Destination need additional
+ * processing */
+ else if (!strcasecmp(stringname, "Location")
+ || !strcasecmp(stringname, "Content-Location")
+ || !strcasecmp(stringname, "URI")
+ || !strcasecmp(stringname, "Destination"))
+ {
+ value = ap_proxy_location_reverse_map(r, dconf, value);
+ }
+
+#if defined(AS400) || defined(_OSD_POSIX)
+ ap_xlate_proto_from_ascii(value, strlen(value));
+#endif
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "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 (strcasecmp(stringname, "Content-Type") == 0) {
+ /* add corresponding filter */
+ ap_set_content_type(r, apr_pstrdup(r->pool, value));
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ajp_unmarshal_response: ap_set_content_type done");
+ }
+ }
+
+ 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)
+{
+ ajp_msg_t *msg;
+ apr_status_t rc;
+
+ rc = ajp_msg_create(r->pool, buffsize, &msg);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_send_header: ajp_msg_create failed");
+ return rc;
+ }
+
+ rc = ajp_marshal_into_msgb(msg, r, uri);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_send_header: ajp_marshal_into_msgb failed");
+ return rc;
+ }
+
+ rc = ajp_ilink_send(sock, msg);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_read_header: ajp_msg_reuse failed");
+ return rc;
+ }
+ }
+ else {
+ rc = ajp_msg_create(r->pool, buffsize, msg);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_read_header: ajp_ilink_receive failed");
+ return rc;
+ }
+ rc = ajp_msg_peek_uint8(*msg, &result);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ajp_read_header: ajp_ilink_received %02x", 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ajp_parse_type: got %02x", 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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_parse_headers: ajp_msg_get_byte failed");
+ return rc;
+ }
+ if (result != CMD_AJP13_SEND_HEADERS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_parse_headers: wrong type %02x expecting 0x04", result);
+ 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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_parse_data: ajp_msg_get_byte failed");
+ return rc;
+ }
+ if (result != CMD_AJP13_SEND_BODY_CHUNK) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_parse_data: wrong type %02x expecting 0x03", result);
+ 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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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;
+}
+
+/*
+ * 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);
+
+}
Added: trunk/mod_cluster/native/mod_proxy/ajp_header.h
===================================================================
--- trunk/mod_cluster/native/mod_proxy/ajp_header.h (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/ajp_header.h 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,175 @@
+/* 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_ARE_DONE (unsigned char)0xFF
+
+/*
+ * 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
+
+
+/*
+ * 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 */
+/** @} */
Added: trunk/mod_cluster/native/mod_proxy/ajp_link.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/ajp_link.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/ajp_link.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,126 @@
+/* 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"
+
+
+apr_status_t ajp_ilink_send(apr_socket_t *sock, ajp_msg_t *msg)
+{
+ char *buf;
+ apr_status_t status;
+ apr_size_t length;
+
+ if (sock == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "ajp_ilink_send(): NULL socket provided");
+ return AJP_EINVAL;
+ }
+
+ 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,
+ "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;
+
+ if (sock == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "ajp_ilink_receive(): NULL socket provided");
+ return AJP_EINVAL;
+ }
+
+ hlen = msg->header_len;
+
+ status = ilink_read(sock, msg->buf, hlen);
+
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL,
+ "ajp_ilink_receive() can't receive header");
+ return AJP_ENO_HEADER;
+ }
+
+ status = ajp_msg_check_header(msg, &blen);
+
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "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,
+ "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,
+ "ajp_ilink_receive() received packet len=%" APR_SIZE_T_FMT
+ "type=%d",
+ blen, (int)msg->buf[hlen]);
+
+ return APR_SUCCESS;
+}
+
Added: trunk/mod_cluster/native/mod_proxy/ajp_msg.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/ajp_msg.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/ajp_msg.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,607 @@
+/* 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"
+
+
+static char *hex_table = "0123456789ABCDEF";
+
+/**
+ * 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
+ * @return dump message
+ */
+char * ajp_msg_dump(apr_pool_t *pool, ajp_msg_t *msg, char *err)
+{
+ apr_size_t i, j;
+ char line[80];
+ char *current;
+ char *rv, *p;
+ apr_size_t bl = 8192;
+ apr_byte_t x;
+ apr_size_t len = msg->len;
+
+ /* Display only first 1024 bytes */
+ if (len > 1024)
+ len = 1024;
+ rv = apr_palloc(pool, bl);
+ apr_snprintf(rv, bl,
+ "ajp_msg_dump(): %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);
+ bl -= strlen(rv);
+ p = rv + strlen(rv);
+ for (i = 0; i < len; i += 16) {
+ current = line;
+
+ for (j = 0; j < 16; j++) {
+ x = msg->buf[i + j];
+
+ *current++ = hex_table[x >> 4];
+ *current++ = hex_table[x & 0x0f];
+ *current++ = ' ';
+ }
+ *current++ = ' ';
+ *current++ = '-';
+ *current++ = ' ';
+ for (j = 0; j < 16; j++) {
+ x = msg->buf[i + j];
+
+ if (x > 0x20 && x < 0x7F) {
+ *current++ = x;
+ }
+ else {
+ *current++ = '.';
+ }
+ }
+
+ *current++ = '\0';
+ apr_snprintf(p, bl,
+ "ajp_msg_dump(): %.4lx %s\n",
+ (unsigned long)i, line);
+ bl -= strlen(rv);
+ p = rv + strlen(rv);
+
+ }
+
+ return rv;
+}
+
+
+/**
+ * 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,
+ "ajp_check_msg_header() got bad signature %x%x",
+ 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,
+ "ajp_check_msg_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,
+ "%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)
+{
+ size_t len;
+
+ if (value == NULL) {
+ return(ajp_msg_append_uint16(msg, 0xFFFF));
+ }
+
+ len = strlen(value);
+ if ((msg->len + len + 2) > 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 */
+ ajp_xlate_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));
+
+ if (!msg) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "ajp_msg_create(): can't allocate AJP message memory");
+ return APR_ENOPOOL;
+ }
+
+ msg->server_side = 0;
+
+ msg->buf = (apr_byte_t *)apr_palloc(pool, size);
+
+ /* XXX: This should never happen
+ * In case if the OS cannont allocate 8K of data
+ * we are in serious trouble
+ * No need to check the alloc return value, cause the
+ * core dump is probably the best solution anyhow.
+ */
+ if (msg->buf == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "ajp_msg_create(): can't allocate AJP message memory");
+ return APR_ENOPOOL;
+ }
+
+ 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 (dmsg == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "ajp_msg_copy(): destination msg is null");
+ return AJP_EINVAL;
+ }
+
+ if (smsg->len > smsg->max_size) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ "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;
+}
Added: trunk/mod_cluster/native/mod_proxy/ajp_utils.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/ajp_utils.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/ajp_utils.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,105 @@
+/* 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"
+
+/*
+ * 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Into ajp_handle_cping_cpong");
+
+ rc = ajp_msg_create(r->pool, AJP_PING_PONG_SZ, &msg);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_handle_cping_cpong: ajp_msg_create failed");
+ return rc;
+ }
+
+ rc = ajp_msg_serialize_cping(msg);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_handle_cping_cpong: ajp_marshal_into_msgb failed");
+ return rc;
+ }
+
+ rc = ajp_ilink_send(sock, msg);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_handle_cping_cpong: ajp_ilink_send failed");
+ return rc;
+ }
+
+ rc = apr_socket_timeout_get(sock, &org);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_handle_cping_cpong: ajp_ilink_receive failed");
+ goto cleanup;
+ }
+
+ rv = ajp_msg_get_uint8(msg, &result);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_handle_cping_cpong: invalid CPONG message");
+ goto cleanup;
+ }
+ if (result != CMD_AJP13_CPONG) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "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_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "ajp_handle_cping_cpong: apr_socket_timeout_set failed");
+ return rc;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ajp_handle_cping_cpong: Done");
+ return rv;
+}
Added: trunk/mod_cluster/native/mod_proxy/buildconf
===================================================================
--- trunk/mod_cluster/native/mod_proxy/buildconf (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/buildconf 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+rm -rf aclocal.m4 autom4te*.cache
+
+echo "Creating configure ..."
+### do some work to toss config.cache?
+if ${AUTOCONF:-autoconf}; then
+ :
+else
+ echo "autoconf failed"
+ exit 1
+fi
Property changes on: trunk/mod_cluster/native/mod_proxy/buildconf
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/mod_cluster/native/mod_proxy/cluster_util.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/cluster_util.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/cluster_util.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,128 @@
+/* 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 (addon for mod_cluster) */
+#include "mod_proxy.h"
+
+/**
+ * Parse a given timeout parameter string into an apr_interval_time_t value.
+ * The unit of the time interval is given as postfix string to the numeric
+ * string. Currently the following units are understood:
+ *
+ * ms : milliseconds
+ * s : seconds
+ * mi[n] : minutes
+ * h : hours
+ *
+ * If no unit is contained in the given timeout parameter the default_time_unit
+ * will be used instead.
+ * @param timeout_parameter The string containing the timeout parameter.
+ * @param timeout The timeout value to be returned.
+ * @param default_time_unit The default time unit to use if none is specified
+ * in timeout_parameter.
+ * @return Status value indicating whether the parsing was successful or not.
+ */
+PROXY_DECLARE(apr_status_t) cluster_timeout_parameter_parse(
+ const char *timeout_parameter,
+ apr_interval_time_t *timeout,
+ const char *default_time_unit)
+{
+ char *endp;
+ const char *time_str;
+ apr_int64_t tout;
+
+ tout = apr_strtoi64(timeout_parameter, &endp, 10);
+ if (errno) {
+ return errno;
+ }
+ if (!endp || !*endp) {
+ time_str = default_time_unit;
+ }
+ else {
+ time_str = endp;
+ }
+
+ switch (*time_str) {
+ /* Time is in seconds */
+ case 's':
+ *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
+ break;
+ case 'h':
+ /* Time is in hours */
+ *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
+ break;
+ case 'm':
+ switch (*(++time_str)) {
+ /* Time is in miliseconds */
+ case 's':
+ *timeout = (apr_interval_time_t) tout * 1000;
+ break;
+ /* Time is in minutes */
+ case 'i':
+ *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+ break;
+ default:
+ return APR_EGENERAL;
+ }
+ return APR_SUCCESS;
+}
+
+/* This one is in 2.2.8+ but not in 2.2.3 and 2.2.3 is the base for a some distribution (Suse) */
+typedef struct hdr_ptr {
+ ap_filter_t *f;
+ apr_bucket_brigade *bb;
+} hdr_ptr;
+static int send_header(void *data, const char *key, const char *val)
+{
+ ap_fputstrs(((hdr_ptr*)data)->f, ((hdr_ptr*)data)->bb,
+ key, ": ", val, CRLF, NULL);
+ return 1;
+}
+
+PROXY_DECLARE(void) cluster_send_interim_response(request_rec *r, int send_headers)
+{
+ hdr_ptr x;
+ char *status_line = NULL;
+
+ if (r->proto_num < 1001) {
+ /* don't send interim response to HTTP/1.0 Client */
+ return;
+ }
+ if (!ap_is_HTTP_INFO(r->status)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ "Status is %d - not sending interim response", r->status);
+ return;
+ }
+
+ status_line = apr_pstrcat(r->pool, AP_SERVER_PROTOCOL, " ", r->status_line, CRLF, NULL);
+ ap_xlate_proto_to_ascii(status_line, strlen(status_line));
+
+ x.f = r->connection->output_filters;
+ x.bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ ap_fputs(x.f, x.bb, status_line);
+ if (send_headers) {
+ apr_table_do(send_header, &x, r->headers_out, NULL);
+ apr_table_clear(r->headers_out);
+ }
+ ap_fputs(x.f, x.bb, CRLF);
+ ap_fflush(x.f, x.bb);
+ apr_brigade_destroy(x.bb);
+}
Added: trunk/mod_cluster/native/mod_proxy/configure.in
===================================================================
--- trunk/mod_cluster/native/mod_proxy/configure.in (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/configure.in 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,24 @@
+dnl configure for mod_manager
+dnl
+
+AC_INIT(mod_proxy.c)
+
+AC_MSG_CHECKING(for Apache httpd installation)
+AC_ARG_WITH(apache,
+[ --with-apache[=DIR] DIR is the apache base installation
+],
+[ if test "$withval" = "yes"; then
+ withval=/usr/local/etc/httpd
+ fi
+ if test "$withval" != "no"; then
+ APACHE_BASE=$withval
+ else
+ AC_MSG_ERROR(mod_proxy need a valid apache location)
+ fi
+],
+[ AC_MSG_ERROR(Please use --with-apache[=DIR])])
+CLUSTER_BASE=`pwd`
+
+AC_SUBST(APACHE_BASE)
+AC_SUBST(CLUSTER_BASE)
+AC_OUTPUT(Makefile)
Added: trunk/mod_cluster/native/mod_proxy/mod_proxy.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/mod_proxy.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/mod_proxy.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,2400 @@
+/* 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.
+ */
+
+#define CORE_PRIVATE
+
+#include "mod_proxy.h"
+#include "mod_core.h"
+#include "apr_optional.h"
+#include "scoreboard.h"
+#include "mod_status.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_is_https, (conn_rec *));
+APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
+ (apr_pool_t *, server_rec *,
+ conn_rec *, request_rec *, char *));
+#endif
+
+#ifndef MAX
+#define MAX(x,y) ((x) >= (y) ? (x) : (y))
+#endif
+
+/*
+ * 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.
+ * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy:
+ * fix_ups: convert the URL stored in the filename to the
+ * canonical form.
+ * handler: handle proxy requests
+ */
+
+/* -------------------------------------------------------------- */
+/* Translate the URL into a 'filename' */
+
+#define PROXY_COPY_CONF_PARAMS(w, c) \
+ do { \
+ (w)->timeout = (c)->timeout; \
+ (w)->timeout_set = (c)->timeout_set; \
+ (w)->recv_buffer_size = (c)->recv_buffer_size; \
+ (w)->recv_buffer_size_set = (c)->recv_buffer_size_set; \
+ (w)->io_buffer_size = (c)->io_buffer_size; \
+ (w)->io_buffer_size_set = (c)->io_buffer_size_set; \
+ } while (0)
+
+static const char *set_worker_param(apr_pool_t *p,
+ 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 BalancerMamber,
+ * it is a number between 1 and 100.
+ */
+ worker->lbfactor = atoi(val);
+ if (worker->lbfactor < 1 || worker->lbfactor > 100)
+ return "LoadFactor must be number between 1..100";
+ }
+ 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->retry = apr_time_from_sec(ival);
+ worker->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->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->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->hmax = ival;
+ }
+ /* XXX: More inteligent 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->smax = ival;
+ }
+ else if (!strcasecmp(key, "acquire")) {
+ /* Acquire in given unit (default is milliseconds).
+ * If set this will be the maximum time to
+ * wait for a free connection.
+ */
+ if (cluster_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->acquire = timeout;
+ worker->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->timeout = apr_time_from_sec(ival);
+ worker->timeout_set = 1;
+ }
+ else if (!strcasecmp(key, "iobuffersize")) {
+ long s = atol(val);
+ worker->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
+ worker->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->recv_buffer_size = ival;
+ worker->recv_buffer_size_set = 1;
+ }
+ else if (!strcasecmp(key, "keepalive")) {
+ if (!strcasecmp(val, "on"))
+ worker->keepalive = 1;
+ else if (!strcasecmp(val, "off"))
+ worker->keepalive = 0;
+ else
+ return "KeepAlive must be On|Off";
+ worker->keepalive_set = 1;
+ }
+ else if (!strcasecmp(key, "disablereuse")) {
+ if (!strcasecmp(val, "on"))
+ worker->disablereuse = 1;
+ else if (!strcasecmp(val, "off"))
+ worker->disablereuse = 0;
+ else
+ return "DisableReuse must be On|Off";
+ worker->disablereuse_set = 1;
+ }
+ else if (!strcasecmp(key, "route")) {
+ /* Worker route.
+ */
+ if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
+ return "Route length must be < 64 characters";
+ worker->route = apr_pstrdup(p, val);
+ }
+ else if (!strcasecmp(key, "redirect")) {
+ /* Worker redirection route.
+ */
+ if (strlen(val) > PROXY_WORKER_MAX_ROUTE_SIZ)
+ return "Redirect length must be < 64 characters";
+ worker->redirect = apr_pstrdup(p, val);
+ }
+ else if (!strcasecmp(key, "status")) {
+ const char *v;
+ int mode = 1;
+ /* Worker status.
+ */
+ for (v = val; *v; v++) {
+ if (*v == '+') {
+ mode = 1;
+ v++;
+ }
+ else if (*v == '-') {
+ mode = 0;
+ v++;
+ }
+ if (*v == 'D' || *v == 'd') {
+ if (mode)
+ worker->status |= PROXY_WORKER_DISABLED;
+ else
+ worker->status &= ~PROXY_WORKER_DISABLED;
+ }
+ else if (*v == 'S' || *v == 's') {
+ if (mode)
+ worker->status |= PROXY_WORKER_STOPPED;
+ else
+ worker->status &= ~PROXY_WORKER_STOPPED;
+ }
+ else if (*v == 'E' || *v == 'e') {
+ if (mode)
+ worker->status |= PROXY_WORKER_IN_ERROR;
+ else
+ worker->status &= ~PROXY_WORKER_IN_ERROR;
+ }
+ else if (*v == 'H' || *v == 'h') {
+ if (mode)
+ worker->status |= PROXY_WORKER_HOT_STANDBY;
+ else
+ worker->status &= ~PROXY_WORKER_HOT_STANDBY;
+ }
+ else if (*v == 'I' || *v == 'i') {
+ if (mode)
+ worker->status |= PROXY_WORKER_IGNORE_ERRORS;
+ else
+ worker->status &= ~PROXY_WORKER_IGNORE_ERRORS;
+ }
+ else {
+ return "Unknown status parameter option";
+ }
+ }
+ }
+ else if (!strcasecmp(key, "flushpackets")) {
+ if (!strcasecmp(val, "on"))
+ worker->flush_packets = flush_on;
+ else if (!strcasecmp(val, "off"))
+ worker->flush_packets = flush_off;
+ else if (!strcasecmp(val, "auto"))
+ worker->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->flush_wait = PROXY_FLUSH_WAIT;
+ else
+ worker->flush_wait = ival * 1000; /* change to microseconds */
+ }
+ else if (!strcasecmp(key, "lbset")) {
+ ival = atoi(val);
+ if (ival < 0 || ival > 99)
+ return "lbset must be between 0 and 99";
+ worker->lbset = ival;
+ }
+ else if (!strcasecmp(key, "ping")) {
+ /* Ping/Pong timeout in given unit (default is second).
+ */
+ if (cluster_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->ping_timeout = timeout;
+ worker->ping_timeout_set = 1;
+ }
+ else if (!strcasecmp(key, "connectiontimeout")) {
+ /* Request timeout in given unit (default is second).
+ * Defaults to connection timeout
+ */
+ if (cluster_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->conn_timeout = timeout;
+ worker->conn_timeout_set = 1;
+ }
+ 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")) {
+ /* Balancer sticky session name.
+ * Set to something like JSESSIONID or
+ * PHPSESSIONID, etc..,
+ */
+ balancer->sticky = apr_pstrdup(p, val);
+ }
+ 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->sticky_force = 1;
+ else if (!strcasecmp(val, "off"))
+ balancer->sticky_force = 0;
+ else
+ return "failover must be On|Off";
+ }
+ 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->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->max_attempts = ival;
+ balancer->max_attempts_set = 1;
+ }
+ else if (!strcasecmp(key, "lbmethod")) {
+ proxy_balancer_method *provider;
+ provider = ap_lookup_provider(PROXY_LBMETHOD, val, "0");
+ if (provider) {
+ balancer->lbmethod = provider;
+ return NULL;
+ }
+ 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->scolonsep = 1;
+ else if (!strcasecmp(val, "off"))
+ balancer->scolonsep = 0;
+ else
+ return "scolonpathdelim must be On|Off";
+ }
+ 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;
+}
+
+/* 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
+ && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))
+ && 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_pstrndup(r->pool, start+2, end-(start+2));
+ val = apr_table_get(r->subprocess_env, var);
+ firstpart = apr_pstrndup(r->pool, str, (start-str));
+
+ if (val == NULL) {
+ return apr_pstrcat(r->pool, firstpart,
+ proxy_interpolate(r, end+1), NULL);
+ }
+ else {
+ return apr_pstrcat(r->pool, firstpart, val,
+ proxy_interpolate(r, end+1), NULL);
+ }
+}
+static apr_array_header_t *proxy_vars(request_rec *r,
+ apr_array_header_t *hdr)
+{
+ int i;
+ apr_array_header_t *ret = apr_array_make(r->pool, hdr->nelts,
+ sizeof (struct proxy_alias));
+ struct proxy_alias *old = (struct proxy_alias *) hdr->elts;
+
+ for (i = 0; i < hdr->nelts; ++i) {
+ struct proxy_alias *newcopy = apr_array_push(ret);
+ newcopy->fake = (old[i].flags & PROXYPASS_INTERPOLATE)
+ ? proxy_interpolate(r, old[i].fake) : old[i].fake;
+ newcopy->real = (old[i].flags & PROXYPASS_INTERPOLATE)
+ ? proxy_interpolate(r, old[i].real) : old[i].real;
+ }
+ return ret;
+}
+static int proxy_trans(request_rec *r)
+{
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+ int i, len;
+ struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
+ proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
+ &proxy_module);
+ const char *fake;
+ const char *real;
+ ap_regmatch_t regm[AP_MAX_REG_MATCH];
+ ap_regmatch_t reg1[AP_MAX_REG_MATCH];
+ char *found = NULL;
+ int mismatch = 0;
+
+ if (r->proxyreq) {
+ /* someone has already set up the proxy, it was possibly ourselves
+ * in proxy_detect
+ */
+ return OK;
+ }
+
+ /* XXX: since r->uri has been manipulated already we're not really
+ * compliant with RFC1945 at this point. But this probably isn't
+ * an issue because this is a hybrid proxy/origin server.
+ */
+
+ for (i = 0; i < conf->aliases->nelts; i++) {
+ unsigned int nocanon = ent[i].flags & PROXYPASS_NOCANON;
+ const char *use_uri = nocanon ? r->unparsed_uri : r->uri;
+ if ((dconf->interpolate_env == 1)
+ && (ent[i].flags & PROXYPASS_INTERPOLATE)) {
+ fake = proxy_interpolate(r, ent[i].fake);
+ real = proxy_interpolate(r, ent[i].real);
+ }
+ else {
+ fake = ent[i].fake;
+ real = ent[i].real;
+ }
+ if (ent[i].regex) {
+ if (!ap_regexec(ent[i].regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) {
+ if ((real[0] == '!') && (real[1] == '\0')) {
+ return DECLINED;
+ }
+ /* test that we haven't reduced the URI */
+ if (nocanon && ap_regexec(ent[i].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);
+ /* 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 (found && strcmp(found, real)) {
+ found = apr_pstrcat(r->pool, "proxy:", found, NULL);
+ }
+ else {
+ found = apr_pstrcat(r->pool, "proxy:", real,
+ use_uri, NULL);
+ }
+ }
+ }
+ else {
+ len = alias_match(r->uri, fake);
+
+ if (len != 0) {
+ if ((real[0] == '!') && (real[1] == '\0')) {
+ return DECLINED;
+ }
+ if (nocanon
+ && len != alias_match(r->unparsed_uri, ent[i].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,
+ "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon");
+ }
+
+ if (found) {
+ 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");
+ }
+ return OK;
+ }
+ }
+ return DECLINED;
+}
+
+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->server->lookup_defaults;
+ 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;
+
+ for (j = 0; j < num_sec; ++j)
+ {
+ entry_config = sec_proxy[j];
+ entry_proxy = ap_get_module_config(entry_config, &proxy_module);
+
+ /* XXX: What about case insensitive matching ???
+ * Compare regex, fnmatch or string as appropriate
+ * If the entry doesn't relate, then continue
+ */
+ if (entry_proxy->r
+ ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0)
+ : (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;
+
+ 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
+ || strcasecmp(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_set(r->headers_out, "Location", nuri);
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ "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;
+ long 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;
+
+ /* is this for us? */
+ if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
+ return DECLINED;
+
+ /* handle max-forwards / OPTIONS / TRACE */
+ if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) {
+ maxfwd = strtol(str, NULL, 10);
+ if (maxfwd < 1) {
+ switch (r->method_number) {
+ case M_TRACE: {
+ int access_status;
+ r->proxyreq = PROXYREQ_NONE;
+ if ((access_status = ap_send_http_trace(r)))
+ ap_die(access_status, r);
+ else
+ ap_finalize_request_protocol(r);
+ return OK;
+ }
+ case M_OPTIONS: {
+ int access_status;
+ r->proxyreq = PROXYREQ_NONE;
+ if ((access_status = ap_send_http_options(r)))
+ ap_die(access_status, r);
+ else
+ ap_finalize_request_protocol(r);
+ return OK;
+ }
+ default: {
+ return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+ "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_set(r->headers_in, "Max-Forwards",
+ apr_psprintf(r->pool, "%ld", maxfwd));
+ }
+
+ if (r->method_number == M_TRACE) {
+ core_server_config *coreconf = (core_server_config *)
+ ap_get_module_config(sconf, &core_module);
+
+ 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,
+ "proxy: 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,
+ "proxy: 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,
+ "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_pstrndup(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) ? "NoProxy for %s" : "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;
+ }
+ if (balancer && balancer->max_attempts_set && !max_attempts)
+ max_attempts = balancer->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 && strcasecmp(scheme, ents[i].scheme) == 0) ||
+ (p2 != NULL &&
+ strncasecmp(url, ents[i].scheme,
+ strlen(ents[i].scheme)) == 0)) {
+
+ /* handle the scheme */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "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;
+ char *end;
+ apr_off_t cl;
+
+ /*
+ * An fatal error or success, so no point in
+ * retrying with a direct connection.
+ */
+ if (access_status != HTTP_BAD_GATEWAY) {
+ goto cleanup;
+ }
+ cl_a = apr_table_get(r->headers_in, "Content-Length");
+ if (cl_a) {
+ apr_strtoff(&cl, cl_a, &end, 0);
+ /*
+ * The request body is of length > 0. We cannot
+ * retry with a direct connection since we already
+ * sent (parts of) the request body to the proxy
+ * and do not have any longer.
+ */
+ if (cl > 0) {
+ goto cleanup;
+ }
+ }
+ /*
+ * Transfer-Encoding was set as input header, so we had
+ * a request body. We cannot retry with a direct
+ * connection for the same reason as above.
+ */
+ if (apr_table_get(r->headers_in, "Transfer-Encoding")) {
+ goto cleanup;
+ }
+ }
+ }
+ }
+ }
+
+ /* 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Running scheme %s handler (attempt %d)",
+ scheme, attempts);
+ access_status = proxy_run_scheme_handler(r, worker, conf,
+ url, NULL, 0);
+ if (access_status == OK)
+ 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_IN_ERROR;
+ 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_IN_ERROR;
+ }
+ }
+ 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_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: No protocol handler was valid for the URL %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);
+ access_status = HTTP_INTERNAL_SERVER_ERROR;
+ goto cleanup;
+ }
+cleanup:
+ if (balancer) {
+ int post_status = proxy_run_post_request(worker, balancer, r, conf);
+ if (post_status == DECLINED) {
+ post_status = OK; /* no post_request handler available */
+ /* TODO: recycle direct worker */
+ }
+ }
+
+ proxy_run_request_status(&access_status, r);
+
+ 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->allowed_connect_ports = apr_array_make(p, 10, sizeof(int));
+ 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->viaopt = via_off; /* initially backward compatible with 1.3.1 */
+ ps->viaopt_set = 0; /* 0 means default */
+ ps->req = 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->error_override = 0;
+ ps->error_override_set = 0;
+ ps->preserve_host_set = 0;
+ ps->preserve_host = 0;
+ ps->timeout = 0;
+ ps->timeout_set = 0;
+ ps->badopt = bad_error;
+ ps->badopt_set = 0;
+ ps->pool = p;
+
+ return ps;
+}
+
+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->proxies = apr_array_append(p, base->proxies, overrides->proxies);
+ ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
+ ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
+ ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
+ ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
+ ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
+ ps->workers = apr_array_append(p, base->workers, overrides->workers);
+ ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
+ ps->forward = overrides->forward ? overrides->forward : base->forward;
+ ps->reverse = overrides->reverse ? overrides->reverse : base->reverse;
+
+ ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
+ 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->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->error_override = (overrides->error_override_set == 0) ? base->error_override : overrides->error_override;
+ ps->error_override_set = overrides->error_override_set || base->error_override_set;
+ ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host;
+ ps->preserve_host_set = overrides->preserve_host_set || base->preserve_host_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->pool = p;
+ return ps;
+}
+
+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->cookie_path_str = apr_strmatch_precompile(p, "path=", 0);
+ new->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0);
+ new->interpolate_env = -1; /* unset */
+
+ return (void *) new;
+}
+
+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;
+
+ /* 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->cookie_path_str = base->cookie_path_str;
+ new->cookie_domain_str = base->cookie_domain_str;
+ new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env
+ : add->interpolate_env;
+ new->ftp_directory_charset = add->ftp_directory_charset ?
+ add->ftp_directory_charset :
+ base->ftp_directory_charset;
+ 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);
+}
+
+static const char *
+ add_pass(cmd_parms *cmd, void *dummy, const char *arg, int is_regex)
+{
+ 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;
+ 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;
+ int use_regex = is_regex;
+ unsigned int flags = 0;
+
+ while (*arg) {
+ word = ap_getword_conf(cmd->pool, &arg);
+ if (!f) {
+ if (!strcmp(word, "~")) {
+ if (is_regex) {
+ return "ProxyPassMatch invalid syntax ('~' usage).";
+ }
+ use_regex = 1;
+ 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 {
+ 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';
+ apr_table_setn(params, word, val);
+ }
+ };
+
+ if (r == NULL)
+ return "ProxyPass|ProxyPassMatch needs a path when not defined in a location";
+
+ new = apr_array_push(conf->aliases);
+ new->fake = apr_pstrdup(cmd->pool, f);
+ new->real = apr_pstrdup(cmd->pool, r);
+ new->flags = flags;
+ if (use_regex) {
+ 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 (strncasecmp(r, "balancer:", 9) == 0) {
+ proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
+ if (!balancer) {
+ const char *err = ap_proxy_add_balancer(&balancer,
+ cmd->pool,
+ conf, r);
+ if (err)
+ return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+ }
+ 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);
+ }
+ }
+ else {
+ proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, conf, r);
+ if (!worker) {
+ const char *err = ap_proxy_add_worker(&worker, cmd->pool, conf, r);
+ if (err)
+ return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ "worker %s already used by another worker", worker->name);
+ }
+ PROXY_COPY_CONF_PARAMS(worker, conf);
+
+ for (i = 0; i < arr->nelts; i++) {
+ const char *err = set_worker_param(cmd->pool, 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;
+
+ if (cmd->path == NULL) {
+ fake = f;
+ real = r;
+ interp = i;
+ if (r == NULL || !strcasecmp(r, "interpolate")) {
+ return "ProxyPassReverse needs a path when not defined in a location";
+ }
+ }
+ else {
+ fake = cmd->path;
+ real = f;
+ if (r && strcasecmp(r, "interpolate")) {
+ return "ProxyPassReverse can not have a path when defined in a location";
+ }
+ interp = r;
+ }
+
+ new = apr_array_push(conf->raliases);
+ new->fake = fake;
+ new->real = real;
+ new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
+
+ return NULL;
+}
+static const char* cookie_path(cmd_parms *cmd, void *dconf, const char *f,
+ const char *r, 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;
+ }
+ }
+
+ 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;
+}
+
+/*
+ * 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;
+ proxy_server_conf *conf =
+ ap_get_module_config(s->module_config, &proxy_module);
+ int *New;
+
+ if (!apr_isdigit(arg[0]))
+ return "AllowCONNECT: port number must be numeric";
+
+ New = apr_array_push(conf->allowed_connect_ports);
+ *New = atoi(arg);
+ 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;
+ }
+
+ 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,
+ "Parsed addr %s", inet_ntoa(New->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "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,
+ "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,
+ "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;
+
+ if (flag && !psf->forward) {
+ psf->forward = ap_proxy_create_worker(parms->pool);
+ psf->forward->name = "proxy:forward";
+ psf->forward->hostname = "*";
+ psf->forward->scheme = "*";
+ }
+ return NULL;
+}
+
+static const char *
+ set_proxy_error_override(cmd_parms *parms, void *dummy, int flag)
+{
+ proxy_server_conf *psf =
+ ap_get_module_config(parms->server->module_config, &proxy_module);
+
+ psf->error_override = flag;
+ psf->error_override_set = 1;
+ return NULL;
+}
+static const char *
+ set_preserve_host(cmd_parms *parms, void *dummy, int flag)
+{
+ proxy_server_conf *psf =
+ ap_get_module_config(parms->server->module_config, &proxy_module);
+
+ psf->preserve_host = flag;
+ psf->preserve_host_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);
+
+ psf->io_buffer_size = ((s > AP_IOBUFSIZE) ? 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 *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;
+ 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;
+
+ if (cmd->path)
+ path = apr_pstrdup(cmd->pool, cmd->path);
+ while (*arg) {
+ word = ap_getword_conf(cmd->pool, &arg);
+ if (!path)
+ path = word;
+ else if (!name)
+ name = word;
+ else {
+ char *val = strchr(word, '=');
+ if (!val)
+ 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";
+
+ ap_str_tolower(path); /* lowercase scheme://hostname */
+
+ /* Try to find existing worker */
+ worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
+ if (!worker) {
+ const char *err;
+ if ((err = ap_proxy_add_worker(&worker, cmd->pool, conf, name)) != NULL)
+ return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ "worker %s already used by another worker", worker->name);
+ }
+ PROXY_COPY_CONF_PARAMS(worker, conf);
+
+ arr = apr_table_elts(params);
+ elts = (const apr_table_entry_t *)arr->elts;
+ for (i = 0; i < arr->nelts; i++) {
+ const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
+ elts[i].val);
+ if (err)
+ return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+ }
+ /* Try to find the balancer */
+ balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, path);
+ if (!balancer) {
+ const char *err = ap_proxy_add_balancer(&balancer,
+ cmd->pool,
+ conf, path);
+ if (err)
+ return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+ }
+ /* Add the worker to the load balancer */
+ ap_proxy_add_worker_to_balancer(cmd->pool, balancer, worker);
+ 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;
+ const char *err;
+ int in_proxy_section = 0;
+
+ 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';
+ in_proxy_section = 1;
+ }
+ else {
+ /* Standard set directive with worker/balancer
+ * name as first param.
+ */
+ name = ap_getword_conf(cmd->temp_pool, &arg);
+ }
+
+ if (strncasecmp(name, "balancer:", 9) == 0) {
+ balancer = ap_proxy_get_balancer(cmd->pool, conf, name);
+ if (!balancer) {
+ if (in_proxy_section) {
+ err = ap_proxy_add_balancer(&balancer,
+ cmd->pool,
+ conf, name);
+ 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 {
+ worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
+ if (!worker) {
+ if (in_proxy_section) {
+ err = ap_proxy_add_worker(&worker, cmd->pool,
+ conf, name);
+ 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, 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 const char *set_ftp_directory_charset(cmd_parms *cmd, void *dconf,
+ const char *arg)
+{
+ proxy_dir_conf *conf = dconf;
+
+ conf->ftp_directory_charset = arg;
+ 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;
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ 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;
+
+ 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";
+ }
+ }
+ else if (!strcmp(cmd->path, "~")) {
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ if (!cmd->path)
+ return "<Proxy ~ > block must specify a path";
+ if (strncasecmp(cmd->path, "proxy:", 6))
+ cmd->path += 6;
+ r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED);
+ if (!r) {
+ return "Regex could not be compiled";
+ }
+ }
+
+ /* 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);
+
+ 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 (strncasecmp(conf->p, "balancer:", 9) == 0) {
+ balancer = ap_proxy_get_balancer(cmd->pool, sconf, conf->p);
+ if (!balancer) {
+ err = ap_proxy_add_balancer(&balancer,
+ cmd->pool,
+ sconf, conf->p);
+ if (err)
+ return apr_pstrcat(cmd->temp_pool, thiscmd->name,
+ " ", err, NULL);
+ }
+ }
+ else {
+ worker = ap_proxy_get_worker(cmd->temp_pool, sconf,
+ conf->p);
+ if (!worker) {
+ err = ap_proxy_add_worker(&worker, cmd->pool,
+ sconf, conf->p);
+ if (err)
+ return apr_pstrcat(cmd->temp_pool, thiscmd->name,
+ " ", err, NULL);
+ }
+ }
+ 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, 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,
+ (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_ITERATE("AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF,
+ "A list of ports which CONNECT may connect to"),
+ AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF,
+ "Configure Via: proxy header header to one of: on | off | block | full"),
+ AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF,
+ "use our error handling pages instead of the servers' we are proxying"),
+ AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_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("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("ProxyFtpDirCharset", set_ftp_directory_charset, NULL,
+ RSRC_CONF|ACCESS_CONF, "Define the character set for proxied FTP listings"),
+ {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_is_https) *proxy_is_https = NULL;
+static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *proxy_ssl_val = 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 (proxy_ssl_enable) {
+ return c ? proxy_ssl_enable(c) : 1;
+ }
+
+ return 0;
+}
+
+PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
+{
+ if (proxy_ssl_disable) {
+ return proxy_ssl_disable(c);
+ }
+
+ return 0;
+}
+
+PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c)
+{
+ if (proxy_is_https) {
+ return proxy_is_https(c);
+ }
+ else
+ return 0;
+}
+
+PROXY_DECLARE(const char *) ap_proxy_ssl_val(apr_pool_t *p, server_rec *s,
+ conn_rec *c, request_rec *r,
+ const char *var)
+{
+ if (proxy_ssl_val) {
+ /* XXX Perhaps the casting useless */
+ return (const char *)proxy_ssl_val(p, s, c, r, (char *)var);
+ }
+ else
+ return NULL;
+}
+
+static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+
+ proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
+ proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
+ proxy_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
+ proxy_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
+
+ 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 (flags & AP_STATUS_SHORT || 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++) {
+ ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r);
+ ap_rvputs(r, balancer->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->sticky) {
+ ap_rvputs(r, "<td>", balancer->sticky, NULL);
+ }
+ else {
+ ap_rputs("<td> - ", r);
+ }
+ ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
+ apr_time_sec(balancer->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>Wr</th><th>Rd</th>"
+ "</tr>\n", r);
+
+ worker = (proxy_worker *)balancer->workers->elts;
+ for (n = 0; n < balancer->workers->nelts; n++) {
+ char fbuf[50];
+ ap_rvputs(r, "<tr>\n<td>", worker->scheme, "</td>", NULL);
+ ap_rvputs(r, "<td>", worker->hostname, "</td><td>", NULL);
+ if (worker->s->status & PROXY_WORKER_DISABLED)
+ ap_rputs("Dis", r);
+ else if (worker->s->status & PROXY_WORKER_IN_ERROR)
+ ap_rputs("Err", r);
+ else if (worker->s->status & PROXY_WORKER_INITIALIZED)
+ ap_rputs("Ok", r);
+ else
+ ap_rputs("-", r);
+ ap_rvputs(r, "</td><td>", worker->s->route, NULL);
+ ap_rvputs(r, "</td><td>", worker->s->redirect, NULL);
+ ap_rprintf(r, "</td><td>%d</td>", worker->s->lbfactor);
+ ap_rprintf(r, "<td>%d</td>", worker->s->lbset);
+ ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td><td>", worker->s->elected);
+ 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);
+
+ ++worker;
+ }
+ ap_rputs("</table>\n", r);
+ ++balancer;
+ }
+ 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 in %</td></tr>\n"
+ "<tr><th>Acc</th><td>Number of requests</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;
+
+ 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);
+ /* Initialize worker's shared scoreboard data */
+ worker = (proxy_worker *)conf->workers->elts;
+ for (i = 0; i < conf->workers->nelts; i++) {
+ ap_proxy_initialize_worker_share(conf, worker, s);
+ ap_proxy_initialize_worker(worker, s);
+ worker++;
+ }
+ /* Initialize forward worker if defined */
+ if (conf->forward) {
+ ap_proxy_initialize_worker_share(conf, conf->forward, s);
+ ap_proxy_initialize_worker(conf->forward, s);
+ /* Do not disable worker in case of errors */
+ conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS;
+ /* Disable address cache for generic forward worker */
+ conf->forward->is_address_reusable = 0;
+ }
+ if (!reverse) {
+ reverse = ap_proxy_create_worker(p);
+ reverse->name = "proxy:reverse";
+ reverse->hostname = "*";
+ reverse->scheme = "*";
+ ap_proxy_initialize_worker_share(conf, reverse, s);
+ ap_proxy_initialize_worker(reverse, s);
+ /* Do not disable worker in case of errors */
+ reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS;
+ /* Disable address cache for generic reverse worker */
+ reverse->is_address_reusable = 0;
+ }
+ conf->reverse = reverse;
+ s = s->next;
+ }
+}
+
+/*
+ * This routine is called before the server processes the configuration
+ * files. There is no return value.
+ */
+static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL,
+ APR_HOOK_MIDDLE);
+ /* Reset workers count on gracefull restart */
+ proxy_lb_workers = 0;
+ 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", NULL};
+
+ APR_REGISTER_OPTIONAL_FN(ap_proxy_lb_workers);
+ /* handler */
+ ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
+ /* filename-to-URI translation */
+ ap_hook_translate_name(proxy_trans, 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);
+
+}
+
+module AP_MODULE_DECLARE_DATA proxy_module =
+{
+ 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_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, 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, 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)
Added: trunk/mod_cluster/native/mod_proxy/mod_proxy.h
===================================================================
--- trunk/mod_cluster/native/mod_proxy/mod_proxy.h (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/mod_proxy.h 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,794 @@
+/* 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
+ * @{
+ */
+
+/*
+
+ Also note numerous FIXMEs and CHECKMEs which should be eliminated.
+
+ This code is once again experimental!
+
+ Things to do:
+
+ 1. Make it completely work (for FTP too)
+
+ 2. HTTP/1.1
+
+ Chuck Murcko <chuck(a)topsail.org> 02-06-01
+
+ */
+
+#define CORE_PRIVATE
+
+#include "apr_hooks.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 "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 "util_filter.h"
+#include "util_ebcdic.h"
+#include "ap_provider.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
+};
+
+#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
+
+/* 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 */
+ apr_port_t port; /* the port for 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 */
+};
+
+#define PROXYPASS_NOCANON 0x01
+#define PROXYPASS_INTERPOLATE 0x02
+struct proxy_alias {
+ const char *real;
+ const char *fake;
+ ap_regex_t *regex;
+ unsigned int flags;
+};
+
+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 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;
+
+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 *allowed_connect_ports;
+ apr_array_header_t *workers;
+ apr_array_header_t *balancers;
+ 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 */
+ int req; /* true if proxy requests are enabled */
+ char req_set;
+ enum {
+ via_off,
+ via_on,
+ via_block,
+ via_full
+ } viaopt; /* how to deal with proxy Via: headers */
+ char viaopt_set;
+ apr_size_t recv_buffer_size;
+ char recv_buffer_size_set;
+ apr_size_t io_buffer_size;
+ char io_buffer_size_set;
+ long maxfwd;
+ char maxfwd_set;
+ /**
+ * 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
+ */
+ int error_override;
+ int error_override_set;
+ int preserve_host;
+ int preserve_host_set;
+ apr_interval_time_t timeout;
+ char timeout_set;
+ enum {
+ bad_error,
+ bad_ignore,
+ bad_body
+ } badopt; /* how to deal with bad headers */
+ char badopt_set;
+/* putting new stuff on the end maximises binary back-compatibility.
+ * the strmatch_patterns are really a const just to have a
+ * case-independent strstr.
+ */
+ enum {
+ status_off,
+ status_on,
+ status_full
+ } proxy_status; /* Status display options */
+ char proxy_status_set;
+ apr_pool_t *pool; /* Pool used for allocating this struct */
+} proxy_server_conf;
+
+
+typedef struct {
+ const char *p; /* The path */
+ int p_is_fnmatch; /* Is this path an fnmatch candidate? */
+ ap_regex_t *r; /* Is this a regex? */
+
+/* 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:
+ * 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;
+ const apr_strmatch_pattern* cookie_path_str;
+ const apr_strmatch_pattern* cookie_domain_str;
+ const char *ftp_directory_charset;
+ int interpolate_env;
+} proxy_dir_conf;
+
+/* if we interpolate env vars per-request, we'll need a per-request
+ * copy of the reverse proxy config
+ */
+typedef struct {
+ apr_array_header_t *raliases;
+ apr_array_header_t* cookie_paths;
+ apr_array_header_t* cookie_domains;
+} proxy_req_conf;
+
+typedef struct {
+ conn_rec *connection;
+ const char *hostname;
+ apr_port_t port;
+ int is_ssl;
+ apr_pool_t *pool; /* Subpool for hostname and addr data */
+ apr_socket_t *sock; /* Connection socket */
+ apr_sockaddr_t *addr; /* Preparsed remote address info */
+ apr_uint32_t flags; /* Conection flags */
+ int close; /* Close 'this' connection */
+ int close_on_recycle; /* Close the connection when returning to pool */
+ proxy_worker *worker; /* Connection pool this connection belongs to */
+ void *data; /* per scheme connection data */
+#if APR_HAS_THREADS
+ int inreslist; /* connection in apr_reslist? */
+#endif
+ apr_pool_t *scpool; /* Subpool used for socket and connection data */
+ request_rec *r; /* Request record of the frontend request
+ * which the backend currently answers. */
+ int need_flush;/* Flag to decide whether we need to flush the
+ * filter chain or not */
+} proxy_conn_rec;
+
+typedef struct {
+ 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 */
+#if APR_HAS_THREADS
+ apr_reslist_t *res; /* Connection resource list */
+#endif
+ proxy_conn_rec *conn; /* Single connection for prefork mpm's */
+};
+
+/* worker status flags */
+#define PROXY_WORKER_INITIALIZED 0x0001
+#define PROXY_WORKER_IGNORE_ERRORS 0x0002
+#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_NOT_USABLE_BITMAP ( PROXY_WORKER_IN_SHUTDOWN | \
+PROXY_WORKER_DISABLED | PROXY_WORKER_STOPPED | PROXY_WORKER_IN_ERROR )
+
+/* NOTE: these check the shared status */
+#define PROXY_WORKER_IS_INITIALIZED(f) ( (f)->s && \
+ ( (f)->s->status & PROXY_WORKER_INITIALIZED ) )
+
+#define PROXY_WORKER_IS_STANDBY(f) ( (f)->s && \
+ ( (f)->s->status & PROXY_WORKER_HOT_STANDBY ) )
+
+#define PROXY_WORKER_IS_USABLE(f) ( (f)->s && \
+ ( !( (f)->s->status & PROXY_WORKER_NOT_USABLE_BITMAP) ) && \
+ PROXY_WORKER_IS_INITIALIZED(f) )
+
+/* default worker retry timeout in seconds */
+#define PROXY_WORKER_DEFAULT_RETRY 60
+#define PROXY_WORKER_MAX_ROUTE_SIZ 63
+
+/* Runtime worker status informations. Shared in scoreboard */
+typedef struct {
+ int status;
+ apr_time_t error_time; /* time of the last error */
+ int retries; /* number of retries on this worker */
+ int lbstatus; /* Current lbstatus */
+ int lbfactor; /* dynamic lbfactor */
+ apr_off_t transferred;/* Number of bytes transferred to remote */
+ apr_off_t read; /* Number of bytes read from remote */
+ apr_size_t elected; /* Number of times the worker was elected */
+ char route[PROXY_WORKER_MAX_ROUTE_SIZ+1];
+ char redirect[PROXY_WORKER_MAX_ROUTE_SIZ+1];
+ void *context; /* general purpose storage */
+ apr_size_t busy; /* busyness factor */
+ int lbset; /* load balancer cluster set */
+} proxy_worker_stat;
+
+/* Worker configuration */
+struct proxy_worker {
+ int id; /* scoreboard id */
+ apr_interval_time_t retry; /* retry interval */
+ int lbfactor; /* initial load balancing factor */
+ const char *name;
+ const char *scheme; /* scheme to use ajp|http|https */
+ const char *hostname; /* remote backend address */
+ const char *route; /* balancing route */
+ const char *redirect; /* temporary balancing redirection route */
+ int status; /* temporary worker status */
+ apr_port_t port;
+ 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 */
+ 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 timeout; /* connection timeout */
+ char timeout_set;
+ apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */
+ char acquire_set;
+ apr_size_t recv_buffer_size;
+ char recv_buffer_size_set;
+ apr_size_t io_buffer_size;
+ char io_buffer_size_set;
+ char keepalive;
+ char keepalive_set;
+ proxy_conn_pool *cp; /* Connection pool to use */
+ proxy_worker_stat *s; /* Shared data */
+ void *opaque; /* per scheme worker data */
+ int is_address_reusable;
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *mutex; /* Thread lock for updating address cache */
+#endif
+ void *context; /* general purpose storage */
+ enum {
+ flush_off,
+ flush_on,
+ flush_auto
+ } flush_packets; /* control AJP flushing */
+ int flush_wait; /* poll wait time in microseconds if flush_auto */
+ int lbset; /* load balancer cluster set */
+ apr_interval_time_t ping_timeout;
+ char ping_timeout_set;
+ char retry_set;
+ char disablereuse;
+ char disablereuse_set;
+ apr_interval_time_t conn_timeout;
+ char conn_timeout_set;
+};
+
+/*
+ * Wait 10000 microseconds to find out if more data is currently
+ * available at the backend. Just an arbitrary choose.
+ */
+#define PROXY_FLUSH_WAIT 10000
+
+struct proxy_balancer {
+ apr_array_header_t *workers; /* array of proxy_workers */
+ const char *name; /* name of the load balancer */
+ const char *sticky; /* sticky session identifier */
+ int sticky_force; /* Disable failover for sticky sessions */
+ apr_interval_time_t timeout; /* Timeout for waiting on free connection */
+ int max_attempts; /* Number of attempts before failing */
+ char max_attempts_set;
+ proxy_balancer_method *lbmethod;
+
+ /* XXX: Perhaps we will need the proc mutex too.
+ * Altrough we are only using arithmetic operations
+ * it may lead to a incorrect calculations.
+ * For now use only the thread mutex.
+ */
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *mutex; /* Thread lock for updating lb params */
+#endif
+ void *context; /* general purpose storage */
+ int scolonsep; /* true if ';' seps sticky session paths */
+};
+
+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 */
+};
+
+#if APR_HAS_THREADS
+#define PROXY_THREAD_LOCK(x) apr_thread_mutex_lock((x)->mutex)
+#define PROXY_THREAD_UNLOCK(x) apr_thread_mutex_unlock((x)->mutex)
+#else
+#define PROXY_THREAD_LOCK(x) APR_SUCCESS
+#define PROXY_THREAD_UNLOCK(x) APR_SUCCESS
+#endif
+
+/* 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
+
+/**
+ * Hook an optional proxy hook. Unlike static hooks, this uses a macro
+ * instead of a function.
+ */
+#define PROXY_OPTIONAL_HOOK(name,fn,pre,succ,order) \
+ APR_OPTIONAL_HOOK(proxy,name,fn,pre,succ,order)
+
+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, 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 coresponding 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(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r);
+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(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(const char *)ap_proxy_date_canon(apr_pool_t *p, const char *x);
+PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val);
+PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val);
+PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x);
+PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y);
+PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message);
+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);
+PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr);
+PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r);
+PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos);
+PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key);
+/* DEPRECATED (will be replaced with ap_proxy_connect_backend */
+PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, server_rec *, apr_pool_t *);
+PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
+ request_rec *r);
+PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c);
+PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c);
+PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c);
+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 */
+/**
+ * Get the worker from proxy configuration
+ * @param p memory pool used for finding worker
+ * @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_server_conf *conf,
+ const char *url);
+/**
+ * Add the worker to proxy configuration
+ * @param worker the new worker
+ * @param p memory pool to allocate worker from
+ * @param conf current proxy server configuration
+ * @param url url containing worker name
+ * @return error message or NULL if successfull
+ */
+PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
+ apr_pool_t *p,
+ proxy_server_conf *conf,
+ const char *url);
+
+/**
+ * Create new worker
+ * @param p memory pool to allocate worker from
+ * @return new worker
+ */
+PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p);
+
+/**
+ * Initize the worker's shared data
+ * @param conf current proxy server configuration
+ * @param worker worker to initialize
+ * @param s current server record
+ * @param worker worker to initialize
+ */
+PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,
+ proxy_worker *worker,
+ server_rec *s);
+
+
+/**
+ * Initize the worker
+ * @param worker worker to initialize
+ * @param s current server record
+ * @return APR_SUCCESS or error code
+ */
+PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker,
+ server_rec *s);
+/**
+ * Get the balancer from proxy configuration
+ * @param p memory pool used for finding balancer
+ * @param conf current proxy server configuration
+ * @param url url to find the worker from. Has to have balancer:// prefix
+ * @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);
+/**
+ * Add the balancer to proxy configuration
+ * @param balancer the new balancer
+ * @param p memory pool to allocate balancer from
+ * @param conf current proxy server configuration
+ * @param url url containing balancer name
+ * @return error message or NULL if successfull
+ */
+PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
+ apr_pool_t *p,
+ proxy_server_conf *conf,
+ const char *url);
+
+/**
+ * Add the worker to the balancer
+ * @param pool memory pool for adding worker
+ * @param balancer balancer to add to
+ * @param balancer worker to add
+ * @note Single worker can be added to multiple balancers.
+ */
+PROXY_DECLARE(void) ap_proxy_add_worker_to_balancer(apr_pool_t *pool,
+ proxy_balancer *balancer,
+ proxy_worker *worker);
+/**
+ * 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 When ever 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);
+
+/**
+ * Request status function
+ * @param status status of proxy request
+ * @return OK or DECLINED
+ */
+ PROXY_DECLARE(int) ap_proxy_request_status(int *status, request_rec *r);
+
+/**
+ * Deternime 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 s proxy
+ * @param proxyport proxy host port
+ * @param server_portstr Via headers server port
+ * @param server_portstr_size size of the server_portstr buffer
+ * @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 conf current proxy server configuration
+ * @param worker worker used for retrying
+ * @param s current server record
+ * @return OK if marked for retry, DECLINED otherwise
+ * @note Worker will be marker for retry if the time of the last retry
+ * has been ellapsed. In case there is no retry option set, defaults to
+ * number_of_retries seconds.
+ */
+PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
+ proxy_worker *worker,
+ server_rec *s);
+/**
+ * Acquire a connection from workers 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 number of connections is exhaused the function will
+ * block untill the timeout is reached.
+ */
+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);
+/**
+ * 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 record for backend connection
+ * @param proxy_function calling proxy scheme (http, ajp, ...)
+ * @param conn acquired connection
+ * @param c client connection record
+ * @param s current server record
+ * @return OK or HTTP_XXX error
+ */
+PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
+ proxy_conn_rec *conn,
+ conn_rec *c, server_rec *s);
+/**
+ * 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);
+
+/* Scoreboard */
+#if MODULE_MAGIC_NUMBER_MAJOR > 20020903
+#define PROXY_HAS_SCOREBOARD 1
+#else
+#define PROXY_HAS_SCOREBOARD 0
+#endif
+
+/**
+ * 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 current request record of client request. Only used for logging
+ * purposes
+ * @param from the brigade that contains the buckets to transform
+ * @param to the brigade that will receive the transformed buckets
+ * @return APR_SUCCESS if all buckets could be transformed APR_EGENERAL
+ * otherwise
+ */
+PROXY_DECLARE(apr_status_t)
+ap_proxy_buckets_lifetime_transform(request_rec *r, apr_bucket_brigade *from,
+ apr_bucket_brigade *to);
+
+#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 number of maximum number of workers in scoreboard.
+ * @return number of workers to allocate in the scoreboard
+ */
+int ap_proxy_lb_workers(void);
+
+/* For proxy_util */
+extern module PROXY_DECLARE_DATA proxy_module;
+
+extern int PROXY_DECLARE_DATA proxy_lb_workers;
+
+#endif /*MOD_PROXY_H*/
+/** @} */
Added: trunk/mod_cluster/native/mod_proxy/mod_proxy_ajp.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/mod_proxy_ajp.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/mod_proxy_ajp.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,713 @@
+/* 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 = AJP13_DEF_PORT;
+
+ /* ap_port_of_scheme() */
+ if (strncasecmp(url, "ajp:", 4) == 0) {
+ url += 4;
+ }
+ else {
+ return DECLINED;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: AJP: 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,
+ "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 {
+ path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
+ r->proxyreq);
+ search = r->args;
+ }
+ if (path == NULL)
+ return HTTP_BAD_REQUEST;
+
+ apr_snprintf(sport, sizeof(sport), ":%d", port);
+
+ if (ap_strchr_c(host, ':')) {
+ /* if literal IPv6 address */
+ host = apr_pstrcat(r->pool, "[", host, "]", NULL);
+ }
+ r->filename = apr_pstrcat(r->pool, "proxy: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 resent it to a backend again
+ * automatically.
+ */
+ if (r->args) {
+ return METHOD_IDEMPOTENT_WITH_ARGS;
+ }
+ return METHOD_IDEMPOTENT;
+ /* Everything else is not considered idempotent. */
+ default:
+ return METHOD_NON_IDEMPOTENT;
+ }
+}
+
+static apr_off_t get_content_length(request_rec * r)
+{
+ apr_off_t len = 0;
+
+ if (r->clength > 0) {
+ return r->clength;
+ }
+ else if (r->main == NULL) {
+ const char *clp = apr_table_get(r->headers_in, "Content-Length");
+
+ if (clp) {
+ char *errp;
+ if (apr_strtoff(&len, clp, &errp, 10) || *errp || len < 0) {
+ len = 0; /* 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 miliseconds 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;
+ const char *tenc;
+ int havebody = 1;
+ int output_failed = 0;
+ int backend_failed = 0;
+ apr_off_t bb_len;
+ int data_sent = 0;
+ int headers_sent = 0;
+ int rv = 0;
+ 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;
+
+ 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);
+
+ /*
+ * Send the AJP request to the remote server
+ */
+
+ /* send request headers */
+ status = ajp_send_header(conn->sock, r, maxsize, uri);
+ if (status != APR_SUCCESS) {
+ conn->close++;
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: AJP: request failed to %pI (%s)",
+ conn->worker->cp->addr,
+ conn->worker->hostname);
+ if (status == AJP_EOVERFLOW)
+ return HTTP_BAD_REQUEST;
+ 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++;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: ajp_alloc_data_msg failed");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* read the first bloc of data */
+ input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
+ tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
+ if (tenc && (strcasecmp(tenc, "chunked") == 0)) {
+ /* The AJP protocol does not want body data yet */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: request is chunked");
+ } else {
+ /* Get client provided Content-Length header */
+ content_length = get_content_length(r);
+ 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++;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: ap_get_brigade failed");
+ apr_brigade_destroy(input_brigade);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* have something */
+ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: APR_BUCKET_IS_EOS");
+ }
+
+ /* Try to send something */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: 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++;
+ apr_brigade_destroy(input_brigade);
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: apr_brigade_flatten");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ apr_brigade_cleanup(input_brigade);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: got %" APR_SIZE_T_FMT " bytes of data", bufsiz);
+ if (bufsiz > 0) {
+ status = ajp_send_data_msg(conn->sock, msg, bufsiz);
+ if (status != APR_SUCCESS) {
+ /* We had a failure: Close connection to backend */
+ conn->close++;
+ apr_brigade_destroy(input_brigade);
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: send failed to %pI (%s)",
+ conn->worker->cp->addr,
+ conn->worker->hostname);
+ /*
+ * 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_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: read zero bytes, expecting"
+ " %" APR_OFF_T_FMT " bytes",
+ content_length);
+ status = ajp_send_data_msg(conn->sock, msg, 0);
+ if (status != APR_SUCCESS) {
+ /* We had a failure: Close connection to backend */
+ conn->close++;
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: send failed to %pI (%s)",
+ conn->worker->cp->addr,
+ conn->worker->hostname);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ else {
+ /* Client send zero bytes with C-L > 0
+ */
+ 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++;
+ apr_brigade_destroy(input_brigade);
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: read response failed from %pI (%s)",
+ conn->worker->cp->addr,
+ conn->worker->hostname);
+ /*
+ * This is only non fatal when we have not sent (parts) of a possible
+ * request body so far (we do not store it and thus cannot sent it
+ * again) and the method is idempotent. In this case we can dare to
+ * retry it with a different worker if we are a balancer member.
+ */
+ if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) {
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ /* parse the reponse */
+ result = ajp_parse_type(r, conn->data);
+ 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_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
+ "proxy: 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_error(APLOG_MARK, APLOG_DEBUG, status,
+ r->server,
+ "ap_get_brigade failed");
+ output_failed = 1;
+ break;
+ }
+ bufsiz = maxsize;
+ status = apr_brigade_flatten(input_brigade, buff,
+ &bufsiz);
+ apr_brigade_cleanup(input_brigade);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, status,
+ r->server,
+ "apr_brigade_flatten failed");
+ output_failed = 1;
+ break;
+ }
+ }
+
+ ajp_msg_reset(msg);
+ /* will go in ajp_send_data_msg */
+ status = ajp_send_data_msg(conn->sock, msg, bufsiz);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
+ "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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ap_proxy_ajp_request error read after end");
+ backend_failed = 1;
+ }
+ break;
+ case CMD_AJP13_SEND_HEADERS:
+ /* AJP13_SEND_HEADERS: process them */
+ status = ajp_parse_header(r, conf, conn->data);
+ if (status != APR_SUCCESS) {
+ backend_failed = 1;
+ }
+ 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) {
+ /* 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "Ignoring flush message received before headers");
+ }
+ }
+ else {
+ e = apr_bucket_transient_create(send_body_chunk_buff, size,
+ r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(output_brigade, e);
+
+ if ((conn->worker->flush_packets == flush_on) ||
+ ((conn->worker->flush_packets == flush_auto) &&
+ (apr_poll(conn_poll, 1, &conn_poll_fd,
+ conn->worker->flush_wait)
+ == APR_TIMEUP) ) ) {
+ 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 (ap_pass_brigade(r->output_filters,
+ output_brigade) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "proxy: error processing body");
+ output_failed = 1;
+ }
+ data_sent = 1;
+ apr_brigade_cleanup(output_brigade);
+ }
+ else {
+ backend_failed = 1;
+ }
+ break;
+ case CMD_AJP13_END_RESPONSE:
+ 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_ERR, 0, r,
+ "proxy: error processing end");
+ output_failed = 1;
+ }
+ /* XXX: what about flush here? See mod_jk */
+ data_sent = 1;
+ break;
+ default:
+ backend_failed = 1;
+ break;
+ }
+
+ /*
+ * If connection has been aborted by client: Stop working.
+ * Nevertheless, we regard our operation so far as a success:
+ * So reset output_failed to 0 and set result to CMD_AJP13_END_RESPONSE
+ * But: Close this connection to the backend.
+ */
+ if (r->connection->aborted) {
+ conn->close++;
+ output_failed = 0;
+ result = CMD_AJP13_END_RESPONSE;
+ }
+
+ /*
+ * We either have finished successfully or we failed.
+ * So bail out
+ */
+ if ((result == CMD_AJP13_END_RESPONSE) || backend_failed
+ || output_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_error(APLOG_MARK, APLOG_DEBUG, status, r->server,
+ "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 || output_failed) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: Processing of request failed backend: %i, "
+ "output: %i", backend_failed, output_failed);
+ /* We had a failure: Close connection to backend */
+ conn->close++;
+ /* Return DONE to avoid error messages being added to the stream */
+ if (data_sent) {
+ rv = DONE;
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: got response from %pI (%s)",
+ conn->worker->cp->addr,
+ conn->worker->hostname);
+ rv = OK;
+ }
+
+ if (backend_failed) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: dialog to %pI (%s) failed",
+ conn->worker->cp->addr,
+ conn->worker->hostname);
+ /*
+ * 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
+ rv = HTTP_SERVICE_UNAVAILABLE;
+ }
+
+ /*
+ * 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 && 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, sent it */
+ if (!APR_BRIGADE_EMPTY(output_brigade))
+ ap_pass_brigade(r->output_filters, output_brigade);
+
+ apr_brigade_destroy(output_brigade);
+
+ 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);
+
+ /*
+ * Note: Memory pool allocation.
+ * A downstream keepalive connection is always connected to the existence
+ * (or not) of an upstream keepalive connection. If this is not done then
+ * load balancing against multiple backend servers breaks (one backend
+ * server ends up taking 100% of the load), and the risk is run of
+ * downstream keepalive connections being kept open unnecessarily. This
+ * keeps webservers busy and ties up resources.
+ *
+ * As a result, we allocate all sockets out of the upstream connection
+ * pool, and when we want to reuse a socket, we check first whether the
+ * connection ID of the current upstream connection is the same as that
+ * of the connection when the socket was opened.
+ */
+ apr_pool_t *p = r->connection->pool;
+ apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
+
+
+ if (strncasecmp(url, "ajp:", 4) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: AJP: declining URL %s", url);
+ return DECLINED;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: AJP: serving URL %s", url);
+
+ /* create space for state information */
+ if (!backend) {
+ status = ap_proxy_acquire_connection(scheme, &backend, worker,
+ r->server);
+ if (status != OK) {
+ if (backend) {
+ backend->close_on_recycle = 1;
+ ap_proxy_release_connection(scheme, backend, r->server);
+ }
+ return status;
+ }
+ }
+
+ backend->is_ssl = 0;
+ backend->close_on_recycle = 0;
+
+ retry = 0;
+ while (retry < 2) {
+ char *locurl = url;
+ /* Step One: Determine Who To Connect To */
+ status = ap_proxy_determine_connection(p, r, conf, worker, backend,
+ uri, &locurl, proxyname, proxyport,
+ server_portstr,
+ sizeof(server_portstr));
+
+ if (status != OK)
+ break;
+
+ /* Step Two: Make the Connection */
+ if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "proxy: AJP: failed to make connection to backend: %s",
+ backend->hostname);
+ status = HTTP_SERVICE_UNAVAILABLE;
+ break;
+ }
+
+ /* Handle CPING/CPONG */
+ if (worker->ping_timeout_set) {
+ status = ajp_handle_cping_cpong(backend->sock, r,
+ worker->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++;
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: AJP: cping/cpong failed to %pI (%s)",
+ worker->cp->addr,
+ worker->hostname);
+ 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);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_ajp_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 apr_table_t */
+ ap_proxy_http_register_hook /* register hooks */
+};
+
Added: trunk/mod_cluster/native/mod_proxy/mod_proxy_http.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/mod_proxy_http.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/mod_proxy_http.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,2014 @@
+/* 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 apr_status_t ap_proxy_http_cleanup(const char *scheme,
+ request_rec *r,
+ proxy_conn_rec *backend);
+
+/*
+ * 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_http_canon(request_rec *r, char *url)
+{
+ char *host, *path, sport[7];
+ char *search = NULL;
+ const char *err;
+ const char *scheme;
+ apr_port_t port, def_port;
+
+ /* ap_port_of_scheme() */
+ if (strncasecmp(url, "http:", 5) == 0) {
+ url += 5;
+ scheme = "http";
+ }
+ else if (strncasecmp(url, "https:", 6) == 0) {
+ url += 6;
+ scheme = "https";
+ }
+ else {
+ return DECLINED;
+ }
+ def_port = apr_uri_port_of_scheme(scheme);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: HTTP: canonicalising URL %s", url);
+
+ /* do syntatic check.
+ * We break the URL into host, port, path, search
+ */
+ port = def_port;
+ err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "error parsing URL %s: %s",
+ 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 {
+ path = ap_proxy_canonenc(r->pool, url, strlen(url),
+ enc_path, 0, r->proxyreq);
+ search = r->args;
+ }
+ break;
+ case PROXYREQ_PROXY:
+ path = url;
+ break;
+ }
+
+ if (path == NULL)
+ 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);
+ }
+ r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
+ "/", path, (search) ? "?" : "", (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 int clear_conn_headers(void *data, const char *key, const char *val)
+{
+ apr_table_t *headers = ((header_dptr*)data)->table;
+ apr_pool_t *pool = ((header_dptr*)data)->pool;
+ const char *name;
+ char *next = apr_pstrdup(pool, val);
+ while (*next) {
+ name = next;
+ while (*next && !apr_isspace(*next) && (*next != ',')) {
+ ++next;
+ }
+ while (*next && (apr_isspace(*next) || (*next == ','))) {
+ *next++ = '\0';
+ }
+ apr_table_unset(headers, name);
+ }
+ return 1;
+}
+static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
+{
+ header_dptr x;
+ x.pool = p;
+ x.table = headers;
+ apr_table_unset(headers, "Proxy-Connection");
+ apr_table_do(clear_conn_headers, &x, headers, "Connection", NULL);
+ apr_table_unset(headers, "Connection");
+}
+static void add_te_chunked(apr_pool_t *p,
+ apr_bucket_alloc_t *bucket_alloc,
+ apr_bucket_brigade *header_brigade)
+{
+ 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);
+}
+
+#define ASCII_CRLF "\015\012"
+#define ASCII_ZERO "\060"
+
+static void terminate_headers(apr_bucket_alloc_t *bucket_alloc,
+ apr_bucket_brigade *header_brigade)
+{
+ apr_bucket *e;
+
+ /* add empty line at the end of the headers */
+ e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+}
+
+static int pass_brigade(apr_bucket_alloc_t *bucket_alloc,
+ request_rec *r, proxy_conn_rec *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)
+ conn->worker->s->transferred += transferred;
+ status = ap_pass_brigade(origin->output_filters, bb);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: pass request body failed to %pI (%s)",
+ conn->addr, conn->hostname);
+ if (origin->aborted) {
+ return APR_STATUS_IS_TIMEUP(status) ? HTTP_GATEWAY_TIME_OUT : HTTP_BAD_GATEWAY;
+ }
+ else {
+ return HTTP_BAD_REQUEST;
+ }
+ }
+ apr_brigade_cleanup(bb);
+ return OK;
+}
+
+#define MAX_MEM_SPOOL 16384
+
+static int stream_reqbody_chunked(apr_pool_t *p,
+ request_rec *r,
+ proxy_conn_rec *p_conn,
+ conn_rec *origin,
+ apr_bucket_brigade *header_brigade,
+ apr_bucket_brigade *input_brigade)
+{
+ int seen_eos = 0, rv = OK;
+ apr_size_t hdr_len;
+ apr_off_t bytes;
+ apr_status_t status;
+ apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
+ apr_bucket_brigade *bb;
+ apr_bucket *e;
+
+ add_te_chunked(p, bucket_alloc, header_brigade);
+ terminate_headers(bucket_alloc, header_brigade);
+
+ while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
+ {
+ char chunk_hdr[20]; /* must be here due to transient bucket. */
+
+ /* If this brigade contains EOS, either stop or remove it. */
+ 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);
+
+ 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(ASCII_CRLF, 2, bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(input_brigade, e);
+
+ if (header_brigade) {
+ /* we never sent the header brigade, so go ahead and
+ * take care of that now
+ */
+ bb = header_brigade;
+
+ /*
+ * Save input_brigade in bb 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
+ * bb brigade already has been created and does not need to get
+ * created by ap_save_brigade.
+ */
+ status = ap_save_brigade(NULL, &bb, &input_brigade, p);
+ if (status != APR_SUCCESS) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ header_brigade = NULL;
+ }
+ else {
+ bb = input_brigade;
+ }
+
+ /* The request is flushed below this loop with chunk EOS header */
+ rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0);
+ if (rv != OK) {
+ return rv;
+ }
+
+ if (seen_eos) {
+ break;
+ }
+
+ status = ap_get_brigade(r->input_filters, input_brigade,
+ AP_MODE_READBYTES, APR_BLOCK_READ,
+ HUGE_STRING_LEN);
+
+ if (status != APR_SUCCESS) {
+ return HTTP_BAD_REQUEST;
+ }
+ }
+
+ if (header_brigade) {
+ /* we never sent the header brigade because there was no request body;
+ * send it now
+ */
+ bb = header_brigade;
+ }
+ else {
+ if (!APR_BRIGADE_EMPTY(input_brigade)) {
+ /* input brigade still has an EOS which we can't pass to the output_filters. */
+ e = APR_BRIGADE_LAST(input_brigade);
+ AP_DEBUG_ASSERT(APR_BUCKET_IS_EOS(e));
+ apr_bucket_delete(e);
+ }
+ bb = input_brigade;
+ }
+
+ e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
+ /* <trailers> */
+ ASCII_CRLF,
+ 5, bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+
+ /* Now we have headers-only, or the chunk EOS mark; flush it */
+ rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1);
+ return rv;
+}
+
+static int stream_reqbody_cl(apr_pool_t *p,
+ request_rec *r,
+ proxy_conn_rec *p_conn,
+ conn_rec *origin,
+ apr_bucket_brigade *header_brigade,
+ apr_bucket_brigade *input_brigade,
+ const char *old_cl_val)
+{
+ int seen_eos = 0, rv = 0;
+ apr_status_t status = APR_SUCCESS;
+ apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
+ apr_bucket_brigade *bb;
+ apr_bucket *e;
+ apr_off_t cl_val = 0;
+ apr_off_t bytes;
+ apr_off_t bytes_streamed = 0;
+
+ if (old_cl_val) {
+ add_cl(p, bucket_alloc, header_brigade, old_cl_val);
+ if (APR_SUCCESS != (status = apr_strtoff(&cl_val, old_cl_val, NULL,
+ 0))) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ terminate_headers(bucket_alloc, header_brigade);
+
+ while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
+ {
+ apr_brigade_length(input_brigade, 1, &bytes);
+ bytes_streamed += bytes;
+
+ /* If this brigade contains EOS, either stop or remove it. */
+ 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);
+ }
+
+ /* C-L < bytes streamed?!?
+ * We will error out after the body is completely
+ * consumed, but we can't stream more bytes at the
+ * back end since they would in part be interpreted
+ * as another request! If nothing is sent, then
+ * just send nothing.
+ *
+ * Prevents HTTP Response Splitting.
+ */
+ if (bytes_streamed > cl_val)
+ continue;
+
+ if (header_brigade) {
+ /* we never sent the header brigade, so go ahead and
+ * take care of that now
+ */
+ bb = header_brigade;
+
+ /*
+ * Save input_brigade in bb 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
+ * bb brigade already has been created and does not need to get
+ * created by ap_save_brigade.
+ */
+ status = ap_save_brigade(NULL, &bb, &input_brigade, p);
+ if (status != APR_SUCCESS) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ header_brigade = NULL;
+ }
+ else {
+ bb = input_brigade;
+ }
+
+ /* Once we hit EOS, we are ready to flush. */
+ rv = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos);
+ if (rv != OK) {
+ return rv ;
+ }
+
+ if (seen_eos) {
+ break;
+ }
+
+ status = ap_get_brigade(r->input_filters, input_brigade,
+ AP_MODE_READBYTES, APR_BLOCK_READ,
+ HUGE_STRING_LEN);
+
+ if (status != APR_SUCCESS) {
+ return HTTP_BAD_REQUEST;
+ }
+ }
+
+ if (bytes_streamed != cl_val) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "proxy: client %s given Content-Length did not match"
+ " number of body bytes read", r->connection->remote_ip);
+ return HTTP_BAD_REQUEST;
+ }
+
+ if (header_brigade) {
+ /* we never sent the header brigade since there was no request
+ * body; send it now with the flush flag
+ */
+ bb = header_brigade;
+ return(pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1));
+ }
+
+ return OK;
+}
+
+static int spool_reqbody_cl(apr_pool_t *p,
+ request_rec *r,
+ proxy_conn_rec *p_conn,
+ conn_rec *origin,
+ apr_bucket_brigade *header_brigade,
+ apr_bucket_brigade *input_brigade,
+ int force_cl)
+{
+ int seen_eos = 0;
+ apr_status_t status;
+ apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
+ apr_bucket_brigade *body_brigade;
+ apr_bucket *e;
+ apr_off_t bytes, bytes_spooled = 0, fsize = 0;
+ apr_file_t *tmpfile = NULL;
+
+ body_brigade = apr_brigade_create(p, bucket_alloc);
+
+ while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade)))
+ {
+ /* If this brigade contains EOS, either stop or remove it. */
+ 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);
+
+ 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_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: 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_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: 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_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: 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;
+
+ if (seen_eos) {
+ break;
+ }
+
+ status = ap_get_brigade(r->input_filters, input_brigade,
+ AP_MODE_READBYTES, APR_BLOCK_READ,
+ HUGE_STRING_LEN);
+
+ if (status != APR_SUCCESS) {
+ return HTTP_BAD_REQUEST;
+ }
+ }
+
+ if (bytes_spooled || force_cl) {
+ add_cl(p, bucket_alloc, header_brigade, apr_off_t_toa(p, bytes_spooled));
+ }
+ terminate_headers(bucket_alloc, header_brigade);
+ APR_BRIGADE_CONCAT(header_brigade, body_brigade);
+ if (tmpfile) {
+ /* For platforms where the size of the file may be larger than
+ * that which can be stored in a single bucket (where the
+ * length field is an apr_size_t), split it into several
+ * buckets: */
+ if (sizeof(apr_off_t) > sizeof(apr_size_t)
+ && fsize > AP_MAX_SENDFILE) {
+ e = apr_bucket_file_create(tmpfile, 0, AP_MAX_SENDFILE, p,
+ bucket_alloc);
+ while (fsize > AP_MAX_SENDFILE) {
+ apr_bucket *ce;
+ apr_bucket_copy(e, &ce);
+ APR_BRIGADE_INSERT_TAIL(header_brigade, ce);
+ e->start += AP_MAX_SENDFILE;
+ fsize -= AP_MAX_SENDFILE;
+ }
+ e->length = (apr_size_t)fsize; /* Resize just the last bucket */
+ }
+ else {
+ e = apr_bucket_file_create(tmpfile, 0, (apr_size_t)fsize, p,
+ bucket_alloc);
+ }
+ APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+ }
+ /* This is all a single brigade, pass with flush flagged */
+ return(pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1));
+}
+
+static
+int ap_proxy_http_request(apr_pool_t *p, request_rec *r,
+ proxy_conn_rec *p_conn, conn_rec *origin,
+ proxy_server_conf *conf,
+ apr_uri_t *uri,
+ char *url, char *server_portstr)
+{
+ conn_rec *c = r->connection;
+ apr_bucket_alloc_t *bucket_alloc = c->bucket_alloc;
+ apr_bucket_brigade *header_brigade;
+ apr_bucket_brigade *input_brigade;
+ apr_bucket_brigade *temp_brigade;
+ apr_bucket *e;
+ char *buf;
+ const apr_array_header_t *headers_in_array;
+ const apr_table_entry_t *headers_in;
+ int counter;
+ apr_status_t status;
+ enum rb_methods {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED, RB_SPOOL_CL};
+ enum rb_methods rb_method = RB_INIT;
+ const char *old_cl_val = NULL;
+ const char *old_te_val = NULL;
+ apr_off_t bytes_read = 0;
+ apr_off_t bytes;
+ int force10, rv;
+ apr_table_t *headers_in_copy;
+
+ header_brigade = apr_brigade_create(p, origin->bucket_alloc);
+
+ /*
+ * Send the HTTP/1.1 request to the remote server
+ */
+
+ if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
+ buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
+ force10 = 1;
+ /*
+ * According to RFC 2616 8.2.3 we are not allowed to forward an
+ * Expect: 100-continue to an HTTP/1.0 server. Instead we MUST return
+ * a HTTP_EXPECTATION_FAILED
+ */
+ if (r->expecting_100) {
+ return HTTP_EXPECTATION_FAILED;
+ }
+ p_conn->close++;
+ } else {
+ buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
+ force10 = 0;
+ }
+ if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
+ origin->keepalive = AP_CONN_CLOSE;
+ p_conn->close++;
+ }
+ 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 (conf->preserve_host == 0) {
+ if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
+ buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str,
+ CRLF, NULL);
+ } else {
+ buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
+ }
+ }
+ else {
+ /* don't want to use r->hostname, as the incoming header might have a
+ * port attached
+ */
+ const char* hostname = apr_table_get(r->headers_in,"Host");
+ if (!hostname) {
+ hostname = r->server->server_hostname;
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "proxy: no HTTP 0.9 request (with no host line) "
+ "on incoming request and preserve host set "
+ "forcing hostname to be %s for uri %s",
+ hostname,
+ r->uri );
+ }
+ buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
+ }
+ ap_xlate_proto_to_ascii(buf, strlen(buf));
+ e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+
+ /* handle Via */
+ if (conf->viaopt == via_block) {
+ /* Block all outgoing Via: headers */
+ apr_table_unset(r->headers_in, "Via");
+ } else if (conf->viaopt != via_off) {
+ const char *server_name = ap_get_server_name(r);
+ /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
+ * then the server name returned by ap_get_server_name() is the
+ * origin server name (which does make too much sense with Via: headers)
+ * so we use the proxy vhost's name instead.
+ */
+ if (server_name == r->hostname)
+ server_name = r->server->server_hostname;
+ /* Create a "Via:" request header entry and merge it */
+ /* Generate outgoing Via: header with/without server comment: */
+ apr_table_mergen(r->headers_in, "Via",
+ (conf->viaopt == via_full)
+ ? apr_psprintf(p, "%d.%d %s%s (%s)",
+ HTTP_VERSION_MAJOR(r->proto_num),
+ HTTP_VERSION_MINOR(r->proto_num),
+ server_name, server_portstr,
+ AP_SERVER_BASEVERSION)
+ : apr_psprintf(p, "%d.%d %s%s",
+ HTTP_VERSION_MAJOR(r->proto_num),
+ HTTP_VERSION_MINOR(r->proto_num),
+ server_name, server_portstr)
+ );
+ }
+
+ /* X-Forwarded-*: handling
+ *
+ * XXX Privacy Note:
+ * -----------------
+ *
+ * These request headers are only really useful when the mod_proxy
+ * is used in a reverse proxy configuration, so that useful info
+ * about the client can be passed through the reverse proxy and on
+ * to the backend server, which may require the information to
+ * function properly.
+ *
+ * In a forward proxy situation, these options are a potential
+ * privacy violation, as information about clients behind the proxy
+ * are revealed to arbitrary servers out there on the internet.
+ *
+ * The HTTP/1.1 Via: header is designed for passing client
+ * information through proxies to a server, and should be used in
+ * a forward proxy configuation instead of X-Forwarded-*. See the
+ * ProxyVia option for details.
+ */
+
+ if (PROXYREQ_REVERSE == r->proxyreq) {
+ const char *buf;
+
+ /* Add X-Forwarded-For: so that the upstream has a chance to
+ * determine, where the original request came from.
+ */
+ apr_table_mergen(r->headers_in, "X-Forwarded-For",
+ c->remote_ip);
+
+ /* Add X-Forwarded-Host: so that upstream knows what the
+ * original request hostname was.
+ */
+ if ((buf = apr_table_get(r->headers_in, "Host"))) {
+ apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
+ }
+
+ /* Add X-Forwarded-Server: so that upstream knows what the
+ * name of this proxy server is (if there are more than one)
+ * XXX: This duplicates Via: - do we strictly need it?
+ */
+ apr_table_mergen(r->headers_in, "X-Forwarded-Server",
+ r->server->server_hostname);
+ }
+
+ proxy_run_fixups(r);
+ /*
+ * Make a copy of the headers_in table before clearing the connection
+ * headers as we need the connection headers later in the http output
+ * filter to prepare the correct response headers.
+ *
+ * Note: We need to take r->pool for apr_table_copy as the key / value
+ * pairs in r->headers_in have been created out of r->pool and
+ * p might be (and actually is) a longer living pool.
+ * This would trigger the bad pool ancestry abort in apr_table_copy if
+ * apr is compiled with APR_POOL_DEBUG.
+ */
+ headers_in_copy = apr_table_copy(r->pool, r->headers_in);
+ ap_proxy_clear_connection(p, headers_in_copy);
+ /* send request headers */
+ headers_in_array = apr_table_elts(headers_in_copy);
+ headers_in = (const apr_table_entry_t *) headers_in_array->elts;
+ for (counter = 0; counter < headers_in_array->nelts; counter++) {
+ if (headers_in[counter].key == NULL
+ || headers_in[counter].val == NULL
+
+ /* Already sent */
+ || !strcasecmp(headers_in[counter].key, "Host")
+
+ /* Clear out hop-by-hop request headers not to send
+ * RFC2616 13.5.1 says we should strip these headers
+ */
+ || !strcasecmp(headers_in[counter].key, "Keep-Alive")
+ || !strcasecmp(headers_in[counter].key, "TE")
+ || !strcasecmp(headers_in[counter].key, "Trailer")
+ || !strcasecmp(headers_in[counter].key, "Upgrade")
+
+ ) {
+ continue;
+ }
+ /* Do we want to strip Proxy-Authorization ?
+ * If we haven't used it, then NO
+ * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
+ * So let's make it configurable by env.
+ */
+ if (!strcasecmp(headers_in[counter].key,"Proxy-Authorization")) {
+ if (r->user != NULL) { /* we've authenticated */
+ if (!apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
+ continue;
+ }
+ }
+ }
+
+ /* Skip Transfer-Encoding and Content-Length for now.
+ */
+ if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) {
+ old_te_val = headers_in[counter].val;
+ continue;
+ }
+ if (!strcasecmp(headers_in[counter].key, "Content-Length")) {
+ old_cl_val = headers_in[counter].val;
+ continue;
+ }
+
+ /* for sub-requests, ignore freshness/expiry headers */
+ if (r->main) {
+ if ( !strcasecmp(headers_in[counter].key, "If-Match")
+ || !strcasecmp(headers_in[counter].key, "If-Modified-Since")
+ || !strcasecmp(headers_in[counter].key, "If-Range")
+ || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since")
+ || !strcasecmp(headers_in[counter].key, "If-None-Match")) {
+ continue;
+ }
+ }
+
+ buf = apr_pstrcat(p, headers_in[counter].key, ": ",
+ headers_in[counter].val, CRLF,
+ NULL);
+ ap_xlate_proto_to_ascii(buf, strlen(buf));
+ e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+ }
+
+ /* We have headers, let's figure out our request body... */
+ input_brigade = apr_brigade_create(p, bucket_alloc);
+
+ /* 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 unplesant.
+ */
+ if (r->main) {
+ /* XXX: Why DON'T sub-requests use keepalives? */
+ p_conn->close++;
+ if (old_cl_val) {
+ old_cl_val = NULL;
+ apr_table_unset(r->headers_in, "Content-Length");
+ }
+ if (old_te_val) {
+ old_te_val = NULL;
+ apr_table_unset(r->headers_in, "Transfer-Encoding");
+ }
+ 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 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 (old_te_val && strcmp(old_te_val, "chunked") != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "proxy: %s Transfer-Encoding is not supported",
+ old_te_val);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (old_cl_val && old_te_val) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_ENOTIMPL, r->server,
+ "proxy: client %s (%s) requested Transfer-Encoding "
+ "chunked body with Content-Length (C-L ignored)",
+ c->remote_ip, c->remote_host ? c->remote_host: "");
+ apr_table_unset(r->headers_in, "Content-Length");
+ old_cl_val = NULL;
+ origin->keepalive = AP_CONN_CLOSE;
+ p_conn->close++;
+ }
+
+ /* Prefetch MAX_MEM_SPOOL 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, bucket_alloc);
+ do {
+ status = ap_get_brigade(r->input_filters, temp_brigade,
+ AP_MODE_READBYTES, APR_BLOCK_READ,
+ MAX_MEM_SPOOL - bytes_read);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: prefetch request body failed to %pI (%s)"
+ " from %s (%s)",
+ p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
+ c->remote_ip, c->remote_host ? c->remote_host: "");
+ return 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_error(APLOG_MARK, APLOG_ERR, status, r->server,
+ "proxy: processing prefetched request body failed"
+ " to %pI (%s) from %s (%s)",
+ p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
+ c->remote_ip, c->remote_host ? c->remote_host: "");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* 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 less than MAX_MEM_SPOOL
+ * (an arbitrary value.)
+ */
+ } while ((bytes_read < MAX_MEM_SPOOL - 80)
+ && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade)));
+
+ /* 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 desireable.
+ *
+ * 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_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 (old_cl_val || old_te_val || bytes_read) {
+ old_cl_val = apr_off_t_toa(r->pool, bytes_read);
+ }
+ rb_method = RB_STREAM_CL;
+ }
+ else if (old_te_val) {
+ if (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"))) {
+ rb_method = RB_SPOOL_CL;
+ }
+ else {
+ rb_method = RB_STREAM_CHUNKED;
+ }
+ }
+ else if (old_cl_val) {
+ if (r->input_filters == r->proto_input_filters) {
+ rb_method = RB_STREAM_CL;
+ }
+ else if (!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")) {
+ rb_method = RB_STREAM_CHUNKED;
+ }
+ else {
+ 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.
+ */
+ rb_method = RB_SPOOL_CL;
+ }
+
+/* Yes I hate gotos. This is the subrequest shortcut */
+skip_body:
+ /*
+ * 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 (!force10) {
+ if (p_conn->close || p_conn->close_on_recycle) {
+ buf = apr_pstrdup(p, "Connection: close" CRLF);
+ }
+ else {
+ buf = apr_pstrdup(p, "Connection: Keep-Alive" CRLF);
+ }
+ 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);
+ }
+
+ /* send the request body, if any. */
+ switch(rb_method) {
+ case RB_STREAM_CHUNKED:
+ rv = stream_reqbody_chunked(p, r, p_conn, origin, header_brigade,
+ input_brigade);
+ break;
+ case RB_STREAM_CL:
+ rv = stream_reqbody_cl(p, r, p_conn, origin, header_brigade,
+ input_brigade, old_cl_val);
+ break;
+ case RB_SPOOL_CL:
+ rv = spool_reqbody_cl(p, r, p_conn, origin, header_brigade,
+ input_brigade, (old_cl_val != NULL)
+ || (old_te_val != NULL)
+ || (bytes_read > 0));
+ break;
+ default:
+ /* shouldn't be possible */
+ rv = HTTP_INTERNAL_SERVER_ERROR ;
+ break;
+ }
+
+ if (rv != OK) {
+ /* apr_errno value has been logged in lower level method */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ "proxy: pass request body failed to %pI (%s)"
+ " from %s (%s)",
+ p_conn->addr,
+ p_conn->hostname ? p_conn->hostname: "",
+ c->remote_ip,
+ c->remote_host ? c->remote_host: "");
+ return rv;
+ }
+
+ return OK;
+}
+
+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 ( !strcasecmp(date_hdrs[i], key) ) {
+ apr_table_add(r->headers_out, key,
+ ap_proxy_date_canon(r->pool, value)) ;
+ return ;
+ }
+ }
+ for ( i = 0 ; transform_hdrs[i].name ; ++i ) {
+ if ( !strcasecmp(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) ;
+ return ;
+}
+
+/*
+ * 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 void 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;
+ char field[MAX_STRING_LEN];
+ int saw_headers = 0;
+ void *sconf = r->server->module_config;
+ proxy_server_conf *psc;
+ proxy_dir_conf *dconf;
+
+ 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);
+ *pread_len = 0;
+
+ /*
+ * Read header lines until we get the empty separator line, a read error,
+ * the connection closes (EOF), or we timeout.
+ */
+ while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
+
+ 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 ;
+ }
+ 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_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: Starting body due to bogus non-header in headers "
+ "returned by %s (%s)", r->uri, r->method);
+ *pread_len = len;
+ return ;
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: No HTTP headers "
+ "returned by %s (%s)", r->uri, r->method);
+ return ;
+ }
+ }
+ }
+ /* this is the psc->badopt == bad_ignore case */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: 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;
+
+ /* the header was too long; at the least we should skip extra data */
+ if (len >= size - 1) {
+ while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
+ >= MAX_STRING_LEN - 1) {
+ /* soak up the extra data */
+ }
+ if (len == 0) /* time to exit the larger loop as well */
+ break;
+ }
+ }
+}
+
+
+
+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 fold, int *writen)
+{
+ char *tmp_s = s;
+ apr_status_t rv;
+ apr_size_t len;
+
+ rv = ap_rgetline(&tmp_s, n, &len, r, fold, bb);
+ apr_brigade_cleanup(bb);
+
+ if (rv == APR_SUCCESS) {
+ *writen = (int) len;
+ } else if (rv == APR_ENOSPC) {
+ *writen = n;
+ } else {
+ *writen = -1;
+ }
+
+ return rv;
+}
+
+/*
+ * Limit the number of interim respones we sent back to the client. Otherwise
+ * we suffer from a memory build up. Besides there is NO sense in sending back
+ * an unlimited number of interim responses to the client. Thus if we cross
+ * this limit send back a 502 (Bad Gateway).
+ */
+#ifndef AP_MAX_INTERIM_RESPONSES
+#define AP_MAX_INTERIM_RESPONSES 10
+#endif
+
+static
+apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
+ proxy_conn_rec *backend,
+ conn_rec *origin,
+ proxy_server_conf *conf,
+ char *server_portstr) {
+ conn_rec *c = r->connection;
+ char buffer[HUGE_STRING_LEN];
+ const char *buf;
+ char keepchar;
+ request_rec *rp;
+ apr_bucket *e;
+ apr_bucket_brigade *bb, *tmp_bb;
+ apr_bucket_brigade *pass_bb;
+ int len, backasswards;
+ int interim_response = 0; /* non-zero whilst interim 1xx responses
+ * are being read. */
+ int pread_len = 0;
+ apr_table_t *save_table;
+ int backend_broke = 0;
+ static const char *hop_by_hop_hdrs[] =
+ {"Keep-Alive", "Proxy-Authenticate", "TE", "Trailer", "Upgrade", NULL};
+ int i;
+ const char *te = NULL;
+
+ bb = apr_brigade_create(p, c->bucket_alloc);
+ pass_bb = apr_brigade_create(p, c->bucket_alloc);
+
+ /* Get response from the remote server, and pass it up the
+ * filter chain
+ */
+
+ rp = ap_proxy_make_fake_req(origin, r);
+ /* In case anyone needs to know, this is a fake request that is really a
+ * response.
+ */
+ rp->proxyreq = PROXYREQ_RESPONSE;
+ tmp_bb = apr_brigade_create(p, c->bucket_alloc);
+ do {
+ apr_status_t rc;
+
+ apr_brigade_cleanup(bb);
+
+ rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), rp, 0, &len);
+ if (len == 0) {
+ /* handle one potential stray CRLF */
+ rc = ap_proxygetline(tmp_bb, buffer, sizeof(buffer), rp, 0, &len);
+ }
+ if (len <= 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
+ "proxy: error reading status line from remote "
+ "server %s", backend->hostname);
+ if (rc == APR_TIMEUP) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: read timeout");
+ }
+ /*
+ * 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 &&
+ rc != APR_TIMEUP) {
+ apr_bucket *eos;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: Closing connection to client because"
+ " reading from backend server %s failed. Number"
+ " of keepalives %i", backend->hostname,
+ c->keepalives);
+ ap_proxy_backend_broke(r, bb);
+ /*
+ * Add an EOC bucket to signal the ap_http_header_filter
+ * that it should get out of our way, BUT ensure that the
+ * EOC bucket is inserted BEFORE an EOS bucket in bb as
+ * some resource filters like mod_deflate pass everything
+ * up to the EOS down the chain immediately and sent the
+ * remainder of the brigade later (or even never). But in
+ * this case the ap_http_header_filter does not get out of
+ * our way soon enough.
+ */
+ e = ap_bucket_eoc_create(c->bucket_alloc);
+ eos = APR_BRIGADE_LAST(bb);
+ while ((APR_BRIGADE_SENTINEL(bb) != eos)
+ && !APR_BUCKET_IS_EOS(eos)) {
+ eos = APR_BUCKET_PREV(eos);
+ }
+ if (eos == APR_BRIGADE_SENTINEL(bb)) {
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ }
+ else {
+ APR_BUCKET_INSERT_BEFORE(eos, e);
+ }
+ ap_pass_brigade(r->output_filters, bb);
+ /* Need to return OK to avoid sending an error message */
+ return OK;
+ }
+ else if (!c->keepalives) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: NOT Closing connection to client"
+ " although reading from backend server %s"
+ " failed.", backend->hostname);
+ }
+ return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+ "Error reading from remote server");
+ }
+ /* 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/#.# ###*")) {
+ int major, minor;
+
+ if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
+ major = 1;
+ minor = 1;
+ }
+ /* If not an HTTP/1 message or
+ * if the status line was > 8192 bytes
+ */
+ else if ((buffer[5] != '1') || (len >= sizeof(buffer)-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';
+ r->status = atoi(&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';
+ }
+ r->status_line = apr_pstrdup(p, &buffer[9]);
+
+
+ /* read the headers. */
+ /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
+ /* Also, take care with headers with multiple occurences. */
+
+ /* 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 */
+ ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin,
+ &pread_len);
+
+ if (r->headers_out == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+ r->server, "proxy: 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);
+ }
+
+ /* can't have both Content-Length and Transfer-Encoding */
+ if (apr_table_get(r->headers_out, "Transfer-Encoding")
+ && 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: server %s returned Transfer-Encoding"
+ " and Content-Length", backend->hostname);
+ backend->close += 1;
+ }
+
+ /*
+ * Save a possible Transfer-Encoding header as we need it later for
+ * ap_http_filter to know where to end.
+ */
+ te = apr_table_get(r->headers_out, "Transfer-Encoding");
+ /* strip connection listed hop-by-hop headers from response */
+ backend->close += ap_proxy_liststr(apr_table_get(r->headers_out,
+ "Connection"),
+ "close");
+ ap_proxy_clear_connection(p, r->headers_out);
+ if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
+ ap_set_content_type(r, apr_pstrdup(p, buf));
+ }
+ if (!ap_is_HTTP_INFO(r->status)) {
+ ap_proxy_pre_http_request(origin, rp);
+ }
+
+ /* Clear hop-by-hop headers */
+ for (i=0; hop_by_hop_hdrs[i]; ++i) {
+ 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 (conf->viaopt != via_off && conf->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",
+ (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)
+ );
+ }
+
+ /* cancel keepalive if HTTP/1.0 or less */
+ if ((major < 1) || (minor < 1)) {
+ backend->close += 1;
+ origin->keepalive = AP_CONN_CLOSE;
+ }
+ } else {
+ /* an http/0.9 response */
+ backasswards = 1;
+ r->status = 200;
+ r->status_line = "200 OK";
+ backend->close += 1;
+ }
+
+ if (ap_is_HTTP_INFO(r->status)) {
+ interim_response++;
+ }
+ else {
+ interim_response = 0;
+ }
+ if (interim_response) {
+ /* 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.
+ */
+ const char *policy = apr_table_get(r->subprocess_env,
+ "proxy-interim-response");
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ "proxy: HTTP: received interim %d response",
+ r->status);
+ if (!policy || !strcasecmp(policy, "RFC")) {
+ cluster_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_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
+ "undefined proxy interim response policy");
+ }
+ }
+ /* Moved the fixups of Date headers and those affected by
+ * ProxyPassReverse/etc from here to ap_proxy_read_headers
+ */
+
+ 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: origin server sent 401 without WWW-Authenticate header");
+ }
+ }
+
+ 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 */
+ !interim_response && /* not any 1xx response */
+ (r->status != HTTP_NO_CONTENT) && /* not 204 */
+ (r->status != HTTP_NOT_MODIFIED)) { /* not 304 */
+
+ /* 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.
+ */
+ rp->headers_in = apr_table_copy(r->pool, r->headers_out);
+ /*
+ * Restore Transfer-Encoding header from response if we saved
+ * one before and there is none left. We need it for the
+ * ap_http_filter. See above.
+ */
+ if (te && !apr_table_get(rp->headers_in, "Transfer-Encoding")) {
+ apr_table_add(rp->headers_in, "Transfer-Encoding", te);
+ }
+
+ apr_table_unset(r->headers_out,"Transfer-Encoding");
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: start body send");
+
+ /*
+ * if we are overriding the errors, we can't put the content
+ * of the page into the brigade
+ */
+ if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) {
+ /* read the body, pass it to the output filters */
+ apr_read_type_e mode = APR_NONBLOCK_READ;
+ int finish = FALSE;
+
+ do {
+ apr_off_t readbytes;
+ apr_status_t rv;
+
+ rv = ap_get_brigade(rp->input_filters, bb,
+ AP_MODE_READBYTES, mode,
+ conf->io_buffer_size);
+
+ /* ap_get_brigade will return success with an empty brigade
+ * for a non-blocking read which would block: */
+ if (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) {
+ 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_cerror(APLOG_MARK, APLOG_ERR, rv, c,
+ "proxy: error reading response");
+ 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;
+
+ apr_brigade_length(bb, 0, &readbytes);
+ backend->worker->s->read += readbytes;
+#if DEBUGGING
+ {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+ r->server, "proxy (PID %d): readbytes: %#x",
+ getpid(), readbytes);
+ }
+#endif
+ /* sanity check */
+ if (APR_BRIGADE_EMPTY(bb)) {
+ apr_brigade_cleanup(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(bb))) {
+ /* signal that we must leave */
+ finish = TRUE;
+ }
+
+ /* try send what we read */
+ if (ap_pass_brigade(r->output_filters, pass_bb) != APR_SUCCESS
+ || c->aborted) {
+ /* Ack! Phbtt! Die! User aborted! */
+ backend->close = 1; /* this causes socket close below */
+ finish = TRUE;
+ }
+
+ /* make sure we always clean up after ourselves */
+ apr_brigade_cleanup(bb);
+ apr_brigade_cleanup(pass_bb);
+
+ } while (!finish);
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: end body send");
+ }
+ else if (!interim_response) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: header only");
+
+ /* Pass EOS bucket down the filter chain. */
+ e = apr_bucket_eos_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS
+ || c->aborted) {
+ /* Ack! Phbtt! Die! User aborted! */
+ backend->close = 1; /* this causes socket close below */
+ }
+
+ apr_brigade_cleanup(bb);
+ }
+ } while (interim_response && (interim_response < AP_MAX_INTERIM_RESPONSES));
+
+ /* See define of AP_MAX_INTERIM_RESPONSES for why */
+ if (interim_response >= AP_MAX_INTERIM_RESPONSES) {
+ return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+ apr_psprintf(p,
+ "Too many (%d) interim responses from origin server",
+ interim_response));
+ }
+
+ /* If our connection with the client is to be aborted, return DONE. */
+ if (c->aborted || backend_broke) {
+ return DONE;
+ }
+
+ if (conf->error_override) {
+ /* the code above this checks for 'OK' which is what the hook expects */
+ if (!ap_is_HTTP_ERROR(r->status))
+ return OK;
+ else {
+ /* clear r->status for override error, otherwise ErrorDocument
+ * thinks that this is a recursive error, and doesn't find the
+ * custom error page
+ */
+ int status = r->status;
+ r->status = HTTP_OK;
+ /* Discard body, if one is expected */
+ if (!r->header_only && /* not HEAD request */
+ (status != HTTP_NO_CONTENT) && /* not 204 */
+ (status != HTTP_NOT_MODIFIED)) { /* not 304 */
+ ap_discard_request_body(rp);
+ }
+ return status;
+ }
+ } else
+ 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;
+ char server_portstr[32];
+ char *scheme;
+ const char *proxy_function;
+ const char *u;
+ proxy_conn_rec *backend = NULL;
+ int is_ssl = 0;
+ conn_rec *c = r->connection;
+ /*
+ * Use a shorter-lived pool to reduce memory usage
+ * and avoid a memory leak
+ */
+ apr_pool_t *p = r->pool;
+ apr_uri_t *uri = apr_palloc(p, sizeof(*uri));
+
+ /* find the scheme */
+ u = strchr(url, ':');
+ if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
+ return DECLINED;
+ if ((u - url) > 14)
+ return HTTP_BAD_REQUEST;
+ scheme = apr_pstrndup(p, url, u - url);
+ /* scheme is lowercase */
+ ap_str_tolower(scheme);
+ /* is it for us? */
+ if (strcmp(scheme, "https") == 0) {
+ if (!ap_proxy_ssl_enable(NULL)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: HTTPS: declining URL %s"
+ " (mod_ssl not configured?)", url);
+ return DECLINED;
+ }
+ is_ssl = 1;
+ proxy_function = "HTTPS";
+ }
+ else if (!(strcmp(scheme, "http") == 0 || (strcmp(scheme, "ftp") == 0 && proxyname))) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: HTTP: declining URL %s", url);
+ return DECLINED; /* only interested in HTTP, or FTP via proxy */
+ }
+ else {
+ if (*scheme == 'h')
+ proxy_function = "HTTP";
+ else
+ proxy_function = "FTP";
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: HTTP: serving URL %s", url);
+
+
+ /* create space for state information */
+ if ((status = ap_proxy_acquire_connection(proxy_function, &backend,
+ worker, r->server)) != OK)
+ goto cleanup;
+
+
+ backend->is_ssl = is_ssl;
+ if (is_ssl) {
+ ap_proxy_ssl_connection_cleanup(backend, r);
+ }
+
+ /*
+ * 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 expects 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 */
+ if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend,
+ uri, &url, proxyname,
+ proxyport, server_portstr,
+ sizeof(server_portstr))) != OK)
+ goto cleanup;
+
+ /* Step Two: Make the Connection */
+ if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
+ if (r->proxyreq == PROXYREQ_PROXY)
+ status = HTTP_NOT_FOUND;
+ else
+ status = HTTP_SERVICE_UNAVAILABLE;
+ goto cleanup;
+ }
+
+ /* Step Three: Create conn_rec */
+ if (!backend->connection) {
+ if ((status = ap_proxy_connection_create(proxy_function, backend,
+ c, r->server)) != OK)
+ goto cleanup;
+ }
+
+ /* Step Four: Send the Request */
+ if ((status = ap_proxy_http_request(p, r, backend, backend->connection,
+ conf, uri, url, server_portstr)) != OK)
+ goto cleanup;
+
+ /* Step Five: Receive the Response */
+ if ((status = ap_proxy_http_process_response(p, r, backend,
+ backend->connection,
+ conf, server_portstr)) != OK)
+ goto cleanup;
+
+ /* Step Six: Clean Up */
+
+cleanup:
+ if (backend) {
+ if (status != OK)
+ backend->close = 1;
+ ap_proxy_http_cleanup(proxy_function, r, backend);
+ }
+ return status;
+}
+static apr_status_t warn_rx_free(void *p)
+{
+ ap_pregfree((apr_pool_t*)p, warn_rx);
+ return APR_SUCCESS;
+}
+static void ap_proxy_http_register_hook(apr_pool_t *p)
+{
+ 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);
+ apr_pool_cleanup_register(p, p, warn_rx_free, apr_pool_cleanup_null);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_http_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 apr_table_t */
+ ap_proxy_http_register_hook/* register hooks */
+};
+
Added: trunk/mod_cluster/native/mod_proxy/proxy_util.c
===================================================================
--- trunk/mod_cluster/native/mod_proxy/proxy_util.c (rev 0)
+++ trunk/mod_cluster/native/mod_proxy/proxy_util.c 2009-02-19 12:14:53 UTC (rev 2300)
@@ -0,0 +1,2511 @@
+/* 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"
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h> /* for getpid() */
+#endif
+
+#if (APR_MAJOR_VERSION < 1)
+#undef apr_socket_create
+#define apr_socket_create apr_socket_create_ex
+#endif
+
+/* Global balancer counter */
+int PROXY_DECLARE_DATA proxy_lb_workers = 0;
+static int lb_workers_limit = 0;
+
+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);
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
+ (request_rec *r, request_rec *pr), (r, pr),
+ OK, DECLINED)
+
+/* already called in the knowledge that the characters are hex digits */
+PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
+{
+ int i, ch;
+
+#if !APR_CHARSET_EBCDIC
+ 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(apr_pool_t *p, const char *x, int len,
+ enum enctype t, int forcedec,
+ 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 */
+
+/*
+ * 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 || (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]);
+ i += 2;
+ if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */
+ ap_proxy_c2hex(ch, &y[j]);
+ j += 2;
+ continue;
+ }
+ }
+/* 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;
+}
+
+/*
+ * 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 seperating '/' */
+ }
+
+ /* 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;
+}
+
+/*
+ * If the date is a valid RFC 850 date or asctime() date, then it
+ * is converted to the RFC 1123 format.
+ */
+PROXY_DECLARE(const char *)
+ ap_proxy_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;
+}
+
+PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
+{
+ request_rec *rp = apr_pcalloc(r->pool, sizeof(*r));
+
+ rp->pool = r->pool;
+ rp->status = HTTP_OK;
+
+ rp->headers_in = apr_table_make(r->pool, 50);
+ rp->subprocess_env = apr_table_make(r->pool, 50);
+ rp->headers_out = apr_table_make(r->pool, 12);
+ rp->err_headers_out = apr_table_make(r->pool, 5);
+ rp->notes = apr_table_make(r->pool, 5);
+
+ rp->server = r->server;
+ rp->proxyreq = r->proxyreq;
+ 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->request_config = ap_create_request_config(r->pool);
+ proxy_run_create_req(r, rp);
+
+ return rp;
+}
+
+
+/*
+ * list is a comma-separated list of case-insensitive tokens, with
+ * optional whitespace around the tokens.
+ * The return returns 1 if the token val is found in the list, or 0
+ * otherwise.
+ */
+PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
+{
+ int len, i;
+ const char *p;
+
+ len = strlen(val);
+
+ while (list != NULL) {
+ p = ap_strchr_c(list, ',');
+ if (p != NULL) {
+ i = p - list;
+ do {
+ p++;
+ } while (apr_isspace(*p));
+ }
+ else {
+ i = strlen(list);
+ }
+
+ while (i > 0 && apr_isspace(list[i - 1])) {
+ i--;
+ }
+ if (i == len && strncasecmp(list, val, len) == 0) {
+ return 1;
+ }
+ list = p;
+ }
+ return 0;
+}
+
+/*
+ * list is a comma-separated list of case-insensitive tokens, with
+ * optional whitespace around the tokens.
+ * if val appears on the list of tokens, it is removed from the list,
+ * and the new list is returned.
+ */
+PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
+{
+ int len, i;
+ const char *p;
+ char *new = NULL;
+
+ len = strlen(val);
+
+ while (list != NULL) {
+ p = ap_strchr_c(list, ',');
+ if (p != NULL) {
+ i = p - list;
+ do {
+ p++;
+ } while (apr_isspace(*p));
+ }
+ else {
+ i = strlen(list);
+ }
+
+ while (i > 0 && apr_isspace(list[i - 1])) {
+ i--;
+ }
+ if (i == len && strncasecmp(list, val, len) == 0) {
+ /* do nothing */
+ }
+ else {
+ if (new) {
+ new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
+ }
+ else {
+ new = apr_pstrndup(pool, list, i);
+ }
+ }
+ list = p;
+ }
+ return new;
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
+{
+ int i, ch;
+ unsigned int j;
+
+ for (i = 0, j = 0; i < 8; i++) {
+ ch = x[i];
+ j <<= 4;
+ if (apr_isdigit(ch)) {
+ j |= ch - '0';
+ }
+ else if (apr_isupper(ch)) {
+ j |= ch - ('A' - 10);
+ }
+ else {
+ j |= ch - ('a' - 10);
+ }
+ }
+ if (j == 0xffffffff) {
+ return -1; /* so that it works with 8-byte ints */
+ }
+ else {
+ return j;
+ }
+}
+
+/*
+ * Converts a time integer to 8 hex digits
+ */
+PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
+{
+ int i, ch;
+ unsigned int j = t;
+
+ for (i = 7; i >= 0; i--) {
+ ch = j & 0xF;
+ j >>= 4;
+ if (ch >= 10) {
+ y[i] = ch + ('A' - 10);
+ }
+ else {
+ y[i] = ch + '0';
+ }
+ }
+ y[8] = '\0';
+}
+
+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 "
+ "<em><a href=\"", ap_escape_html(r->pool, r->uri),
+ "\">", ap_escape_html(r->pool, r->method),
+ " ",
+ ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
+ "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", apr_pstrdup(r->pool, "*"));
+
+ r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "proxy: %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;
+ 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, "%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,
+ "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,
+ "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,
+ " 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) {
+ 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,
+ "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s", inet_ntoa(This->mask));
+#endif
+ return 1;
+ }
+#if DEBUGGING
+ else {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%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,
+ "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,
+ "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s", inet_ntoa(This->mask));
+#endif
+ return 1;
+ }
+#if DEBUGGING
+ else {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%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,
+ "@@@@ 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;
+}
+
+/* checks whether a host in uri_addr matches proxyblock */
+PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf,
+ apr_sockaddr_t *uri_addr)
+{
+ int j;
+ apr_sockaddr_t * src_uri_addr = uri_addr;
+ /* 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 = npent[j].addr;
+ uri_addr = src_uri_addr;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
+ if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
+ || npent[j].name[0] == '*') {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
+ return HTTP_FORBIDDEN;
+ }
+ while (conf_addr) {
+ uri_addr = src_uri_addr;
+ while (uri_addr) {
+ char *conf_ip;
+ char *uri_ip;
+ apr_sockaddr_ip_get(&conf_ip, conf_addr);
+ apr_sockaddr_ip_get(&uri_ip, uri_addr);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
+ if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
+ return HTTP_FORBIDDEN;
+ }
+ uri_addr = uri_addr->next;
+ }
+ conf_addr = conf_addr->next;
+ }
+ }
+ 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;
+}
+
+/*
+ * 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 ap_proxy_string_read() will not work properly on non ASCII
+ * (EBCDIC) machines either.
+ */
+PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
+ char *buff, apr_size_t bufflen, int *eos)
+{
+ 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;
+
+ /* 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;
+ }
+ }
+ APR_BUCKET_REMOVE(e);
+ apr_bucket_destroy(e);
+ }
+ *pos = '\0';
+ }
+
+ return APR_SUCCESS;
+}
+
+/* unmerge an element in the table */
+PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
+{
+ apr_off_t offset = 0;
+ apr_off_t count = 0;
+ char *value = NULL;
+
+ /* get the value to unmerge */
+ const char *initial = apr_table_get(t, key);
+ if (!initial) {
+ return;
+ }
+ value = apr_pstrdup(p, initial);
+
+ /* remove the value from the headers */
+ apr_table_unset(t, key);
+
+ /* find each comma */
+ while (value[count]) {
+ if (value[count] == ',') {
+ value[count] = 0;
+ apr_table_add(t, key, value + offset);
+ offset = count + 1;
+ }
+ count++;
+ }
+ apr_table_add(t, key, value + offset);
+}
+
+PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
+ proxy_dir_conf *conf, const char *url)
+{
+ proxy_req_conf *rconf;
+ struct proxy_alias *ent;
+ int i, l1, l2;
+ char *u;
+
+ /*
+ * XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
+ * after the hostname
+ */
+ if (r->proxyreq != PROXYREQ_REVERSE) {
+ return url;
+ }
+
+ l1 = strlen(url);
+ 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;
+ real = ent[i].real;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: real: %s", real);
+ /*
+ * First check if mapping against a balancer and see
+ * if we have such a entity. If so, then we need to
+ * find the particulars of the actual worker which may
+ * or may not be the right one... basically, we need
+ * to find which member actually handled this request.
+ */
+ if ((strncasecmp(real, "balancer:", 9) == 0) &&
+ (balancer = ap_proxy_get_balancer(r->pool, sconf, real))) {
+ int n;
+ proxy_worker *worker;
+ worker = (proxy_worker *)balancer->workers->elts;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: checking balancer: %s",
+ balancer->name);
+ for (n = 0; n < balancer->workers->nelts; n++) {
+ if (worker->port) {
+ u = apr_psprintf(r->pool, "%s://%s:%d/", worker->scheme,
+ worker->hostname, worker->port);
+ }
+ else {
+ u = apr_psprintf(r->pool, "%s://%s/", worker->scheme,
+ worker->hostname);
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: matching member (%s) and URL (%s)",
+ u, url);
+
+ l2 = strlen(u);
+ if (l1 >= l2 && strncasecmp(u, url, l2) == 0) {
+ u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "ppr: matched member (%s)", u);
+ return ap_construct_url(r->pool, u, r);
+ }
+ worker++;
+ }
+ }
+
+ l2 = strlen(real);
+ if (l1 >= l2 && strncasecmp(real, url, l2) == 0) {
+ u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
+ return ap_construct_url(r->pool, u, r);
+ }
+ }
+
+ 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;
+ 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;
+ size_t l1, l2, poffs = 0, doffs = 0;
+ int i;
+ int ddiff = 0;
+ int pdiff = 0;
+ char *ret;
+
+ if (r->proxyreq != PROXYREQ_REVERSE) {
+ return str;
+ }
+
+ /*
+ * Find the match and replacement, but save replacing until we've done
+ * both path and domain so we know the new strlen
+ */
+ if ((pathp = apr_strmatch(conf->cookie_path_str, str, len)) != NULL) {
+ pathp += 5;
+ poffs = pathp - str;
+ pathe = ap_strchr_c(pathp, ';');
+ l1 = pathe ? (pathe - pathp) : strlen(pathp);
+ pathe = pathp + 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;
+ }
+ }
+ }
+
+ if ((domainp = apr_strmatch(conf->cookie_domain_str, str, len)) != NULL) {
+ domainp += 7;
+ doffs = domainp - str;
+ domaine = ap_strchr_c(domainp, ';');
+ l1 = domaine ? (domaine - domainp) : strlen(domainp);
+ domaine = domainp + 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;
+ }
+ }
+ }
+
+ 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, domainp - 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, pathp - 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 + pdiff + 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;
+}
+
+PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
+ proxy_server_conf *conf,
+ const char *url)
+{
+ proxy_balancer *balancer;
+ char *c, *uri = apr_pstrdup(p, url);
+ int i;
+
+ 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';
+ }
+ balancer = (proxy_balancer *)conf->balancers->elts;
+ for (i = 0; i < conf->balancers->nelts; i++) {
+ if (strcasecmp(balancer->name, uri) == 0) {
+ return balancer;
+ }
+ balancer++;
+ }
+ return NULL;
+}
+
+PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
+ apr_pool_t *p,
+ proxy_server_conf *conf,
+ const char *url)
+{
+ char *c, *q, *uri = apr_pstrdup(p, url);
+ proxy_balancer_method *lbmethod;
+
+ c = strchr(uri, ':');
+ if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
+ return "Bad syntax for a balancer name";
+ /* 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, which we assume
+ * exists!
+ */
+ lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
+ if (!lbmethod) {
+ return "Can't find 'byrequests' lb method";
+ }
+
+ (*balancer)->name = uri;
+ (*balancer)->lbmethod = lbmethod;
+ (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker));
+ /* XXX Is this a right place to create mutex */
+#if APR_HAS_THREADS
+ if (apr_thread_mutex_create(&((*balancer)->mutex),
+ APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
+ /* XXX: Do we need to log something here */
+ return "can not create thread mutex";
+ }
+#endif
+
+ return NULL;
+}
+
+PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
+ proxy_server_conf *conf,
+ const char *url)
+{
+ 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;
+
+ c = ap_strchr_c(url, ':');
+ if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') {
+ return NULL;
+ }
+
+ url_copy = apr_pstrdup(p, url);
+ url_length = strlen(url);
+
+ /*
+ * 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);
+ }
+
+ worker = (proxy_worker *)conf->workers->elts;
+
+ /*
+ * 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.
+ */
+ for (i = 0; i < conf->workers->nelts; i++) {
+ if ( ((worker_name_length = strlen(worker->name)) <= url_length)
+ && (worker_name_length >= min_match)
+ && (worker_name_length > max_match)
+ && (strncmp(url_copy, worker->name, worker_name_length) == 0) ) {
+ max_worker = worker;
+ max_match = worker_name_length;
+ }
+ worker++;
+ }
+ return max_worker;
+}
+
+#if APR_HAS_THREADS
+static apr_status_t conn_pool_cleanup(void *theworker)
+{
+ proxy_worker *worker = (proxy_worker *)theworker;
+ if (worker->cp->res) {
+ worker->cp->pool = NULL;
+ }
+ return APR_SUCCESS;
+}
+#endif
+
+static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
+{
+ apr_pool_t *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");
+ /*
+ * 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;
+ worker->cp = cp;
+}
+
+PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
+ apr_pool_t *p,
+ proxy_server_conf *conf,
+ const char *url)
+{
+ int rv;
+ apr_uri_t uri;
+
+ rv = apr_uri_parse(p, url, &uri);
+
+ if (rv != APR_SUCCESS) {
+ return "Unable to parse URL";
+ }
+ if (!uri.hostname || !uri.scheme) {
+ return "URL must be absolute!";
+ }
+
+ ap_str_tolower(uri.hostname);
+ ap_str_tolower(uri.scheme);
+ *worker = apr_array_push(conf->workers);
+ memset(*worker, 0, sizeof(proxy_worker));
+ (*worker)->name = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD);
+ (*worker)->scheme = uri.scheme;
+ (*worker)->hostname = uri.hostname;
+ (*worker)->port = uri.port;
+ (*worker)->id = proxy_lb_workers;
+ (*worker)->flush_packets = flush_off;
+ (*worker)->flush_wait = PROXY_FLUSH_WAIT;
+ (*worker)->smax = -1;
+ /* Increase the total worker count */
+ proxy_lb_workers++;
+ init_conn_pool(p, *worker);
+#if APR_HAS_THREADS
+ if (apr_thread_mutex_create(&((*worker)->mutex),
+ APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
+ /* XXX: Do we need to log something here */
+ return "can not create thread mutex";
+ }
+#endif
+
+ return NULL;
+}
+
+PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)
+{
+
+ proxy_worker *worker;
+ worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));
+ worker->id = proxy_lb_workers;
+ worker->smax = -1;
+ /* Increase the total worker count */
+ proxy_lb_workers++;
+ init_conn_pool(p, worker);
+
+ return worker;
+}
+
+PROXY_DECLARE(void)
+ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer,
+ proxy_worker *worker)
+{
+ proxy_worker *runtime;
+
+ runtime = apr_array_push(balancer->workers);
+ memcpy(runtime, worker, sizeof(proxy_worker));
+ runtime->id = proxy_lb_workers;
+ /* Increase the total runtime count */
+ proxy_lb_workers++;
+
+}
+
+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) {
+ *worker = ap_proxy_get_worker(r->pool, conf, *url);
+ if (*worker) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: %s: found worker %s for %s",
+ (*worker)->scheme, (*worker)->name, *url);
+
+ *balancer = NULL;
+ access_status = OK;
+ }
+ else if (r->proxyreq == PROXYREQ_PROXY) {
+ if (conf->forward) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: *: found forward proxy worker for %s",
+ *url);
+ *balancer = NULL;
+ *worker = conf->forward;
+ access_status = OK;
+ }
+ }
+ else if (r->proxyreq == PROXYREQ_REVERSE) {
+ if (conf->reverse) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: *: found reverse proxy worker for %s",
+ *url);
+ *balancer = NULL;
+ *worker = conf->reverse;
+ access_status = OK;
+ }
+ }
+ }
+ else if (access_status == DECLINED && *balancer != NULL) {
+ /* All the workers are busy */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "proxy: 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;
+ if (balancer) {
+ access_status = proxy_run_post_request(worker, balancer, r, conf);
+ }
+ else {
+ access_status = OK;
+ }
+
+ 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,
+ server_rec *s,
+ apr_pool_t *p)
+{
+ 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, p)) != APR_SUCCESS) {
+ loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+ ap_log_error(APLOG_MARK, loglevel, rv, s,
+ "proxy: %s: error creating fam %d socket for target %s",
+ 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 !defined(TPF) && !defined(BEOS)
+ if (conf->recv_buffer_size > 0 &&
+ (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
+ conf->recv_buffer_size))) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "apr_socket_opt_set(SO_RCVBUF): Failed to set "
+ "ProxyReceiveBufferSize, using default");
+ }
+#endif
+
+ 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,
+ "apr_socket_opt_set(APR_TCP_NODELAY): "
+ "Failed to set");
+ }
+
+ /* Set a timeout on the socket */
+ if (conf->timeout_set == 1) {
+ apr_socket_timeout_set(*newsock, conf->timeout);
+ }
+ else {
+ apr_socket_timeout_set(*newsock, s->timeout);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: fam %d socket created to connect to %s",
+ proxy_function, backend_addr->family, backend_name);
+
+ /* 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,
+ "proxy: %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;
+}
+
+static apr_status_t connection_cleanup(void *theconn)
+{
+ proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
+ proxy_worker *worker = conn->worker;
+
+ /*
+ * If the connection pool is NULL the worker
+ * cleanup has been run. Just return.
+ */
+ if (!worker->cp) {
+ return APR_SUCCESS;
+ }
+
+#if APR_HAS_THREADS
+ /* Sanity check: Did we already return the pooled connection? */
+ if (conn->inreslist) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conn->pool,
+ "proxy: Pooled connection 0x%pp for worker %s has been"
+ " already returned to the connection pool.", conn,
+ worker->name);
+ return APR_SUCCESS;
+ }
+#endif
+
+ /* determine if the connection need to be closed */
+ if (conn->close_on_recycle || conn->close || worker->disablereuse ||
+ !worker->is_address_reusable) {
+ 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");
+ }
+#if APR_HAS_THREADS
+ if (worker->hmax && worker->cp->res) {
+ conn->inreslist = 1;
+ apr_reslist_release(worker->cp->res, (void *)conn);
+ }
+ else
+#endif
+ {
+ worker->cp->conn = conn;
+ }
+
+ /* Always return the SUCCESS */
+ return APR_SUCCESS;
+}
+
+static void socket_cleanup(proxy_conn_rec *conn)
+{
+ conn->sock = NULL;
+ conn->connection = NULL;
+ apr_pool_clear(conn->scpool);
+}
+
+PROXY_DECLARE(apr_status_t) ap_proxy_ssl_connection_cleanup(proxy_conn_rec *conn,
+ request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ apr_status_t rv;
+
+ /*
+ * If we have an existing SSL connection it might be possible that the
+ * server sent some SSL message we have not read so far (e.g. a SSL
+ * shutdown message if the server closed the keepalive connection while
+ * the connection was held unused in our pool).
+ * So ensure that if present (=> APR_NONBLOCK_READ) it is read and
+ * processed. We don't expect any data to be in the returned brigade.
+ */
+ if (conn->sock && conn->connection) {
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ rv = ap_get_brigade(conn->connection->input_filters, bb,
+ AP_MODE_READBYTES, APR_NONBLOCK_READ,
+ HUGE_STRING_LEN);
+ if ((rv != APR_SUCCESS) && !APR_STATUS_IS_EAGAIN(rv)) {
+ socket_cleanup(conn);
+ }
+ if (!APR_BRIGADE_EMPTY(bb)) {
+ apr_off_t len;
+
+ rv = apr_brigade_length(bb, 0, &len);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r,
+ "proxy: SSL cleanup brigade contained %"
+ APR_OFF_T_FMT " bytes of data.", len);
+ }
+ apr_brigade_destroy(bb);
+ }
+ return APR_SUCCESS;
+}
+
+/* reslist constructor */
+static apr_status_t connection_constructor(void **resource, void *params,
+ apr_pool_t *pool)
+{
+ apr_pool_t *ctx;
+ apr_pool_t *scpool;
+ proxy_conn_rec *conn;
+ proxy_worker *worker = (proxy_worker *)params;
+
+ /*
+ * 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;
+#if APR_HAS_THREADS
+ conn->inreslist = 1;
+#endif
+ *resource = conn;
+
+ return APR_SUCCESS;
+}
+
+#if APR_HAS_THREADS /* only needed when threads are used */
+/* reslist destructor */
+static apr_status_t connection_destructor(void *resource, void *params,
+ apr_pool_t *pool)
+{
+ proxy_conn_rec *conn = (proxy_conn_rec *)resource;
+
+ /* Destroy the pool only if not called from reslist_destroy */
+ if (conn->worker->cp->pool) {
+ apr_pool_destroy(conn->pool);
+ }
+
+ return APR_SUCCESS;
+}
+#endif
+
+/*
+ * ap_proxy_initialize_worker_share() concerns itself
+ * with initializing those parts of worker which
+ * are, or could be, shared. Basically worker->s
+ */
+PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,
+ proxy_worker *worker,
+ server_rec *s)
+{
+#if PROXY_HAS_SCOREBOARD
+ lb_score *score = NULL;
+#else
+ void *score = NULL;
+#endif
+
+ if (PROXY_WORKER_IS_INITIALIZED(worker)) {
+ /* The worker share is already initialized */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: worker %s already initialized",
+ worker->name);
+ return;
+ }
+#if PROXY_HAS_SCOREBOARD
+ /* Get scoreboard slot */
+ if (ap_scoreboard_image) {
+ score = ap_get_scoreboard_lb(worker->id);
+ if (!score) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+ "proxy: ap_get_scoreboard_lb(%d) failed in child %" APR_PID_T_FMT " for worker %s",
+ worker->id, getpid(), worker->name);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: grabbed scoreboard slot %d in child %" APR_PID_T_FMT " for worker %s",
+ worker->id, getpid(), worker->name);
+ }
+ }
+#endif
+ if (!score) {
+ score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat));
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: initialized plain memory in child %" APR_PID_T_FMT " for worker %s",
+ getpid(), worker->name);
+ }
+ worker->s = (proxy_worker_stat *)score;
+ /*
+ * recheck to see if we've already been here. Possible
+ * if proxy is using scoreboard to hold shared stats
+ */
+ if (PROXY_WORKER_IS_INITIALIZED(worker)) {
+ /* The worker share is already initialized */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: worker %s already initialized",
+ worker->name);
+ return;
+ }
+ if (worker->route) {
+ strcpy(worker->s->route, worker->route);
+ }
+ else {
+ *worker->s->route = '\0';
+ }
+ if (worker->redirect) {
+ strcpy(worker->s->redirect, worker->redirect);
+ }
+ else {
+ *worker->s->redirect = '\0';
+ }
+
+ worker->s->status |= (worker->status | PROXY_WORKER_INITIALIZED);
+
+}
+
+PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)
+{
+ apr_status_t rv;
+
+#if APR_HAS_THREADS
+ int mpm_threads;
+#endif
+
+ if (worker->status & PROXY_WORKER_INITIALIZED) {
+ /* The worker is already initialized */
+ return APR_SUCCESS;
+ }
+
+ /* Set default parameters */
+ if (!worker->retry_set) {
+ worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
+ }
+ /* By default address is reusable unless DisableReuse is set */
+ if (worker->disablereuse) {
+ worker->is_address_reusable = 0;
+ }
+ else {
+ worker->is_address_reusable = 1;
+ }
+
+#if APR_HAS_THREADS
+ ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
+ if (mpm_threads > 1) {
+ /* Set hard max to no more then mpm_threads */
+ if (worker->hmax == 0 || worker->hmax > mpm_threads) {
+ worker->hmax = mpm_threads;
+ }
+ if (worker->smax == -1 || worker->smax > worker->hmax) {
+ worker->smax = worker->hmax;
+ }
+ /* Set min to be lower then smax */
+ if (worker->min > worker->smax) {
+ worker->min = worker->smax;
+ }
+ }
+ else {
+ /* This will supress the apr_reslist creation */
+ worker->min = worker->smax = worker->hmax = 0;
+ }
+ if (worker->hmax) {
+ rv = apr_reslist_create(&(worker->cp->res),
+ worker->min, worker->smax,
+ worker->hmax, worker->ttl,
+ connection_constructor, connection_destructor,
+ worker, worker->cp->pool);
+
+ apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
+ conn_pool_cleanup,
+ apr_pool_cleanup_null);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: initialized worker %d in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
+ worker->id, getpid(), worker->hostname, worker->min,
+ worker->hmax, worker->smax);
+
+#if (APR_MAJOR_VERSION > 0)
+ /* Set the acquire timeout */
+ if (rv == APR_SUCCESS && worker->acquire_set) {
+ apr_reslist_timeout_set(worker->cp->res, worker->acquire);
+ }
+#endif
+ }
+ else
+#endif
+ {
+
+ rv = connection_constructor((void **)&(worker->cp->conn), worker, worker->cp->pool);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: initialized single connection worker %d in child %" APR_PID_T_FMT " for (%s)",
+ worker->id, getpid(), worker->hostname);
+ }
+ if (rv == APR_SUCCESS) {
+ worker->status |= (PROXY_WORKER_INITIALIZED);
+ }
+ return rv;
+}
+
+PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
+ proxy_worker *worker,
+ server_rec *s)
+{
+ if (worker->s->status & PROXY_WORKER_IN_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: retrying the worker for (%s)",
+ proxy_function, worker->hostname);
+ if (apr_time_now() > worker->s->error_time + worker->retry) {
+ ++worker->s->retries;
+ worker->s->status &= ~PROXY_WORKER_IN_ERROR;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: worker for (%s) has been marked for retry",
+ proxy_function, worker->hostname);
+ return OK;
+ }
+ else {
+ return DECLINED;
+ }
+ }
+ else {
+ return OK;
+ }
+}
+
+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,
+ "proxy: %s: disabled connection for (%s)",
+ proxy_function, worker->hostname);
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+ }
+#if APR_HAS_THREADS
+ if (worker->hmax && worker->cp->res) {
+ rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
+ }
+ else
+#endif
+ {
+ /* create the new connection if the previous was destroyed */
+ if (!worker->cp->conn) {
+ 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,
+ "proxy: %s: failed to acquire connection for (%s)",
+ proxy_function, worker->hostname);
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: has acquired connection for (%s)",
+ proxy_function, worker->hostname);
+
+ (*conn)->worker = worker;
+ (*conn)->close = 0;
+ (*conn)->close_on_recycle = 0;
+#if APR_HAS_THREADS
+ (*conn)->inreslist = 0;
+#endif
+
+ 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,
+ "proxy: %s: has released connection for (%s)",
+ proxy_function, conn->worker->hostname);
+ 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;
+ apr_status_t uerr = APR_SUCCESS;
+
+ /*
+ * 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 = apr_uri_port_of_scheme(uri->scheme);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: 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.
+ */
+ /* are we connecting directly, or via a proxy? */
+ if (!proxyname) {
+ *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
+ uri->query ? uri->query : "",
+ uri->fragment ? "#" : "",
+ uri->fragment ? uri->fragment : "", NULL);
+ }
+ /*
+ * Make sure that we pick the the correct and valid worker.
+ * If a single keepalive connection triggers different workers,
+ * then we have a problem (we don't select the correct one).
+ * Do an expensive check in this case, where we compare the
+ * the hostnames associated between the two.
+ *
+ * TODO: Handle this much better...
+ */
+ if (!conn->hostname || !worker->is_address_reusable ||
+ worker->disablereuse ||
+ (r->connection->keepalives &&
+ (r->proxyreq == PROXYREQ_PROXY || r->proxyreq == PROXYREQ_REVERSE) &&
+ (strcasecmp(conn->hostname, uri->hostname) != 0) ) ) {
+ if (proxyname) {
+ conn->hostname = apr_pstrdup(conn->pool, proxyname);
+ conn->port = proxyport;
+ }
+ else {
+ conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
+ conn->port = uri->port;
+ }
+ socket_cleanup(conn);
+ err = apr_sockaddr_info_get(&(conn->addr),
+ conn->hostname, APR_UNSPEC,
+ conn->port, 0,
+ conn->pool);
+ }
+ else if (!worker->cp->addr) {
+ if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server,
+ "proxy: lock");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /*
+ * Worker can have the single constant backend adress.
+ * The single DNS lookup is used once per worker.
+ * If dynamic change is needed then set the addr to NULL
+ * inside dynamic config to force the lookup.
+ */
+ err = apr_sockaddr_info_get(&(worker->cp->addr),
+ conn->hostname, APR_UNSPEC,
+ conn->port, 0,
+ worker->cp->pool);
+ conn->addr = worker->cp->addr;
+ if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, uerr, r->server,
+ "proxy: unlock");
+ }
+ }
+ 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);
+ if (ap_is_default_port(server_port, r)) {
+ strcpy(server_portstr,"");
+ }
+ else {
+ apr_snprintf(server_portstr, server_portstr_size, ":%d",
+ server_port);
+ }
+ }
+ /* check if ProxyBlock directive on this host */
+ if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
+ return ap_proxyerror(r, HTTP_FORBIDDEN,
+ "Connect to remote machine blocked");
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+ "proxy: 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)
+static int 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
+static int 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 */
+
+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 connected = 0;
+ int loglevel;
+ apr_sockaddr_t *backend_addr = conn->addr;
+ apr_socket_t *newsock;
+ void *sconf = s->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+
+ if (conn->sock) {
+ if (!(connected = is_socket_connected(conn->sock))) {
+ socket_cleanup(conn);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: backend socket is disconnected.",
+ proxy_function);
+ }
+ }
+ while (backend_addr && !connected) {
+ if ((rv = apr_socket_create(&newsock, backend_addr->family,
+ SOCK_STREAM, APR_PROTO_TCP,
+ conn->scpool)) != APR_SUCCESS) {
+ loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+ ap_log_error(APLOG_MARK, loglevel, rv, s,
+ "proxy: %s: error creating fam %d socket for target %s",
+ proxy_function,
+ backend_addr->family,
+ worker->hostname);
+ /*
+ * this could be an IPv6 address from the DNS but the
+ * local machine won't give us an IPv6 socket; hopefully the
+ * DNS returned an additional address to try
+ */
+ backend_addr = backend_addr->next;
+ continue;
+ }
+ conn->connection = NULL;
+
+#if !defined(TPF) && !defined(BEOS)
+ if (worker->recv_buffer_size > 0 &&
+ (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
+ worker->recv_buffer_size))) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "apr_socket_opt_set(SO_RCVBUF): Failed to set "
+ "ProxyReceiveBufferSize, using default");
+ }
+#endif
+
+ 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,
+ "apr_socket_opt_set(APR_TCP_NODELAY): "
+ "Failed to set");
+ }
+
+ /* Set a timeout for connecting to the backend on the socket */
+ if (worker->conn_timeout_set) {
+ apr_socket_timeout_set(newsock, worker->conn_timeout);
+ }
+ else if (worker->timeout_set == 1) {
+ apr_socket_timeout_set(newsock, worker->timeout);
+ }
+ else if (conf->timeout_set == 1) {
+ apr_socket_timeout_set(newsock, conf->timeout);
+ }
+ else {
+ apr_socket_timeout_set(newsock, s->timeout);
+ }
+ /* Set a keepalive option */
+ if (worker->keepalive) {
+ if ((rv = apr_socket_opt_set(newsock,
+ APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
+ " Keepalive");
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: fam %d socket created to connect to %s",
+ proxy_function, backend_addr->family, worker->hostname);
+
+ /* 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,
+ "proxy: %s: attempt to connect to %pI (%s) failed",
+ proxy_function,
+ backend_addr,
+ worker->hostname);
+ backend_addr = backend_addr->next;
+ continue;
+ }
+
+ /* Set a timeout on the socket */
+ if (worker->timeout_set == 1) {
+ apr_socket_timeout_set(newsock, worker->timeout);
+ }
+ else if (conf->timeout_set == 1) {
+ apr_socket_timeout_set(newsock, conf->timeout);
+ }
+ else {
+ apr_socket_timeout_set(newsock, s->timeout);
+ }
+
+ conn->sock = newsock;
+ connected = 1;
+ }
+ /*
+ * Put the entire worker to error state if
+ * the PROXY_WORKER_IGNORE_ERRORS flag is not set.
+ * Altrough some connections may be alive
+ * no further connections to the worker could be made
+ */
+ if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
+ !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
+ worker->s->status |= PROXY_WORKER_IN_ERROR;
+ worker->s->error_time = apr_time_now();
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+ "ap_proxy_connect_backend disabling worker for (%s)",
+ worker->hostname);
+ }
+ else {
+ worker->s->error_time = 0;
+ worker->s->retries = 0;
+ }
+ return connected ? OK : DECLINED;
+}
+
+PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
+ proxy_conn_rec *conn,
+ conn_rec *c,
+ server_rec *s)
+{
+ apr_sockaddr_t *backend_addr = conn->addr;
+ int rc;
+ apr_interval_time_t current_timeout;
+ apr_bucket_alloc_t *bucket_alloc;
+
+ if (conn->connection) {
+ return OK;
+ }
+
+ bucket_alloc = apr_bucket_alloc_create(conn->scpool);
+ /*
+ * The socket is now open, create a new backend server connection
+ */
+ conn->connection = ap_run_create_connection(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, "proxy: %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_enable(conn->connection)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+ s, "proxy: %s: failed to enable ssl support "
+ "for %pI (%s)", proxy_function,
+ backend_addr, conn->hostname);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ else {
+ /* TODO: See if this will break FTP */
+ ap_proxy_ssl_disable(conn->connection);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: connection complete to %pI (%s)",
+ proxy_function, backend_addr, conn->hostname);
+
+ /*
+ * save the timout 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,
+ "proxy: %s: pre_connection setup failed (%d)",
+ proxy_function, rc);
+ return rc;
+ }
+ apr_socket_timeout_set(conn->sock, current_timeout);
+
+ return OK;
+}
+
+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;
+}
+
+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);
+}
+
+/*
+ * 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.
+ */
+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_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, r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(to, new);
+ }
+ else if (APR_BUCKET_IS_FLUSH(e)) {
+ new = apr_bucket_flush_create(r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(to, new);
+ }
+ else if (APR_BUCKET_IS_EOS(e)) {
+ new = apr_bucket_eos_create(r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(to, new);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "proxy: Unhandled bucket type of type %s in"
+ " ap_proxy_buckets_lifetime_transform", e->type->name);
+ rv = APR_EGENERAL;
+ }
+ }
+ return rv;
+}
+
15 years, 11 months
JBoss Native SVN: r2299 - trunk/build/daemon.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-17 05:31:47 -0500 (Tue, 17 Feb 2009)
New Revision: 2299
Modified:
trunk/build/daemon/buildmdc.bat
Log:
windows tests activated again.
Modified: trunk/build/daemon/buildmdc.bat
===================================================================
--- trunk/build/daemon/buildmdc.bat 2009-02-17 09:58:21 UTC (rev 2298)
+++ trunk/build/daemon/buildmdc.bat 2009-02-17 10:31:47 UTC (rev 2299)
@@ -47,10 +47,10 @@
pushd %BUILD_HOME%\windows
REM build mod_cluster bundles.
-call build.bat sdk x86 mod_cluster -ssl -cache
-call build.bat sdk x86 mod_cluster -cache
-call build.bat sdk x64 mod_cluster -ssl -cache
-call build.bat sdk x64 mod_cluster -cache
+call build.bat sdk x86 mod_cluster -ssl -cache -test
+call build.bat sdk x86 mod_cluster -cache -test
+call build.bat sdk x64 mod_cluster -ssl -cache -test
+call build.bat sdk x64 mod_cluster -cache -test
REM
REM call build.bat sdk i64 mod_cluster -ssl -cache
REM
15 years, 11 months
JBoss Native SVN: r2298 - trunk/build/unix.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-17 04:58:21 -0500 (Tue, 17 Feb 2009)
New Revision: 2298
Modified:
trunk/build/unix/package.list
Log:
Back in dev and upgrade components (MODCLUSTER-55).
Modified: trunk/build/unix/package.list
===================================================================
--- trunk/build/unix/package.list 2009-02-16 12:10:04 UTC (rev 2297)
+++ trunk/build/unix/package.list 2009-02-17 09:58:21 UTC (rev 2298)
@@ -26,5 +26,5 @@
# mod_cluster but using lastest ASF httpd.
mod_cluster|1.0.0.Beta2|2.2.10|ssl:v:0.9.8i|zlib:v:1.2.3|jk:v:1.2.26|iconv:v:1.11|expat:v:1.95.8|cluster:t:trunk
mod_cluster|1.0.0.Beta3|2.2.10|ssl:v:0.9.8i|zlib:v:1.2.3|jk:v:1.2.26|iconv:v:1.11|expat:v:1.95.8|cluster:t:mod_cluster/1.0.0.Beta3
-mod_cluster|1.0.0.dev|2.2.10|ssl:v:0.9.8i|zlib:v:1.2.3|jk:v:1.2.26|iconv:v:1.11|expat:v:1.95.8|cluster:t:trunk
mod_cluster|1.0.0.Beta4|2.2.10|ssl:v:0.9.8i|zlib:v:1.2.3|jk:v:1.2.26|iconv:v:1.11|expat:v:1.95.8|cluster:t:mod_cluster/1.0.0.Beta4
+mod_cluster|1.0.0.dev|2.2.11|ssl:v:0.9.8j|zlib:v:1.2.3|jk:v:1.2.27|iconv:v:1.11|expat:v:1.95.8|cluster:t:trunk
15 years, 11 months
JBoss Native SVN: r2297 - trunk/mod_cluster/native/advertise.
by jbossnative-commits@lists.jboss.org
Author: mladen.turk(a)jboss.com
Date: 2009-02-16 07:10:04 -0500 (Mon, 16 Feb 2009)
New Revision: 2297
Modified:
trunk/mod_cluster/native/advertise/mod_advertise.c
Log:
Bind the socket to local interface before doing join
Modified: trunk/mod_cluster/native/advertise/mod_advertise.c
===================================================================
--- trunk/mod_cluster/native/advertise/mod_advertise.c 2009-02-14 09:36:34 UTC (rev 2296)
+++ trunk/mod_cluster/native/advertise/mod_advertise.c 2009-02-16 12:10:04 UTC (rev 2297)
@@ -86,6 +86,7 @@
static apr_socket_t *ma_listen_socket = NULL;
static apr_sockaddr_t *ma_mgroup_sa = NULL;
static apr_sockaddr_t *ma_listen_sa = NULL;
+static apr_sockaddr_t *ma_niface_sa = NULL;
static server_rec *ma_server_rec = NULL;
@@ -338,9 +339,24 @@
APR_INET, port,
APR_UNSPEC, pool)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
- "mod_advertise: ma_group_join apr_sockaddr_info_get failed");
+ "mod_advertise: ma_group_join apr_sockaddr_info_get(%s:%d) failed",
+ addr, port);
return rv;
}
+ if ((rv = apr_sockaddr_info_get(&ma_listen_sa, NULL,
+ ma_mgroup_sa->family, 0,
+ APR_UNSPEC, pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "mod_advertise: ma_group_join apr_sockaddr_info_get(0.0.0.0:0) failed");
+ return rv;
+ }
+ if ((rv = apr_sockaddr_info_get(&ma_niface_sa, NULL,
+ ma_mgroup_sa->family, 0,
+ APR_UNSPEC, pool)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "mod_advertise: ma_group_join apr_sockaddr_info_get(0.0.0.0:0) failed");
+ return rv;
+ }
if ((rv = apr_socket_create(&ma_mgroup_socket,
ma_mgroup_sa->family,
SOCK_DGRAM,
@@ -350,8 +366,19 @@
"mod_advertise: ma_group_join apr_socket_create failed");
return rv;
}
+ if ((rv = apr_socket_opt_set(ma_mgroup_socket,
+ APR_SO_REUSEADDR, 1)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "mod_advertise: ma_group_join apr_socket_opt_set failed");
+ return rv;
+ }
+ if ((rv = apr_socket_bind(ma_mgroup_socket, ma_listen_sa)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "mod_advertise: ma_group_join apr_socket_bind failed");
+ return rv;
+ }
if ((rv = apr_mcast_join(ma_mgroup_socket, ma_mgroup_sa,
- NULL, NULL)) != APR_SUCCESS) {
+ ma_niface_sa, NULL)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
"mod_advertise: ma_group_join apr_mcast_join failed");
if ((rv = apr_mcast_loopback(ma_mgroup_socket, 1)) != APR_SUCCESS) {
15 years, 11 months
JBoss Native SVN: r2296 - tags/mod_cluster/1.0.0.Beta4/test/java.
by jbossnative-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2009-02-14 04:36:34 -0500 (Sat, 14 Feb 2009)
New Revision: 2296
Modified:
tags/mod_cluster/1.0.0.Beta4/test/java/build.properties.default
Log:
Typo.
Modified: tags/mod_cluster/1.0.0.Beta4/test/java/build.properties.default
===================================================================
--- tags/mod_cluster/1.0.0.Beta4/test/java/build.properties.default 2009-02-14 08:49:52 UTC (rev 2295)
+++ tags/mod_cluster/1.0.0.Beta4/test/java/build.properties.default 2009-02-14 09:36:34 UTC (rev 2296)
@@ -42,4 +42,4 @@
jboss-ejb-api.jar.loc=${base-jboss.loc}/jboss-ejb-api/${jboss-ejb-api.version}/jboss-ejb-api-${jboss-ejb-api.version}.jar
jboss-ejb-api.jar=${base.path}/${jboss-ejb-api.version}/jboss-ejb-api-spi-${jboss-ejb-api.version}.jar
-mod_cluster.jar=../../target/mod-cluster-1.0.0-Beta4.jar
+mod_cluster.jar=../../target/mod-cluster-1.0.0.Beta4.jar
15 years, 11 months