diff options
Diffstat (limited to 'sandbox/sebastien/cpp/apr-2/modules/wsgi')
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",) + |