summaryrefslogtreecommitdiffstats
path: root/sca-cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sca-cpp/trunk/modules/http/httpd.hpp8
-rwxr-xr-xsca-cpp/trunk/modules/wsgi/composite.py104
2 files changed, 76 insertions, 36 deletions
diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp
index bd0e23f76b..dfc3dcc47e 100644
--- a/sca-cpp/trunk/modules/http/httpd.hpp
+++ b/sca-cpp/trunk/modules/http/httpd.hpp
@@ -213,10 +213,18 @@ const failable<int> writeResult(const failable<list<string> >& ls, const string&
const string ob(str(os));
debug(ob, "httpd::result");
+ // Make sure browsers come back and check for updated dynamic content
+ apr_table_setn(r->headers_out, "Expires", "Tue, 01 Jan 1980 00:00:00 GMT");
+
+ // Compute and return an Etag for the returned content
const string etag(ap_md5(r->pool, (const unsigned char*)c_str(ob)));
+
+ // Check for an If-None-Match header and just return a 304 not-modified status
+ // if the Etag matches the Etag presented by the client, to save bandwith
const char* match = apr_table_get(r->headers_in, "If-None-Match");
apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, c_str(etag)));
if (match != NULL && etag == match) {
+
r->status = HTTP_NOT_MODIFIED;
return OK;
}
diff --git a/sca-cpp/trunk/modules/wsgi/composite.py b/sca-cpp/trunk/modules/wsgi/composite.py
index 0434a6b3af..4be44c3101 100755
--- a/sca-cpp/trunk/modules/wsgi/composite.py
+++ b/sca-cpp/trunk/modules/wsgi/composite.py
@@ -24,6 +24,7 @@ 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 *
@@ -53,28 +54,59 @@ def requestBody(e):
l = int(e.get("CONTENT_LENGTH", "0"))
return (i.read(l),)
-# Return an HTTP status
-def errstatus(st):
- return (st, "<html><head><title>"+ st + "</title></head><body><h1>" + st[4:] + "</h1></body></html>")
+def requestIfNoneMatch(e):
+ return e.get("HTTP_IF_NONE_MATCH", "");
-def status(st, b):
- if st == 200:
- return cons("200 OK", b)
- if st == 404:
- return errstatus("404 Not Found")
- if st == 201:
- return cons("201 Created", b)
- return errstatus("500 Internal Server Error")
+# 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 result
-def result(r, st, h = (), b = ()):
- s = status(st, b)
- r(car(s), list(h))
- return cdr(s)
+# Return an HTTP success result
+def result(e, r, st, h = (), b = None):
+ if st == 201:
+ r("201 Created", list(h))
+ return ()
-# Send a file
-def fileresult(f):
- return tuple(FileWrapper(open("htdocs" + f)))
+ 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
+
+ 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):
@@ -90,23 +122,23 @@ def application(e, r):
# Debug hook
if fpath == "/debug":
- return result(r, 200, (("Content-type", "text/plain"),), ("Debug",))
+ return result(e, r, 200, (("Content-type", "text/plain"),), ("Debug",))
# Serve static files
if m == "GET":
if fpath.endswith(".html"):
- return result(r, 200, (("Content-type", "text/html"),), fileresult(fpath))
+ return fileresult(e, r, "text/html", fpath)
if fpath.endswith(".js"):
- return result(r, 200, (("Content-type", "application/x-javascript"),), fileresult(fpath))
+ return fileresult(e, r, "application/x-javascript", fpath)
if fpath.endswith(".png"):
- return result(r, 200, (("Content-type", "image/png"),), fileresult(fpath))
+ return fileresult(e, r, "image/png", fpath)
# Find the requested component
path = tokens(fpath)
uc = uriToComponent(path, comps)
uri = car(uc)
if uri == None:
- return result(r, 404)
+ return failure(e, r, 404)
comp = cadr(uc)
# Call the requested component function
@@ -116,12 +148,12 @@ def application(e, r):
# Write returned content-type / content pair
if not isinstance(cadr(v), basestring):
- return result(r, 200, (("Content-type", car(v)),), cadr(v))
+ return result(e, r, 200, (("Content-type", car(v)),), cadr(v))
# Write an ATOM feed or entry
if isNil(id):
- return result(r, 200, (("Content-type", "application/atom+xml;type=feed"),), writeATOMFeed(feedValuesToElements(v)))
- return result(r, 200, (("Content-type", "application/atom+xml;type=entry"),), writeATOMEntry(entryValuesToElements(v)))
+ 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)
@@ -134,32 +166,32 @@ def application(e, r):
func = funcName(cadr(assoc("'method", args)))
params = cadr(assoc("'params", args))
v = comp(func, *params)
- return result(r, 200, (("Content-type", "application/json-rpc"),), jsonResult(jid, v))
+ 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 result(r, 500)
- return result(r, 201, (("Location", request_uri(e) + "/" + "/".join(v)),))
- return result(r, 500)
+ 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 result(r, 404)
- return result(r, 200)
+ return failure(e, r, 404)
+ return result(e, r, 200)
if m == "DELETE":
v = comp("delete", id)
if v == False:
- return result(r, 404)
- return result(r, 200)
+ return failure(e, r, 404)
+ return result(e, r, 200)
- return result(r, 500)
+ return failure(e, r, 500)
# Return the WSGI server type
def serverType(e):