summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/cpp/apr-2/modules/wsgi/composite.py
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/sebastien/cpp/apr-2/modules/wsgi/composite.py')
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/wsgi/composite.py251
1 files changed, 251 insertions, 0 deletions
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()
+