summaryrefslogtreecommitdiffstats
path: root/sca-cpp/branches/lightweight-sca/modules/http
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp/branches/lightweight-sca/modules/http')
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/Makefile.am88
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/alt-host-conf32
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf69
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cache-conf41
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cache-manifest26
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf41
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf74
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types608
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp98
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp53
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp92
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/form-auth-conf82
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/group-auth-conf57
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html32
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html51
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html44
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/http-test34
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/http.hpp1042
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-addr53
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-conf375
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-debug25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf45
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf32
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-restart25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf259
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-start25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-stop25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-test42
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf38
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf45
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp753
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mass-host-conf62
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf68
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/minify-css34
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/minify-html34
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/minify-js34
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp478
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf44
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mod-security-conf184
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp379
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/open-auth-conf100
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp100
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf31
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf50
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf48
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-conf60
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf45
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf67
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf40
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf40
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-test40
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf96
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf76
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find30
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf55
56 files changed, 6526 insertions, 0 deletions
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am
new file mode 100644
index 0000000000..a504adcda5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am
@@ -0,0 +1,88 @@
+# 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.
+
+INCLUDES = -I${HTTPD_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/http
+
+dist_mod_SCRIPTS = httpd-conf httpd-addr httpd-start httpd-stop httpd-restart ssl-ca-conf ssl-cert-conf ssl-cert-find httpd-ssl-conf basic-auth-conf cert-auth-conf form-auth-conf open-auth-conf passwd-auth-conf group-auth-conf cache-conf cache-ssl-conf cache-manifest proxy-conf proxy-base-conf proxy-ssl-conf proxy-balancer-conf proxy-member-conf proxy-ssl-member-conf proxy-ssl-nossl-member-conf alt-host-conf mass-host-conf mass-host-ssl-conf httpd-tunnel-ssl-conf tunnel-ssl-conf httpd-worker-conf httpd-event-conf httpd-loglevel-conf minify-html minify-js minify-css
+moddir = $(prefix)/modules/http
+
+curl_test_SOURCES = curl-test.cpp
+curl_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+curl_get_SOURCES = curl-get.cpp
+curl_get_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+curl_connect_SOURCES = curl-connect.cpp
+curl_connect_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+mod_LTLIBRARIES = libmod_tuscany_ssltunnel.la libmod_tuscany_openauth.la
+noinst_DATA = libmod_tuscany_ssltunnel${libsuffix} libmod_tuscany_openauth${libsuffix}
+
+libmod_tuscany_ssltunnel_la_SOURCES = mod-ssltunnel.cpp
+libmod_tuscany_ssltunnel_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+libmod_tuscany_ssltunnel${libsuffix}:
+ ln -s .libs/libmod_tuscany_ssltunnel${libsuffix}
+
+libmod_tuscany_openauth_la_SOURCES = mod-openauth.cpp
+libmod_tuscany_openauth_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+libmod_tuscany_openauth${libsuffix}:
+ ln -s .libs/libmod_tuscany_openauth${libsuffix}
+
+mod_DATA = httpd.prefix httpd-apachectl.prefix httpd-modules.prefix curl.prefix pagespeed.prefix
+nobase_dist_mod_DATA = conf/*
+
+EXTRA_DIST = htdocs/index.html htdocs/login/index.html htdocs/logout/index.html
+
+httpd.prefix: $(top_builddir)/config.status
+ echo ${HTTPD_PREFIX} >httpd.prefix
+httpd-apachectl.prefix: $(top_builddir)/config.status
+ echo ${HTTPD_APACHECTL_PREFIX} >httpd-apachectl.prefix
+httpd-modules.prefix: $(top_builddir)/config.status
+ echo ${HTTPD_MODULES_PREFIX} >httpd-modules.prefix
+curl.prefix: $(top_builddir)/config.status
+ echo ${CURL_PREFIX} >curl.prefix
+
+if WANT_PAGESPEED
+
+pagespeed.prefix: $(top_builddir)/config.status
+ echo ${PAGESPEED_PREFIX} >pagespeed.prefix
+
+else
+
+pagespeed.prefix: $(top_builddir)/config.status
+ echo "" >pagespeed.prefix
+
+endif
+
+if WANT_MODSECURITY
+
+modsecurity.prefix: $(top_builddir)/config.status
+ echo ${MODSECURITY_PREFIX} >modsecurity.prefix
+
+dist_modsecurity_SCRIPTS = mod-security-conf mod-security-audit-conf
+modsecurity_DATA = modsecurity.prefix
+modsecuritydir = $(prefix)/modules/http
+
+endif
+
+dist_noinst_SCRIPTS = httpd-test http-test proxy-test
+noinst_PROGRAMS = curl-test curl-get curl-connect
+TESTS = httpd-test http-test proxy-test
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf b/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf
new file mode 100755
index 0000000000..f6148173b4
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# 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.
+
+# Generate an alternate host name configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+
+cat >>$root/conf/hostcond.conf <<EOF
+# Generated by: alt-host-conf $*
+RewriteCond %{HTTP_HOST} !^$host [NC]
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf
new file mode 100755
index 0000000000..8710d1fdf7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD basic authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+if [ "$3" = "" ]; then
+ loc="/"
+else
+ loc="$3"
+fi
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: basic-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate basic authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: basic-auth-conf $*
+# Require clients to present a userid + password for HTTP
+# basic authentication
+<Location $loc>
+AuthType Basic
+AuthName "$host"
+AuthBasicProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+Require valid-user
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-conf b/sca-cpp/branches/lightweight-sca/modules/http/cache-conf
new file mode 100755
index 0000000000..09ad5dc444
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-conf
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD cache configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+mkdir -p $root/cache
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: cache-conf $*
+
+# Enable caching
+CacheEnable disk /
+CacheQuickHandler Off
+CacheRoot $root/cache
+CacheHeader On
+#CacheDetailHeader On
+CacheIgnoreQueryString On
+CacheIgnoreCacheControl On
+CacheIgnoreHeaders Set-Cookie
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest b/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest
new file mode 100755
index 0000000000..3752a61dde
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a cache-manifest.cmf file from a template and the list
+# of files in the cached application
+root=$1
+shift
+sha=`cat $* | shasum | awk '{ print $1 }'`
+cat $root/cache-template.cmf | sed -e "s/SHA1/$sha/" >$root/cache-manifest.cmf
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf
new file mode 100755
index 0000000000..7a902c37eb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD cache configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+mkdir -p $root/cache
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: cache-conf $*
+
+# Enable caching
+CacheEnable disk /
+CacheQuickHandler Off
+CacheRoot $root/cache
+CacheHeader On
+#CacheDetailHeader On
+CacheIgnoreQueryString On
+CacheIgnoreCacheControl On
+CacheIgnoreHeaders Set-Cookie
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf
new file mode 100755
index 0000000000..a30fdfff8c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD certificate-based authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: cert-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: cert-auth-conf $*
+# Require clients to present a valid client certificate
+SSLVerifyClient require
+SSLVerifyDepth 1
+
+<Location />
+AuthType Basic
+AuthName "$host"
+AuthBasicProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+Require valid-user
+</Location>
+
+EOF
+
+# Create password file and certificate-based users
+cat >>$root/conf/httpd.passwd <<EOF
+/C=US/ST=CA/L=San Francisco/O=$host/OU=server/CN=$host:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=$host/OU=proxy/CN=$host:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=$host/OU=tunnel/CN=$host:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=localhost/OU=server/CN=localhost:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=localhost/OU=tunnel/CN=localhost:xxj31ZMTZzkVA
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types b/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types
new file mode 100644
index 0000000000..3f083f9a32
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types
@@ -0,0 +1,608 @@
+# 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.
+
+# This file controls what Internet media types are sent to the client for
+# given file extension(s). Sending the correct media type to the client
+# is important so they know how to handle the content of the file.
+# Extra types can either be added here or by using an AddType directive
+# in your config files. For more information about Internet media types,
+# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type
+# registry is at <http://www.iana.org/assignments/media-types/>.
+
+# MIME type Extensions
+application/activemessage
+application/andrew-inset ez
+application/applefile
+application/atom+xml atom
+application/atomicmail
+application/batch-smtp
+application/beep+xml
+application/cals-1840
+application/cnrp+xml
+application/commonground
+application/cpl+xml
+application/cybercash
+application/dca-rft
+application/dec-dx
+application/dvcs
+application/edi-consent
+application/edifact
+application/edi-x12
+application/eshop
+application/font-tdpfr
+application/http
+application/hyperstudio
+application/iges
+application/index
+application/index.cmd
+application/index.obj
+application/index.response
+application/index.vnd
+application/iotp
+application/ipp
+application/isup
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+application/macwriteii
+application/marc
+application/mathematica
+application/mathml+xml mathml
+application/msword doc
+application/news-message-id
+application/news-transmission
+application/ocsp-request
+application/ocsp-response
+application/octet-stream bin dms lha lzh exe class so dll dmg
+application/oda oda
+application/ogg ogg
+application/parityfec
+application/pdf pdf
+application/pgp-encrypted
+application/pgp-keys
+application/pgp-signature
+application/pkcs10
+application/pkcs7-mime
+application/pkcs7-signature
+application/pkix-cert
+application/pkix-crl
+application/pkixcmp
+application/postscript ai eps ps
+application/prs.alvestrand.titrax-sheet
+application/prs.cww
+application/prs.nprend
+application/prs.plucker
+application/qsig
+application/rdf+xml rdf
+application/reginfo+xml
+application/remote-printing
+application/riscos
+application/rtf
+application/sdp
+application/set-payment
+application/set-payment-initiation
+application/set-registration
+application/set-registration-initiation
+application/sgml
+application/sgml-open-catalog
+application/sieve
+application/slate
+application/smil smi smil
+application/srgs gram
+application/srgs+xml grxml
+application/timestamp-query
+application/timestamp-reply
+application/tve-trigger
+application/vemmi
+application/vnd.3gpp.pic-bw-large
+application/vnd.3gpp.pic-bw-small
+application/vnd.3gpp.pic-bw-var
+application/vnd.3gpp.sms
+application/vnd.3m.post-it-notes
+application/vnd.accpac.simply.aso
+application/vnd.accpac.simply.imp
+application/vnd.acucobol
+application/vnd.acucorp
+application/vnd.adobe.xfdf
+application/vnd.aether.imp
+application/vnd.amiga.ami
+application/vnd.anser-web-certificate-issue-initiation
+application/vnd.anser-web-funds-transfer-initiation
+application/vnd.audiograph
+application/vnd.blueice.multipass
+application/vnd.bmi
+application/vnd.businessobjects
+application/vnd.canon-cpdl
+application/vnd.canon-lips
+application/vnd.cinderella
+application/vnd.claymore
+application/vnd.commerce-battelle
+application/vnd.commonspace
+application/vnd.contact.cmsg
+application/vnd.cosmocaller
+application/vnd.criticaltools.wbs+xml
+application/vnd.ctc-posml
+application/vnd.cups-postscript
+application/vnd.cups-raster
+application/vnd.cups-raw
+application/vnd.curl
+application/vnd.cybank
+application/vnd.data-vision.rdz
+application/vnd.dna
+application/vnd.dpgraph
+application/vnd.dreamfactory
+application/vnd.dxr
+application/vnd.ecdis-update
+application/vnd.ecowin.chart
+application/vnd.ecowin.filerequest
+application/vnd.ecowin.fileupdate
+application/vnd.ecowin.series
+application/vnd.ecowin.seriesrequest
+application/vnd.ecowin.seriesupdate
+application/vnd.enliven
+application/vnd.epson.esf
+application/vnd.epson.msf
+application/vnd.epson.quickanime
+application/vnd.epson.salt
+application/vnd.epson.ssf
+application/vnd.ericsson.quickcall
+application/vnd.eudora.data
+application/vnd.fdf
+application/vnd.ffsns
+application/vnd.fints
+application/vnd.flographit
+application/vnd.framemaker
+application/vnd.fsc.weblaunch
+application/vnd.fujitsu.oasys
+application/vnd.fujitsu.oasys2
+application/vnd.fujitsu.oasys3
+application/vnd.fujitsu.oasysgp
+application/vnd.fujitsu.oasysprs
+application/vnd.fujixerox.ddd
+application/vnd.fujixerox.docuworks
+application/vnd.fujixerox.docuworks.binder
+application/vnd.fut-misnet
+application/vnd.grafeq
+application/vnd.groove-account
+application/vnd.groove-help
+application/vnd.groove-identity-message
+application/vnd.groove-injector
+application/vnd.groove-tool-message
+application/vnd.groove-tool-template
+application/vnd.groove-vcard
+application/vnd.hbci
+application/vnd.hhe.lesson-player
+application/vnd.hp-hpgl
+application/vnd.hp-hpid
+application/vnd.hp-hps
+application/vnd.hp-pcl
+application/vnd.hp-pclxl
+application/vnd.httphone
+application/vnd.hzn-3d-crossword
+application/vnd.ibm.afplinedata
+application/vnd.ibm.electronic-media
+application/vnd.ibm.minipay
+application/vnd.ibm.modcap
+application/vnd.ibm.rights-management
+application/vnd.ibm.secure-container
+application/vnd.informix-visionary
+application/vnd.intercon.formnet
+application/vnd.intertrust.digibox
+application/vnd.intertrust.nncp
+application/vnd.intu.qbo
+application/vnd.intu.qfx
+application/vnd.irepository.package+xml
+application/vnd.is-xpr
+application/vnd.japannet-directory-service
+application/vnd.japannet-jpnstore-wakeup
+application/vnd.japannet-payment-wakeup
+application/vnd.japannet-registration
+application/vnd.japannet-registration-wakeup
+application/vnd.japannet-setstore-wakeup
+application/vnd.japannet-verification
+application/vnd.japannet-verification-wakeup
+application/vnd.jisp
+application/vnd.kde.karbon
+application/vnd.kde.kchart
+application/vnd.kde.kformula
+application/vnd.kde.kivio
+application/vnd.kde.kontour
+application/vnd.kde.kpresenter
+application/vnd.kde.kspread
+application/vnd.kde.kword
+application/vnd.kenameaapp
+application/vnd.koan
+application/vnd.liberty-request+xml
+application/vnd.llamagraphics.life-balance.desktop
+application/vnd.llamagraphics.life-balance.exchange+xml
+application/vnd.lotus-1-2-3
+application/vnd.lotus-approach
+application/vnd.lotus-freelance
+application/vnd.lotus-notes
+application/vnd.lotus-organizer
+application/vnd.lotus-screencam
+application/vnd.lotus-wordpro
+application/vnd.mcd
+application/vnd.mediastation.cdkey
+application/vnd.meridian-slingshot
+application/vnd.micrografx.flo
+application/vnd.micrografx.igx
+application/vnd.mif mif
+application/vnd.minisoft-hp3000-save
+application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf
+application/vnd.mobius.dis
+application/vnd.mobius.mbk
+application/vnd.mobius.mqy
+application/vnd.mobius.msl
+application/vnd.mobius.plc
+application/vnd.mobius.txf
+application/vnd.mophun.application
+application/vnd.mophun.certificate
+application/vnd.motorola.flexsuite
+application/vnd.motorola.flexsuite.adsi
+application/vnd.motorola.flexsuite.fis
+application/vnd.motorola.flexsuite.gotap
+application/vnd.motorola.flexsuite.kmr
+application/vnd.motorola.flexsuite.ttc
+application/vnd.motorola.flexsuite.wem
+application/vnd.mozilla.xul+xml xul
+application/vnd.ms-artgalry
+application/vnd.ms-asf
+application/vnd.ms-excel xls
+application/vnd.ms-lrm
+application/vnd.ms-powerpoint ppt
+application/vnd.ms-project
+application/vnd.ms-tnef
+application/vnd.ms-works
+application/vnd.ms-wpl
+application/vnd.mseq
+application/vnd.msign
+application/vnd.music-niff
+application/vnd.musician
+application/vnd.netfpx
+application/vnd.noblenet-directory
+application/vnd.noblenet-sealer
+application/vnd.noblenet-web
+application/vnd.novadigm.edm
+application/vnd.novadigm.edx
+application/vnd.novadigm.ext
+application/vnd.obn
+application/vnd.osa.netdeploy
+application/vnd.palm
+application/vnd.pg.format
+application/vnd.pg.osasli
+application/vnd.powerbuilder6
+application/vnd.powerbuilder6-s
+application/vnd.powerbuilder7
+application/vnd.powerbuilder7-s
+application/vnd.powerbuilder75
+application/vnd.powerbuilder75-s
+application/vnd.previewsystems.box
+application/vnd.publishare-delta-tree
+application/vnd.pvi.ptid1
+application/vnd.pwg-multiplexed
+application/vnd.pwg-xhtml-print+xml
+application/vnd.quark.quarkxpress
+application/vnd.rapid
+application/vnd.s3sms
+application/vnd.sealed.net
+application/vnd.seemail
+application/vnd.shana.informed.formdata
+application/vnd.shana.informed.formtemplate
+application/vnd.shana.informed.interchange
+application/vnd.shana.informed.package
+application/vnd.smaf
+application/vnd.sss-cod
+application/vnd.sss-dtf
+application/vnd.sss-ntf
+application/vnd.street-stream
+application/vnd.svd
+application/vnd.swiftview-ics
+application/vnd.triscape.mxs
+application/vnd.trueapp
+application/vnd.truedoc
+application/vnd.ufdl
+application/vnd.uplanet.alert
+application/vnd.uplanet.alert-wbxml
+application/vnd.uplanet.bearer-choice
+application/vnd.uplanet.bearer-choice-wbxml
+application/vnd.uplanet.cacheop
+application/vnd.uplanet.cacheop-wbxml
+application/vnd.uplanet.channel
+application/vnd.uplanet.channel-wbxml
+application/vnd.uplanet.list
+application/vnd.uplanet.list-wbxml
+application/vnd.uplanet.listcmd
+application/vnd.uplanet.listcmd-wbxml
+application/vnd.uplanet.signal
+application/vnd.vcx
+application/vnd.vectorworks
+application/vnd.vidsoft.vidconference
+application/vnd.visio
+application/vnd.visionary
+application/vnd.vividence.scriptfile
+application/vnd.vsf
+application/vnd.wap.sic
+application/vnd.wap.slc
+application/vnd.wap.wbxml wbxml
+application/vnd.wap.wmlc wmlc
+application/vnd.wap.wmlscriptc wmlsc
+application/vnd.webturbo
+application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf
+application/vnd.wv.csp+wbxml
+application/vnd.xara
+application/vnd.xfdl
+application/vnd.yamaha.hv-dic
+application/vnd.yamaha.hv-script
+application/vnd.yamaha.hv-voice
+application/vnd.yellowriver-custom-menu
+application/voicexml+xml vxml
+application/watcherinfo+xml
+application/whoispp-query
+application/whoispp-response
+application/wita
+application/wordperfect5.1
+application/x-bcpio bcpio
+application/x-cdlink vcd
+application/x-chess-pgn pgn
+application/x-compress
+application/x-cpio cpio
+application/x-csh csh
+application/x-director dcr dir dxr
+application/x-dvi dvi
+application/x-futuresplash spl
+application/x-gtar gtar
+application/x-gzip
+application/x-hdf hdf
+application/x-javascript js
+application/x-koan skp skd skt skm
+application/x-latex latex
+application/x-netcdf nc cdf
+application/x-sh sh
+application/x-shar shar
+application/x-shockwave-flash swf
+application/x-stuffit sit
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-tar tar
+application/x-tcl tcl
+application/x-tex tex
+application/x-texinfo texinfo texi
+application/x-troff t tr roff
+application/x-troff-man man
+application/x-troff-me me
+application/x-troff-ms ms
+application/x-ustar ustar
+application/x-wais-source src
+application/x400-bp
+application/xhtml+xml xhtml xht
+application/xslt+xml xslt
+application/xml xml xsl
+application/xml-dtd dtd
+application/xml-external-parsed-entity
+application/zip zip
+audio/32kadpcm
+audio/amr
+audio/amr-wb
+audio/basic au snd
+audio/cn
+audio/dat12
+audio/dsr-es201108
+audio/dvi4
+audio/evrc
+audio/evrc0
+audio/g722
+audio/g.722.1
+audio/g723
+audio/g726-16
+audio/g726-24
+audio/g726-32
+audio/g726-40
+audio/g728
+audio/g729
+audio/g729D
+audio/g729E
+audio/gsm
+audio/gsm-efr
+audio/l8
+audio/l16
+audio/l20
+audio/l24
+audio/lpc
+audio/midi mid midi kar
+audio/mpa
+audio/mpa-robust
+audio/mp4a-latm
+audio/mpeg mpga mp2 mp3
+audio/parityfec
+audio/pcma
+audio/pcmu
+audio/prs.sid
+audio/qcelp
+audio/red
+audio/smv
+audio/smv0
+audio/telephone-event
+audio/tone
+audio/vdvi
+audio/vnd.3gpp.iufp
+audio/vnd.cisco.nse
+audio/vnd.cns.anp1
+audio/vnd.cns.inf1
+audio/vnd.digital-winds
+audio/vnd.everad.plj
+audio/vnd.lucent.voice
+audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800
+audio/vnd.nuera.ecelp7470
+audio/vnd.nuera.ecelp9600
+audio/vnd.octel.sbc
+audio/vnd.qcelp
+audio/vnd.rhetorex.32kadpcm
+audio/vnd.vmx.cvsd
+audio/x-aiff aif aiff aifc
+audio/x-alaw-basic
+audio/x-mpegurl m3u
+audio/x-pn-realaudio ram ra
+audio/x-pn-realaudio-plugin
+application/vnd.rn-realmedia rm
+audio/x-wav wav
+chemical/x-pdb pdb
+chemical/x-xyz xyz
+image/bmp bmp
+image/cgm cgm
+image/g3fax
+image/gif gif
+image/ief ief
+image/jpeg jpeg jpg jpe
+image/naplps
+image/png png b64
+image/prs.btif
+image/prs.pti
+image/svg+xml svg
+image/t38
+image/tiff tiff tif
+image/tiff-fx
+image/vnd.cns.inf2
+image/vnd.djvu djvu djv
+image/vnd.dwg
+image/vnd.dxf
+image/vnd.fastbidsheet
+image/vnd.fpx
+image/vnd.fst
+image/vnd.fujixerox.edmics-mmr
+image/vnd.fujixerox.edmics-rlc
+image/vnd.globalgraphics.pgb
+image/vnd.mix
+image/vnd.ms-modi
+image/vnd.net-fpx
+image/vnd.svf
+image/vnd.wap.wbmp wbmp
+image/vnd.xiff
+image/x-cmu-raster ras
+image/x-icon ico
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+message/delivery-status
+message/disposition-notification
+message/external-body
+message/http
+message/news
+message/partial
+message/rfc822
+message/s-http
+message/sip
+message/sipfrag
+model/iges igs iges
+model/mesh msh mesh silo
+model/vnd.dwf
+model/vnd.flatland.3dml
+model/vnd.gdl
+model/vnd.gs-gdl
+model/vnd.gtw
+model/vnd.mts
+model/vnd.parasolid.transmit.binary
+model/vnd.parasolid.transmit.text
+model/vnd.vtu
+model/vrml wrl vrml
+multipart/alternative
+multipart/appledouble
+multipart/byteranges
+multipart/digest
+multipart/encrypted
+multipart/form-data
+multipart/header-set
+multipart/mixed
+multipart/parallel
+multipart/related
+multipart/report
+multipart/signed
+multipart/voice-message
+text/cache-manifest cmf
+text/calendar ics ifb
+text/css css
+text/directory
+text/enriched
+text/html html htm
+text/parityfec
+text/plain asc txt
+text/prs.lines.tag
+text/rfc822-headers
+text/richtext rtx
+text/rtf rtf
+text/sgml sgml sgm
+text/t140
+text/tab-separated-values tsv
+text/uri-list
+text/vnd.abc
+text/vnd.curl
+text/vnd.dmclientscript
+text/vnd.fly
+text/vnd.fmi.flexstor
+text/vnd.in3d.3dml
+text/vnd.in3d.spot
+text/vnd.iptc.nitf
+text/vnd.iptc.newsml
+text/vnd.latex-z
+text/vnd.motorola.reflex
+text/vnd.ms-mediapackage
+text/vnd.net2phone.commcenter.command
+text/vnd.sun.j2me.app-descriptor
+text/vnd.wap.si
+text/vnd.wap.sl
+text/vnd.wap.wml wml
+text/vnd.wap.wmlscript wmls
+text/x-setext etx
+text/xml
+text/xml-external-parsed-entity
+video/bmpeg
+video/bt656
+video/celb
+video/dv
+video/h261
+video/h263
+video/h263-1998
+video/h263-2000
+video/jpeg
+video/mp1s
+video/mp2p
+video/mp2t
+video/mp4v-es
+video/mpv
+video/mpeg mpeg mpg mpe
+video/nv
+video/parityfec
+video/pointer
+video/quicktime qt mov
+video/smpte292m
+video/vnd.fvt
+video/vnd.motorola.video
+video/vnd.motorola.videop
+video/vnd.mpegurl mxu m4u
+video/vnd.nokia.interleaved-multimedia
+video/vnd.objectvideo
+video/vnd.vivo
+video/x-msvideo avi
+video/x-sgi-movie movie
+x-conference/x-cooltalk ice
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp
new file mode 100644
index 0000000000..9f5ee17368
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTP connect command line test tool.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "http.hpp"
+
+namespace tuscany {
+namespace http {
+
+const bool testConnect(const string& url, const string& ca = "", const string& cert = "", const string& key = "") {
+ gc_scoped_pool p;
+
+ CURLSession cs(ca, cert, key, "", 0);
+ const failable<bool> crc = connect(url, cs);
+ assert(hasContent(crc));
+
+ apr_pollset_t* pollset;
+ apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0);
+ assert(cprc == APR_SUCCESS);
+ apr_socket_t* csock = sock(0, p);
+ const apr_pollfd_t* cpollfd = pollfd(csock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p);
+ apr_pollset_add(pollset, cpollfd);
+ apr_socket_t* tsock = sock(cs);
+ const apr_pollfd_t* tpollfd = pollfd(tsock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p);
+ apr_pollset_add(pollset, tpollfd);
+
+ const apr_pollfd_t* pollfds;
+ apr_int32_t pollcount;
+ for(;;) {
+ apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds);
+ assert(pollrc == APR_SUCCESS);
+
+ for (; pollcount > 0; pollcount--, pollfds++) {
+ if (pollfds->rtnevents & APR_POLLIN) {
+ char data[8192];
+ if (pollfds->desc.s == csock) {
+ const size_t rl = ::read(0, data, sizeof(data));
+ if (rl == (size_t)-1)
+ return false;
+ if (rl > 0) {
+ const failable<bool> src = http::send(data, rl, cs);
+ assert(hasContent(src));
+ }
+ }
+ else {
+ const failable<size_t> frl = http::recv(data, sizeof(data), cs);
+ assert(hasContent(frl));
+ const size_t rl = content(frl);
+ if (rl == 0)
+ return true;
+ const size_t wl = ::write(0, data, rl);
+ assert(wl == rl);
+ }
+ continue;
+ }
+ assert(!(pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)));
+ }
+ }
+ return true;
+}
+
+}
+}
+
+int main(unused const int argc, const char** argv) {
+ if (argc > 2)
+ tuscany::http::testConnect(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4]));
+ else
+ tuscany::http::testConnect(tuscany::string(argv[1]));
+ return 0;
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp
new file mode 100644
index 0000000000..a16daeeae3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTP GET command line test tool.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "http.hpp"
+
+namespace tuscany {
+namespace http {
+
+const bool testGet(const string& url, const string& ca = "", const string& cert = "", const string& key = "") {
+ CURLSession ch(ca, cert, key, "", 0);
+ const failable<value> val = get(url, ch);
+ assert(hasContent(val));
+ cout << content(val) << endl;
+ return true;
+}
+
+}
+}
+
+int main(unused const int argc, const char** argv) {
+ if (argc > 2)
+ tuscany::http::testGet(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4]));
+ else
+ tuscany::http::testGet(tuscany::string(argv[1]));
+ return 0;
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp
new file mode 100644
index 0000000000..f0806ea577
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test HTTP client functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "http.hpp"
+
+namespace tuscany {
+namespace http {
+
+string testURI = "http://localhost:8090";
+
+ostream* curlWriter(const string& s, ostream* os) {
+ (*os) << s;
+ return os;
+}
+
+const bool testGet() {
+ CURLSession ch("", "", "", "", 0);
+ {
+ ostringstream os;
+ const failable<list<ostream*> > r = get<ostream*>(curlWriter, &os, testURI, ch);
+ assert(hasContent(r));
+ assert(contains(str(os), "HTTP/1.1 200 OK"));
+ assert(contains(str(os), "It works"));
+ }
+ {
+ const failable<value> r = getcontent(testURI, ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ }
+ return true;
+}
+
+struct getLoop {
+ CURLSession& ch;
+ getLoop(CURLSession& ch) : ch(ch) {
+ }
+ const bool operator()() const {
+ const failable<value> r = getcontent(testURI, ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ return true;
+ }
+};
+
+const bool testGetPerf() {
+ CURLSession ch("", "", "", "", 0);
+ lambda<bool()> gl = getLoop(ch);
+ cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::gc_scoped_pool p;
+ tuscany::cout << "Testing..." << tuscany::endl;
+ //tuscany::http::testURI = tuscany::string("http://") + tuscany::http::hostName() + ":8090";
+
+ tuscany::http::testGet();
+ tuscany::http::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf
new file mode 100755
index 0000000000..fbe943f3d9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD form authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+if [ "$3" = "" ]; then
+ pw=`cat $root/cert/ca.key | head -2 | tail -1`
+else
+ pw="$3"
+fi
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: form-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate form authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: form-auth-conf $*
+# Require clients to present a userid + password through form-based
+# authentication
+<Location />
+AuthType Form
+AuthName "$host"
+AuthFormProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+AuthFormLoginRequiredLocation /login/
+AuthFormLogoutLocation /
+Session On
+SessionCookieName TuscanyFormAuth domain=.$host; path=/
+SessionCryptoPassphrase $pw
+Require valid-user
+</Location>
+
+<Location /login/dologin>
+SetHandler form-login-handler
+</Location>
+
+<Location /logout/dologout>
+SetHandler form-logout-handler
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf
new file mode 100755
index 0000000000..e9617f696a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# 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.
+
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+user=$2
+group="members"
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: group-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Add user to group
+cat $root/conf/httpd.groups | awk " BEGIN { found = 0 } /$group: / { printf \"%s %s\n\", \$0, \"$user\"; found = 1 } !/$group: / { printf \"%s\n\", \$0 } END { if (found == 0) printf \"%s: %s\n\", \"$group\", \"$user\" } " >$root/conf/.httpd.groups.tmp 2>/dev/null
+cp $root/conf/.httpd.groups.tmp $root/conf/httpd.groups
+rm $root/conf/.httpd.groups.tmp
+
+# Generate HTTPD group authorization configuration
+conf=`cat $root/conf/locauth$sslsuffix.conf | grep "Generated by: group-auth-conf"`
+if [ "$conf" = "" ]; then
+ cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: group-auth-conf $1
+# Allow group member access to root location
+<Location />
+Require group members
+</Location>
+
+EOF
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html
new file mode 100644
index 0000000000..236864edfb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html
@@ -0,0 +1,32 @@
+<!--
+ 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.
+-->
+
+<html>
+<head>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
+<title>It works</title>
+</head>
+<body>
+<h1>It works!</h1>
+</body>
+</html>
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html
new file mode 100644
index 0000000000..fd3bc21889
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html
@@ -0,0 +1,51 @@
+<!--
+ 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.
+-->
+
+<html>
+<head>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
+<script type="text/javascript" src="/all-min.js"></script>
+<title>Sign in</title>
+</head>
+<body>
+<h1>Sign in</h1>
+<br/>
+
+<script type="text/javascript">
+function submitFormSignin() {
+ clearauthcookie();
+ document.formSignin.httpd_location.value = '/';
+ document.formSignin.submit();
+}
+</script>
+
+<form name="formSignin" method="POST" action="/login/dologin">
+<table border="0">
+<tr><td>Username:</td><td><input type="text" name="httpd_username" value=""/></td></tr>
+<tr><td>Password:</td><td><input type="password" name="httpd_password" value=""/></td></tr>
+<tr><td><input type="button" onclick="submitFormSignin()" value="Sign in"/></td><td></td></tr>
+</table>
+<input type="hidden" name="httpd_location" value="/"/>
+</form>
+
+</body>
+</html>
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html
new file mode 100644
index 0000000000..218dd5d52c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html
@@ -0,0 +1,44 @@
+<!--
+ 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.
+-->
+
+<html>
+<body>
+<head>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
+<script type="text/javascript" src="/all-min.js"></script>
+<title>Sign out</title>
+</head>
+<h1>Sign out</h1>
+<br/>
+
+<form name="signout" action="/login" method="GET">
+<script type="text/javascript">
+function submitSignout() {
+ clearauthcookie();
+ document.signout.submit();
+ return true;
+}
+</script>
+<input type="button" onclick="submitSignout()" value="Sign out"/>
+</form>
+</body>
+</html>
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/http-test b/sca-cpp/branches/lightweight-sca/modules/http/http-test
new file mode 100755
index 0000000000..956b13a516
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/http-test
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# 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.
+
+# Setup
+rm -rf tmp
+./httpd-conf tmp localhost 8090 htdocs
+./httpd-event-conf tmp
+./httpd-start tmp
+sleep 2
+
+# Test
+./curl-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/http.hpp b/sca-cpp/branches/lightweight-sca/modules/http/http.hpp
new file mode 100644
index 0000000000..408b9fdee5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/http.hpp
@@ -0,0 +1,1042 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_http_hpp
+#define tuscany_http_hpp
+
+/**
+ * CURL HTTP client functions.
+ */
+
+#include <unistd.h>
+#include <curl/curl.h>
+#include <curl/easy.h>
+#include <apr.h>
+#include <apr_lib.h>
+#include <apr_network_io.h>
+#include <apr_portable.h>
+#include <apr_poll.h>
+#include <apr_uri.h>
+
+#include "string.hpp"
+#include "gc.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../scheme/io.hpp"
+#include "../atom/atom.hpp"
+#include "../rss/rss.hpp"
+#include "../json/json.hpp"
+
+namespace tuscany {
+namespace http {
+
+/**
+ * Enable CURL verbose debug log.
+ */
+//#define WANT_MAINTAINER_CURL_VERBOSE
+
+/**
+ * CURL library runtime, one per process.
+ */
+class CURLRuntime {
+public:
+ CURLRuntime() {
+ curl_global_init(CURL_GLOBAL_ALL);
+ }
+} curlRuntime;
+
+/**
+ * Represents a CURL session handle.
+ */
+class CURLSession {
+public:
+ CURLSession() : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(false), ca(""), cert(""), key(""), cookie(""), timeout(0) {
+ }
+
+ CURLSession(const string& ca, const string& cert, const string& key, const string& cookie, const int timeout) : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(true), ca(ca), cert(cert), key(key), cookie(cookie), timeout(timeout) {
+ }
+
+ CURLSession(const CURLSession& c) : h(c.h), p(c.p), sock(c.sock), wpollset(c.wpollset), wpollfd(c.wpollfd), rpollset(c.rpollset), rpollfd(c.rpollfd), owner(false), ca(c.ca), cert(c.cert), key(c.key), cookie(c.cookie), timeout(c.timeout) {
+ }
+
+ const CURLSession& operator=(const CURLSession& c) {
+ if(this == &c)
+ return *this;
+ h = c.h;
+ p = c.p;
+ sock = c.sock;
+ wpollset = c.wpollset;
+ wpollfd = c.wpollfd;
+ rpollset = c.rpollset;
+ rpollfd = c.rpollfd;
+ owner = false;
+ ca = c.ca;
+ cert = c.cert;
+ key = c.key;
+ cookie = c.cookie;
+ timeout = c.timeout;
+ return *this;
+ }
+
+ ~CURLSession() {
+ if (!owner)
+ return;
+ if (h == NULL)
+ return;
+ curl_easy_cleanup(h);
+ }
+
+private:
+ CURL* h;
+ gc_child_pool p;
+ apr_socket_t* sock;
+ apr_pollset_t* wpollset;
+ apr_pollfd_t* wpollfd;
+ apr_pollset_t* rpollset;
+ apr_pollfd_t* rpollfd;
+ bool owner;
+
+ friend CURL* handle(const CURLSession& cs);
+ friend apr_socket_t* sock(const CURLSession& cs);
+ friend const failable<CURL*> setup(const string& url, CURLSession& cs);
+ friend const failable<bool> cleanup(CURLSession& cs);
+ friend const failable<bool> connect(const string& url, CURLSession& cs);
+ friend const failable<bool> send(const char* c, const size_t l, CURLSession& cs);
+ friend const failable<size_t> recv(char* c, const size_t l, CURLSession& cs);
+
+public:
+ string ca;
+ string cert;
+ string key;
+ string cookie;
+ int timeout;
+};
+
+/**
+ * Returns the CURL handle used by a CURL session.
+ */
+CURL* handle(const CURLSession& cs) {
+ return cs.h;
+}
+
+/**
+ * Return an apr_socket_t for the socket used by a CURL session.
+ */
+apr_socket_t* sock(const CURLSession& cs) {
+ return cs.sock;
+}
+
+/**
+ * Convert a socket fd to an apr_socket_t.
+ */
+apr_socket_t* sock(const int sd, const gc_pool& p) {
+ int fd = sd;
+ apr_socket_t* s = NULL;
+ apr_os_sock_put(&s, &fd, pool(p));
+ return s;
+}
+
+/**
+ * Convert a CURL return code to an error string.
+ */
+const string curlreason(CURLcode rc) {
+ return curl_easy_strerror(rc);
+}
+
+/**
+ * Convert an APR status to an error string.
+ */
+const string apreason(apr_status_t rc) {
+ char buf[256];
+ return apr_strerror(rc, buf, sizeof(buf));
+}
+
+/**
+ * Escape a URI or a query argument.
+ */
+const char escape_c2x[] = "0123456789ABCDEF";
+
+const string escape(const string& unesc, const char* reserv) {
+ char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(unesc) + 3);
+ const unsigned char* s = (const unsigned char *)c_str(unesc);
+ unsigned char* d = (unsigned char*)copy;
+ unsigned c;
+ while ((c = *s)) {
+ if (!apr_isalnum(c) && !strchr(reserv, c)) {
+ *d++ = '%';
+ *d++ = escape_c2x[c >> 4];
+ *d++ = escape_c2x[c & 0xf];
+ }
+ else {
+ *d++ = (unsigned char)c;
+ }
+ ++s;
+ }
+ *d = '\0';
+ return copy;
+}
+
+const string escapeURI(const string& uri) {
+ debug(uri, "http::escapeURI::uri");
+ const string e = escape(uri, "?$-_.+!*'(),:@&=/~%");
+ debug(e, "http::escapeURI::result");
+ return e;
+}
+
+const string escapeArg(const string& arg) {
+ debug(arg, "http::escapeArg::arg");
+ const string e = escape(arg, "-_.~");
+ debug(e, "http::escapeArg::result");
+ return e;
+}
+
+/**
+ * Return true if a URI is absolute.
+ */
+const bool isAbsolute(const string& uri) {
+ return contains(uri, "://");
+}
+
+/**
+ * Parse a URI and return its host name.
+ */
+const string hostName(const string& uri, const gc_pool& p) {
+ apr_uri_t u;
+ const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u);
+ if (rc != APR_SUCCESS)
+ return "";
+ if (u.hostname == NULL)
+ return "";
+ return u.hostname;
+}
+
+/**
+ * Parse a URI and return its scheme.
+ */
+const string scheme(const string& uri, const gc_pool& p) {
+ apr_uri_t u;
+ const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u);
+ if (rc != APR_SUCCESS)
+ return "";
+ if (u.scheme == NULL)
+ return "";
+ return u.scheme;
+}
+
+/**
+ * Return the first subdomain name in a host name.
+ */
+const string subDomain(const string& host) {
+ return substr(host, 0, find(host, '.'));
+}
+
+/**
+ * Return the top domain name in a host name.
+ */
+const string topDomain(const string& host) {
+ const size_t d = find(host, '.');
+ return d == length(host) ? host : substr(host, d + 1);
+}
+
+/**
+ * Setup a CURL session
+ */
+const failable<CURL*> setup(const string& url, CURLSession& cs) {
+
+ // Init CURL session
+ if (cs.h != NULL)
+ cleanup(cs);
+ cs.h = curl_easy_init();
+ CURL* ch = cs.h;
+ curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0");
+#ifdef WANT_MAINTAINER_CURL_VERBOSE
+ curl_easy_setopt(ch, CURLOPT_VERBOSE, true);
+#endif
+
+ // Setup protocol options
+ curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true);
+ curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_easy_setopt(ch, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
+ curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(ch, CURLOPT_TIMEOUT, cs.timeout);
+
+ // Setup SSL options
+ if (cs.ca != "") {
+ debug(cs.ca, "http::setup::ca");
+ curl_easy_setopt(ch, CURLOPT_CAINFO, c_str(cs.ca));
+ curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, true);
+ curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 2);
+ } else
+ curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
+ if (cs.cert != "") {
+ debug(cs.cert, "http::setup::cert");
+ curl_easy_setopt(ch, CURLOPT_SSLCERT, c_str(cs.cert));
+ curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM");
+ }
+ if (cs.key != "") {
+ debug(cs.key, "http::setup::key");
+ curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key));
+ curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM");
+ }
+ if (cs.cookie != "") {
+ debug(cs.cookie, "http::setup::cookie");
+ curl_easy_setopt(ch, CURLOPT_COOKIE, c_str(cs.cookie));
+ }
+
+ // Set up HTTP basic auth if requested
+ apr_uri_t u;
+ apr_pool_t* p = gc_current_pool();
+ const apr_status_t prc = apr_uri_parse(p, c_str(url), &u);
+ if (prc == APR_SUCCESS) {
+ if (u.user != NULL) {
+ debug(u.user, "http::setup::user");
+ curl_easy_setopt(ch, CURLOPT_USERNAME, u.user);
+ }
+ if (u.password != NULL) {
+ debug(u.password, "http::setup::pass");
+ curl_easy_setopt(ch, CURLOPT_PASSWORD, u.password);
+ }
+ if (u.user != NULL || u.password != NULL) {
+ curl_easy_setopt(ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+
+ // Set target URL, omitting the user:password part
+ curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(apr_uri_unparse(p, &u, APR_URI_UNP_OMITUSERINFO))));
+
+ return ch;
+ }
+ }
+
+ // Set target URL
+ curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(url)));
+
+ return ch;
+}
+
+/**
+ * Cleanup a CURL session
+ */
+const failable<bool> cleanup(CURLSession& cs) {
+ if (cs.h == NULL)
+ return true;
+ curl_easy_cleanup(cs.h);
+ cs.h = NULL;
+ return true;
+}
+
+/**
+ * Context passed to the read callback function.
+ */
+class CURLReadContext {
+public:
+ CURLReadContext(const list<string>& ilist) : ilist(ilist) {
+ }
+ list<string> ilist;
+};
+
+/**
+ * Called by CURL to read data to send.
+ */
+size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLReadContext& rcx = *static_cast<CURLReadContext*>(data);
+ if (isNil(rcx.ilist))
+ return 0;
+ const list<string> f(fragment(rcx.ilist, size * nmemb));
+ const string s = car(f);
+ rcx.ilist = cdr(f);
+ memcpy(ptr, c_str(s), length(s));
+ return length(s);
+}
+
+/**
+ * Context passed to CURL write callback function.
+ */
+template<typename R> class CURLWriteContext {
+public:
+ CURLWriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) {
+ }
+ const lambda<R(const string&, const R)> reduce;
+ R accum;
+};
+
+/**
+ * Called by CURL to write received data.
+ */
+template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data));
+ const size_t realsize = size * nmemb;
+ wcx.accum = wcx.reduce(string((const char*)ptr, realsize), wcx.accum);
+ return realsize;
+}
+
+/**
+ * Apply an HTTP verb to a list containing a list of headers and a list of content, and
+ * a reduce function used to process the response.
+ */
+curl_slist* headers(curl_slist* cl, const list<string>& h) {
+ if (isNil(h))
+ return cl;
+ return headers(curl_slist_append(cl, c_str(string(car(h)))), cdr(h));
+}
+
+template<typename R> const failable<list<R> > apply(const list<list<string> >& hdr, const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const string& verb, CURLSession& cs) {
+ debug(url, "http::apply::url");
+ debug(verb, "http::apply::verb");
+
+ // Setup the CURL session
+ const failable<CURL*> fch = setup(url, cs);
+ if (!hasContent(fch)) {
+ cleanup(cs);
+ return mkfailure<list<R>>(fch);
+ }
+ CURL* ch = content(fch);
+
+ // Set the request headers
+ curl_slist* hl = headers(NULL, car(hdr));
+ if (hl != NULL)
+ curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl);
+
+ // Convert request body to a string
+ // TODO use HTTP chunking instead
+ ostringstream os;
+ write(cadr(hdr), os);
+ const string s = str(os);
+ const size_t sz = length(s);
+
+ // Setup the read, write header and write data callbacks
+ CURLReadContext rcx(mklist(s));
+ curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback);
+ curl_easy_setopt(ch, CURLOPT_READDATA, &rcx);
+ CURLWriteContext<R> hcx(reduce, initial);
+ curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback<R>));
+ curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx);
+ CURLWriteContext<R> wcx(reduce, initial);
+ curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback<R>));
+ curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx);
+
+ // Apply the HTTP verb
+ if (verb == "POST") {
+ curl_easy_setopt(ch, CURLOPT_POST, true);
+ curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz);
+ } else if (verb == "PUT") {
+ curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
+ curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz);
+ } else if (verb == "PATCH") {
+ curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
+ curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "PATCH");
+ curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz);
+ } else if (verb == "DELETE")
+ curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE");
+ const CURLcode rc = curl_easy_perform(ch);
+
+ // Free the headers
+ if (hl != NULL)
+ curl_slist_free_all(hl);
+
+ // Return the HTTP return code or content
+ if (rc) {
+ cleanup(cs);
+ return mkfailure<list<R> >(string(curl_easy_strerror(rc)));
+ }
+ long httprc;
+ curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc);
+ if (httprc != 200 && httprc != 201) {
+ cleanup(cs);
+ ostringstream es;
+ es << "HTTP code " << httprc;
+ return mkfailure<list<R> >(str(es));
+ }
+
+ cleanup(cs);
+ return mklist<R>(hcx.accum, wcx.accum);
+}
+
+/**
+ * Evaluate an expression remotely, at the given URL.
+ */
+const failable<value> evalExpr(const value& expr, const string& url, CURLSession& cs) {
+ debug(url, "http::evalExpr::url");
+ debug(expr, "http::evalExpr::input");
+
+ // Convert expression to a JSON-RPC request
+ js::JSContext cx;
+ const failable<list<string> > jsreq = json::jsonRequest(1, car<value>(expr), cdr<value>(expr), cx);
+ if (!hasContent(jsreq))
+ return mkfailure<value>(jsreq);
+
+ // POST it to the URL
+ const list<string> h = mklist<string>("Content-Type: application/json-rpc");
+ const failable<list<list<string> > > res = apply<list<string> >(mklist<list<string> >(h, content(jsreq)), rcons<string>, list<string>(), url, "POST", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ // Parse and return JSON-RPC result
+ const failable<value> rval = json::jsonResultValue(cadr<list<string> >(content(res)), cx);
+ debug(rval, "http::evalExpr::result");
+ if (!hasContent(rval))
+ return mkfailure<value>(rval);
+ return content(rval);
+}
+
+/**
+ * Find and return a header.
+ */
+const maybe<string> header(const char* prefix, const list<string>& h) {
+ if (isNil(h))
+ return maybe<string>();
+ const string s = car(h);
+ if (find(s, prefix) != 0)
+ return header(prefix, cdr(h));
+ const string l(substr(s, length(prefix)));
+ return substr(l, 0, find_first_of(l, "\r\n"));
+}
+
+/**
+ * Find and return a location header.
+ */
+const string location(const list<string>& h) {
+ const maybe<string> l = header("Location: ", h);
+ return hasContent(l)? content(l) : "";
+}
+
+/**
+ * Convert a location to an entry id.
+ */
+const value entryId(const failable<string> l) {
+ if (!hasContent(l))
+ return list<value>();
+ const string ls(content(l));
+ return value(mklist<value>(string(substr(ls, find_last(ls, '/') + 1))));
+}
+
+/**
+ * Find and return a content-type header.
+ */
+const string contentType(const list<string>& h) {
+ const maybe<string> ct = header("Content-Type: ", h);
+ return hasContent(ct)? content(ct) : "";
+}
+
+/**
+ * HTTP GET, return the resource at the given URL.
+ */
+template<typename R> const failable<list<R> > get(const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, CURLSession& cs) {
+ debug(url, "http::get::url");
+ const list<list<string> > req = mklist(list<string>(), list<string>());
+ return apply(req, reduce, initial, url, "GET", cs);
+}
+
+/**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value> getcontent(const string& url, CURLSession& cs) {
+ debug(url, "http::get::url");
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<string> > > res = get<list<string>>(rcons<string>, list<string>(), url, cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+ const list<string> ls(reverse(cadr(content(res))));
+
+ // Return the content as a list of values
+ const value val(mkvalues(ls));
+ debug(val, "http::get::result");
+ return val;
+}
+
+/**
+ * Convert an HTTP content response to a value.
+ */
+const failable<value> responseValue(const list<list<string> > res) {
+
+ // Parse the returned content
+ const string ct = contentType(car(res));
+ debug(ct, "http::responseValue::contentType");
+
+ const list<string> ls(reverse(cadr(res)));
+ debug(ls, "http::responseValue::content");
+
+ if (atom::isATOMEntry(ls)) {
+ // Read an ATOM entry
+ const value val(elementsToValues(content(atom::readATOMEntry(ls))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "application/atom+xml") || atom::isATOMFeed(ls)) {
+ // Read an ATOM feed
+ const value val(elementsToValues(content(atom::readATOMFeed(ls))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "application/rss+xml") || rss::isRSSFeed(ls)) {
+ // Read an RSS feed
+ const value val(elementsToValues(content(rss::readRSSFeed(ls))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "text/javascript") || contains(ct, "application/json") || json::isJSON(ls)) {
+ // Read a JSON document
+ js::JSContext cx;
+ const value val(json::jsonValues(content(json::readJSON(ls, cx))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "application/x-javascript")) {
+ // Read a JSON document enclosed in a javascript function call
+ // Extract the JSON out of the enclosing parenthesis
+ ostringstream os;
+ write(ls, os);
+ const string s = str(os);
+ const size_t fp = find(s, '(');
+ const size_t lp = find_last(s, ')');
+ const list<string> jls = mklist<string>(substr(s, fp + 1, lp - (fp + 1)));
+ debug(jls, "http::responseValue::javascript::content");
+
+ js::JSContext cx;
+ const value val(json::jsonValues(content(json::readJSON(jls, cx))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "text/xml") || contains(ct, "application/xml") || isXML(ls)) {
+ // Read an XML document
+ const value val(elementsToValues(readXML(ls)));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+
+ // Return the content type and a content list
+ const value val(mklist<value>(ct, mkvalues(ls)));
+ debug(val, "http::responseValue::result");
+ return val;
+}
+
+/**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value> get(const string& url, CURLSession& cs) {
+ debug(url, "http::get::url");
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<string> > > res = get<list<string> >(rcons<string>, list<string>(), url, cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ // Parse the returned content
+ return responseValue(content(res));
+}
+
+/**
+ * Form an HTTP content request.
+ */
+const failable<list<list<string> > > writeRequest(const failable<list<string> >& ls, const string& ct) {
+ if (!hasContent(ls))
+ return mkfailure<list<list<string> > >(ls);
+ const list<list<string> > req = mklist<list<string> >(mklist<string>(string("Content-Type: ") + ct), content(ls));
+ debug(req, "http::writeRequest::req");
+ return req;
+}
+
+/**
+ * Convert a value to an HTTP content request.
+ */
+const failable<list<list<string> > > contentRequest(const value& c, unused const string& url) {
+
+ // Check if the client requested a specific format
+ //TODO derive that from given URL
+ const list<value> fmt = assoc<value>("format", list<value>());
+
+ // Write as a scheme value if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "scheme")
+ return writeRequest(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8");
+
+ // Write a simple value as a JSON value
+ if (!isList(c)) {
+ js::JSContext cx;
+ if (isSymbol(c)) {
+ const list<value> lc = mklist<value>(mklist<value>("name", value(string(c))));
+ debug(lc, "http::contentRequest::symbol");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+ const list<value> lc = mklist<value>(mklist<value>("value", c));
+ debug(lc, "http::contentRequest::value");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+
+ // Write an empty list as a JSON empty value
+ if (isNil((list<value>)c)) {
+ js::JSContext cx;
+ debug(list<value>(), "http::contentRequest::empty");
+ return writeRequest(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8");
+ }
+
+ // Write content-type / content-list pair
+ if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c)))
+ return writeRequest(convertValues<string>(cadr<value>(c)), car<value>(c));
+
+ // Write an assoc value as JSON
+ if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) {
+ js::JSContext cx;
+ const list<value> lc = mklist<value>(c);
+ debug(lc, "http::contentRequest::assoc");
+ debug(valuesToElements(lc), "http::contentRequest::assoc::element");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+
+ // Write value as JSON if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "json") {
+ js::JSContext cx;
+ return writeRequest(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8");
+ }
+
+ // Convert list of values to element values
+ const list<value> e = valuesToElements(c);
+ debug(e, "http::contentRequest::elements");
+
+ // Write an ATOM feed or entry
+ if (isList(car<value>(e)) && !isNil(car<value>(e))) {
+ const list<value> el = car<value>(e);
+ if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) {
+ if (cadr<value>(el) == atom::feed)
+ return writeRequest(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8");
+ if (cadr<value>(el) == atom::entry)
+ return writeRequest(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8");
+ }
+ }
+
+ // Write any other compound value as a JSON value
+ js::JSContext cx;
+ return writeRequest(json::writeJSON(e, cx), "application/json; charset=utf-8");
+}
+
+/**
+ * HTTP POST.
+ */
+const failable<value> post(const value& val, const string& url, CURLSession& cs) {
+ debug(url, "http::post::url");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(req);
+ debug(content(req), "http::post::input");
+
+ // POST it to the URL
+ const failable<list<list<string> > > res = apply<list<string>>(content(req), rcons<string>, list<string>(), url, "POST", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ // Return the new entry id from the HTTP location header, if any
+ const string loc = location(car(content(res)));
+ if (length(loc) != 0) {
+ const value eid(entryId(location(car(content(res)))));
+ debug(eid, "http::post::result");
+ return eid;
+ }
+
+ // Return the returned content
+ return responseValue(content(res));
+}
+
+/**
+ * HTTP PUT.
+ */
+const failable<value> put(const value& val, const string& url, CURLSession& cs) {
+ debug(url, "http::put::url");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(req);
+ debug(content(req), "http::put::input");
+
+ // PUT it to the URL
+ const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PUT", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ debug(true, "http::put::result");
+ return value(true);
+}
+
+/**
+ * HTTP PATCH.
+ */
+const failable<value> patch(const value& val, const string& url, CURLSession& cs) {
+ debug(url, "http::put::patch");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(req);
+ debug(content(req), "http::patch::input");
+
+ // PATCH it to the URL
+ const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PATCH", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ debug(true, "http::patch::result");
+ return value(true);
+}
+
+/**
+ * HTTP DELETE.
+ */
+const failable<value, string> del(const string& url, CURLSession& cs) {
+ debug(url, "http::delete::url");
+
+ const list<list<string> > req = mklist(list<string>(), list<string>());
+ const failable<list<list<string> > > res = apply<list<string> >(req, rcons<string>, list<string>(), url, "DELETE", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ debug(true, "http::delete::result");
+ return value(true);
+}
+
+/**
+ * Returns the current host name.
+ */
+const string hostName() {
+ char h[256];
+ if (gethostname(h, 256) == -1)
+ return "localhost";
+ return h;
+}
+
+/**
+ * Create an APR pollfd for a socket.
+ */
+apr_pollfd_t* pollfd(apr_socket_t* s, const int e, const gc_pool& p) {
+ apr_pollfd_t* pfd = gc_new<apr_pollfd_t>(p);
+ pfd->p = pool(p);
+ pfd->desc_type = APR_POLL_SOCKET;
+ pfd->reqevents = (apr_int16_t)e;
+ pfd->rtnevents = (apr_int16_t)e;
+ pfd->desc.s = s;
+ pfd->client_data = NULL;
+ return pfd;
+}
+
+/**
+ * Connect to a URL.
+ */
+const failable<bool> connect(const string& url, CURLSession& cs) {
+ debug(url, "http::connect::url");
+
+ // Setup the CURL session
+ const failable<CURL*> fch = setup(url, cs);
+ if (!hasContent(fch)) {
+ cleanup(cs);
+ return mkfailure<bool>(fch);
+ }
+ CURL* ch = content(fch);
+
+ // Connect
+ curl_easy_setopt(ch, CURLOPT_CONNECT_ONLY, true);
+ const CURLcode rc = curl_easy_perform(ch);
+ if (rc) {
+ cleanup(cs);
+ return mkfailure<bool>(string(curl_easy_strerror(rc)));
+ }
+
+ // Convert the connected socket to an apr_socket_t
+ int sd;
+ const CURLcode grc = curl_easy_getinfo(ch, CURLINFO_LASTSOCKET, &sd);
+ if (grc) {
+ cleanup(cs);
+ return mkfailure<bool>(string(curl_easy_strerror(grc)));
+ }
+ cs.sock = sock(sd, cs.p);
+
+ // Create pollsets and pollfds which can be used to poll the socket
+ apr_status_t rpcrc = apr_pollset_create(&cs.rpollset, 1, pool(cs.p), 0);
+ if (rpcrc != APR_SUCCESS) {
+ cleanup(cs);
+ return mkfailure<bool>(apreason(rpcrc));
+ }
+ cs.rpollfd = pollfd(cs.sock, APR_POLLIN, cs.p);
+ apr_pollset_add(cs.rpollset, cs.rpollfd);
+ apr_status_t wpcrc = apr_pollset_create(&cs.wpollset, 1, pool(cs.p), 0);
+ if (wpcrc != APR_SUCCESS) {
+ cleanup(cs);
+ return mkfailure<bool>(apreason(wpcrc));
+ }
+ cs.wpollfd = pollfd(cs.sock, APR_POLLOUT, cs.p);
+ apr_pollset_add(cs.wpollset, cs.wpollfd);
+
+ return true;
+}
+
+/**
+ * Send an array of chars.
+ */
+const failable<bool> send(const char* c, const size_t l, CURLSession& cs) {
+
+ // Send the data
+ size_t wl = 0;
+ const CURLcode rc = curl_easy_send(cs.h, c, (size_t)l, &wl);
+ if (rc == CURLE_OK && wl == (size_t)l)
+ return true;
+ if (rc != CURLE_AGAIN) {
+ cleanup(cs);
+ return mkfailure<bool>(curlreason(rc));
+ }
+
+ // If the socket was not ready, wait for it to become ready
+ const apr_pollfd_t* pollfds;
+ apr_int32_t pollcount;
+ apr_status_t pollrc = apr_pollset_poll(cs.wpollset, -1, &pollcount, &pollfds);
+ if (pollrc != APR_SUCCESS)
+ return mkfailure<bool>(apreason(pollrc));
+
+ // Send what's left
+ return send(c + wl, l - wl, cs);
+}
+
+/**
+ * Receive an array of chars.
+ */
+const failable<size_t> recv(char* c, const size_t l, CURLSession& cs) {
+
+ // Receive data
+ size_t rl;
+ const CURLcode rc = curl_easy_recv(cs.h, c, (size_t)l, &rl);
+ if (rc == CURLE_OK)
+ return (size_t)rl;
+ if (rc == 1)
+ return 0;
+ if (rc != CURLE_AGAIN) {
+ cleanup(cs);
+ return mkfailure<size_t>(curlreason(rc));
+ }
+
+ // If the socket was not ready, wait for it to become ready
+ const apr_pollfd_t* pollfds;
+ apr_int32_t pollcount;
+ apr_status_t pollrc = apr_pollset_poll(cs.rpollset, -1, &pollcount, &pollfds);
+ if (pollrc != APR_SUCCESS) {
+ cleanup(cs);
+ return mkfailure<size_t>(apreason(pollrc));
+ }
+
+ // Receive again
+ return recv(c, l, cs);
+}
+
+/**
+ * Converts a list of key value pairs to a query string.
+ */
+ostringstream& queryString(const list<list<value> > args, ostringstream& os) {
+ if (isNil(args))
+ return os;
+ const list<value> arg = car(args);
+ debug(arg, "http::queryString::arg");
+ if (isNil(arg) || isNil(cdr(arg)))
+ return queryString(cdr(args), os);
+ os << car(arg) << "=" << c_str(cadr(arg));
+ if (!isNil(cdr(args)))
+ os << "&";
+ return queryString(cdr(args), os);
+}
+
+const string queryString(const list<list<value> > args) {
+ ostringstream os;
+ return str(queryString(args, os));
+}
+
+/**
+ * Filter path segment in a list of arguments.
+ */
+const bool filterPath(const value& arg) {
+ return isString(arg) || isSymbol(arg);
+}
+
+/**
+ * Filter query string arguments in a list of arguments.
+ */
+const bool filterQuery(const value& arg) {
+ return isList(arg);
+}
+
+/**
+ * Escape a query string argument.
+ */
+const value escapeQuery(const value& arg) {
+ return arg;
+ //return mklist<value>(car<value>(arg), escapeArg(cadr<value>(arg)));
+}
+
+/**
+ * HTTP client proxy function.
+ */
+struct proxy {
+ proxy(const string& uri, const string& ca, const string& cert, const string& key, const string& cookie, const int timeout, const gc_pool& p) : p(p), uri(uri), ca(ca), cert(cert), key(key), cookie(cookie), cs(*(new (gc_new<CURLSession>(p)) CURLSession(ca, cert, key, cookie, timeout))) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ debug(args, "http::proxy::args");
+ const value fun = car(args);
+ if (fun == "get") {
+ const list<value> lp = filter<value>(filterPath, cadr(args));
+ debug(lp, "http::proxy::path");
+ const list<value> lq = map<value, value>(escapeQuery, filter<value>(filterQuery, cadr(args)));
+ debug(lp, "http::proxy::query");
+ const value p = path(lp);
+ const value q = queryString(lq);
+ const failable<value> val = get(uri + p + (q != ""? string("?") + q : string("")), cs);
+ return content(val);
+ }
+ if (fun == "post") {
+ const failable<value> val = post(caddr(args), uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ if (fun == "put") {
+ const failable<value> val = put(caddr(args), uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ if (fun == "patch") {
+ const failable<value> val = patch(caddr(args), uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ if (fun == "delete") {
+ const failable<value> val = del(uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ const failable<value> val = evalExpr(args, uri, cs);
+ return content(val);
+ }
+
+ const gc_pool p;
+ const string uri;
+ const string ca;
+ const string cert;
+ const string key;
+ const string cookie;
+ CURLSession& cs;
+};
+
+}
+}
+
+#endif /* tuscany_http_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr b/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr
new file mode 100755
index 0000000000..735c152f43
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+# 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.
+
+# Parse a string in the form ip-addr:local-port/public-port
+addr=`echo $2 | awk -F "/" '{ print $1 }'`
+ip=`echo $addr | awk -F ":" '{ print $1 }'`
+port=`echo $addr | awk -F ":" '{ print $2 }'`
+if [ "$port" = "" ]; then
+ port=$ip
+ ip=""
+ listen=$port
+ vhost="*:$port"
+else
+ listen="$ip:$port"
+ vhost="$ip:$port"
+fi
+pport=`echo $2 | awk -F "/" '{ print $2 }'`
+if [ "$pport" = "" ]; then
+ pport=$port
+fi
+
+# Return the requested part
+if [ "$1" = "ip" ]; then
+ echo $ip
+fi
+if [ "$1" = "port" ]; then
+ echo $port
+fi
+if [ "$1" = "pport" ]; then
+ echo $pport
+fi
+if [ "$1" = "listen" ]; then
+ echo $listen
+fi
+if [ "$1" = "vhost" ]; then
+ echo $vhost
+fi
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf
new file mode 100755
index 0000000000..730775fa89
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf
@@ -0,0 +1,375 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+port=`$here/httpd-addr port $3`
+pport=`$here/httpd-addr pport $3`
+listen=`$here/httpd-addr listen $3`
+vhost=`$here/httpd-addr vhost $3`
+if [ "$pport" = "80" ]; then
+ pportsuffix=""
+else
+ pportsuffix=":$pport"
+fi
+
+mkdir -p $4
+htdocs=`echo "import os; print os.path.realpath('$4')" | python`
+
+user=`id -un`
+group=`id -gn`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+ sendfile=Off
+else
+ libsuffix=".so"
+ sendfile=On
+fi
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+mkdir -p $root
+mkdir -p $root/logs
+mkdir -p $root/conf
+cat >$root/conf/httpd.conf <<EOF
+# Generated by: httpd-conf $*
+# Apache HTTPD server configuration
+
+# Main server name
+ServerName http://$host$pportsuffix
+PidFile $root/logs/httpd.pid
+
+# Load configured MPM
+Include conf/mpm.conf
+
+# Load required modules
+Include conf/modules.conf
+
+# Basic security precautions
+User $user
+Group $group
+ServerSignature Off
+ServerTokens Prod
+Timeout 45
+RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
+LimitRequestBody 1048576
+HostNameLookups Off
+#MaxKeepAliveRequests 25
+#MaxConnectionsPerChild 100
+
+# Log HTTP requests
+# [timestamp] [access] remote-host remote-ident remote-user "request-line"
+# status response-size "referrer" "user-agent" "user-track" local-IP
+# virtual-host response-time bytes-received bytes-sent
+LogFormat "[%{%a %b %d %H:%M:%S %Y}t] [access] %h %l %u \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{cookie}n\" %A %V %D %I %O %{mod_security-message}i" combined
+Include conf/log.conf
+
+# Configure Mime types and default charsets
+TypesConfig $here/conf/mime.types
+AddDefaultCharset utf-8
+AddCharset utf-8 .html .js .css
+
+# Configure cache control
+<Directory />
+ExpiresActive On
+ExpiresDefault A604800
+Header onsuccess merge Cache-Control public env=!private-cache
+</Directory>
+
+# Enable Linux Kernel sendfile
+EnableSendFile $sendfile
+
+# Configure auth modules
+Include conf/auth.conf
+
+# Set default document root
+DocumentRoot $htdocs
+DirectoryIndex index-min.html index.html
+
+# Protect server files
+<Directory />
+Options None
+AllowOverride None
+Require all denied
+</Directory>
+
+# Configure output filters to enable compression and rate limiting
+<Location />
+#SetOutputFilter RATE_LIMIT;DEFLATE
+SetOutputFilter DEFLATE
+
+BrowserMatch ^Mozilla/4 gzip-only-text/html
+BrowserMatch ^Mozilla/4\.0[678] no-gzip
+BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
+BrowserMatch ^check_http/ check_http
+SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
+Header append Vary User-Agent env=!dont-vary
+
+#SetEnv rate-limit 400
+</Location>
+
+# Listen on HTTP port
+Listen $listen
+
+# Setup HTTP virtual host
+<VirtualHost $vhost>
+ServerName http://$host$pportsuffix
+
+<Location />
+RewriteEngine on
+Include conf/hostcond.conf
+RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC]
+RewriteCond %{REQUEST_URI} !^/server-status [NC]
+RewriteCond %{REQUEST_URI} !^/balancer-manager [NC]
+RewriteCond %{REQUEST_URI} !^/proxy/ [NC]
+RewriteRule .* http://$host$pportsuffix%{REQUEST_URI} [R]
+</Location>
+
+Include conf/svhost.conf
+
+# Configure authentication
+Include conf/noauth.conf
+Include conf/locauth.conf
+Include conf/pubauth.conf
+Include conf/adminauth.conf
+
+</VirtualHost>
+
+EOF
+
+# Configure logging
+cat >$root/conf/log.conf <<EOF
+# Generated by: httpd-conf $*
+ErrorLog $root/logs/error_log
+CustomLog $root/logs/access_log combined
+
+EOF
+
+# Run with the prefork MPM
+cat >$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+LoadModule mpm_prefork_module ${modules_prefix}/modules/mod_mpm_prefork.so
+
+EOF
+
+if [ $uname = "Darwin" ]; then
+ cat >>$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+# Set thread stack size
+ThreadStackSize 2097152
+
+EOF
+fi
+
+# Generate modules list
+cat >$root/conf/modules.conf <<EOF
+# Generated by: httpd-conf $*
+# Load a minimal set of modules, the load order is important
+# (e.g. load mod_headers before mod_rewrite, so its hooks execute
+# after mod_rewrite's hooks)
+LoadModule headers_module ${modules_prefix}/modules/mod_headers.so
+LoadModule alias_module ${modules_prefix}/modules/mod_alias.so
+LoadModule authn_file_module ${modules_prefix}/modules/mod_authn_file.so
+LoadModule authn_socache_module ${modules_prefix}/modules/mod_authn_socache.so
+LoadModule authn_core_module ${modules_prefix}/modules/mod_authn_core.so
+LoadModule authz_host_module ${modules_prefix}/modules/mod_authz_host.so
+LoadModule authz_groupfile_module ${modules_prefix}/modules/mod_authz_groupfile.so
+LoadModule authz_user_module ${modules_prefix}/modules/mod_authz_user.so
+LoadModule authz_core_module ${modules_prefix}/modules/mod_authz_core.so
+LoadModule auth_basic_module ${modules_prefix}/modules/mod_auth_basic.so
+LoadModule auth_digest_module ${modules_prefix}/modules/mod_auth_digest.so
+LoadModule auth_form_module ${modules_prefix}/modules/mod_auth_form.so
+LoadModule request_module ${modules_prefix}/modules/mod_request.so
+LoadModule deflate_module ${modules_prefix}/modules/mod_deflate.so
+LoadModule filter_module ${modules_prefix}/modules/mod_filter.so
+LoadModule proxy_module ${modules_prefix}/modules/mod_proxy.so
+LoadModule proxy_connect_module ${modules_prefix}/modules/mod_proxy_connect.so
+LoadModule proxy_http_module ${modules_prefix}/modules/mod_proxy_http.so
+LoadModule proxy_balancer_module ${modules_prefix}/modules/mod_proxy_balancer.so
+LoadModule lbmethod_byrequests_module ${modules_prefix}/modules/mod_lbmethod_byrequests.so
+LoadModule socache_shmcb_module ${modules_prefix}/modules/mod_socache_shmcb.so
+LoadModule cache_module ${modules_prefix}/modules/mod_cache.so
+LoadModule cache_disk_module ${modules_prefix}/modules/mod_cache_disk.so
+LoadModule rewrite_module ${modules_prefix}/modules/mod_rewrite.so
+LoadModule mime_module ${modules_prefix}/modules/mod_mime.so
+LoadModule status_module ${modules_prefix}/modules/mod_status.so
+LoadModule negotiation_module ${modules_prefix}/modules/mod_negotiation.so
+LoadModule dir_module ${modules_prefix}/modules/mod_dir.so
+LoadModule setenvif_module ${modules_prefix}/modules/mod_setenvif.so
+LoadModule env_module ${modules_prefix}/modules/mod_env.so
+LoadModule expires_module ${modules_prefix}/modules/mod_expires.so
+<IfModule !log_config_module>
+LoadModule log_config_module ${modules_prefix}/modules/mod_log_config.so
+</IfModule>
+LoadModule logio_module ${modules_prefix}/modules/mod_logio.so
+LoadModule usertrack_module ${modules_prefix}/modules/mod_usertrack.so
+LoadModule vhost_alias_module ${modules_prefix}/modules/mod_vhost_alias.so
+LoadModule cgi_module ${modules_prefix}/modules/mod_cgi.so
+LoadModule actions_module ${modules_prefix}/modules/mod_actions.so
+LoadModule unixd_module ${modules_prefix}/modules/mod_unixd.so
+LoadModule session_module ${modules_prefix}/modules/mod_session.so
+LoadModule session_crypto_module ${modules_prefix}/modules/mod_session_crypto.so
+LoadModule slotmem_shm_module ${modules_prefix}/modules/mod_slotmem_shm.so
+LoadModule ratelimit_module ${modules_prefix}/modules/mod_ratelimit.so
+LoadModule reqtimeout_module ${modules_prefix}/modules/mod_reqtimeout.so
+LoadModule ssl_module ${modules_prefix}/modules/mod_ssl.so
+
+EOF
+
+# Generate auth configuration
+cat >$root/conf/auth.conf <<EOF
+# Generated by: httpd-conf $*
+
+EOF
+
+cat >$root/conf/locauth.conf <<EOF
+# Generated by: httpd-conf $*
+# Authentication and authorization configuration
+
+# Allow authorized access to document root
+<Directory "$htdocs">
+Options FollowSymLinks
+Require all granted
+</Directory>
+
+# Allow authorized access to root location
+<Location />
+Options FollowSymLinks
+AuthUserFile "$root/conf/httpd.passwd"
+AuthGroupFile "$root/conf/httpd.groups"
+Require all granted
+</Location>
+
+EOF
+
+cat >$root/conf/pubauth.conf <<EOF
+# Generated by: httpd-conf $*
+# Allow everyone to access public locations
+<Location /login>
+AuthType None
+Require all granted
+# Mark login page with a header
+Header set X-Login open-auth
+</Location>
+<Location /logout>
+AuthType None
+Require all granted
+</Location>
+<Location /public>
+AuthType None
+Require all granted
+</Location>
+<Location /proxy/public>
+AuthType None
+Require all granted
+</Location>
+<Location /favicon.ico>
+AuthType None
+Require all granted
+</Location>
+<Location /robots.txt>
+AuthType None
+Require all granted
+</Location>
+
+EOF
+
+cat >$root/conf/adminauth.conf <<EOF
+
+# Allow the server admin to view the server status
+<Location /server-status>
+Require user admin
+</Location>
+
+EOF
+
+# Create password and group files
+cat >$root/conf/httpd.passwd <<EOF
+# Generated by: httpd-conf $*
+EOF
+
+cat >$root/conf/httpd.groups <<EOF
+# Generated by: httpd-conf $*
+EOF
+
+# Allow public access to server resources
+cat >$root/conf/noauth.conf <<EOF
+# Generated by: httpd-conf $*
+# Allow public access to server resources
+
+# Allow access to document root
+<Directory "$htdocs">
+AuthType None
+Require all granted
+</Directory>
+
+# Allow everyone to access root location
+<Location />
+AuthType None
+Require all granted
+</Location>
+
+EOF
+
+# Generate vhost configuration
+cat >$root/conf/vhost.conf <<EOF
+# Generated by: httpd-conf $*
+# Virtual host configuration
+UseCanonicalName Off
+
+# Enable HTTP reverse proxy
+ProxyRequests Off
+ProxyPreserveHost On
+ProxyStatus On
+
+# Enable server status
+<Location /server-status>
+SetHandler server-status
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >$root/conf/svhost.conf <<EOF
+# Generated by: httpd-conf $*
+# Static virtual host configuration
+Include conf/vhost.conf
+
+EOF
+
+cat >$root/conf/dvhost.conf <<EOF
+# Generated by: httpd-conf $*
+# Mass dynamic virtual host configuration
+Include conf/vhost.conf
+
+EOF
+
+# Generate host name check condition
+cat >$root/conf/hostcond.conf <<EOF
+# Generated by: httpd-conf $*
+RewriteCond %{HTTP_HOST} !^$host [NC]
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug b/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug
new file mode 100755
index 0000000000..df0d21fbe5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# 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.
+
+# Start httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+httpd=`cat $here/httpd.prefix`
+$httpd/bin/httpd -X -E $root/logs/error_log -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf
new file mode 100755
index 0000000000..093bbe78d9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# 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.
+
+# Configure HTTPD to run with the event MPM
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+mkdir -p $root
+mkdir -p $root/conf
+cat >$root/conf/mpm.conf <<EOF
+# Generated by: httpd-event-conf $*
+# Use HTTPD event MPM
+LoadModule mpm_event_module ${modules_prefix}/modules/mod_mpm_event.so
+
+EOF
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ cat >>$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+# Set thread stack size
+ThreadStackSize 2097152
+
+EOF
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf
new file mode 100755
index 0000000000..c9d2ad81d5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# 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.
+
+# Configure HTTPD log level
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+level=$2
+
+# Configure log level
+cat >>$root/conf/log.conf <<EOF
+# Generated by: httpd-loglevel-conf $*
+LogLevel $level
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind b/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind
new file mode 100755
index 0000000000..55e113bbd3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# 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.
+
+# Start httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+httpd=`cat $here/httpd.prefix`
+$here/../../etc/memgrind $httpd/bin/httpd -X -E $root/logs/error_log -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart b/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart
new file mode 100755
index 0000000000..81b098d85d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# 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.
+
+# Restart httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+apachectl=`cat $here/httpd-apachectl.prefix`
+$apachectl -k graceful -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf
new file mode 100755
index 0000000000..f99a10071c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf
@@ -0,0 +1,259 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD SSL configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+gport=`echo $conf | awk '{ print $7 }'`
+port=`$here/httpd-addr port $gport`
+pport=`$here/httpd-addr pport $gport`
+
+sslpport=`$here/httpd-addr pport $2`
+sslport=`$here/httpd-addr listen $2`
+sslvhost=`$here/httpd-addr vhost $2`
+if [ "$sslpport" = "443" ]; then
+ sslpportsuffix=""
+else
+ sslpportsuffix=":$sslpport"
+fi
+
+dothost=`echo $host | grep "\."`
+
+htdocs=`echo $conf | awk '{ print $8 }'`
+mkdir -p $htdocs
+htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+# Extract organization name from our CA certificate
+org=`openssl x509 -noout -subject -nameopt multiline -in $root/cert/ca.crt | grep organizationName | awk -F "= " '{ print $2 }'`
+
+# Generate HTTPD configuration
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+
+# Configure SSL support
+AddType application/x-x509-ca-cert .crt
+AddType application/x-pkcs7-crl .crl
+SSLPassPhraseDialog builtin
+SSLSessionCache "shmcb:$root/logs/ssl_scache(512000)"
+SSLSessionCacheTimeout 300
+Mutex "file:$root/logs" ssl-cache
+SSLRandomSeed startup builtin
+SSLRandomSeed connect builtin
+
+# Listen on HTTPS port
+Listen $sslport
+
+# HTTPS virtual host
+<VirtualHost $sslvhost>
+ServerName https://$host$sslpportsuffix
+
+<Location />
+RewriteEngine on
+Include conf/hostcond.conf
+RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC]
+RewriteCond %{REQUEST_URI} !^/server-status [NC]
+RewriteCond %{REQUEST_URI} !^/balancer-manager [NC]
+RewriteCond %{REQUEST_URI} !^/proxy/ [NC]
+RewriteRule .* https://$host$sslpportsuffix%{REQUEST_URI} [R]
+</Location>
+
+Include conf/svhost-ssl.conf
+
+# Configure authentication
+Include conf/noauth-ssl.conf
+Include conf/locauth-ssl.conf
+Include conf/pubauth-ssl.conf
+Include conf/adminauth-ssl.conf
+
+# Configure tracking
+Include conf/tracking-ssl.conf
+
+</VirtualHost>
+
+EOF
+
+# Generate auth configuration
+cat >$root/conf/locauth-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Authentication and authorization configuration
+Include conf/locauth.conf
+
+EOF
+
+cat >$root/conf/pubauth-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Allow everyone to access public locations
+Include conf/pubauth.conf
+
+EOF
+
+cat >$root/conf/adminauth-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Allow admin access
+Include conf/adminauth.conf
+
+EOF
+
+# Allow public access to server resources
+cat >$root/conf/noauth-ssl.conf <<EOF
+# Generated by: httpd-conf $*
+# Allow public access to server resources
+Include conf/noauth.conf
+
+EOF
+
+# Generate HTTP vhost configuration
+cat >>$root/conf/svhost.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Redirect HTTP traffic to HTTPS
+<Location />
+RewriteEngine on
+RewriteCond %{SERVER_PORT} ^$port$ [OR]
+RewriteCond %{SERVER_PORT} ^$pport$
+RewriteRule .* https://$host$sslpportsuffix%{REQUEST_URI} [R]
+</Location>
+
+EOF
+
+# Redirect HTTP traffic to HTTPS in HTTP vhost
+cat >>$root/conf/dvhost.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Redirect HTTP traffic to HTTPS
+<Location />
+RewriteEngine on
+RewriteCond %{SERVER_PORT} ^$port$ [OR]
+RewriteCond %{SERVER_PORT} ^$pport$
+RewriteRule .* https://%{SERVER_NAME}$sslpportsuffix%{REQUEST_URI} [R]
+</Location>
+
+EOF
+
+# Generate HTTPS vhost configuration
+cat >$root/conf/vhost-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Virtual host configuration
+UseCanonicalName Off
+
+# Enable SSL
+SSLEngine on
+SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
+BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
+SSLOptions +StrictRequire +OptRenegotiate +FakeBasicAuth
+
+# Require clients to use SSL and authenticate
+<Location />
+SSLRequireSSL
+SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128
+</Location>
+
+# Log SSL requests
+# [timestamp] [sslaccess] remote-host remote-ident remote-user SSL-protocol
+# SSL-cipher "request-line" status response-size "referrer" "user-agent"
+# "SSL-client-I-DN" "SSL-client-S-DN" "user-track" local-IP virtual-host
+# response-time bytes-received bytes-sent
+LogFormat "[%{%a %b %d %H:%M:%S %Y}t] [sslaccess] %h %l %u %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{SSL_CLIENT_I_DN}x\" \"%{SSL_CLIENT_S_DN}x\" \"%{cookie}n\" %A %V %D %I %O %{mod_security-message}i" sslcombined
+Include conf/log-ssl.conf
+
+# Enable HTTPS reverse proxy
+ProxyRequests Off
+ProxyPreserveHost On
+ProxyStatus On
+SSLProxyEngine on
+SSLProxyCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
+
+# Verify server certificates
+SSLProxyVerify require
+SSLProxyVerifyDepth 1
+SSLProxyCheckPeerCN Off
+
+# Enable server status
+<Location /server-status>
+SetHandler server-status
+HostnameLookups on
+</Location>
+
+EOF
+
+# Generate tracking configuration
+cat >$root/conf/tracking-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Configure tracking
+CookieTracking on
+CookieName TuscanyVisitorId
+CookieStyle Cookie
+CookieExpires 31556926
+
+EOF
+
+if [ "$dothost" != "" ]; then
+ cat >>$root/conf/tracking-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+CookieDomain .$dothost
+
+EOF
+
+fi
+
+# Configure logging
+cat >$root/conf/log-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+CustomLog $root/logs/ssl_access_log sslcombined
+
+EOF
+
+# Configure virtual hosts
+cat >$root/conf/svhost-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Static virtual host configuration
+Include conf/vhost-ssl.conf
+
+# Declare SSL certificates used in this virtual host
+SSLCACertificateFile "$root/cert/ca.crt"
+SSLCertificateChainFile "$root/cert/ca.crt"
+SSLCertificateFile "$root/cert/server.crt"
+SSLCertificateKeyFile "$root/cert/server.key"
+
+EOF
+
+cat >$root/conf/dvhost-ssl.conf <<EOF
+# Mass dynamic virtual host configuration
+# Generated by: httpd-ssl-conf $*
+Include conf/vhost-ssl.conf
+
+# Declare wildcard SSL certificates used in this virtual host
+SSLCACertificateFile "$root/cert/ca.crt"
+SSLCertificateChainFile "$root/cert/ca.crt"
+SSLCertificateFile "$root/cert/vhost.crt"
+SSLCertificateKeyFile "$root/cert/vhost.key"
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-start b/sca-cpp/branches/lightweight-sca/modules/http/httpd-start
new file mode 100755
index 0000000000..e38c2f9a94
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-start
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# 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.
+
+# Start httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+apachectl=`cat $here/httpd-apachectl.prefix`
+$apachectl -E $root/logs/error_log -k start -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop b/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop
new file mode 100755
index 0000000000..9010e4dd17
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# 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.
+
+# Stop httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+apachectl=`cat $here/httpd-apachectl.prefix`
+$apachectl -k graceful-stop -d $root -f $root/conf/httpd.conf 2>/dev/null
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-test b/sca-cpp/branches/lightweight-sca/modules/http/httpd-test
new file mode 100755
index 0000000000..ab6ab5ad41
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-test
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# 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.
+
+echo "Testing..."
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+rm -rf tmp
+./httpd-conf tmp localhost 8090 htdocs
+./httpd-event-conf tmp
+./httpd-start tmp
+sleep 2
+
+# Test HTTP GET
+$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html
+diff tmp/index.html htdocs/index.html
+rc=$?
+
+# Cleanup
+./httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf
new file mode 100755
index 0000000000..0028576364
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD SSL Tunnel configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+# Generate required modules list
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: httpd-tunnel-ssl-conf $*
+LoadModule mod_tuscany_ssltunnel $here/libmod_tuscany_ssltunnel$libsuffix
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf
new file mode 100755
index 0000000000..0a061dbffa
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# 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.
+
+# Configure HTTPD to run with the worker MPM
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+mkdir -p $root
+mkdir -p $root/conf
+cat >$root/conf/mpm.conf <<EOF
+# Generated by: httpd-worker-conf $*
+# Use HTTPD worker MPM
+LoadModule mpm_worker_module ${modules_prefix}/modules/mod_mpm_worker.so
+
+EOF
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ cat >>$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+# Set thread stack size
+ThreadStackSize 2097152
+
+EOF
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp b/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp
new file mode 100644
index 0000000000..6470d6c587
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp
@@ -0,0 +1,753 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_httpd_hpp
+#define tuscany_httpd_hpp
+
+/**
+ * HTTPD module implementation functions.
+ */
+
+extern "C" {
+#include <apr_strings.h>
+#include <apr_fnmatch.h>
+#include <apr_lib.h>
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+#include <apr_base64.h>
+
+#include <httpd.h>
+// Hack to workaround compile error with CLang/LLVM
+#undef strtoul
+// Hack to workaround compile error with HTTPD 2.3.8
+#define new new_
+#include <http_config.h>
+#undef new
+#include <http_core.h>
+#include <http_connection.h>
+#include <http_request.h>
+// Ignore conversion warnings in HTTPD 2.3.15 header
+#ifdef WANT_MAINTAINER_WARNINGS
+#ifndef IS_DARWIN
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#endif
+#include <http_protocol.h>
+// Re-enable conversion warnings
+#ifdef WANT_MAINTAINER_WARNINGS
+#ifndef IS_DARWIN
+#pragma GCC diagnostic warning "-Wconversion"
+#endif
+#endif
+// Hack to workaround compile error with HTTPD 2.3.8
+#define aplog_module_index aplog_module_index = 0
+#include <http_log.h>
+#undef aplog_module_index
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX (aplog_module_index ? *aplog_module_index : APLOG_NO_MODULE)
+#include <http_main.h>
+#include <util_script.h>
+#include <util_md5.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <ap_mpm.h>
+#include <mod_core.h>
+#include <ap_provider.h>
+#include <mod_auth.h>
+#include <mod_session.h>
+}
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "sstream.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "http.hpp"
+
+
+namespace tuscany {
+namespace httpd {
+
+/**
+ * Returns a server-scoped module configuration.
+ */
+template<typename C> void* makeServerConf(apr_pool_t* p, server_rec* s) {
+ return new (gc_new<C>(p)) C(p, s);
+}
+
+template<typename C> const C& serverConf(const request_rec* r, const module* mod) {
+ return *(C*)ap_get_module_config(r->server->module_config, mod);
+}
+
+template<typename C> C& serverConf(const server_rec* s, const module* mod) {
+ return *(C*)ap_get_module_config(s->module_config, mod);
+}
+
+template<typename C> C& serverConf(const cmd_parms* cmd, const module* mod) {
+ return *(C*)ap_get_module_config(cmd->server->module_config, mod);
+}
+
+/**
+ * Returns a directory-scoped module configuration.
+ */
+template<typename C> void* makeDirConf(apr_pool_t *p, char* d) {
+ return new (gc_new<C>(p)) C(p, d);
+}
+
+template<typename C> const C& dirConf(const request_rec* r, const module* mod) {
+ return *(C*)ap_get_module_config(r->per_dir_config, mod);
+}
+
+template<typename C> C& dirConf(const void* c) {
+ return *(C*)c;
+}
+
+/**
+ * Returns a request-scoped module configuration.
+ */
+template<typename C> C& makeRequestConf(const request_rec* r, const module* mod) {
+ C* c = new (gc_new<C>(r->pool)) C(r->pool, r);
+ ap_set_module_config(r->request_config, mod, c);
+ return *c;
+}
+
+template<typename C> C& requestConf(const request_rec* r, const module* mod) {
+ C* c = (C*)ap_get_module_config(r->request_config, mod);
+ if (c == NULL)
+ return makeRequestConf<C>(r, mod);
+ return *c;
+}
+
+/**
+ * Return the host name for a server.
+ */
+const string hostName(const server_rec* s, const string& def = "localhost") {
+ return s->server_hostname != NULL? s->server_hostname : def;
+}
+
+/**
+ * Return the host name from an HTTP request.
+ */
+const string hostName(request_rec* r, const string& def = "localhost") {
+ const char* fh = apr_table_get(r->headers_in, "X-Forwarded-Server");
+ if (fh != NULL)
+ return fh;
+ const char* h = ap_get_server_name(r);
+ return h != NULL? h : (r->server->server_hostname != NULL? r->server->server_hostname : def);
+}
+
+/**
+ * Convert a host name to a realm.
+ */
+const string realm(const string& host) {
+ const string pre = substr(host, 0, 4);
+ return pre == "www." || pre == "ww1." || pre == "ww2." || pre == "ww3."? substr(host, 4) : host;
+}
+
+/**
+ * Return the protocol scheme for a server.
+ */
+const string scheme(const server_rec* s, const string& def = "http") {
+ return s->server_scheme != NULL? s->server_scheme : def;
+}
+
+/**
+ * Return the protocol scheme from an HTTP request.
+ */
+const string scheme(request_rec* r, const string& def = "http") {
+ const char* fs = apr_table_get(r->headers_in, "X-Forwarded-HTTPS");
+ if (fs != NULL)
+ return !strcmp(fs, "on")? "https" : "http";
+ return r->server->server_scheme != NULL? r->server->server_scheme : def;
+}
+
+/**
+ * Return the port number for a server.
+ */
+const int port(const server_rec* s, const int def = 80) {
+ return s->port != 0? s->port : def;
+}
+
+/**
+ * Return the port number from an HTTP request.
+ */
+const int port(request_rec* r, const int def = 80) {
+ const char* fp = apr_table_get(r->headers_in, "X-Forwarded-Port");
+ if (fp != NULL)
+ return atoi(fp);
+ const int p = ap_get_server_port(r);
+ return p != 0? p : def;
+}
+
+/**
+ * Return the name of a server.
+ */
+const string serverName(const server_rec* s, const string& def = "localhost") {
+ ostringstream n;
+ const string sc = scheme(s);
+ const string h = hostName(s, def);
+ const int p = port(s, sc == "https"? 443 : 80);
+ n << sc << "://" << h;
+ if (!((sc == "http" && p == 80) || (sc == "https" && p == 443)))
+ n << ":" << p;
+ n << (s->path != NULL? string(s->path, s->pathlen) : "");
+ return str(n);
+}
+
+/**
+ * Determine the name of a server from an HTTP request.
+ */
+const string serverName(request_rec* r, const string& def = "localhost") {
+ ostringstream n;
+ const string s = scheme(r);
+ const string h = hostName(r, def);
+ const int p = port(r, s == "https"? 443 : 80);
+ n << s << "://" << h;
+ if (!((s == "http" && p == 80) || (s == "https" && p == 443)))
+ n << ":" << p;
+ n << (r->server->path != NULL? string(r->server->path, r->server->pathlen) : "");
+ return str(n);
+}
+
+/**
+ * Return true if a request is targeting a virtual host.
+ */
+const bool isVhostRequest(const server_rec* s, const string& d, request_rec* r) {
+ const string rh = hostName(r);
+ return rh != hostName(s) && http::topDomain(rh) == d;
+}
+
+/**
+ * Return the content type of a request.
+ */
+const string contentType(const request_rec* r) {
+ const char* ct = apr_table_get(r->headers_in, "Content-Type");
+ if (ct == NULL)
+ return "";
+ return ct;
+}
+
+/**
+ * Return the cookie header of a request.
+ */
+const string cookie(const request_rec* r) {
+ const char* c = apr_table_get(r->headers_in, "Cookie");
+ if (c == NULL)
+ return "";
+ return c;
+}
+
+/**
+ * Return the remaining part of a uri after the given path (aka the path info.)
+ */
+const list<value> pathInfo(const list<value>& uri, const list<value>& path) {
+ if (isNil(path))
+ return uri;
+ return pathInfo(cdr(uri), cdr(path));
+}
+
+/**
+ * Convert a URI to an absolute URL.
+ */
+const string url(const string& uri, request_rec* r) {
+ if (contains(uri, "://"))
+ return uri;
+ ostringstream n;
+ const string s = scheme(r);
+ const string h = hostName(r, "localhost");
+ const int p = port(r, s == "https"? 443 : 80);
+ n << s << "://" << h;
+ if (!((s == "http" && p == 80) || (s == "https" && p == 443)))
+ n << ":" << p;
+ n << uri;
+ return str(n);
+}
+
+/**
+ * Convert a URI and a path to an absolute URL.
+ */
+const string url(const string& uri, const list<value>& p, request_rec* r) {
+ return url(uri + path(p), r);
+}
+
+/**
+ * Escape a URI.
+ */
+const char escape_c2x[] = "0123456789ABCDEF";
+const string escape(const string& uri) {
+ debug(uri, "httpd::escape::uri");
+ char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(uri) + 3);
+ const unsigned char* s = (const unsigned char *)c_str(uri);
+ unsigned char* d = (unsigned char*)copy;
+ unsigned c;
+ while ((c = *s)) {
+ if (apr_isalnum(c) || c == '_')
+ *d++ = (unsigned char)c;
+ else if (c == ' ')
+ *d++ = '+';
+ else {
+ *d++ = '%';
+ *d++ = escape_c2x[c >> 4];
+ *d++ = escape_c2x[c & 0xf];
+ }
+ ++s;
+ }
+ *d = '\0';
+ debug(copy, "httpd::escape::result");
+ return copy;
+}
+
+/**
+ * Unescape a URI.
+ */
+const string unescape(const string& uri) {
+ debug(uri, "httpd::unescape::uri");
+ char* b = const_cast<char*>(c_str(string(c_str(uri))));
+ ap_unescape_url(b);
+ debug(b, "httpd::unescape::result");
+ return b;
+}
+
+/**
+ * Unescape a list of key of value pairs representing query args.
+ */
+const list<value> unescapeArg(const list<value> a) {
+ return mklist<value>(car(a), unescape(cadr(a)));
+}
+
+const list<list<value> > unescapeArgs(const list<list<value> > args) {
+ debug(args, "httpd::unescape::args");
+ const list<list<value> > uargs = map<list<value>, list<value>>(unescapeArg, args);
+ debug(uargs, "httpd::unescape::result");
+ return uargs;
+}
+
+/**
+ * Returns a list of key value pairs from the args in a query string.
+ */
+const list<value> queryArg(const string& s) {
+ debug(s, "httpd::queryArg::string");
+ const list<string> t = tokenize("=", s);
+ if (isNil(cdr(t)))
+ return mklist<value>(c_str(car(t)), "");
+ return mklist<value>(c_str(car(t)), cadr(t));
+}
+
+const string fixupQueryArgs(const string& a) {
+ const list<string> t = tokenize("?", a);
+ if (isNil(t) || isNil(cdr(t)))
+ return a;
+ return join("&", t);
+}
+
+const list<list<value> > queryArgs(const string& a) {
+ return map<string, list<value>>(queryArg, tokenize("&", fixupQueryArgs(a)));
+}
+
+/**
+ * Returns a list of key value pairs from the args in an HTTP request.
+ */
+const list<list<value> > queryArgs(const request_rec* r) {
+ if (r->args == NULL)
+ return list<list<value> >();
+ return queryArgs(r->args);
+}
+
+/**
+ * Converts the args received in a POST to a list of key value pairs.
+ */
+const list<list<value> > postArgs(const list<value>& a) {
+ if (isNil(a))
+ return list<list<value> >();
+ const list<value> l = car(a);
+ return cons(l, postArgs(cdr(a)));
+}
+
+/**
+ * Setup the HTTP read policy.
+ */
+const int setupReadPolicy(request_rec* r) {
+ const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
+ if(rc != OK)
+ return rc;
+ ap_should_client_block(r);
+ if(r->read_chunked == true && r->remaining == 0)
+ r->chunked = true;
+ //apr_table_setn(r->headers_out, "Connection", "close");
+ return OK;
+}
+
+/**
+ * Read the content of a POST or PUT.
+ */
+const list<string> read(request_rec* r) {
+ char b[1024];
+ const size_t n = ap_get_client_block(r, b, sizeof(b));
+ if (n <= 0)
+ return list<string>();
+ return cons(string(b, n), read(r));
+}
+
+/**
+ * Write an HTTP result.
+ */
+const failable<int> writeResult(const failable<list<string> >& ls, const string& ct, request_rec* r) {
+ if (!hasContent(ls))
+ return mkfailure<int>(ls);
+ ostringstream os;
+ write(content(ls), os);
+ const string ob(str(os));
+
+ // Make sure browsers come back and check for updated dynamic content
+ apr_table_set(r->headers_out, "Cache-Control", "must-revalidate, max-age=0");
+ apr_table_set(r->headers_out, "Expires", "Tue, 01 Jan 1980 00:00:00 GMT");
+ apr_table_set(r->subprocess_env, "private-cache", "1");
+
+ // Compute and return an Etag for the returned content
+ const string etag(ap_md5_binary(r->pool, (const unsigned char*)c_str(ob), (int)length(ob)));
+
+ // Check for an If-None-Match header and just return a 304 not-modified status
+ // if the Etag matches the Etag presented by the client, to save bandwith
+ const char* match = apr_table_get(r->headers_in, "If-None-Match");
+ apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, c_str(etag)));
+ if (match != NULL && etag == match) {
+ r->status = HTTP_NOT_MODIFIED;
+ debug(r->status, "httpd::writeResult::status");
+ return OK;
+ }
+
+ debug(r->status, "httpd::writeResult::status");
+ debug(ct, "httpd::writeResult::contentType");
+ debug(ob, "httpd::writeResult::content");
+ ap_set_content_type(r, apr_pstrdup(r->pool, c_str(ct)));
+ ap_rwrite(c_str(ob), (int)length(ob), r);
+ return OK;
+}
+
+/**
+ * Report a request execution status.
+ */
+const int reportStatus(const failable<int>& rc) {
+ debug(rc, "httpd::reportStatus::rc");
+ if (!hasContent(rc)) {
+ const int r = rcode(rc);
+ return r == -1 ? HTTP_INTERNAL_SERVER_ERROR : r;
+ }
+ return content(rc);
+}
+
+/**
+ * Convert a value to an HTTPD request struc
+ */
+request_rec* request(const value& v) {
+ return (request_rec*)(long)(double)v;
+}
+
+/**
+ * Convert an HTTPD request struct to a value
+ */
+const value requestValue(request_rec* r) {
+ return value((double)(long)r);
+}
+
+/**
+ * Update request filters in an HTTPD redirect request.
+ * Similar to httpd/modules/http/http_request.c::update_r_in_filters.
+ */
+const bool redirectFilters(ap_filter_t* f, request_rec* from, request_rec* to) {
+ if (f == NULL)
+ return true;
+ if (f->r == from)
+ f->r = to;
+ return redirectFilters(f->next, from, to);
+}
+
+/**
+ * Create an HTTPD internal redirect request.
+ * Similar to httpd/modules/http/http_request.c::internal_internal_redirect.
+ */
+const failable<request_rec*> internalRedirectRequest(const string& nr_uri, request_rec* r) {
+ if (ap_is_recursion_limit_exceeded(r))
+ return mkfailure<request_rec*>("Redirect recursion limit exceeded", HTTP_INTERNAL_SERVER_ERROR);
+
+ // Create a new request
+ request_rec* nr = (request_rec*)apr_pcalloc(r->pool, sizeof(request_rec));
+ nr->connection = r->connection;
+ nr->server = r->server;
+ nr->pool = r->pool;
+ nr->method = r->method;
+ nr->method_number = r->method_number;
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ ap_parse_uri(nr, apr_pstrdup(nr->pool, c_str(nr_uri)));
+ nr->filename = apr_pstrdup(nr->pool, c_str(string("/redirected:") + nr_uri));
+ nr->request_config = ap_create_request_config(r->pool);
+ nr->per_dir_config = r->server->lookup_defaults;
+ nr->prev = r;
+ r->next = nr;
+
+ // Run create request hook
+ ap_run_create_request(nr);
+
+ // Inherit protocol info from the original request
+ nr->the_request = r->the_request;
+ nr->allowed = r->allowed;
+ nr->status = r->status;
+ nr->assbackwards = r->assbackwards;
+ nr->header_only = r->header_only;
+ nr->protocol = r->protocol;
+ nr->proto_num = r->proto_num;
+ nr->hostname = r->hostname;
+ nr->request_time = r->request_time;
+ nr->main = r->main;
+ nr->headers_in = r->headers_in;
+ nr->headers_out = apr_table_make(r->pool, 12);
+ nr->err_headers_out = r->err_headers_out;
+ nr->subprocess_env = r->subprocess_env;
+ nr->notes = apr_table_make(r->pool, 5);
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ nr->htaccess = r->htaccess;
+ nr->no_cache = r->no_cache;
+ nr->expecting_100 = r->expecting_100;
+ nr->no_local_copy = r->no_local_copy;
+ nr->read_length = r->read_length;
+ nr->vlist_validator = r->vlist_validator;
+ nr->user = r->user;
+
+ // Setup input and output filters
+ nr->proto_output_filters = r->proto_output_filters;
+ nr->proto_input_filters = r->proto_input_filters;
+ nr->output_filters = nr->proto_output_filters;
+ nr->input_filters = nr->proto_input_filters;
+ if (nr->main)
+ ap_add_output_filter_handle(ap_subreq_core_filter_handle, NULL, nr, nr->connection);
+ redirectFilters(nr->input_filters, r, nr);
+ redirectFilters(nr->output_filters, r, nr);
+ const int rrc = ap_run_post_read_request(nr);
+ if (rrc != OK && rrc != DECLINED)
+ return mkfailure<request_rec*>("Error handling internal redirect", rrc);
+
+ return nr;
+}
+
+/**
+ * Process an HTTPD internal redirect request.
+ * Similar to httpd/modules/http/http_request.c::ap_internal_redirect.
+ */
+const int internalRedirect(request_rec* nr) {
+ int status = ap_run_quick_handler(nr, 0);
+ if (status == DECLINED) {
+ status = ap_process_request_internal(nr);
+ if (status == OK)
+ status = ap_invoke_handler(nr);
+ }
+ if (status != OK) {
+ nr->status = status;
+ return OK;
+ }
+ ap_finalize_request_protocol(nr);
+ return OK;
+}
+
+/**
+ * Create and process an HTTPD internal redirect request.
+ */
+const int internalRedirect(const string& uri, request_rec* r) {
+ debug(uri, "httpd::internalRedirect");
+ const failable<request_rec*> nr = httpd::internalRedirectRequest(uri, r);
+ if (!hasContent(nr))
+ return rcode(nr);
+ return httpd::internalRedirect(content(nr));
+}
+
+/**
+ * Create an HTTPD sub request.
+ * Similar to httpd/server/request.c::make_sub_request
+ */
+const failable<request_rec*> internalSubRequest(const string& nr_uri, request_rec* r) {
+ if (ap_is_recursion_limit_exceeded(r))
+ return mkfailure<request_rec*>("Redirect recursion limit exceeded", HTTP_INTERNAL_SERVER_ERROR);
+
+ // Create a new sub pool
+ apr_pool_t *nrp;
+ apr_pool_create(&nrp, r->pool);
+ apr_pool_tag(nrp, "subrequest");
+
+ // Create a new POST request
+ request_rec* nr = (request_rec*)apr_pcalloc(nrp, sizeof(request_rec));
+ nr->connection = r->connection;
+ nr->server = r->server;
+ nr->pool = nrp;
+ nr->method = "POST";
+ nr->method_number = M_POST;
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ ap_parse_uri(nr, apr_pstrdup(nr->pool, c_str(nr_uri)));
+ nr->filename = apr_pstrdup(nr->pool, c_str(string("/subreq:") + nr_uri));
+ nr->request_config = ap_create_request_config(r->pool);
+ nr->per_dir_config = r->server->lookup_defaults;
+
+ // Inherit some of the protocol info from the parent request
+ nr->the_request = r->the_request;
+ nr->hostname = r->hostname;
+ nr->request_time = r->request_time;
+ nr->allowed = r->allowed;
+ nr->status = HTTP_OK;
+ nr->assbackwards = r->assbackwards;
+ nr->header_only = r->header_only;
+ nr->protocol = const_cast<char*>("INCLUDED");
+ nr->hostname = r->hostname;
+ nr->request_time = r->request_time;
+ nr->main = r;
+ nr->headers_in = apr_table_make(r->pool, 12);
+ nr->headers_out = apr_table_make(r->pool, 12);
+ nr->err_headers_out = apr_table_make(nr->pool, 5);
+ nr->subprocess_env = r->subprocess_env;
+ nr->subprocess_env = apr_table_copy(nr->pool, r->subprocess_env);
+ nr->notes = apr_table_make(r->pool, 5);
+ nr->htaccess = r->htaccess;
+ nr->no_cache = r->no_cache;
+ nr->expecting_100 = r->expecting_100;
+ nr->no_local_copy = r->no_local_copy;
+ nr->read_length = 0;
+ nr->vlist_validator = r->vlist_validator;
+ nr->user = r->user;
+
+ // Setup input and output filters
+ nr->proto_output_filters = r->proto_output_filters;
+ nr->proto_input_filters = r->proto_input_filters;
+ nr->output_filters = nr->proto_output_filters;
+ nr->input_filters = nr->proto_input_filters;
+ ap_add_output_filter_handle(ap_subreq_core_filter_handle, NULL, nr, nr->connection);
+
+ // Run create request hook
+ ap_run_create_request(nr);
+ nr->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+
+ return nr;
+}
+
+/**
+ * Return an HTTP external redirect request.
+ */
+const int externalRedirect(const string& uri, request_rec* r) {
+ debug(uri, "httpd::externalRedirect");
+ r->status = HTTP_MOVED_TEMPORARILY;
+ apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(uri)));
+ apr_table_setn(r->headers_out, "Cache-Control", "no-store");
+ apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
+ r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/") + uri));
+ return HTTP_MOVED_TEMPORARILY;
+}
+
+/**
+ * Put a value in the process user data.
+ */
+const bool putUserData(const string& k, const void* v, const server_rec* s) {
+ apr_pool_userdata_set((const void *)v, c_str(k), apr_pool_cleanup_null, s->process->pool);
+ return true;
+}
+
+/**
+ * Return a user data value.
+ */
+const void* userData(const string& k, const server_rec* s) {
+ void* v = NULL;
+ apr_pool_userdata_get(&v, c_str(k), s->process->pool);
+ return v;
+}
+
+#ifdef WANT_MAINTAINER_LOG
+
+/**
+ * Debug log.
+ */
+
+/**
+ * Log an optional value.
+ */
+const char* debugOptional(const char* s) {
+ if (s == NULL)
+ return "";
+ return s;
+}
+
+/**
+ * Log a header
+ */
+int debugHeader(unused void* r, const char* key, const char* value) {
+ cdebug << " header key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+/**
+ * Log an environment variable
+ */
+int debugEnv(unused void* r, const char* key, const char* value) {
+ cdebug << " var key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+/**
+ * Log a note.
+ */
+int debugNote(unused void* r, const char* key, const char* value) {
+ cdebug << " note key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+/**
+ * Log a request.
+ */
+const bool debugRequest(request_rec* r, const string& msg) {
+ gc_scoped_pool pool;
+ cdebug << msg << ":" << endl;
+ cdebug << " unparsed uri: " << debugOptional(r->unparsed_uri) << endl;
+ cdebug << " uri: " << debugOptional(r->uri) << endl;
+ cdebug << " path info: " << debugOptional(r->path_info) << endl;
+ cdebug << " filename: " << debugOptional(r->filename) << endl;
+ cdebug << " uri tokens: " << pathTokens(r->uri) << endl;
+ cdebug << " args: " << debugOptional(r->args) << endl;
+ cdebug << " server: " << debugOptional(r->server->server_hostname) << endl;
+ cdebug << " protocol: " << debugOptional(r->protocol) << endl;
+ cdebug << " method: " << debugOptional(r->method) << endl;
+ cdebug << " method number: " << r->method_number << endl;
+ cdebug << " content type: " << contentType(r) << endl;
+ cdebug << " content encoding: " << debugOptional(r->content_encoding) << endl;
+ apr_table_do(debugHeader, r, r->headers_in, NULL);
+ cdebug << " user: " << debugOptional(r->user) << endl;
+ cdebug << " auth type: " << debugOptional(r->ap_auth_type) << endl;
+ apr_table_do(debugEnv, r, r->subprocess_env, NULL);
+ apr_table_do(debugNote, r, r->notes, NULL);
+ return true;
+}
+
+#define debug_httpdRequest(r, msg) do { if (debug_islogging()) httpd::debugRequest(r, msg); } while(0)
+
+#else
+
+#define debug_httpdRequest(r, msg)
+
+#endif
+
+}
+}
+
+#endif /* tuscany_httpd_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf
new file mode 100755
index 0000000000..2da8f4f836
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+# 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.
+
+# Generate mass dynamic virtual hosting configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+vroot=`echo "import os; print os.path.realpath('$2')" | python`
+vhtdocs=$3
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+addr=`echo $conf | awk '{ print $7 }'`
+port=`$here/httpd-addr port $addr`
+pport=`$here/httpd-addr pport $addr`
+vhost=`$here/httpd-addr vhost $addr`
+
+htdocs=`echo $conf | awk '{ print $8 }'`
+mkdir -p $htdocs
+htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python`
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: mass-host-conf $*
+# Enable mass dynamic virtual hosting
+NameVirtualHost $vhost
+
+<VirtualHost $vhost>
+ServerName http://vhost.$host:$pport
+ServerAlias *.$host
+
+# Map /v/<app-name>/<path> to vroot/<app-name>/vhtdocs/<path>
+AliasMatch /v/([^/]+)(.*)$ $vroot/\$1/$vhtdocs/\$2
+
+Include conf/dvhost.conf
+
+# Configure authentication
+Include conf/noauth.conf
+Include conf/locauth.conf
+Include conf/pubauth.conf
+Include conf/adminauth.conf
+
+</VirtualHost>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf
new file mode 100755
index 0000000000..68bd5b4606
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+# 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.
+
+# Generate mass dynamic virtual hosting configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+ssladdr=`echo $sslconf | awk '{ print $6 }'`
+sslport=`$here/httpd-addr port $ssladdr`
+sslpport=`$here/httpd-addr pport $ssladdr`
+sslvhost=`$here/httpd-addr vhost $ssladdr`
+
+vhostconf=`cat $root/conf/httpd.conf | grep "# Generated by: mass-host-conf"`
+vroot=`echo $vhostconf | awk '{ print $6 }'`; vroot=`echo "import os; print os.path.realpath('$vroot')" | python`
+vhtdocs=`echo $vhostconf | awk '{ print $7 }'`
+
+htdocs=`echo $conf | awk '{ print $8 }'`
+mkdir -p $htdocs
+htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python`
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: mass-host-ssl-conf $*
+# Enable mass dynamic virtual hosting over HTTPS
+SSLStrictSNIVHostCheck Off
+
+# HTTPS dynamic virtual host
+NameVirtualHost $sslvhost
+<VirtualHost $sslvhost>
+ServerName https://vhost.$host:$sslpport
+ServerAlias *.$host
+
+# Map /v/<app-name>/<path> to vroot/<app-name>/vhtdocs/<path>
+AliasMatch /v/([^/]+)(.*)$ $vroot/\$1/$vhtdocs/\$2
+
+Include conf/dvhost-ssl.conf
+
+# Configure authentication
+Include conf/noauth-ssl.conf
+Include conf/locauth-ssl.conf
+Include conf/pubauth-ssl.conf
+Include conf/adminauth-ssl.conf
+
+# Configure tracking
+Include conf/tracking-ssl.conf
+
+</VirtualHost>
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-css b/sca-cpp/branches/lightweight-sca/modules/http/minify-css
new file mode 100755
index 0000000000..734d041aba
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-css
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# 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.
+
+# Minify a CSS file
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+pagespeed_prefix=`cat $here/pagespeed.prefix`
+
+if [ "${pagespeed_prefix}" = "" ]; then
+ minify_css="cat"
+else
+ minify_css="${pagespeed_prefix}/jsmin_bin"
+fi
+
+css="$1"
+mincss="$2"
+
+${minify_css} <${css} >${mincss}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-html b/sca-cpp/branches/lightweight-sca/modules/http/minify-html
new file mode 100755
index 0000000000..31bb329d94
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-html
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# 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.
+
+# Minify an HTML file
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+pagespeed_prefix=`cat $here/pagespeed.prefix`
+
+if [ "${pagespeed_prefix}" = "" ]; then
+ minify_html="cp"
+else
+ minify_html="${pagespeed_prefix}/minify_html_bin"
+fi
+
+html="$1"
+minhtml="$2"
+
+${minify_html} ${html} ${minhtml}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-js b/sca-cpp/branches/lightweight-sca/modules/http/minify-js
new file mode 100755
index 0000000000..5e7cbb1bc0
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-js
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# 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.
+
+# Minify a JS file
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+pagespeed_prefix=`cat $here/pagespeed.prefix`
+
+if [ "${pagespeed_prefix}" = "" ]; then
+ minify_js="cat"
+else
+ minify_js="${pagespeed_prefix}/jsmin_bin"
+fi
+
+js="$1"
+minjs="$2"
+
+${minify_js} <${js} >${minjs}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp b/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp
new file mode 100644
index 0000000000..2e308ecedb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp
@@ -0,0 +1,478 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTPD module for Tuscany Open authentication.
+ *
+ * This module allows multiple authentication mechanisms to co-exist in a
+ * single Web site:
+ * - OAuth1 using Tuscany's mod-tuscany-oauth1
+ * - OAuth2 using Tuscany's mod-tuscany-oauth2
+ * - OpenID using mod_auth_openid
+ * - Form-based using HTTPD's mod_auth_form
+ * - SSL certificate using SSLFakeBasicAuth and mod_auth_basic
+ */
+
+#include <sys/stat.h>
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "httpd.hpp"
+#include "http.hpp"
+#include "openauth.hpp"
+
+
+extern "C" {
+extern module AP_MODULE_DECLARE_DATA mod_tuscany_openauth;
+}
+
+namespace tuscany {
+namespace openauth {
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) {
+ }
+
+ const gc_pool p;
+ server_rec* server;
+};
+
+/**
+ * Authentication provider configuration.
+ */
+class AuthnProviderConf {
+public:
+ AuthnProviderConf() : name(), provider(NULL) {
+ }
+ AuthnProviderConf(const string name, const authn_provider* provider) : name(name), provider(provider) {
+ }
+
+ string name;
+ const authn_provider* provider;
+};
+
+/**
+ * Directory configuration.
+ */
+class DirConf {
+public:
+ DirConf(apr_pool_t* p, char* d) : p(p), dir(d), enabled(false), login("") {
+ }
+
+ const gc_pool p;
+ const char* dir;
+ bool enabled;
+ string login;
+ list<AuthnProviderConf> apcs;
+};
+
+#ifdef WANT_MAINTAINER_LOG
+
+/**
+ * Log session entries.
+ */
+int debugSessionEntry(unused void* r, const char* key, const char* value) {
+ cdebug << " session key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+const bool debugSession(request_rec* r, session_rec* z) {
+ apr_table_do(debugSessionEntry, r, z->entries, NULL);
+ return true;
+}
+
+#define debug_authSession(r, z) if (debug_islogging()) openauth::debugSession(r, z)
+
+#else
+
+#define debug_authSession(r, z)
+
+#endif
+
+/**
+ * Run the authnz hooks to authenticate a request.
+ */
+const failable<int> checkAuthnzProviders(const string& user, const string& pw, request_rec* r, const list<AuthnProviderConf>& apcs) {
+ if (isNil(apcs))
+ return mkfailure<int>("Authentication failure for: " + user);
+ const AuthnProviderConf apc = car<AuthnProviderConf>(apcs);
+ if (apc.provider == NULL || !apc.provider->check_password)
+ return checkAuthnzProviders(user, pw, r, cdr(apcs));
+
+ apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, c_str(apc.name));
+ const authn_status auth_result = apc.provider->check_password(r, c_str(user), c_str(pw));
+ apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE);
+ if (auth_result != AUTH_GRANTED)
+ return checkAuthnzProviders(user, pw, r, cdr(apcs));
+ return OK;
+}
+
+const failable<int> checkAuthnz(const string& user, const string& pw, request_rec* r, const DirConf& dc) {
+ if (substr(user, 0, 1) == "/" && pw == "password")
+ return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED);
+
+ if (isNil(dc.apcs)) {
+ const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION);
+ return checkAuthnzProviders(user, pw, r, mklist<AuthnProviderConf>(AuthnProviderConf(AUTHN_DEFAULT_PROVIDER, provider)));
+ }
+ return checkAuthnzProviders(user, pw, r, dc.apcs);
+}
+
+/**
+ * Return the user info from a form auth encrypted session cookie.
+ */
+static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL;
+static int (*ap_session_get_fn) (request_rec * r, session_rec * z, const char *key, const char **value) = NULL;
+
+const failable<value> userInfoFromSession(const string& realm, request_rec* r) {
+ debug("modopenauth::userInfoFromSession");
+ if (ap_session_load_fn == NULL)
+ ap_session_load_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_load);
+ session_rec *z = NULL;
+ ap_session_load_fn(r, &z);
+ if (z == NULL)
+ return mkfailure<value>("Couldn't retrieve user session");
+ debug_authSession(r, z);
+
+ if (ap_session_get_fn == NULL)
+ ap_session_get_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_get);
+ const char* user = NULL;
+ ap_session_get_fn(r, z, c_str(realm + "-user"), &user);
+ if (user == NULL)
+ return mkfailure<value>("Couldn't retrieve user id");
+ const char* pw = NULL;
+ ap_session_get_fn(r, z, c_str(realm + "-pw"), &pw);
+ if (pw == NULL)
+ return mkfailure<value>("Couldn't retrieve password");
+ return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", string(user)), mklist<value>("password", string(pw))));
+}
+
+/**
+ * Return the user info from a form auth session cookie.
+ */
+const failable<value> userInfoFromCookie(const value& sid, const string& realm, request_rec* r) {
+ const list<list<value>> info = httpd::queryArgs(sid);
+ debug(info, "modopenauth::userInfoFromCookie::info");
+ const list<value> user = assoc<value>(realm + "-user", info);
+ if (isNil(user))
+ return userInfoFromSession(realm, r);
+ const list<value> pw = assoc<value>(realm + "-pw", info);
+ if (isNil(pw))
+ return mkfailure<value>("Couldn't retrieve password");
+ return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", cadr(user)), mklist<value>("password", cadr(pw))));
+}
+
+/**
+ * Return the user info from a basic auth header.
+ */
+const failable<value> userInfoFromHeader(const char* header, const string& realm, request_rec* r) {
+ debug(header, "modopenauth::userInfoFromHeader::header");
+ if (strcasecmp(ap_getword(r->pool, &header, ' '), "Basic"))
+ return mkfailure<value>("Wrong authentication scheme");
+
+ while (apr_isspace(*header))
+ header++;
+ char *decoded_line = (char*)apr_palloc(r->pool, apr_base64_decode_len(header) + 1);
+ int length = apr_base64_decode(decoded_line, header);
+ decoded_line[length] = '\0';
+
+ const string user(ap_getword_nulls(r->pool, const_cast<const char**>(&decoded_line), ':'));
+ const string pw(decoded_line);
+
+ return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", user), mklist<value>("password", pw)));
+}
+
+/**
+ * Handle an authenticated request.
+ */
+const failable<int> authenticated(const list<list<value> >& info, request_rec* r) {
+ debug(info, "modopenauth::authenticated::info");
+
+ // Store user info in the request
+ const list<value> realm = assoc<value>("realm", info);
+ if (isNil(realm) || isNil(cdr(realm)))
+ return mkfailure<int>("Couldn't retrieve realm");
+ apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "REALM"), apr_pstrdup(r->pool, c_str(cadr(realm))));
+
+ const list<value> id = assoc<value>("id", info);
+ if (isNil(id) || isNil(cdr(id)))
+ return mkfailure<int>("Couldn't retrieve user id");
+ r->user = apr_pstrdup(r->pool, c_str(cadr(id)));
+
+ apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, c_str(cadr(id))));
+ return OK;
+}
+
+/**
+ * Check user authentication.
+ */
+static int checkAuthn(request_rec *r) {
+ gc_scoped_pool pool(r->pool);
+
+ // Decline if we're not enabled or AuthType is not set to Open
+ const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth);
+ if (!dc.enabled)
+ return DECLINED;
+ const char* atype = ap_auth_type(r);
+ if (atype == NULL || strcasecmp(atype, "Open"))
+ return DECLINED;
+ debug_httpdRequest(r, "modopenauth::checkAuthn::input");
+ debug(atype, "modopenauth::checkAuthn::auth_type");
+
+ // Get the request args
+ const list<list<value> > args = httpd::queryArgs(r);
+
+ // Get session id from the request
+ const maybe<string> sid = sessionID(r, "TuscanyOpenAuth");
+ if (hasContent(sid)) {
+ // Decline if the session id was not created by this module
+ const string stype = substr(content(sid), 0, 7);
+ if (stype == "OAuth2_" || stype == "OAuth1_" || stype == "OpenID_")
+ return DECLINED;
+
+ // Retrieve the auth realm
+ const char* aname = ap_auth_name(r);
+ if (aname == NULL)
+ return httpd::reportStatus(mkfailure<int>("Missing AuthName"));
+
+ // Extract user info from the session id
+ const failable<value> userinfo = userInfoFromCookie(content(sid), aname, r);
+ if (hasContent(userinfo)) {
+
+ // Try to authenticate the request
+ const value uinfo = content(userinfo);
+ const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc);
+ if (!hasContent(authz)) {
+
+ // Authentication failed, redirect to login page
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(login(dc.login, value(), 1, r));
+ }
+
+ // Successfully authenticated, store the user info in the request
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(authenticated(uinfo, r));
+ }
+ }
+
+ // Get basic auth header from the request
+ const char* header = apr_table_get(r->headers_in, (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authorization" : "Authorization");
+ if (header != NULL) {
+
+ // Retrieve the auth realm
+ const char* aname = ap_auth_name(r);
+ if (aname == NULL)
+ return httpd::reportStatus(mkfailure<int>("Missing AuthName"));
+
+ // Extract user info from the session id
+ const failable<value> info = userInfoFromHeader(header, aname, r);
+ if (hasContent(info)) {
+
+ // Try to authenticate the request
+ const value uinfo = content(info);
+ const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc);
+ if (!hasContent(authz)) {
+
+ // Authentication failed, redirect to login page
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(login(dc.login, value(), 1, r));
+ }
+
+ // Successfully authenticated, store the user info in the request
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(authenticated(uinfo, r));
+ }
+ }
+
+ // Decline if the request is for another authentication provider
+ if (!isNil(assoc<value>("openid_identifier", args)))
+ return DECLINED;
+
+ // Redirect to the login page, unless we have a session id from another module
+ if (hasContent(sessionID(r, "TuscanyOpenIDAuth")) ||
+ hasContent(sessionID(r, "TuscanyOAuth1")) ||
+ hasContent(sessionID(r, "TuscanyOAuth2")))
+ return DECLINED;
+
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(login(dc.login, value(), value(), r));
+}
+
+/**
+ * Save the auth session cookie in the response.
+ */
+static int sessionCookieSave(request_rec* r, session_rec* z) {
+ gc_scoped_pool pool(r->pool);
+
+ const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth);
+ if (!dc.enabled)
+ return DECLINED;
+
+ debug(c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r))), "modopenauth::setcookie");
+ apr_table_set(r->err_headers_out, "Set-Cookie", c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r))));
+ return OK;
+}
+
+/**
+ * Load the auth session cookie from the request. Similar
+ */
+static int sessionCookieLoad(request_rec* r, session_rec** z) {
+ gc_scoped_pool pool(r->pool);
+
+ const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth);
+ if (!dc.enabled)
+ return DECLINED;
+
+ // First look in the notes
+ const char* note = apr_pstrcat(r->pool, "mod_openauth", "TuscanyOpenAuth", NULL);
+ session_rec* zz = (session_rec*)(void*)apr_table_get(r->notes, note);
+ if (zz != NULL) {
+ *z = zz;
+ return OK;
+ }
+
+ // Parse the cookie
+ const maybe<string> sid = openauth::sessionID(r, "TuscanyOpenAuth");
+
+ // Create a new session
+ zz = (session_rec*)apr_pcalloc(r->pool, sizeof(session_rec));
+ zz->pool = r->pool;
+ zz->entries = apr_table_make(r->pool, 10);
+ zz->encoded = hasContent(sid)? c_str(content(sid)) : NULL;
+ zz->uuid = (apr_uuid_t *) apr_pcalloc(r->pool, sizeof(apr_uuid_t));
+ *z = zz;
+
+ // Store it in the notes
+ apr_table_setn(r->notes, note, (char*)zz);
+
+ return OK;
+}
+
+/**
+ * Process the module configuration.
+ */
+int postConfigMerge(ServerConf& mainsc, server_rec* s) {
+ if (s == NULL)
+ return OK;
+ debug(httpd::serverName(s), "modopenauth::postConfigMerge::serverName");
+
+ return postConfigMerge(mainsc, s->next);
+}
+
+int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) {
+ gc_scoped_pool pool(p);
+
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_openauth);
+ debug(httpd::serverName(s), "modopenauth::postConfig::serverName");
+
+ // Merge server configurations
+ return postConfigMerge(sc, s);
+}
+
+/**
+ * Child process initialization.
+ */
+void childInit(apr_pool_t* p, server_rec* s) {
+ gc_scoped_pool pool(p);
+
+ ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_openauth);
+ if(psc == NULL) {
+ cfailure << "[Tuscany] Due to one or more errors mod_tuscany_openauth loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+ }
+ ServerConf& sc = *psc;
+
+ // Merge the updated configuration into the virtual hosts
+ postConfigMerge(sc, s->next);
+}
+
+/**
+ * Configuration commands.
+ */
+const char* confEnabled(cmd_parms *cmd, void *c, const int arg) {
+ gc_scoped_pool pool(cmd->pool);
+ DirConf& dc = httpd::dirConf<DirConf>(c);
+ dc.enabled = (bool)arg;
+ return NULL;
+}
+const char* confLogin(cmd_parms *cmd, void *c, const char* arg) {
+ gc_scoped_pool pool(cmd->pool);
+ DirConf& dc = httpd::dirConf<DirConf>(c);
+ dc.login = arg;
+ return NULL;
+}
+const char* confAuthnProvider(cmd_parms *cmd, void *c, const char* arg) {
+ gc_scoped_pool pool(cmd->pool);
+ DirConf& dc = httpd::dirConf<DirConf>(c);
+
+ // Lookup and cache the Authn provider
+ const authn_provider* provider = (authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, arg, AUTHN_PROVIDER_VERSION);
+ if (provider == NULL)
+ return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", arg);
+ if (!provider->check_password)
+ return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support password authentication", arg);
+ dc.apcs = append<AuthnProviderConf>(dc.apcs, mklist<AuthnProviderConf>(AuthnProviderConf(arg, provider)));
+ return NULL;
+}
+
+/**
+ * HTTP server module declaration.
+ */
+const command_rec commands[] = {
+ AP_INIT_ITERATE("AuthOpenAuthProvider", (const char*(*)())confAuthnProvider, NULL, OR_AUTHCFG, "Auth providers for a directory or location"),
+ AP_INIT_FLAG("AuthOpenAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "Tuscany Open Auth authentication On | Off"),
+ AP_INIT_TAKE1("AuthOpenAuthLoginPage", (const char*(*)())confLogin, NULL, OR_AUTHCFG, "Tuscany Open Auth login page"),
+ {NULL, NULL, NULL, 0, NO_ARGS, NULL}
+};
+
+void registerHooks(unused apr_pool_t *p) {
+ ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_check_authn(checkAuthn, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF);
+ ap_hook_session_load(sessionCookieLoad, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_session_save(sessionCookieSave, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+}
+}
+
+extern "C" {
+
+module AP_MODULE_DECLARE_DATA mod_tuscany_openauth = {
+ STANDARD20_MODULE_STUFF,
+ // dir config and merger
+ tuscany::httpd::makeDirConf<tuscany::openauth::DirConf>, NULL,
+ // server config and merger
+ tuscany::httpd::makeServerConf<tuscany::openauth::ServerConf>, NULL,
+ // commands and hooks
+ tuscany::openauth::commands, tuscany::openauth::registerHooks
+};
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf
new file mode 100755
index 0000000000..5914bd1df4
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal mod-security audit configuration.
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+mkdir -p $root/tmp
+
+cat >>$root/conf/mod-security.conf <<EOF
+# Generated by: mod-security-audit-conf $*
+# Enable mod-security audit log
+SecAuditEngine RelevantOnly
+SecAuditLogRelevantStatus "^(?:5|4(?!04))"
+SecAuditLogParts ABIJDEFHKZ
+SecAuditLogType Serial
+Include conf/mod-security-audit-log.conf
+
+EOF
+
+# Configure audit logging
+cat >$root/conf/mod-security-audit-log.conf <<EOF
+# Generated by: mod-security-audit-conf $*
+SecAuditLog $root/logs/secaudit_log
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf
new file mode 100755
index 0000000000..5d03fc5cfb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal mod-security configuration.
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+modsecurity_prefix=`cat $here/modsecurity.prefix`
+
+mkdir -p $root/tmp
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: mod-security-conf $*
+# Load support for mod-security
+LoadModule unique_id_module ${modules_prefix}/modules/mod_unique_id.so
+LoadModule security2_module $modsecurity_prefix/lib/mod_security2.so
+
+EOF
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: mod-security-conf $*
+# Enable mod-security
+Include conf/mod-security.conf
+
+EOF
+
+cat >$root/conf/mod-security.conf <<EOF
+# Generated by: mod-security-conf $*
+# Enable mod-security rules
+SecRuleEngine On
+SecDefaultAction "phase:2,pass,nolog,auditlog"
+
+#SecDebugLog $root/logs//modsec_debug_log
+#SecDebugLogLevel 3
+
+# Process request bodies
+SecRequestBodyAccess Off
+SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
+SecRule REQUEST_HEADERS:Content-Type "application/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
+SecRequestBodyLimit 13107200
+SecRequestBodyNoFilesLimit 131072
+SecRequestBodyInMemoryLimit 131072
+SecRequestBodyLimitAction Reject
+
+# Verify that we've correctly processed the request body
+SecRule REQBODY_ERROR "!@eq 0" "phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
+
+# By default be strict with what we accept in multipart/form-data request body
+SecRule MULTIPART_STRICT_ERROR "!@eq 0" "phase:2,t:none,log,deny,status:44,msg:'Multipart request body failed strict validation: \
+PE %{REQBODY_PROCESSOR_ERROR}, \
+BQ %{MULTIPART_BOUNDARY_QUOTED}, \
+BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
+DB %{MULTIPART_DATA_BEFORE}, \
+DA %{MULTIPART_DATA_AFTER}, \
+HF %{MULTIPART_HEADER_FOLDING}, \
+LF %{MULTIPART_LF_LINE}, \
+SM %{MULTIPART_SEMICOLON_MISSING}, \
+IQ %{MULTIPART_INVALID_QUOTING}, \
+IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
+IH %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
+
+# Did we see anything that might be a boundary?
+SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" "phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'"
+
+# Avoid a potential RegEx DoS condition
+SecPcreMatchLimit 50000
+SecPcreMatchLimitRecursion 50000
+SecRule TX:/^MSC_/ "!@streq 0" "phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
+
+# Detect slow DoS attacks
+SecRule RESPONSE_STATUS "@streq 408" "phase:5,t:none,nolog,pass, setvar:ip.slow_dos_counter=+1,expirevar:ip.slow_dos_counter=60"
+SecRule IP:SLOW_DOS_COUNTER "@gt 5" "phase:1,t:none,log,drop, msg:'Client Connection Dropped due to high # of slow DoS alerts'"
+SecWriteStateLimit 50
+
+# Process response bodies
+SecResponseBodyAccess Off
+SecResponseBodyMimeType text/plain text/html text/xml application/xml
+SecResponseBodyLimit 524288
+SecResponseBodyLimitAction ProcessPartial
+
+# The location where mod-security stores temporary files
+SecTmpDir $root/tmp/
+SecDataDir $root/tmp/
+
+# Use & as application/x-www-form-urlencoded parameter separator
+SecArgumentSeparator &
+
+# Settle on version 0 (zero) cookies.
+SecCookieFormat 0
+
+# Enable anomaly scoring
+SecAction "phase:1,id:'981206',t:none,nolog,pass,setvar:tx.anomaly_score_blocking=on"
+SecAction "phase:1,id:'981207',t:none,nolog,pass, \
+setvar:tx.critical_anomaly_score=5, \
+setvar:tx.error_anomaly_score=4, \
+setvar:tx.warning_anomaly_score=3, \
+setvar:tx.notice_anomaly_score=2"
+SecAction "phase:1,id:'981208',t:none,nolog,pass,setvar:tx.inbound_anomaly_score_level=10"
+SecAction "phase:1,id:'981209',t:none,nolog,pass,setvar:tx.outbound_anomaly_score_level=8"
+
+# Paranoid mode
+SecAction "phase:1,id:'981210',t:none,nolog,pass,setvar:tx.paranoid_mode=0"
+
+# HTTP policy settings
+SecAction "phase:1,id:'981211',t:none,nolog,pass,setvar:tx.max_num_args=255"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.arg_name_length=100"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.arg_length=400"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.total_arg_length=64000"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.max_file_size=1048576"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.combined_file_sizes=1048576"
+SecAction "phase:1,id:'981212',t:none,nolog,pass, \
+setvar:'tx.allowed_methods=GET HEAD POST PUT OPTIONS DELETE CONNECT', \
+setvar:'tx.allowed_request_content_type=application/x-www-form-urlencoded multipart/form-data text/xml application/xml application/json application/json-rpc application/atom+xml', \
+setvar:'tx.allowed_http_versions=HTTP/0.9 HTTP/1.0 HTTP/1.1', \
+setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/', \
+setvar:'tx.restricted_headers=/Proxy-Connection/ /Lock-Token/ /Content-Range/ /Translate/ /via/ /if/'"
+
+# Brute force protection
+SecAction "phase:1,id:'981214',t:none,nolog,pass, \
+setvar:'tx.brute_force_protected_urls=/login', \
+setvar:'tx.brute_force_burst_time_slice=60', \
+setvar:'tx.brute_force_counter_threshold=10', \
+setvar:'tx.brute_force_block_timeout=300'"
+
+# DoS protection
+SecAction "phase:1,id:'981215',t:none,nolog,pass, \
+setvar:'tx.dos_burst_time_slice=60', \
+setvar:'tx.dos_counter_threshold=100', \
+setvar:'tx.dos_block_timeout=600'"
+
+# Check UTF-8 encoding
+SecAction "phase:1,id:'981216',t:none,nolog,pass,setvar:tx.crs_validate_utf8_encoding=1"
+
+# Global and IP collections
+SecRule REQUEST_HEADERS:User-Agent "^(.*)$" "phase:1,id:'981217',t:none,pass,nolog,t:sha1,t:hexEncode,setvar:tx.ua_hash=%{matched_var}"
+SecRule REQUEST_HEADERS:x-forwarded-for "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b" "phase:1,id:'981225',t:none,pass,nolog,capture,setvar:tx.real_ip=%{tx.1}"
+SecRule &TX:REAL_IP "!@eq 0" "phase:1,id:'981226',t:none,pass,nolog,initcol:global=global,initcol:ip=%{tx.real_ip}_%{tx.ua_hash}"
+SecRule &TX:REAL_IP "@eq 0" "phase:1,id:'981218',t:none,pass,nolog,initcol:global=global,initcol:ip=%{remote_addr}_%{tx.ua_hash}"
+
+# Include all base mod-security CRS rules
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_20_protocol_violations.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_xss_attacks.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_50_outbound.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_35_bad_robots.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_47_common_exceptions.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_60_correlation.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_40_generic_attacks.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_21_protocol_anomalies.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_30_http_policy.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_49_inbound_blocking.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_sql_injection_attacks.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_45_trojans.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_59_outbound_blocking.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_23_request_limits.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_42_tight_security.conf
+
+# Include some optional mod-security CRS rules
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_10_ignore_static.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_13_xml_enabler.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_25_cc_known.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_42_comment_spam.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_47_skip_outbound_checks.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_55_application_defects.conf
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp b/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp
new file mode 100644
index 0000000000..b66cd29959
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTPD module used to tunnel traffic over an HTTPS connection.
+ */
+
+#include <sys/stat.h>
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "httpd.hpp"
+#include "http.hpp"
+
+// Ignore cast align warnings in APR macros
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wcast-align"
+#endif
+
+extern "C" {
+extern module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel;
+}
+
+namespace tuscany {
+namespace httpd {
+namespace modssltunnel {
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) {
+ }
+
+ const gc_pool p;
+ server_rec* server;
+ string pass;
+ string host;
+ string path;
+ string ca;
+ string cert;
+ string key;
+};
+
+extern "C" {
+extern module AP_DECLARE_DATA core_module;
+}
+
+/**
+ * Process the module configuration.
+ */
+int M_SSLTUNNEL;
+int postConfigMerge(ServerConf& mainsc, apr_pool_t* p, server_rec* s) {
+ if (s == NULL)
+ return OK;
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_ssltunnel);
+ debug(httpd::serverName(s), "modssltunnel::postConfigMerge::serverName");
+
+ // Merge configuration from main server
+ if (length(sc.ca) == 0 && length(mainsc.ca) !=0)
+ sc.ca = mainsc.ca;
+ if (length(sc.cert) == 0 && length(mainsc.cert) !=0)
+ sc.cert = mainsc.cert;
+ if (length(sc.key) == 0 && length(mainsc.key) !=0)
+ sc.key = mainsc.key;
+
+ // Parse the configured TunnelPass URI
+ if (length(sc.pass) != 0) {
+ apr_uri_t uri;
+ apr_status_t prc = apr_uri_parse(p, c_str(sc.pass), &uri);
+ if (prc != APR_SUCCESS) {
+ mkfailure<int>("Couldn't parse TunnelPass: " + sc.pass + ", " + http::apreason(prc));
+ return prc;
+ }
+ sc.host = uri.hostname;
+ sc.path = uri.path;
+ }
+ return postConfigMerge(mainsc, p, s->next);
+}
+
+int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) {
+ gc_scoped_pool pool(p);
+
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_ssltunnel);
+ debug(httpd::serverName(s), "modssltunnel::postConfig::serverName");
+
+ // Register the SSLTUNNEL method
+ M_SSLTUNNEL = ap_method_register(p, "SSLTUNNEL");
+
+ // Merge and process server configurations
+ return postConfigMerge(sc, p, s);
+}
+
+/**
+ * Close a connection.
+ */
+const int close(conn_rec* conn, apr_socket_t* csock) {
+ debug("modssltunnel::close");
+ apr_socket_close(csock);
+ conn->aborted = 1;
+ return OK;
+}
+
+/**
+ * Abort a connection.
+ */
+const int abort(conn_rec* conn, apr_socket_t* csock, const string& reason) {
+ debug("modssltunnel::abort");
+ apr_socket_close(csock);
+ conn->aborted = 1;
+ return httpd::reportStatus(mkfailure<int>(reason));
+}
+
+/**
+ * Tunnel traffic from a client connection to a target URL.
+ */
+int tunnel(conn_rec* conn, const string& ca, const string& cert, const string& key, const string& url, const string& preamble, const gc_pool& p, unused ap_filter_t* ifilter, ap_filter_t* ofilter) {
+
+ // Create input/output bucket brigades
+ apr_bucket_brigade* ib = apr_brigade_create(pool(p), conn->bucket_alloc);
+ apr_bucket_brigade* ob = apr_brigade_create(pool(p), conn->bucket_alloc);
+
+ // Get client connection socket
+ apr_socket_t* csock = (apr_socket_t*)ap_get_module_config(conn->conn_config, &core_module);
+
+ // Open connection to target
+ http::CURLSession cs(ca, cert, key, "", 0);
+ const failable<bool> crc = http::connect(url, cs);
+ if (!hasContent(crc))
+ return abort(conn, csock, reason(crc));
+ apr_socket_t* tsock = http::sock(cs);
+
+ // Send preamble
+ if (length(preamble) != 0) {
+ debug(preamble, "modssltunnel::tunnel::sendPreambleToTarget");
+ const failable<bool> src = http::send(c_str(preamble), length(preamble), cs);
+ if (!hasContent(src))
+ return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
+ }
+
+ // Create a pollset for the client and target sockets
+ apr_pollset_t* pollset;
+ apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0);
+ if (cprc != APR_SUCCESS)
+ return abort(conn, csock, http::apreason(cprc));
+ const apr_pollfd_t* cpollfd = http::pollfd(csock, APR_POLLIN, p);
+ apr_pollset_add(pollset, cpollfd);
+ const apr_pollfd_t* tpollfd = http::pollfd(tsock, APR_POLLIN, p);
+ apr_pollset_add(pollset, tpollfd);
+
+ // Relay traffic in both directions until end of stream
+ const apr_pollfd_t* pollfds = cpollfd;
+ apr_int32_t pollcount = 1;
+ for(;;) {
+ for (; pollcount > 0; pollcount--, pollfds++) {
+ if (pollfds->rtnevents & APR_POLLIN) {
+ if (pollfds->desc.s == csock) {
+
+ // Receive buckets from client
+ const apr_status_t getrc = ap_get_brigade(conn->input_filters, ib, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
+ if (getrc != APR_SUCCESS)
+ return abort(conn, csock, string("Couldn't receive from client"));
+
+ for (apr_bucket* bucket = APR_BRIGADE_FIRST(ib); bucket != APR_BRIGADE_SENTINEL(ib); bucket = APR_BUCKET_NEXT(bucket)) {
+ if (APR_BUCKET_IS_FLUSH(bucket))
+ continue;
+
+ // Client connection closed
+ if (APR_BUCKET_IS_EOS(bucket))
+ return close(conn, csock);
+
+ const char *data;
+ apr_size_t rl;
+ apr_bucket_read(bucket, &data, &rl, APR_BLOCK_READ);
+ if (rl > 0) {
+ debug(string(data, rl), "modssltunnel::tunnel::sendToTarget");
+
+ // Send to target
+ const failable<bool> src = http::send(data, rl, cs);
+ if (!hasContent(src))
+ return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
+ }
+ }
+ apr_brigade_cleanup(ib);
+ } else {
+
+ // Receive from target
+ char data[8192];
+ const failable<size_t> frl = http::recv(data, sizeof(data), cs);
+ if (!hasContent(frl))
+ return abort(conn, csock, string("Couldn't receive from target") + reason(frl));
+ const size_t rl = content(frl);
+
+ // Target connection closed
+ if (rl == 0)
+ return close(conn, csock);
+
+
+ // Send bucket to client
+ debug(string(data, rl), "modssltunnel::tunnel::sendToClient");
+ APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_transient_create(data, rl, conn->bucket_alloc));
+ APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_flush_create(conn->bucket_alloc));
+ if (ap_pass_brigade(ofilter, ob) != APR_SUCCESS)
+ return abort(conn, csock, "Couldn't send data bucket to client");
+ apr_brigade_cleanup(ob);
+ }
+ }
+
+ // Error
+ if (pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) {
+ if (pollfds->desc.s == csock)
+ return abort(conn, csock, "Couldn't receive from client");
+ else
+ return abort(conn, csock, "Couldn't receive from target");
+ }
+ }
+
+ // Poll the client and target sockets
+ debug("modssltunnel::tunnel::poll");
+ apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds);
+ if (pollrc != APR_SUCCESS)
+ return abort(conn, csock, "Couldn't poll sockets");
+ debug(pollcount, "modssltunnel::tunnel::pollfds");
+ }
+
+ // Close client connection
+ return close(conn, csock);
+}
+
+/**
+ * Return the first connection filter in a list of filters.
+ */
+ap_filter_t* connectionFilter(ap_filter_t* f) {
+ if (f == NULL)
+ return f;
+ if (f->frec->ftype < AP_FTYPE_CONNECTION)
+ return connectionFilter(f->next);
+ return f;
+}
+
+/**
+ * Process a client connection and relay it to a tunnel.
+ */
+int processConnection(conn_rec *conn) {
+ // Only allow configured virtual hosts
+ if (!conn->base_server->is_virtual)
+ return DECLINED;
+ if (ap_get_module_config(conn->base_server->module_config, &mod_tuscany_ssltunnel) == NULL)
+ return DECLINED;
+
+ gc_scoped_pool pool(conn->pool);
+
+ // Get the server configuration
+ const ServerConf& sc = httpd::serverConf<ServerConf>(conn->base_server, &mod_tuscany_ssltunnel);
+ if (length(sc.pass) == 0)
+ return DECLINED;
+ debug(sc.pass, "modssltunnel::processConnection::pass");
+
+ // Run the tunnel
+ const string preamble = string("SSLTUNNEL ") + sc.path + string(" HTTP/1.1\r\nHost: ") + sc.host + string("\r\n\r\n");
+ debug(preamble, "modssltunnel::processConnection::preamble");
+ return tunnel(conn, sc.ca, sc.cert, sc.key, sc.pass, preamble, gc_pool(conn->pool), connectionFilter(conn->input_filters), connectionFilter(conn->output_filters));
+}
+
+/**
+ * Tunnel a SSLTUNNEL request to a target host/port.
+ */
+int handler(request_rec* r) {
+ if (r->method_number != M_SSLTUNNEL)
+ return DECLINED;
+
+ // Only allow HTTPS
+ if (strcmp(r->server->server_scheme, "https"))
+ return DECLINED;
+
+ gc_scoped_pool pool(r->pool);
+
+ // Build the target URL
+ debug(r->uri, "modssltunnel::handler::uri");
+ const list<value> path(pathValues(r->uri));
+ const string url = string(cadr(path)) + ":" + caddr(path);
+ debug(url, "modssltunnel::handler::target");
+
+ // Run the tunnel
+ return tunnel(r->connection, "", "", "", url, "", gc_pool(r->pool), connectionFilter(r->proto_input_filters), connectionFilter(r->proto_output_filters));
+}
+
+/**
+ * Configuration commands.
+ */
+const char* confTunnelPass(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_ssltunnel);
+ sc.pass = arg;
+ return NULL;
+}
+const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_ssltunnel);
+ sc.ca = arg;
+ return NULL;
+}
+const char* confCertFile(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_ssltunnel);
+ sc.cert = arg;
+ return NULL;
+}
+const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_ssltunnel);
+ sc.key = arg;
+ return NULL;
+}
+
+/**
+ * HTTP server module declaration.
+ */
+const command_rec commands[] = {
+ AP_INIT_TAKE1("TunnelPass", (const char*(*)())confTunnelPass, NULL, RSRC_CONF, "Tunnel server name"),
+ AP_INIT_TAKE1("TunnelSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "Tunnel SSL CA certificate file"),
+ AP_INIT_TAKE1("TunnelSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "Tunnel SSL certificate file"),
+ AP_INIT_TAKE1("TunnelSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "Tunnel SSL certificate key file"),
+ {NULL, NULL, NULL, 0, NO_ARGS, NULL}
+};
+
+void registerHooks(unused apr_pool_t *p) {
+ ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_process_connection(processConnection, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+}
+}
+}
+
+extern "C" {
+
+module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel = {
+ STANDARD20_MODULE_STUFF,
+ // dir config and merger
+ NULL, NULL,
+ // server config and merger
+ tuscany::httpd::makeServerConf<tuscany::httpd::modssltunnel::ServerConf>, NULL,
+ // commands and hooks
+ tuscany::httpd::modssltunnel::commands, tuscany::httpd::modssltunnel::registerHooks
+};
+
+}
+
+// Reenable cast align warnings
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic warning "-Wcast-align"
+#endif
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf
new file mode 100755
index 0000000000..f4715b3a1c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD form authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+if [ "$3" = "" ]; then
+ pw=`cat $root/cert/ca.key | head -2 | tail -1`
+else
+ pw="$3"
+fi
+
+# Configure HTTPD mod_tuscany_openauth module
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: open-auth-conf $*
+# Load support for Open authentication
+LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth$libsuffix
+
+EOF
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: open-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate form authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: open-auth-conf $*
+# Enable Tuscany open authentication
+<Location />
+AuthType Open
+AuthName "$host"
+AuthOpenAuthProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+Session On
+SessionCryptoPassphrase $pw
+AuthOpenAuth On
+AuthOpenAuthLoginPage /login/
+Require valid-user
+</Location>
+
+# Use HTTPD form-based authentication
+<Location /login/dologin>
+AuthType Form
+AuthName "$host"
+AuthFormProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+AuthFormLoginRequiredLocation /login/?openauth_attempt=1
+AuthFormLogoutLocation /
+Require valid-user
+SetHandler form-login-handler
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp b/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp
new file mode 100644
index 0000000000..3ffa88d362
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_openauth_hpp
+#define tuscany_openauth_hpp
+
+/**
+ * Tuscany Open auth support utility functions.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../json/json.hpp"
+#include "../http/httpd.hpp"
+#include "../http/http.hpp"
+
+namespace tuscany {
+namespace openauth {
+
+/**
+ * Return the session id from a request.
+ */
+const char* cookieName(const char* cs) {
+ if (*cs != ' ')
+ return cs;
+ return cookieName(cs + 1);
+}
+const maybe<string> sessionID(const list<string>& c, const string& key) {
+ if (isNil(c))
+ return maybe<string>();
+ const string cn = cookieName(c_str(car(c)));
+ const size_t i = find(cn, "=");
+ if (i < length(cn)) {
+ const list<string> kv = mklist<string>(substr(cn, 0, i), substr(cn, i+1));
+ if (!isNil(kv) && !isNil(cdr(kv))) {
+ if (car(kv) == key)
+ return cadr(kv);
+ }
+ }
+ return sessionID(cdr(c), key);
+}
+
+const maybe<string> sessionID(const request_rec* r, const string& key) {
+ const string c = httpd::cookie(r);
+ debug(c, "openauth::sessionid::cookies");
+ if (length(c) == 0)
+ return maybe<string>();
+ return sessionID(tokenize(";", c), key);
+}
+
+/**
+ * Convert a session id to a cookie string.
+ */
+const string cookie(const string& key, const string& sid, const string& domain) {
+ const time_t t = time(NULL) + 86400;
+ char exp[32];
+ strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t));
+ const string c = key + string("=") + sid + "; expires=" + string(exp) + "; domain=." + httpd::realm(domain) + "; path=/";
+ debug(c, "openauth::cookie");
+ return c;
+}
+
+/**
+ * Redirect to the configured login page.
+ */
+const failable<int> login(const string& page, const value& ref, const value& attempt, request_rec* r) {
+ const list<list<value> > rarg = ref == string("/")? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_referrer", httpd::escape(httpd::url(isNil(ref)? r->uri : ref, r))));
+ const list<list<value> > aarg = isNil(attempt)? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_attempt", attempt));
+ const list<list<value> > largs = append<list<value> >(rarg, aarg);
+ const string loc = isNil(largs)? httpd::url(page, r) : httpd::url(page, r) + string("?") + http::queryString(largs);
+ debug(loc, "openauth::login::uri");
+ return httpd::externalRedirect(loc, r);
+}
+
+}
+}
+
+#endif /* tuscany_openauth_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf
new file mode 100755
index 0000000000..718b96de0a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# 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.
+
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+user=$2
+pass=$3
+
+httpd_prefix=`cat $here/httpd.prefix`
+
+# Create password file
+touch $root/conf/httpd.passwd
+$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd "$user" "$pass" 2>/dev/null
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf
new file mode 100755
index 0000000000..0b63c9e481
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+bal=$2
+if [ "$bal" = "" ]; then
+ bal="cluster"
+fi
+loc=$3
+if [ "$loc" = "" ]; then
+ loc="/"
+fi
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-pass-conf $*
+# Enable load balancing
+ProxyPass $loc balancer://$bal/
+
+<Proxy balancer://$bal>
+Require all granted
+ProxySet lbmethod=byrequests
+</Proxy>
+
+<Location $loc>
+RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
+RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf
new file mode 100755
index 0000000000..377175328d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-base-conf $*
+# Do not proxy admin pages
+ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /proxy !
+
+# Enable balancer manager
+<Location /balancer-manager>
+SetHandler balancer-manager
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >>$root/conf/adminauth.conf <<EOF
+# Generated by: proxy-base-conf $*
+# Allow the server admin to manage the load balancer
+<Location /balancer-manager>
+Require user admin
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf
new file mode 100755
index 0000000000..ce990bbfd3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-conf $*
+# Do not proxy admin pages
+ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /proxy !
+
+# Enable load balancing
+ProxyPass / balancer://cluster/
+<Proxy balancer://cluster>
+Require all granted
+ProxySet lbmethod=byrequests
+</Proxy>
+
+<Location />
+RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
+RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
+</Location>
+
+# Enable balancer manager
+<Location /balancer-manager>
+SetHandler balancer-manager
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >>$root/conf/adminauth.conf <<EOF
+# Generated by: proxy-conf $*
+# Allow the server admin to manage the load balancer
+<Location /balancer-manager>
+Require user admin
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf
new file mode 100755
index 0000000000..a046a4fd08
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# 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.
+
+# Add a proxy balancer member
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+port=`$here/httpd-addr port $3`
+if [ "$port" = "80" ]; then
+ portsuffix=""
+else
+ portsuffix=":$port"
+fi
+
+bal=$4
+if [ "$bal" = "" ]; then
+ bal="cluster"
+fi
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-member-conf $*
+# Add proxy balancer member
+BalancerMember balancer://$bal http://$host$portsuffix
+ProxyPassReverse / http://$host$portsuffix/
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf
new file mode 100755
index 0000000000..ad7f26d83a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-conf $*
+# Do not proxy admin pages
+ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /proxy !
+
+# Enable load balancing
+ProxyPass / balancer://sslcluster/
+<Proxy balancer://sslcluster>
+Require all granted
+ProxySet lbmethod=byrequests
+</Proxy>
+
+<Location />
+RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
+RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
+</Location>
+
+# Enable balancer manager
+<Location /balancer-manager>
+SetHandler balancer-manager
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >>$root/conf/svhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-conf $*
+# Declare proxy SSL client certificates
+#SSLProxyCACertificateFile "$root/cert/ca.crt"
+#SSLProxyMachineCertificateFile "$root/cert/proxy.pem"
+
+EOF
+
+cat >>$root/conf/dvhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-conf $*
+# Declare proxy SSL client certificates
+#SSLProxyCACertificateFile "$root/cert/ca.crt"
+#SSLProxyMachineCertificateFile "$root/cert/proxy.pem"
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf
new file mode 100755
index 0000000000..cb42a1e9db
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# 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.
+
+# Add a proxy balancer member
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+sslport=`$here/httpd-addr port $3`
+if [ "$sslport" = "443" ]; then
+ sslportsuffix=""
+else
+ sslportsuffix=":$sslport"
+fi
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-member-conf $*
+# Add proxy balancer member
+BalancerMember balancer://sslcluster https://$host$sslportsuffix
+ProxyPassReverse / https://$host$sslportsuffix/
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf
new file mode 100755
index 0000000000..17b766d986
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# 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.
+
+# Add a proxy balancer member
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+port=`$here/httpd-addr port $3`
+if [ "$port" = "80" ]; then
+ portsuffix=""
+else
+ portsuffix=":$port"
+fi
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-nossl-member-conf $*
+# Add proxy balancer member
+BalancerMember balancer://sslcluster http://$host$portsuffix
+ProxyPassReverse / http://$host$portsuffix/
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-test b/sca-cpp/branches/lightweight-sca/modules/http/proxy-test
new file mode 100755
index 0000000000..0333dd280b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-test
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# 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.
+
+# Setup
+rm -rf tmp
+./httpd-conf tmp localhost 8091/8090 htdocs
+./httpd-event-conf tmp
+./httpd-start tmp
+./httpd-conf tmp/proxy localhost 8090 tmp/proxy/htdocs
+./httpd-event-conf tmp/proxy
+./proxy-conf tmp/proxy
+./proxy-member-conf tmp/proxy localhost 8091
+./httpd-start tmp/proxy
+sleep 2
+
+# Test
+./curl-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./httpd-stop tmp/proxy
+./httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf b/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf
new file mode 100755
index 0000000000..bceca8f300
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a test certification authority certificate
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+
+# Don't override existing certificate
+if [ -f $root/cert/ca.crt ]; then
+ exit 0
+fi
+
+# Generate openssl configuration
+mkdir -p $root/cert
+umask 0007
+cat >$root/cert/openssl-ca.conf <<EOF
+[ req ]
+default_bits = 1024
+encrypt_key = no
+prompt = no
+distinguished_name = req_distinguished_name
+x509_extensions = v3_ca
+
+[ req_distinguished_name ]
+C = US
+ST = CA
+L = San Francisco
+O = $host
+OU = authority
+CN = $host
+emailAddress = admin@$host
+
+[ v3_ca ]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = CA:true
+
+[ca]
+default_ca = ca_default
+
+[ca_default]
+certificate = $root/cert/ca.crt
+private_key = $root/cert/ca.key
+serial = $root/cert/ca-serial
+database = $root/cert/ca-database
+new_certs_dir = $root/cert
+default_md = sha1
+email_in_dn = no
+default_days = 365
+default_crl_days = 30
+policy = policy_any
+copy_extensions = none
+
+[ policy_any ]
+countryName = supplied
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+EOF
+
+rm -rf $root/cert/*.crt $root/cert/*.pem $root/cert/hash
+rm -f $root/cert/ca-database
+echo 1000 > $root/cert/ca-serial
+touch $root/cert/ca-database
+
+# Generate the certification authority certificate
+openssl req -new -x509 -config $root/cert/openssl-ca.conf -out $root/cert/ca.crt -keyout $root/cert/ca.key
+
+# Add to the hash directory and rehash
+mkdir -p $root/cert/hash
+cp $root/cert/ca.crt $root/cert/hash
+perl /usr/bin/c_rehash $root/cert/hash
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf
new file mode 100755
index 0000000000..9e785ec86e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+# 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.
+
+# Generate a test certificate
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+if [ "$3" != "" ]; then
+ certname=$3
+else
+ certname="server"
+fi
+
+# Don't regenerate the certificate if it already exists
+if [ -f $root/cert/$certname.crt ]; then
+ exit 0
+fi
+
+# Generate openssl configuration
+mkdir -p $root/cert
+umask 0007
+cat >$root/cert/openssl-cert-$certname.conf <<EOF
+[ req ]
+default_bits = 1024
+encrypt_key = no
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = US
+ST = CA
+L = San Francisco
+O = $host
+OU = $certname
+CN = $host
+emailAddress = admin@$host
+EOF
+
+# Generate a certificate request
+openssl req -new -config $root/cert/openssl-cert-$certname.conf -out $root/cert/$certname-req.crt -keyout $root/cert/$certname.key
+
+# Generate a certificate, signed with our test certification authority certificate
+openssl ca -batch -config $root/cert/openssl-ca.conf -out $root/cert/$certname.crt -infiles $root/cert/$certname-req.crt
+
+# Export it to PKCS12 format, that's the format Web browsers want to import
+openssl pkcs12 -export -passout pass: -out $root/cert/$certname.p12 -inkey $root/cert/$certname.key -in $root/cert/$certname.crt -certfile $root/cert/ca.crt
+
+# Convert the certificate to PEM format and concatenate the key to it, for use
+# by mod_proxy
+openssl x509 -in $root/cert/$certname.crt -out $root/cert/$certname.pem
+cat $root/cert/$certname.key >> $root/cert/$certname.pem
+
+# Add to the hash directory and rehash
+mkdir -p $root/cert/hash
+cp $root/cert/$certname.crt $root/cert/hash
+cp $root/cert/$certname.pem $root/cert/hash
+perl /usr/bin/c_rehash $root/cert/hash
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find
new file mode 100755
index 0000000000..7a4b4f0220
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# 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.
+
+# List certificate files, useful to distribute them to another host
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cd $root
+find . -name "*.crt"
+find . -name "*.pem"
+find . -name "*.p12"
+find . -name "*.key"
+find . -name "*.0"
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf
new file mode 100755
index 0000000000..021d205ed9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# 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.
+
+# Generate an SSL tunnel configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+port=`$here/httpd-addr port $2`
+sslhost=$3
+sslport=$4
+tport=$5
+
+# Generate HTTPD configuration
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: tunnel-ssl-conf $*
+# Tunnel TCP/IP traffic over HTTPS
+
+# Listen on local port
+Listen 127.0.0.1:$port
+
+# Tunnel virtual host
+<VirtualHost 127.0.0.1:$port>
+ServerName http://localhost:$port
+
+TunnelPass https://$sslhost:$sslport/tunnel/localhost/$tport
+
+# Declare SSL certificates used in this virtual host
+#TunnelSSLCACertificateFile "$root/cert/ca.crt"
+TunnelSSLCertificateFile "$root/cert/tunnel.crt"
+TunnelSSLCertificateKeyFile "$root/cert/tunnel.key"
+
+</VirtualHost>
+
+EOF
+