summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/cpp/apr-2/modules/wsgi
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/sebastien/cpp/apr-2/modules/wsgi')
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/Makefile.am58
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/app.yaml50
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/atom-test.py168
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/atomutil.py120
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.cpp40
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.py40
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/composite.py251
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/domain-test.composite42
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/elemutil.py168
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/gae-start29
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/gae-stop29
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/gae-test30
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/index.html1
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/entry.xml2
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/feed.xml2
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-request.txt1
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-result.txt1
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/http-test39
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/http-test.py32
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/httputil.py93
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/json-test.py59
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/jsonutil.py152
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/rss-test.py170
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/rssutil.py117
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/scdl.py272
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/server-test30
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/server-test.py44
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/stream-test.py47
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/util-test43
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/util.py145
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/wiring-test75
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-start28
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-stop28
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-test71
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/xml-test.py74
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/wsgi/xmlutil.py122
36 files changed, 2673 insertions, 0 deletions
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/Makefile.am b/sandbox/sebastien/cpp/apr-2/modules/wsgi/Makefile.am
new file mode 100644
index 0000000000..9f67ab37c0
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/Makefile.am
@@ -0,0 +1,58 @@
+# 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.
+
+if WANT_PYTHON
+
+INCLUDES = -I${PYTHON_INCLUDE}
+
+dist_mod_SCRIPTS = composite.py wsgi-start wsgi-stop gae-start gae-stop
+moddir = $(prefix)/modules/wsgi
+
+dist_mod_DATA = app.yaml scdl.py util.py elemutil.py xmlutil.py atomutil.py jsonutil.py
+
+noinst_DATA = target.stamp
+
+target.stamp: app.yaml *.py *.composite htdocs/*
+ mkdir -p target
+ cp app.yaml *.py *.composite target
+ cp -R htdocs target/htdocs
+ touch target.stamp
+
+clean-local:
+ rm -rf target.stamp target
+
+prefix_DATA = gae.prefix
+prefixdir=$(prefix)/modules/wsgi
+gae.prefix: $(top_builddir)/config.status
+ echo ${GAE_PREFIX} >gae.prefix
+
+EXTRA_DIST = domain-test.composite *.py htdocs/*.xml htdocs/*.txt htdocs/*.html
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+noinst_PROGRAMS = client-test
+
+dist_noinst_SCRIPTS = util-test wsgi-test wiring-test http-test server-test
+TESTS = util-test wsgi-test wiring-test http-test server-test
+
+if WANT_GAE
+dist_noinst_SCRIPTS += gae-test
+TESTS += gae-test
+endif
+
+endif
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/app.yaml b/sandbox/sebastien/cpp/apr-2/modules/wsgi/app.yaml
new file mode 100644
index 0000000000..bc70aceced
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/app.yaml
@@ -0,0 +1,50 @@
+# 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.
+
+application: sca-wsgi
+version: 1
+runtime: python
+api_version: 1
+skip_files:
+- ^(.*/)?app\.yaml
+- ^(.*/)?app\.yml
+- ^(.*/)?index\.yaml
+- ^(.*/)?index\.yml
+- ^(.*/)?#.*#
+- ^(.*/)?.*~
+- ^(.*/)?.*\.py[co]
+- ^(.*/)?.*/RCS/.*
+- ^(.*/)?\..*
+- ^(.*/)?.*-test$
+- ^(.*/)?.*\.cpp$
+- ^(.*/)?.*\.o$
+- ^(.*/)?core$
+- ^(.*/)?.*\.out$
+- ^(.*/)?.*\.log$
+- ^(.*/)?Makefile.*
+- ^(.*/)?tmp/.*
+- ^(.*/)?wsgi-start
+- ^(.*/)?wsgi-stop
+
+handlers:
+- url: /(.*\.(html|js|png))
+ static_files: htdocs/\1
+ upload: htdocs/(.*\.(html|js|png))
+
+- url: /.*
+ script: composite.py
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/atom-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/atom-test.py
new file mode 100755
index 0000000000..81a6106519
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/atom-test.py
@@ -0,0 +1,168 @@
+#!/usr/bin/python
+# 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.
+
+# Test ATOM data conversion functions
+
+import unittest
+from elemutil import *
+from atomutil import *
+
+itemEntry = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title type=\"text\">item</title>" \
+ "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" \
+ "<content type=\"application/xml\">" \
+ "<item>" \
+ "<name>Apple</name><price>$2.99</price>" \
+ "</item>" \
+ "</content>" \
+ "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\" />" \
+ "</entry>\n"
+
+textEntry = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title type=\"text\">item</title>" \
+ "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" \
+ "<content type=\"text\">" \
+ "Apple" \
+ "</content>" \
+ "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\" />" \
+ "</entry>\n"
+
+incompleteEntry = \
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title>item</title><content type=\"text/xml\">" \
+ "<Item xmlns=\"http://services/\">" \
+ "<name xmlns=\"\">Orange</name>" \
+ "<price xmlns=\"\">3.55</price>" \
+ "</Item>" \
+ "</content>" \
+ "</entry>"
+
+completedEntry = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title type=\"text\">item</title>" \
+ "<id />" \
+ "<content type=\"application/xml\">" \
+ "<Item xmlns=\"http://services/\">" \
+ "<name xmlns=\"\">Orange</name>" \
+ "<price xmlns=\"\">3.55</price>" \
+ "</Item>" \
+ "</content><link href=\"\" />" \
+ "</entry>\n"
+
+def testEntry():
+ i = (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99"))
+ a = ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", i)
+ s = writeATOMEntry(a);
+ assert car(s) == itemEntry
+
+ a2 = readATOMEntry((itemEntry,))
+ s2 = writeATOMEntry(a2)
+ assert car(s2) == itemEntry
+
+ a3 = readATOMEntry((textEntry,))
+ s3 = writeATOMEntry(a3)
+ assert car(s3) == textEntry
+
+ a4 = readATOMEntry((incompleteEntry,))
+ s4 = writeATOMEntry(a4)
+ assert car(s4) == completedEntry
+ return True
+
+emptyFeed = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title type=\"text\">Feed</title>" \
+ "<id>1234</id>" \
+ "</feed>\n"
+
+itemFeed = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title type=\"text\">Feed</title>" \
+ "<id>1234</id>" \
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title type=\"text\">item</title>" \
+ "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" \
+ "<content type=\"application/xml\">" \
+ "<item>" \
+ "<name>Apple</name><price>$2.99</price>" \
+ "</item>" \
+ "</content>" \
+ "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\" />" \
+ "</entry>" \
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \
+ "<title type=\"text\">item</title>" \
+ "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c</id>" \
+ "<content type=\"application/xml\">" \
+ "<item>" \
+ "<name>Orange</name><price>$3.55</price>" \
+ "</item>" \
+ "</content>" \
+ "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c\" />" \
+ "</entry>" \
+ "</feed>\n"
+
+def testFeed():
+ s = writeATOMFeed(("Feed", "1234"))
+ assert car(s) == emptyFeed
+
+ a2 = readATOMFeed((emptyFeed,))
+ s2 = writeATOMFeed(a2)
+ assert car(s2) == emptyFeed
+
+ i3 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b",
+ (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99"))),
+ ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c",
+ (element, "'item", (element, "'name", "Orange"), (element, "'price", "$3.55"))))
+ a3 = cons("Feed", cons("1234", i3))
+ s3 = writeATOMFeed(a3)
+ assert car(s3) == itemFeed
+
+ i4 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b",
+ valueToElement(("'item", ("'name", "Apple"), ("'price", "$2.99")))),
+ ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c",
+ valueToElement(("'item", ("'name", "Orange"), ("'price", "$3.55")))))
+ a4 = cons("Feed", cons("1234", i4))
+ s4 = writeATOMFeed(a4)
+ assert car(s4) == itemFeed
+
+ a5 = readATOMFeed((itemFeed,));
+ s5 = writeATOMFeed(a5);
+ assert car(s5) == itemFeed
+
+ i6 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b",
+ (("'name", "Apple"), ("'price", "$2.99"))),
+ ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c",
+ (("'name", "Orange"), ("'price", "$3.55"))))
+ a6 = cons("Feed", cons("1234", i6))
+ s6 = writeATOMFeed(feedValuesToElements(a6))
+ assert car(s6) == itemFeed
+
+ return True
+
+if __name__ == "__main__":
+ print "Testing..."
+ testEntry()
+ testFeed()
+ print "OK"
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/atomutil.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/atomutil.py
new file mode 100644
index 0000000000..1e6a7c31b5
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/atomutil.py
@@ -0,0 +1,120 @@
+# 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.
+
+# ATOM data conversion functions
+
+from util import *
+from elemutil import *
+from xmlutil import *
+
+# Convert a list of elements to a list of values representing an ATOM entry
+def entryElementsToValues(e):
+ lt = filter(selector((element, "'title")), e)
+ t = "" if isNil(lt) else elementValue(car(lt))
+ li = filter(selector((element, "'id")), e)
+ i = "" if isNil(li) else elementValue(car(li))
+ lc = filter(selector((element, "'content")), e)
+ return (t, i, elementValue(car(lc)))
+
+# Convert a list of elements to a list of values representing ATOM entries
+def entriesElementsToValues(e):
+ if isNil(e):
+ return e
+ return cons(entryElementsToValues(car(e)), entriesElementsToValues(cdr(e)))
+
+# Convert a list of strings to a list of values representing an ATOM entry
+def readATOMEntry(l):
+ e = readXML(l)
+ if isNil(e):
+ return ()
+ return entryElementsToValues(car(e))
+
+# Convert a list of values representy an ATOM entry to a value
+def entryValue(e):
+ v = elementsToValues((caddr(e),))
+ return cons(car(e), (cadr(e), cdr(car(v))))
+
+# Return true if a list of strings represents an ATOM feed
+def isATOMFeed(l):
+ if not isXML(l):
+ return False
+ return contains(car(l), "<feed") and contains(car(l), "=\"http://www.w3.org/2005/Atom\"")
+
+# Convert a list of strings to a list of values representing an ATOM feed
+def readATOMFeed(l):
+ f = readXML(l)
+ if isNil(f):
+ return ()
+ t = filter(selector((element, "'title")), car(f))
+ i = filter(selector((element, "'id")), car(f))
+ e = filter(selector((element, "'entry")), car(f))
+ if isNil(e):
+ return (elementValue(car(t)), elementValue(car(i)))
+ return cons(elementValue(car(t)), cons(elementValue(car(i)), entriesElementsToValues(e)))
+
+# Convert an ATOM feed containing elements to an ATOM feed containing values
+def feedValuesLoop(e):
+ if (isNil(e)):
+ return e
+ return cons(entryValue(car(e)), feedValuesLoop(cdr(e)))
+
+def feedValues(e):
+ return cons(car(e), cons(cadr(e), feedValuesLoop(cddr(e))))
+
+# Convert a list of values representy an ATOM entry to a list of elements
+def entryElement(l):
+ return (element, "'entry", (attribute, "'xmlns", "http://www.w3.org/2005/Atom"),
+ (element, "'title", (attribute, "'type", "text"), car(l)),
+ (element, "'id", cadr(l)),
+ (element, "'content", (attribute, "'type", ("application/xml" if isList(caddr(l)) else "text")), caddr(l)),
+ (element, "'link", (attribute, "'href", cadr(l))))
+
+# Convert a list of values representing ATOM entries to a list of elements
+def entriesElements(l):
+ if isNil(l):
+ return l
+ return cons(entryElement(car(l)), entriesElements(cdr(l)))
+
+# Convert a list of values representing an ATOM entry to an ATOM entry
+def writeATOMEntry(l):
+ return writeXML((entryElement(l),), True)
+
+# Convert a list of values representing an ATOM feed to an ATOM feed
+def writeATOMFeed(l):
+ f = (element, "'feed", (attribute, "'xmlns", "http://www.w3.org/2005/Atom"),
+ (element, "'title", (attribute, "'type", "text"), car(l)),
+ (element, "'id", cadr(l)))
+ if isNil(cddr(l)):
+ return writeXML((f,), True)
+ fe = append(f, entriesElements(cddr(l)))
+ return writeXML((fe,), True)
+
+# Convert an ATOM entry containing a value to an ATOM entry containing an item element
+def entryValuesToElements(v):
+ if isList(caddr(v)):
+ return cons(car(v), cons(cadr(v), valuesToElements((cons("'item", caddr(v)),))))
+ return cons(car(v), cons(cadr(v), valuesToElements((("'item", caddr(v)),))))
+
+# Convert an ATOM feed containing values to an ATOM feed containing elements
+def feedValuesToElementsLoop(v):
+ if isNil(v):
+ return v
+ return cons(entryValuesToElements(car(v)), feedValuesToElementsLoop(cdr(v)))
+
+def feedValuesToElements(v):
+ return cons(car(v), cons(cadr(v), feedValuesToElementsLoop(cddr(v))))
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.cpp b/sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.cpp
new file mode 100644
index 0000000000..da4fff973b
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "../server/client-test.hpp"
+
+int main(const int argc, const char** argv) {
+ tuscany::cout << "Testing..." << tuscany::endl;
+ tuscany::server::testURI = argc < 2? "http://localhost:8090/wsgi" : argv[1];
+ tuscany::server::testBlobs = false;
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.py
new file mode 100644
index 0000000000..867222e792
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/client-test.py
@@ -0,0 +1,40 @@
+# 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.
+
+import unittest
+
+# JSON-RPC test case
+
+def echo(x, ref):
+ e1 = ref("echo", x)
+ e2 = ref.echo(x)
+ assert e1 == e2
+ return e1
+
+# ATOMPub test case
+
+def get(id, ref):
+ return ref.get(id)
+
+def post(collection, item, ref):
+ return ref.post(collection, item)
+
+def put(id, item, ref):
+ return ref.put(id, item)
+
+def delete(id, ref):
+ return ref.delete(id)
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/composite.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/composite.py
new file mode 100755
index 0000000000..7044483f70
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/composite.py
@@ -0,0 +1,251 @@
+#!/usr/bin/python
+# 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.
+
+# Composite deployment and integration with WSGI
+
+from wsgiref.simple_server import make_server
+from wsgiref.handlers import CGIHandler
+from wsgiref.util import request_uri
+from wsgiref.util import FileWrapper
+from os import environ
+import os.path
+import hashlib
+from sys import stderr, argv
+from util import *
+from scdl import *
+from atomutil import *
+from jsonutil import *
+
+# Cache the deployed components between requests
+comps = None
+
+# Return the path of an HTTP request
+def requestPath(e):
+ return e.get("PATH_INFO", "")
+
+# Return the method of an HTTP request
+def requestMethod(e):
+ return e.get("REQUEST_METHOD", "")
+
+# Return the method of an HTTP request
+def requestContentType(e):
+ return e.get("CONTENT_TYPE", "")
+
+# Return the request body input stream
+def requestBody(e):
+ i = e.get("wsgi.input", None)
+ if i == None:
+ return ()
+ l = int(e.get("CONTENT_LENGTH", "0"))
+ return (i.read(l),)
+
+def requestIfNoneMatch(e):
+ return e.get("HTTP_IF_NONE_MATCH", "");
+
+# Hash a list of strings into an MD5 signature
+def md5update(md, s):
+ if isNil(s):
+ return md.hexdigest()
+ md.update(car(s))
+ return md5update(md, cdr(s))
+
+def md5(s):
+ return md5update(hashlib.md5(), s)
+
+# Return an HTTP success result
+def result(e, r, st, h = (), b = None):
+ if st == 201:
+ r("201 Created", list(h))
+ return ()
+
+ if st == 200:
+ if b == None:
+ r("200 OK", list(h))
+ return ()
+
+ # Handle etags to minimize bandwidth usage
+ md = md5(b)
+ if (md == requestIfNoneMatch(e)):
+ r("304 Not Modified", list((("Etag", md), ("Expires", "Tue, 01 Jan 1980 00:00:00 GMT"))))
+ return ()
+ r("200 OK", list(h + (("Etag", md), ("Expires", "Tue, 01 Jan 1980 00:00:00 GMT"))))
+ return b
+
+ if st == 301:
+ r("301 Moved Permanently", list(h))
+
+ return failure(e, r, 500)
+
+# Return an HTTP failure result
+def failure(e, r, st):
+ s = "404 Not Found" if st == 404 else str(st) + " " + "Internal Server Error"
+ r(s, list((("Content-type", "text/html"),)))
+ return ("<html><head><title>"+ s + "</title></head><body><h1>" + s[4:] + "</h1></body></html>",)
+
+# Return a static file
+def fileresult(e, r, ct, f):
+
+ # Read the file, return a 404 if not found
+ p = "htdocs" + f
+ if not os.path.exists(p):
+ return failure(e, r, 404)
+ c = tuple(FileWrapper(open("htdocs" + f)))
+
+ # Handle etags to minimize bandwidth usage
+ md = md5(c)
+ r("200 OK", list((("Content-type", ct),("Etag", md))))
+ return c
+
+# Converts the args received in a POST to a list of key value pairs
+def postArgs(a):
+ if isNil(a):
+ return ((),)
+ l = car(a);
+ return cons(l, postArgs(cdr(a)))
+
+# Return the URL used to sign out
+def signout(ruri):
+ try:
+ from google.appengine.api import users
+ return users.create_logout_url(ruri)
+ except:
+ return None
+
+# Return the URL used to sign in
+def signin(ruri):
+ try:
+ from google.appengine.api import users
+ return users.create_login_url(ruri)
+ except:
+ return None
+
+# WSGI application function
+def application(e, r):
+ m = requestMethod(e)
+ fpath = requestPath(e)
+
+ # Serve static files
+ if m == "GET":
+ if fpath.endswith(".html"):
+ return fileresult(e, r, "text/html", fpath)
+ if fpath.endswith(".js"):
+ return fileresult(e, r, "application/x-javascript", fpath)
+ if fpath.endswith(".png"):
+ return fileresult(e, r, "image/png", fpath)
+ if fpath == "/":
+ return result(e, r, 301, (("Location", "/index.html"),))
+
+ # Debug hook
+ if fpath == "/debug":
+ return result(e, r, 200, (("Content-type", "text/plain"),), ("Debug",))
+
+ # Sign in and out
+ if fpath == "/login":
+ redir = signin("/")
+ if redir:
+ return result(e, r, 301, (("Location", redir),))
+ if fpath == "/logout":
+ redir = signout(signin("/"))
+ if redir:
+ return result(e, r, 301, (("Location", redir),))
+
+ # Find the requested component
+ path = tokens(fpath)
+ uc = uriToComponent(path, comps)
+ uri = car(uc)
+ if uri == None:
+ return failure(e, r, 404)
+ comp = cadr(uc)
+
+ # Call the requested component function
+ id = path[len(uri):]
+ if m == "GET":
+ v = comp("get", id)
+
+ # Write returned content-type / content pair
+ if not isinstance(cadr(v), basestring):
+ return result(e, r, 200, (("Content-type", car(v)),), cadr(v))
+
+ # Write an ATOM feed or entry
+ if isNil(id):
+ return result(e, r, 200, (("Content-type", "application/atom+xml;type=feed"),), writeATOMFeed(feedValuesToElements(v)))
+ return result(e, r, 200, (("Content-type", "application/atom+xml;type=entry"),), writeATOMEntry(entryValuesToElements(v)))
+
+ if m == "POST":
+ ct = requestContentType(e)
+
+ # Handle a JSON-RPC function call
+ if ct.find("application/json-rpc") != -1 or ct.find("text/plain") != -1 or ct.find("application/x-www-form-urlencoded") != -1:
+ json = elementsToValues(readJSON(requestBody(e)))
+ args = postArgs(json)
+ jid = cadr(assoc("'id", args))
+ func = funcName(cadr(assoc("'method", args)))
+ params = cadr(assoc("'params", args))
+ v = comp(func, *params)
+ return result(e, r, 200, (("Content-type", "application/json-rpc"),), jsonResult(jid, v))
+
+ # Handle an ATOM entry POST
+ if ct.find("application/atom+xml") != -1:
+ ae = entryValue(readATOMEntry(requestBody(e)))
+ v = comp("post", id, ae)
+ if isNil(v):
+ return failure(e, r, 500)
+ return result(e, r, 201, (("Location", request_uri(e) + "/" + "/".join(v)),))
+ return failure(e, r, 500)
+
+ if m == "PUT":
+ # Handle an ATOM entry PUT
+ ae = entryValue(readATOMEntry(requestBody(e)))
+ v = comp("put", id, ae)
+ if v == False:
+ return failure(e, r, 404)
+ return result(e, r, 200)
+
+ if m == "DELETE":
+ v = comp("delete", id)
+ if v == False:
+ return failure(e, r, 404)
+ return result(e, r, 200)
+
+ return failure(e, r, 500)
+
+# Return the WSGI server type
+def serverType(e):
+ return e.get("SERVER_SOFTWARE", "")
+
+def main():
+ # Read the deployed composite and evaluate the configured components
+ global comps
+ if comps == None:
+ domain = "domain.composite" if os.path.exists("domain.composite") else "domain-test.composite"
+ comps = evalComponents(components(parse(domain)))
+
+ # Handle the WSGI request with the WSGI runtime
+ st = serverType(environ)
+ if st.find("App Engine") != -1 or st.find("Development") != -1:
+ from google.appengine.ext.webapp.util import run_wsgi_app
+ run_wsgi_app(application)
+ elif st != "":
+ CGIHandler().run(application)
+ else:
+ make_server("", int(argv[1]), application).serve_forever()
+
+# Run the WSGI application
+if __name__ == "__main__":
+ main()
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/domain-test.composite b/sandbox/sebastien/cpp/apr-2/modules/wsgi/domain-test.composite
new file mode 100644
index 0000000000..9c44ebf5d8
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/domain-test.composite
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://domain/test"
+ name="domain-test">
+
+ <component name="wsgi-test">
+ <t:implementation.python script="server-test.py"/>
+ <service name="test">
+ <t:binding.http uri="wsgi"/>
+ </service>
+ </component>
+
+ <component name="client-test">
+ <t:implementation.python script="client-test.py"/>
+ <service name="client">
+ <t:binding.http uri="client"/>
+ </service>
+ <reference name="ref" target="wsgi-test">
+ <t:binding.http/>
+ </reference>
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/elemutil.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/elemutil.py
new file mode 100644
index 0000000000..b4b28d5110
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/elemutil.py
@@ -0,0 +1,168 @@
+# 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.
+
+# Functions to help represent data as lists of elements and attributes
+
+from util import *
+
+element = "'element"
+attribute = "'attribute"
+atsign = "'@"
+
+# Return true if a value is an element
+def isElement(v):
+ if not isList(v) or isNil(v) or v == None or car(v) != element:
+ return False
+ return True
+
+# Return true if a value is an attribute
+def isAttribute(v):
+ if not isList(v) or isNil(v) or v == None or car(v) != attribute:
+ return False
+ return True
+
+# Return the name of attribute
+def attributeName(l):
+ return cadr(l)
+
+# Return the value of attribute
+def attributeValue(l):
+ return caddr(l)
+
+# Return the name of an element
+def elementName(l):
+ return cadr(l)
+
+# Return true if an element has children
+def elementHasChildren(l):
+ return not isNil(cddr(l))
+
+# Return the children of an element
+def elementChildren(l):
+ return cddr(l)
+
+# Return true if an element has a value
+def elementHasValue(l):
+ r = reverse(l)
+ if isSymbol(car(r)):
+ return False
+ if isList(car(r)) and not isNil(car(r)) and isSymbol(car(car(r))):
+ return False
+ return True
+
+# Return the value of an element
+def elementValue(l):
+ return car(reverse(l))
+
+# Convert an element to a value
+def elementToValueIsList(v):
+ if not isList(v):
+ return False
+ return isNil(v) or not isSymbol(car(v))
+
+def elementToValue(t):
+ if isTaggedList(t, attribute):
+ return (atsign + attributeName(t)[1:], attributeValue(t))
+ if isTaggedList(t, element):
+ if elementHasValue(t):
+ if not elementToValueIsList(elementValue(t)):
+ return (elementName(t), elementValue(t))
+ return cons(elementName(t), (elementsToValues(elementValue(t)),))
+ return cons(elementName(t), elementsToValues(elementChildren(t)))
+ if not isList(t):
+ return t
+ return elementsToValues(t)
+
+# Convert a list of elements to a list of values
+def elementToValueIsSymbol(v):
+ if not isList(v):
+ return False
+ if (isNil(v)):
+ return False
+ if not isSymbol(car(v)):
+ return False
+ return True
+
+def elementToValueGroupValues(v, l):
+ if isNil(l) or not elementToValueIsSymbol(v) or not elementToValueIsSymbol(car(l)):
+ return cons(v, l)
+ if car(car(l)) != car(v):
+ return cons(v, l)
+ if not elementToValueIsList(cadr(car(l))):
+ g = (car(v), (cdr(v), cdr(car(l))))
+ return elementToValueGroupValues(g, cdr(l))
+ g = (car(v), cons(cdr(v), cadr(car(l))))
+ return elementToValueGroupValues(g, cdr(l))
+
+def elementsToValues(e):
+ if isNil(e):
+ return e
+ return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e)))
+
+# Convert a value to an element
+def valueToElement(t):
+ if isList(t) and not isNil(t) and isSymbol(car(t)):
+ n = car(t)
+ v = () if isNil(cdr(t)) else cadr(t)
+ if not isList(v):
+ if n[0:2] == atsign:
+ return (attribute, n[1:], v)
+ return (element, n, v)
+ if isNil(v) or not isSymbol(car(v)):
+ return cons(element, cons(n, (valuesToElements(v),)))
+ return cons(element, cons(n, valuesToElements(cdr(t))))
+ if not isList(t):
+ return t
+ return valuesToElements(t)
+
+# Convert a list of values to a list of elements
+def valuesToElements(l):
+ if isNil(l):
+ return l
+ return cons(valueToElement(car(l)), valuesToElements(cdr(l)))
+
+# Return a selector lambda function which can be used to filter elements
+def evalSelect(s, v):
+ if isNil(s):
+ return True
+ if isNil(v):
+ return False
+ if car(s) != car(v):
+ return False
+ return evalSelect(cdr(s), cdr(v))
+
+def selector(s):
+ return lambda v: evalSelect(s, v)
+
+# Return the value of the attribute with the given name
+def namedAttributeValue(name, l):
+ f = filter(lambda v: isAttribute(v) and attributeName(v) == name, l)
+ if isNil(f):
+ return None
+ return caddr(car(f))
+
+# Return child elements with the given name
+def namedElementChildren(name, l):
+ return filter(lambda v: isElement(v) and elementName(v) == name, l)
+
+# Return the child element with the given name
+def namedElementChild(name, l):
+ f = namedElementChildren(name, l)
+ if isNil(f):
+ return None
+ return car(f)
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-start b/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-start
new file mode 100755
index 0000000000..a3ee8765cb
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-start
@@ -0,0 +1,29 @@
+#!/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 Google AppEngine server
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+port=$2
+
+python_prefix=`cat $here/../python/python.prefix`
+gae_prefix=`cat $here/gae.prefix`
+cd $root
+$python_prefix/bin/python $gae_prefix/dev_appserver.py -a 0.0.0.0 -p $port $root &
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-stop b/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-stop
new file mode 100755
index 0000000000..69de7f0c2b
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-stop
@@ -0,0 +1,29 @@
+#!/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 Google AppEngine server
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+port=$2
+
+python_prefix=`cat $here/../python/python.prefix`
+gae_prefix=`cat $here/gae.prefix`
+py="$python_prefix/bin/python $gae_prefix/dev_appserver.py -a 0.0.0.0 -p $port $root"
+
+kill `ps -ef | grep -v grep | grep "${py}" | awk '{ print $2 }'`
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-test b/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-test
new file mode 100755
index 0000000000..1791a830ca
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/gae-test
@@ -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.
+
+# Setup
+./gae-start target 8090 2>/dev/null
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./gae-stop target 8090
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/index.html b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/index.html
new file mode 100644
index 0000000000..cd25bf17b3
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/index.html
@@ -0,0 +1 @@
+<html><body><h1>It works!</h1></body></html> \ No newline at end of file
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/entry.xml b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/entry.xml
new file mode 100644
index 0000000000..d26a46f25b
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/entry.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111" /></entry>
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/feed.xml b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/feed.xml
new file mode 100644
index 0000000000..0be99f6eaf
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/feed.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111" /></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item><name>Orange</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>3.55</price></item></content><link href="222" /></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item><name>Pear</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>1.55</price></item></content><link href="333" /></entry></feed>
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-request.txt b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-request.txt
new file mode 100644
index 0000000000..b4bd07fc46
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-request.txt
@@ -0,0 +1 @@
+{"id":1,"method":"echo","params":["Hello"]}
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-result.txt b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-result.txt
new file mode 100644
index 0000000000..121bf74902
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/htdocs/test/json-result.txt
@@ -0,0 +1 @@
+{"id":1,"result":"Hello"} \ No newline at end of file
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/http-test b/sandbox/sebastien/cpp/apr-2/modules/wsgi/http-test
new file mode 100755
index 0000000000..6676f6514c
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/http-test
@@ -0,0 +1,39 @@
+#!/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.
+
+uri=$1
+if [ "$uri" = "" ]; then
+ uri="http://localhost:8090"
+fi
+
+# Setup
+mkdir -p tmp
+./wsgi-start target 8090 2>/dev/null
+sleep 2
+
+# Test JSON-RPC
+here=`readlink -f $0`; here=`dirname $here`
+python_prefix=`cat $here/../python/python.prefix`
+$python_prefix/bin/python http-test.py
+rc=$?
+
+# Cleanup
+./wsgi-stop target 8090
+sleep 2
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/http-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/http-test.py
new file mode 100755
index 0000000000..45a1ecd3cc
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/http-test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# HTTP client proxy functions
+
+from httputil import *
+
+def testClient():
+ c = mkclient("http://localhost:8090/wsgi")
+ assert c.echo("Hey") == "Hey"
+ return True
+
+if __name__ == "__main__":
+ print "Testing..."
+ testClient()
+ print "OK"
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/httputil.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/httputil.py
new file mode 100644
index 0000000000..723e1a7284
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/httputil.py
@@ -0,0 +1,93 @@
+#!/usr/bin/python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# HTTP client proxy functions
+
+from httplib import HTTPConnection, HTTPSConnection
+from urlparse import urlparse
+from StringIO import StringIO
+import os.path
+from string import strip
+from base64 import b64encode
+from sys import stderr
+from util import *
+from atomutil import *
+from jsonutil import *
+
+# JSON request id
+id = 1
+
+# Make a callable HTTP client
+class client:
+ def __init__(self, url):
+ self.url = urlparse(url)
+
+ def __call__(self, func, *args):
+
+ # Connect to the configured URL
+ print >> stderr, "Client POST", self.url.geturl()
+ c, headers = connect(self.url)
+
+ # POST a JSON-RPC request
+ global id
+ req = StringIO()
+ writeStrings(jsonRequest(id, func, args), req)
+ id = id + 1
+ headers["Content-type"] = "application/json-rpc"
+ c.request("POST", self.url.path, req.getvalue(), headers)
+ res = c.getresponse()
+ print >> stderr, "Client status", res.status
+ if res.status != 200:
+ return None
+ return jsonResultValue((res.read(),))
+
+ def __getattr__(self, name):
+ if name[0] == '_':
+ raise AttributeError()
+ if name == "eval":
+ return self
+ l = lambda *args: self.__call__(name, *args)
+ self.__dict__[name] = l
+ return l
+
+ def __repr__(self):
+ return repr((self.url,))
+
+def mkclient(url):
+ return client(url)
+
+# Connect to a URL, return a connection and any authorization headers
+def connect(url):
+ if url.scheme == "https":
+
+ # With HTTPS, use a cerficate or HTTP basic authentication
+ if os.path.exists("server.key"):
+ c = HTTPSConnection(url.hostname, 443 if url.port == None else url.port, "server.key", "server.crt")
+ return c, {}
+ else:
+ c = HTTPSConnection(url.hostname, 443 if url.port == None else url.port)
+
+ # For HTTP basic authentication the user and password are
+ # provided by htpasswd.py
+ import htpasswd
+ auth = 'Basic ' + b64encode(htpasswd.user + ':' + htpasswd.passwd)
+ return c, {"Authorization": auth}
+ else:
+ c = HTTPConnection(url.hostname, 80 if url.port == None else url.port)
+ return c, {}
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/json-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/json-test.py
new file mode 100755
index 0000000000..2f2a755bff
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/json-test.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+# 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.
+
+# Test JSON data conversion functions
+
+import unittest
+from elemutil import *
+from jsonutil import *
+
+def testJSON():
+ ad = ((attribute, "'city", "san francisco"), (attribute, "'state", "ca"))
+ ac = ((element, "'id", "1234"), (attribute, "'balance", 1000))
+ cr = ((attribute, "'name", "jdoe"), cons(element, cons("'address", ad)), cons(element, cons("'account", ac)))
+ c = (cons(element, cons("'customer", cr)),)
+ s = writeJSON(c);
+ assert car(s) == "{\"customer\":{\"account\":{\"@balance\":1000,\"id\":\"1234\"},\"@name\":\"jdoe\",\"address\":{\"@city\":\"san francisco\",\"@state\":\"ca\"}}}"
+
+ phones = ("408-1234", "650-1234")
+ l = ((element, "'phones", phones), (element, "'lastName", "test\ttab"), (attribute, "'firstName", "test1"))
+ s2 = writeJSON(l);
+ assert car(s2) == "{\"phones\":[\"408-1234\",\"650-1234\"],\"@firstName\":\"test1\",\"lastName\":\"test\\ttab\"}"
+
+ r = readJSON(s2)
+ assert r == ((element, "'lastName", "test\ttab"), (attribute, "'firstName", "test1"), (element, "'phones", phones))
+ w = writeJSON(r)
+ assert car(w) == "{\"lastName\":\"test\\ttab\",\"@firstName\":\"test1\",\"phones\":[\"408-1234\",\"650-1234\"]}"
+
+ l4 = (("'ns1:echoString", ("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), ("'text", "Hello World!")),)
+ s4 = writeJSON(valuesToElements(l4))
+ assert car(s4) == "{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/services/echo\",\"text\":\"Hello World!\"}}"
+
+ r4 = elementsToValues(readJSON(s4))
+ assert r4 == (("'ns1:echoString", ("'text", 'Hello World!'), ("'@xmlns:ns1", 'http://ws.apache.org/axis2/services/echo')),)
+ return True
+
+def testJSONRPC():
+ return True
+
+if __name__ == "__main__":
+ print "Testing..."
+ testJSON()
+ testJSONRPC()
+ print "OK"
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/jsonutil.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/jsonutil.py
new file mode 100644
index 0000000000..ad8f5fcc6b
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/jsonutil.py
@@ -0,0 +1,152 @@
+# 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.
+
+# JSON data conversion functions
+
+try:
+ import json
+except:
+ from django.utils import simplejson as json
+
+from StringIO import StringIO
+from util import *
+from elemutil import *
+
+# Return true if a list represents a JS array
+def isJSArray(l):
+ if isNil(l):
+ return True
+ v = car(l)
+ if isSymbol(v):
+ return False
+ if isList(v):
+ if not isNil(v) and isSymbol(car(v)):
+ return False
+ return True
+
+# Converts JSON properties to values
+def jsPropertiesToValues(propertiesSoFar, o, i):
+ if isNil(i):
+ return propertiesSoFar
+ p = car(i)
+ jsv = o[p]
+ v = jsValToValue(jsv)
+
+ if isinstance(p, basestring):
+ n = str(p)
+ if n[0:1] == "@":
+ return jsPropertiesToValues(cons((attribute, "'" + n[1:], v), propertiesSoFar), o, cdr(i))
+ if isList(v) and not isJSArray(v):
+ return jsPropertiesToValues(cons(cons(element, cons("'" + n, v)), propertiesSoFar), o, cdr(i))
+ return jsPropertiesToValues(cons((element, "'" + n, v), propertiesSoFar), o, cdr(i))
+ return jsPropertiesToValues(cons(v, propertiesSoFar), o, cdr(i))
+
+# Converts a JSON val to a value
+def jsValToValue(jsv):
+ if isinstance(jsv, dict):
+ return jsPropertiesToValues((), jsv, tuple(jsv.keys()))
+ if isList(jsv):
+ return jsPropertiesToValues((), jsv, tuple(reversed(range(0, len(jsv)))))
+ if isinstance(jsv, basestring):
+ return str(jsv)
+ return jsv
+
+# Return true if a list of strings contains a JSON document
+def isJSON(l):
+ if isNil(l):
+ return False
+ s = car(l)[0:1]
+ return s == "[" or s == "{"
+
+# Convert a list of strings representing a JSON document to a list of values
+def readJSON(l):
+ s = StringIO()
+ writeStrings(l, s)
+ val = json.loads(s.getvalue())
+ return jsValToValue(val)
+
+# Convert a list of values to JSON array elements
+def valuesToJSElements(a, l, i):
+ if isNil(l):
+ return a
+ pv = valueToJSVal(car(l))
+ a[i] = pv
+ return valuesToJSElements(a, cdr(l), i + 1)
+
+# Convert a value to a JSON value
+def valueToJSVal(v):
+ if not isList(v):
+ return v
+ if isJSArray(v):
+ return valuesToJSElements(list(range(0, len(v))), v, 0)
+ return valuesToJSProperties({}, v)
+
+# Convert a list of values to JSON properties
+def valuesToJSProperties(o, l):
+ if isNil(l):
+ return o
+ token = car(l)
+ if isTaggedList(token, attribute):
+ pv = valueToJSVal(attributeValue(token))
+ o["@" + attributeName(token)[1:]] = pv
+ elif isTaggedList(token, element):
+ if elementHasValue(token):
+ pv = valueToJSVal(elementValue(token))
+ o[elementName(token)[1:]] = pv
+ else:
+ child = {}
+ o[elementName(token)[1:]] = child
+ valuesToJSProperties(child, elementChildren(token))
+ return valuesToJSProperties(o, cdr(l))
+
+# Convert a list of values to a list of strings representing a JSON document
+def writeJSON(l):
+ if isJSArray(l):
+ jsv = valuesToJSElements(list(range(0, len(l))), l, 0)
+ else:
+ jsv = valuesToJSProperties({}, l)
+ s = json.dumps(jsv, separators=(',',':'))
+ return (s,)
+
+# Convert a list + params to a JSON-RPC request
+def jsonRequest(id, func, params):
+ r = (("'id", id), ("'method", func), ("'params", params))
+ return writeJSON(valuesToElements(r))
+
+# Convert a value to a JSON-RPC result
+def jsonResult(id, val):
+ return writeJSON(valuesToElements((("'id", id), ("'result", val))))
+
+# Convert a JSON-RPC result to a value
+def jsonResultValue(s):
+ jsres = readJSON(s)
+ res = elementsToValues(jsres)
+ val = cadr(assoc("'result", res))
+ if isList(val) and not isJSArray(val):
+ return (val,)
+ return val
+
+# Return a portable function name from a JSON-RPC function name
+def funcName(f):
+ if f.startswith("."):
+ return f[1:]
+ if f.startswith("system."):
+ return f[7:]
+ if f.startswith("Service."):
+ return f[8:]
+ return f
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/rss-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/rss-test.py
new file mode 100755
index 0000000000..e8a094b7d8
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/rss-test.py
@@ -0,0 +1,170 @@
+#!/usr/bin/python
+# 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.
+
+# Test RSS data conversion functions
+
+import unittest
+from elemutil import *
+from rssutil import *
+
+itemEntry = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<item>" \
+ "<title>fruit</title>" \
+ "<link>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</link>" \
+ "<description>" \
+ "<item>" \
+ "<name>Apple</name><price>$2.99</price>" \
+ "</item>" \
+ "</description>" \
+ "</item>\n"
+
+textEntry = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<item>" \
+ "<title>fruit</title>" \
+ "<link>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</link>" \
+ "<description>" \
+ "Apple" \
+ "</description>" \
+ "</item>\n"
+
+incompleteEntry = \
+ "<item>" \
+ "<title>fruit</title><description>" \
+ "<fruit xmlns=\"http://services/\">" \
+ "<name xmlns=\"\">Orange</name>" \
+ "<price xmlns=\"\">3.55</price>" \
+ "</fruit>" \
+ "</description>" \
+ "</item>"
+
+completedEntry = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<item>" \
+ "<title>fruit</title>" \
+ "<link />" \
+ "<description>" \
+ "<fruit xmlns=\"http://services/\">" \
+ "<name xmlns=\"\">Orange</name>" \
+ "<price xmlns=\"\">3.55</price>" \
+ "</fruit>" \
+ "</description>" \
+ "</item>\n"
+
+def testEntry():
+ i = (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99"))
+ a = ("fruit", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", i)
+ s = writeRSSEntry(a);
+ assert car(s) == itemEntry
+
+ a2 = readRSSEntry((itemEntry,))
+ s2 = writeRSSEntry(a2)
+ assert car(s2) == itemEntry
+
+ a3 = readRSSEntry((textEntry,))
+ s3 = writeRSSEntry(a3)
+ assert car(s3) == textEntry
+
+ a4 = readRSSEntry((incompleteEntry,))
+ s4 = writeRSSEntry(a4)
+ assert car(s4) == completedEntry
+ return True
+
+emptyFeed = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<rss version=\"2.0\">" \
+ "<channel>" \
+ "<title>Feed</title>" \
+ "<link>1234</link>" \
+ "<description>Feed</description>" \
+ "</channel>" \
+ "</rss>\n"
+
+itemFeed = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<rss version=\"2.0\">" \
+ "<channel>" \
+ "<title>Feed</title>" \
+ "<link>1234</link>" \
+ "<description>Feed</description>" \
+ "<item>" \
+ "<title>fruit</title>" \
+ "<link>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</link>" \
+ "<description>" \
+ "<item>" \
+ "<name>Apple</name><price>$2.99</price>" \
+ "</item>" \
+ "</description>" \
+ "</item>" \
+ "<item>" \
+ "<title>fruit</title>" \
+ "<link>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c</link>" \
+ "<description>" \
+ "<item>" \
+ "<name>Orange</name><price>$3.55</price>" \
+ "</item>" \
+ "</description>" \
+ "</item>" \
+ "</channel>" \
+ "</rss>\n"
+
+def testFeed():
+ s = writeRSSFeed(("Feed", "1234"))
+ assert car(s) == emptyFeed
+
+ a2 = readRSSFeed((emptyFeed,))
+ s2 = writeRSSFeed(a2)
+ assert car(s2) == emptyFeed
+
+ i3 = (("fruit", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b",
+ (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99"))),
+ ("fruit", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c",
+ (element, "'item", (element, "'name", "Orange"), (element, "'price", "$3.55"))))
+ a3 = cons("Feed", cons("1234", i3))
+ s3 = writeRSSFeed(a3)
+ assert car(s3) == itemFeed
+
+ i4 = (("fruit", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b",
+ valueToElement(("'item", ("'name", "Apple"), ("'price", "$2.99")))),
+ ("fruit", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c",
+ valueToElement(("'item", ("'name", "Orange"), ("'price", "$3.55")))))
+ a4 = cons("Feed", cons("1234", i4))
+ s4 = writeRSSFeed(a4)
+ assert car(s4) == itemFeed
+
+ a5 = readRSSFeed((itemFeed,));
+ s5 = writeRSSFeed(a5);
+ assert car(s5) == itemFeed
+
+ i6 = (("fruit", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b",
+ (("'name", "Apple"), ("'price", "$2.99"))),
+ ("fruit", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c",
+ (("'name", "Orange"), ("'price", "$3.55"))))
+ a6 = cons("Feed", cons("1234", i6))
+ s6 = writeRSSFeed(feedValuesToElements(a6))
+ assert car(s6) == itemFeed
+
+ return True
+
+if __name__ == "__main__":
+ print "Testing..."
+ testEntry()
+ testFeed()
+ print "OK"
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/rssutil.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/rssutil.py
new file mode 100644
index 0000000000..984d71b690
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/rssutil.py
@@ -0,0 +1,117 @@
+# 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.
+
+# RSS data conversion functions
+
+from util import *
+from elemutil import *
+from xmlutil import *
+
+# Convert a list of elements to a list of values representing an RSS entry
+def entryElementsToValues(e):
+ lt = filter(selector((element, "'title")), e)
+ t = "" if isNil(lt) else elementValue(car(lt))
+ li = filter(selector((element, "'link")), e)
+ i = "" if isNil(li) else elementValue(car(li))
+ lc = filter(selector((element, "'description")), e)
+ return (t, i, elementValue(car(lc)))
+
+# Convert a list of elements to a list of values representing RSS entries
+def entriesElementsToValues(e):
+ if isNil(e):
+ return e
+ return cons(entryElementsToValues(car(e)), entriesElementsToValues(cdr(e)))
+
+# Convert a list of strings to a list of values representing an RSS entry
+def readRSSEntry(l):
+ e = readXML(l)
+ if isNil(e):
+ return ()
+ return entryElementsToValues(car(e))
+
+# Convert a list of values representy an RSS entry to a value
+def entryValue(e):
+ v = elementsToValues((caddr(e),))
+ return cons(car(e), (cadr(e), cdr(car(v))))
+
+# Return true if a list of strings represents an RSS feed
+def isRSSFeed(l):
+ if not isXML(l):
+ return False
+ return contains(car(l), "<rss")
+
+# Convert a list of strings to a list of values representing an RSS feed
+def readRSSFeed(l):
+ f = readXML(l)
+ if isNil(f):
+ return ()
+ c = filter(selector((element, "'channel")), car(f))
+ t = filter(selector((element, "'title")), car(c))
+ i = filter(selector((element, "'link")), car(c))
+ e = filter(selector((element, "'item")), car(c))
+ if isNil(e):
+ return (elementValue(car(t)), elementValue(car(i)))
+ return cons(elementValue(car(t)), cons(elementValue(car(i)), entriesElementsToValues(e)))
+
+# Convert an RSS feed containing elements to an RSS feed containing values
+def feedValuesLoop(e):
+ if (isNil(e)):
+ return e
+ return cons(entryValue(car(e)), feedValuesLoop(cdr(e)))
+
+def feedValues(e):
+ return cons(car(e), cons(cadr(e), feedValuesLoop(cddr(e))))
+
+# Convert a list of values representy an RSS entry to a list of elements
+def entryElement(l):
+ return (element, "'item",
+ (element, "'title", car(l)),
+ (element, "'link", cadr(l)),
+ (element, "'description", caddr(l)))
+
+# Convert a list of values representing RSS entries to a list of elements
+def entriesElements(l):
+ if isNil(l):
+ return l
+ return cons(entryElement(car(l)), entriesElements(cdr(l)))
+
+# Convert a list of values representing an RSS entry to an RSS entry
+def writeRSSEntry(l):
+ return writeXML((entryElement(l),), True)
+
+# Convert a list of values representing an RSS feed to an RSS feed
+def writeRSSFeed(l):
+ c = ((element, "'title", car(l)),
+ (element, "'link", cadr(l)),
+ (element, "'description", car(l)))
+ ce = c if isNil(cddr(l)) else append(c, entriesElements(cddr(l)))
+ fe = (element, "'rss", (attribute, "'version", "2.0"), append((element, "'channel"), ce))
+ return writeXML((fe,), True)
+
+# Convert an RSS entry containing a value to an RSS entry containing an item element
+def entryValuesToElements(v):
+ return cons(car(v), cons(cadr(v), valuesToElements((cons("'item", caddr(v)),))))
+
+# Convert an RSS feed containing values to an RSS feed containing elements
+def feedValuesToElementsLoop(v):
+ if isNil(v):
+ return v
+ return cons(entryValuesToElements(car(v)), feedValuesToElementsLoop(cdr(v)))
+
+def feedValuesToElements(v):
+ return cons(car(v), cons(cadr(v), feedValuesToElementsLoop(cddr(v))))
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/scdl.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/scdl.py
new file mode 100644
index 0000000000..97c2f7dd69
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/scdl.py
@@ -0,0 +1,272 @@
+# 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.
+
+# SCDL parsing functions
+
+from xml.etree.cElementTree import iterparse
+from sys import stderr
+from os import environ
+from util import *
+from httputil import *
+
+# Element tree utility functions, used to parse SCDL documents
+def parse(file):
+ return map(lambda x: x, iterparse(file, events=("start", "end")))
+
+def evt(e):
+ return car(e)
+
+def elt(e):
+ return cadr(e)
+
+def att(e):
+ return elt(e).attrib
+
+def text(e):
+ return elt(e).text
+
+def match(e, ev, tag):
+ return evt(e) == ev and elt(e).tag.find("}" + tag) != -1
+
+# Make a callable component
+class component:
+ def __init__(self, name, impl, svcs, refs, props):
+ self.name = name
+ self.impl = impl
+ self.mod = None
+ self.svcs = svcs
+ self.refs = refs
+ self.props = props
+ self.proxies = ()
+
+ def __call__(self, func, *args):
+ return self.mod.__getattribute__(func)(*(args + self.proxies))
+
+ def __getattr__(self, name):
+ if name[0] == '_':
+ raise AttributeError()
+ if name == "eval":
+ return self
+ l = lambda *args: self.__call__(name, *args)
+ self.__dict__[name] = l
+ return l
+
+ def __repr__(self):
+ return repr((self.name, self.impl, self.mod, self.svcs, self.refs, self.props, self.proxies))
+
+def mkcomponent(name, impl, svcs, refs, props):
+ return component(name, impl, svcs, refs, props)
+
+# Return the Python module name of a component implementation
+def implementation(e):
+ if len(e) == 0 or match(car(e), "end", "component") == True:
+ return ""
+ if match(car(e), "start", "implementation.python") == False:
+ return implementation(cdr(e))
+ if "script" in att(car(e)):
+ s = att(car(e))["script"]
+ return s[0:len(s) - 3]
+ return None
+
+# Return the URI of a binding under a SCDL service or reference element
+def binding(e):
+ if len(e) == 0 or match(car(e), "end", "reference") == True or match(car(e), "end", "service") == True:
+ return ()
+ if match(car(e), "start", "binding.") == False:
+ return binding(cdr(e))
+ return att(car(e))["uri"]
+
+# Return the list of references under a SCDL component element
+def references(e):
+ if len(e) == 0 or match(car(e), "end", "component") == True:
+ return ()
+ if match(car(e), "start", "reference") == False:
+ return references(cdr(e))
+ if "target" in att(car(e)):
+ return cons((att(car(e))["name"], car(tokens(att(car(e))["target"]))), references(cdr(e)))
+ return cons((att(car(e))["name"], binding(e)), references(cdr(e)))
+
+# Return the list of properties under a SCDL component element
+def properties(e):
+ if len(e) == 0 or match(car(e), "end", "component") == True:
+ return ()
+ if match(car(e), "start", "property") == False:
+ return properties(cdr(e))
+ return cons((att(car(e))["name"], text(car(e))), properties(cdr(e)))
+
+# Return the list of services under a SCDL component element
+def services(e):
+ if len(e) == 0 or match(car(e), "end", "component") == True:
+ return ()
+ if match(car(e), "start", "service") == False:
+ return services(cdr(e))
+ return cons(tokens(binding(e)), services(cdr(e)))
+
+# Return the name attribute of a SCDL element
+def name(e):
+ return att(car(e))["name"]
+
+# Return the list of components under a SCDL composite element
+def components(e):
+ if len(e) == 0:
+ return ()
+ if match(car(e), "start", "component") == False:
+ return components(cdr(e))
+ n = name(e)
+ return cons(mkcomponent(n, implementation(e), services(e), references(e), properties(e)), components(cdr(e)))
+
+# Find a component with a given name
+def nameToComponent(name, comps):
+ if comps == ():
+ return None
+ if car(comps).name == name:
+ return car(comps)
+ return nameToComponent(name, cdr(comps))
+
+# Find the URI matching a given URI in a list of service URIs
+def matchingURI(u, svcs):
+ if svcs == ():
+ return None
+ if car(svcs) == u[0:len(car(svcs))]:
+ return car(svcs)
+ return matchingURI(u, cdr(svcs))
+
+# Return the (service URI, component) pair matching a given URI
+def uriToComponent(u, comps):
+ if car(u) == "components":
+ return componentURIToComponent(u, comps)
+ if car(u) == "references":
+ return referenceURIToComponent(u, comps)
+ return serviceURIToComponent(u, comps)
+
+def serviceURIToComponent(u, comps):
+ if comps == ():
+ return (None, None)
+ m = matchingURI(u, car(comps).svcs)
+ if m != None:
+ return (m, car(comps))
+ return serviceURIToComponent(u, cdr(comps))
+
+def componentURIToComponent(u, comps):
+ comp = nameToComponent(cadr(u), comps)
+ if comps == None:
+ return (None, None)
+ return (u[0:2], comp)
+
+def referenceURIToComponent(u, comps):
+ sc = nameToComponent(cadr(u), comps)
+ if sc == None:
+ return (None, None)
+
+ def referenceToComponent(r, refs):
+ if refs == ():
+ return None
+ if r == car(car(refs)):
+ return cadr(car(refs))
+ return referenceToComponent(r, cdr(refs))
+
+ tn = referenceToComponent(caddr(u), sc.refs)
+ if tn == None:
+ return (None, None)
+ tc = nameToComponent(tn, comps)
+ if tc == None:
+ return (None, None)
+ return (u[0:3], tc)
+
+# Evaluate a reference, return a proxy to the resolved component or an
+# HTTP client configured with the reference target uri
+def evalReference(r, comps):
+ t = cadr(r)
+ if t.startswith("http://") or t.startswith("https://"):
+ return mkclient(t)
+ return nameToComponent(t, comps)
+
+# Make a callable property
+class property:
+ def __init__(self, name, l):
+ self.name = name
+ self.l = l
+
+ def __call__(self, *args):
+ return self.l(*args)
+
+ def __getattr__(self, name):
+ if name == "eval":
+ return self
+ raise AttributeError()
+
+ def __repr__(self):
+ return repr((self.name, self.l()))
+
+def mkproperty(name, l):
+ return property(name, l)
+
+# Evaluate a property, return a lambda function returning the property
+# value. The host, user, realm, nickname and email properties are configured
+# with the values from the HTTP request, if any.
+def evalProperty(p):
+ if car(p) == "host":
+ return mkproperty(car(p), lambda: hostProperty(cadr(p), environ))
+ if car(p) == "user":
+ return mkproperty(car(p), lambda: userProperty(cadr(p)))
+ if car(p) == "realm":
+ return mkproperty(car(p), lambda: hostProperty(cadr(p), environ))
+ if car(p) == "nickname":
+ return mkproperty(car(p), lambda: nicknameProperty(cadr(p)))
+ if car(p) == "email":
+ return mkproperty(car(p), lambda: emailProperty(cadr(p)))
+ return mkproperty(car(p), lambda: cadr(p))
+
+def currentUser():
+ try:
+ from google.appengine.api import users
+ return users.get_current_user()
+ except:
+ return None
+
+def userProperty(v):
+ user = currentUser()
+ return user.federated_identity() if user else v
+
+def nicknameProperty(v):
+ user = currentUser()
+ return user.nickname() if user else v
+
+def hostProperty(v, e):
+ return e.get("HTTP_HOST", e.get("SERVER_NAME", v)).split(":")[0]
+
+def emailProperty(v):
+ user = currentUser()
+ return user.email() if user else v
+
+# Evaluate a component, resolve its implementation, references and
+# properties
+def evalComponent(comp, comps):
+ comp.mod = __import__(comp.impl)
+
+ # Make a list of proxy lambda functions for the component references and properties
+ # A reference proxy is the callable lambda function of the component wired to the reference
+ # A property proxy is a lambda function that returns the value of the property
+ print >> stderr, "evalComponent", comp.impl, comp.svcs, comp.refs, comp.props
+ comp.proxies = tuple(map(lambda r: evalReference(r, comps), comp.refs)) + tuple(map(lambda p: evalProperty(p), comp.props))
+
+ return comp
+
+# Evaluate a list of components
+def evalComponents(comps):
+ return tuple(map(lambda c: evalComponent(c, comps), comps))
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/server-test b/sandbox/sebastien/cpp/apr-2/modules/wsgi/server-test
new file mode 100755
index 0000000000..9bd862c53a
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/server-test
@@ -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.
+
+# Setup
+./wsgi-start target 8090 2>/dev/null
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./wsgi-stop target 8090
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/server-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/server-test.py
new file mode 100644
index 0000000000..28f88efefc
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/server-test.py
@@ -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.
+
+# JSON-RPC test case
+
+def echo(x):
+ return x
+
+# ATOMPub test case
+
+def get(id):
+ if id == ("index.html",):
+ return ("text/plain", ("It works!",))
+ if id == ():
+ return ("Sample Feed", "123456789",
+ ("Item", "111", (("'name", "Apple"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 2.99))),
+ ("Item", "222", (("'name", "Orange"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 3.55))),
+ ("Item", "333", (("'name", "Pear"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 1.55))))
+
+ entry = (("'name", "Apple"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 2.99))
+ return ("Item", id[0], entry)
+
+def post(collection, item):
+ return ("123456789",)
+
+def put(id, item):
+ return True
+
+def delete(id):
+ return True
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/stream-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/stream-test.py
new file mode 100755
index 0000000000..2cff038f1e
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/stream-test.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+# 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.
+
+# Test stream functions
+
+import unittest
+from util import *
+
+def testStream():
+
+ s = cons_stream(0, lambda: cons_stream(1, lambda: cons(2, ())))
+ assert len(s) == 3
+ assert car(s) == 0
+ assert cadr(s) == 1
+ assert len(cdr(s)) == 2
+ assert s[0] == 0
+ assert s[1] == 1
+ assert s[2] == 2
+ assert s[:1] == (0, 1)
+ assert s[:5] == (0, 1, 2)
+ assert s[2:5] == (2,)
+ assert s[4:5] == ()
+ assert s[0:] == (0, 1, 2)
+ assert (0, 1, 2) == s[0:]
+
+ return True
+
+if __name__ == "__main__":
+ print "Testing..."
+ testStream()
+ print "OK"
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/util-test b/sandbox/sebastien/cpp/apr-2/modules/wsgi/util-test
new file mode 100755
index 0000000000..aa8725a200
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/util-test
@@ -0,0 +1,43 @@
+#!/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.
+
+# Run Python util test cases
+here=`readlink -f $0`; here=`dirname $here`
+python_prefix=`cat $here/../python/python.prefix`
+
+$python_prefix/bin/python stream-test.py
+rc=$?
+if [ "$rc" = "0" ]; then
+ $python_prefix/bin/python xml-test.py
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $python_prefix/bin/python atom-test.py
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $python_prefix/bin/python rss-test.py
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $python_prefix/bin/python json-test.py
+ rc=$?
+fi
+
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/util.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/util.py
new file mode 100644
index 0000000000..560101e32d
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/util.py
@@ -0,0 +1,145 @@
+# 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.
+
+# Simple utility functions
+from sys import maxint
+
+# Scheme-like lists
+def cons(a, b):
+ return (a,) + b
+
+def car(l):
+ return l[0]
+
+def first(l):
+ return car(l)
+
+def cdr(l):
+ return l[1:]
+
+def rest(l):
+ return cdr(l)
+
+def cadr(l):
+ return car(cdr(l))
+
+def cddr(l):
+ return cdr(cdr(l))
+
+def caddr(l):
+ return car(cddr(l))
+
+def append(a, b):
+ return a + b
+
+def reverse(l):
+ r = list(l)
+ r.reverse()
+ return tuple(r)
+
+def isNil(l):
+ if isinstance(l, streampair):
+ return l.isNil()
+ return l == ()
+
+def isSymbol(v):
+ return isinstance(v, basestring) and v[0:1] == "'"
+
+def isList(v):
+ if getattr(v, '__iter__', False) == False:
+ return False
+ if isinstance(v, basestring) or isinstance(v, dict):
+ return False
+ return True
+
+def isTaggedList(v, t):
+ return isList(v) and not isNil(v) and car(v) == t
+
+
+# Scheme-like streams
+class streampair(object):
+ def __init__(self, car, cdr):
+ self.car = car
+ self.cdr = cdr
+
+ def __repr__(self):
+ return repr(self[0:len(self)])
+
+ def isNil(self):
+ return self.cdr == ()
+
+ def __len__(self):
+ if self.cdr == ():
+ return 0
+ return 1 + len(self.cdr())
+
+ def __getitem__(self, i):
+ if i == 0:
+ return self.car
+ return self.cdr()[i - 1]
+
+ def __getslice__(self, i, j):
+ if isNil(self):
+ return ()
+ if i > 0:
+ if j == maxint:
+ return self.cdr()[i - 1: j]
+ return self.cdr()[i - 1: j - 1]
+ if j == maxint:
+ return self
+ if j == 0:
+ return (self.car,)
+ return (self.car,) + self.cdr()[: j - 1]
+
+ def __eq__(self, other):
+ sl = len(self)
+ ol = len(other)
+ if sl != ol:
+ return False
+ return self[0: sl] == other[0: ol]
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+def cons_stream(car, cdr):
+ return streampair(car, cdr)
+
+
+# Scheme-like associations
+def assoc(k, l):
+ if l == ():
+ return None
+
+ if k == car(car(l)):
+ return car(l)
+ return assoc(k, cdr(l))
+
+# Currying / partial function application
+def curry(f, *args):
+ return lambda *a: f(*(args + a))
+
+# Split a path into a list of segments
+def tokens(path):
+ return tuple(filter(lambda s: len(s) != 0, path.split("/")))
+
+# Write a list of strings to a stream
+def writeStrings(l, os):
+ if l == ():
+ return os
+ os.write(car(l))
+ return writeStrings(cdr(l), os)
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/wiring-test b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wiring-test
new file mode 100755
index 0000000000..f3c1bbb840
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wiring-test
@@ -0,0 +1,75 @@
+#!/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=`readlink -f $0`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+uri=$1
+if [ "$uri" = "" ]; then
+ uri="http://localhost:8090"
+fi
+
+# Setup
+mkdir -p tmp
+./wsgi-start target 8090 2>/dev/null
+sleep 2
+
+# Test HTTP GET
+$curl_prefix/bin/curl $uri/index.html 2>/dev/null >tmp/index.html
+diff tmp/index.html htdocs/index.html
+rc=$?
+
+# Test ATOMPub
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl $uri/client/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml htdocs/test/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl $uri/client/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml htdocs/test/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl $uri/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl $uri/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl $uri/client/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl $uri/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt htdocs/test/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
+./wsgi-stop target 8090
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-start b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-start
new file mode 100755
index 0000000000..d020f3da14
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-start
@@ -0,0 +1,28 @@
+#!/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 WSGI server
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+port=$2
+
+python_prefix=`cat $here/../python/python.prefix`
+cd $root
+$python_prefix/bin/python composite.py $port &
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-stop b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-stop
new file mode 100755
index 0000000000..7e12967adb
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-stop
@@ -0,0 +1,28 @@
+#!/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 WSGI server
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+port=$2
+
+python_prefix=`cat $here/../python/python.prefix`
+py="$python_prefix/bin/python composite.py $port"
+
+kill `ps -ef | grep -v grep | grep "${py}" | awk '{ print $2 }'`
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-test b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-test
new file mode 100755
index 0000000000..369ca5a677
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/wsgi-test
@@ -0,0 +1,71 @@
+#!/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=`readlink -f $0`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+mkdir -p tmp
+./wsgi-start target 8090 2>/dev/null
+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=$?
+
+# Test ATOMPub
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/wsgi/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml htdocs/test/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/wsgi/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml htdocs/test/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/wsgi/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/wsgi/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/wsgi/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/wsgi/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt htdocs/test/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
+./wsgi-stop target 8090
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/xml-test.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/xml-test.py
new file mode 100755
index 0000000000..f60322bdc1
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/xml-test.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+# 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.
+
+# Test XML handling functions
+
+import unittest
+from elemutil import *
+from xmlutil import *
+
+customerXML = \
+ "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \
+ "<customer>" \
+ "<name>jdoe</name>" \
+ "<address><city>san francisco</city><state>ca</state></address>" \
+ "<account><id>1234</id><balance>1000</balance></account>" \
+ "<account><id>6789</id><balance>2000</balance></account>" \
+ "<account><id>4567</id><balance>3000</balance></account>" \
+ "</customer>\n"
+
+def testElements():
+ ad = (("'city", "san francisco"), ("'state", "ca"))
+ ac1 = (("'id", "1234"), ("'balance", 1000))
+ ac2 = (("'id", "6789"), ("'balance", 2000))
+ ac3 = (("'id", "4567"), ("'balance", 3000))
+ c = (("'customer", ("'name", "jdoe"), cons("'address", ad), ("'account", (ac1, ac2, ac3))),)
+ e = valuesToElements(c)
+ v = elementsToValues(e)
+ assert v == c
+ s = writeXML(e, True)
+ assert car(s) == customerXML
+
+ c2 = (("'customer", ("'name", "jdoe"), cons("'address", ad), cons("'account", ac1), cons("'account", ac2), cons("'account", ac3)),)
+ e2 = valuesToElements(c2);
+ v2 = elementsToValues(e2);
+ s2 = writeXML(e2, True)
+ assert car(s2) == customerXML
+
+ c3 = readXML((customerXML,))
+ v3 = elementsToValues(c3)
+ e3 = valuesToElements(v3)
+ s3 = writeXML(e3, True)
+ assert car(s3) == customerXML
+ return True
+
+def testValues():
+ l = (("'ns1:echoString", ("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), ("'text", "Hello World!")),)
+ e = valuesToElements(l)
+ lx = writeXML(e, True)
+ x = readXML(lx)
+ v = elementsToValues(x)
+ assert v == l
+ return True
+
+if __name__ == "__main__":
+ print "Testing..."
+ testElements()
+ testValues()
+ print "OK"
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/wsgi/xmlutil.py b/sandbox/sebastien/cpp/apr-2/modules/wsgi/xmlutil.py
new file mode 100644
index 0000000000..35ccb7f803
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/wsgi/xmlutil.py
@@ -0,0 +1,122 @@
+# 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.
+
+# XML handling functions
+
+from StringIO import StringIO
+from xml.parsers import expat
+import xml.etree.ElementTree as et
+from util import *
+from elemutil import *
+
+# Read a list of XML attributes
+def readAttributes(a):
+ if a == ():
+ return a
+ return cons((attribute, "'" + car(car(a)), cadr(car(a))), readAttributes(cdr(a)))
+
+# Read an XML element
+def readElement(e):
+ l = (element, "'" + e.tag) + readAttributes(tuple(e.items())) + readElements(tuple(e.getchildren()))
+ if e.text == None:
+ return l
+ return l + (e.text,)
+
+# Read a list of XML elements
+def readElements(l):
+ if l == ():
+ return l
+ return cons(readElement(car(l)), readElements(cdr(l)))
+
+# Return true if a list of strings represents an XML document
+def isXML(l):
+ if isNil(l):
+ return False
+ if car(l)[0:5] != "<?xml":
+ return False
+ return True
+
+# Parse a list of strings representing an XML document
+class NamespaceParser(et.XMLTreeBuilder):
+ def __init__(self):
+ et.XMLTreeBuilder.__init__(self)
+ self._parser = parser = expat.ParserCreate(None)
+ parser.DefaultHandlerExpand = self._default
+ parser.StartElementHandler = self._start
+ parser.EndElementHandler = self._end
+ parser.CharacterDataHandler = self._data
+ try:
+ parser.buffer_text = 1
+ except AttributeError:
+ pass
+ try:
+ parser.ordered_attributes = 1
+ parser.specified_attributes = 1
+ parser.StartElementHandler = self._start_list
+ except AttributeError:
+ pass
+
+def parseXML(l):
+ s = StringIO()
+ writeStrings(l, s)
+ parser = NamespaceParser()
+ parser.feed(s.getvalue())
+ return parser.close()
+
+# Read a list of values from a list of strings representing an XML document
+def readXML(l):
+ e = parseXML(l)
+ return (readElement(e),)
+
+# Write a list of XML element and attribute tokens
+def expandElementValues(n, l):
+ if isNil(l):
+ return l
+ return cons(cons(element, cons(n, car(l))), expandElementValues(n, cdr(l)))
+
+def writeList(l, xml):
+ if isNil(l):
+ return xml
+ token = car(l)
+ if isTaggedList(token, attribute):
+ xml.attrib[attributeName(token)[1:]] = str(attributeValue(token))
+ elif isTaggedList(token, element):
+ if elementHasValue(token):
+ v = elementValue(token)
+ if isList(v):
+ e = expandElementValues(elementName(token), v)
+ writeList(e, xml)
+ else:
+ child = et.Element(elementName(token)[1:])
+ writeList(elementChildren(token), child)
+ xml.append(child)
+ else:
+ child = et.Element(elementName(token)[1:])
+ writeList(elementChildren(token), child)
+ xml.append(child)
+ else:
+ xml.text = str(token)
+ writeList(cdr(l), xml)
+ return xml
+
+# Convert a list of values to a list of strings representing an XML document
+def writeXML(l, xmlTag):
+ e = writeList(l, [])
+ if not xmlTag:
+ return (et.tostring(car(e)),)
+ return (et.tostring(car(e), "UTF-8") + "\n",)
+