summaryrefslogtreecommitdiffstats
path: root/sca-cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp')
-rw-r--r--sca-cpp/trunk/kernel/gc.hpp29
-rw-r--r--sca-cpp/trunk/kernel/list.hpp2
-rw-r--r--sca-cpp/trunk/kernel/value.hpp28
-rw-r--r--sca-cpp/trunk/modules/http/http.hpp98
-rwxr-xr-xsca-cpp/trunk/modules/http/httpd-conf6
-rw-r--r--sca-cpp/trunk/modules/http/httpd.hpp20
-rw-r--r--sca-cpp/trunk/modules/js/eval.hpp8
-rw-r--r--sca-cpp/trunk/modules/js/htdocs/jsonutil.js2
-rw-r--r--sca-cpp/trunk/modules/scheme/environment.hpp5
-rw-r--r--sca-cpp/trunk/modules/scheme/eval-test.cpp38
-rw-r--r--sca-cpp/trunk/modules/scheme/io.hpp43
-rw-r--r--sca-cpp/trunk/modules/wsgi/jsonutil.py2
12 files changed, 214 insertions, 67 deletions
diff --git a/sca-cpp/trunk/kernel/gc.hpp b/sca-cpp/trunk/kernel/gc.hpp
index 09dcd1e5ac..d843b50ca0 100644
--- a/sca-cpp/trunk/kernel/gc.hpp
+++ b/sca-cpp/trunk/kernel/gc.hpp
@@ -36,6 +36,23 @@
namespace tuscany
{
+#ifdef WANT_MAINTAINER_MODE
+
+/**
+ * Force a core dump on assertion violation.
+ */
+bool assertOrFail(const bool expr) {
+ if (!expr)
+ *(char*)NULL = '\0';
+ return true;
+}
+
+#else
+
+#define assertOrFail(expr)
+
+#endif
+
/**
* Pointer to a value.
*/
@@ -117,7 +134,7 @@ private:
apr_pool_t* mkpool() {
apr_pool_t* p = NULL;
apr_pool_create(&p, NULL);
- assert(p != NULL);
+ assertOrFail(p != NULL);
return p;
}
@@ -164,7 +181,7 @@ apr_pool_t* gc_current_pool() {
// Create a parent pool for the current thread
apr_pool_create(&apr_pool, NULL);
- assert(apr_pool != NULL);
+ assertOrFail(apr_pool != NULL);
gc_pool_stack = apr_pool;
return apr_pool;
}
@@ -196,7 +213,7 @@ public:
gc_scoped_pool() : gc_pool(NULL), prev(gc_current_pool()), owner(true) {
apr_pool_create(&apr_pool, NULL);
- assert(apr_pool != NULL);
+ assertOrFail(apr_pool != NULL);
gc_push_pool(apr_pool);
}
@@ -230,7 +247,7 @@ template<typename T> apr_status_t gc_pool_cleanup(void* v) {
template<typename T> T* gc_new(apr_pool_t* p) {
void* gc_new_ptr = apr_palloc(p, sizeof(T));
- assert(gc_new_ptr != NULL);
+ assertOrFail(gc_new_ptr != NULL);
apr_pool_cleanup_register(p, gc_new_ptr, gc_pool_cleanup<T>, apr_pool_cleanup_null) ;
return static_cast<T*>(gc_new_ptr);
}
@@ -254,7 +271,7 @@ template<typename T> apr_status_t gc_pool_acleanup(void* v) {
template<typename T> T* gc_anew(apr_pool_t* p, size_t n) {
size_t* gc_anew_ptr = static_cast<size_t*>(apr_palloc(p, sizeof(size_t) + sizeof(T[n])));
- assert(gc_anew_ptr != NULL);
+ assertOrFail(gc_anew_ptr != NULL);
*gc_anew_ptr = n;
apr_pool_cleanup_register(p, gc_anew_ptr, gc_pool_acleanup<T>, apr_pool_cleanup_null) ;
return (T*)(gc_anew_ptr + 1);
@@ -273,7 +290,7 @@ template<typename T> T* gc_anew(size_t n) {
*/
char* gc_cnew(apr_pool_t* p, size_t n) {
char* gc_cnew_ptr = static_cast<char*>(apr_palloc(p, n));
- assert(gc_cnew_ptr != NULL);
+ assertOrFail(gc_cnew_ptr != NULL);
return gc_cnew_ptr;
}
diff --git a/sca-cpp/trunk/kernel/list.hpp b/sca-cpp/trunk/kernel/list.hpp
index a8dbcc1b0c..df7bc27c03 100644
--- a/sca-cpp/trunk/kernel/list.hpp
+++ b/sca-cpp/trunk/kernel/list.hpp
@@ -301,7 +301,7 @@ template<typename T> const list<T> mklist(const T& a, const T& b, const T& c, co
*/
template<typename T> const T car(const list<T>& p) {
// Abort if trying to access the car of a nil list
- assert(!isNil(p.cdr));
+ assertOrFail(!isNil(p.cdr));
return p.car;
}
diff --git a/sca-cpp/trunk/kernel/value.hpp b/sca-cpp/trunk/kernel/value.hpp
index 07be7f5c82..3e0dd0002f 100644
--- a/sca-cpp/trunk/kernel/value.hpp
+++ b/sca-cpp/trunk/kernel/value.hpp
@@ -95,10 +95,10 @@ class value {
public:
enum ValueType {
- Undefined, Symbol, String, List, Number, Bool, Lambda, Ptr
+ Nil, Symbol, String, List, Number, Bool, Lambda, Ptr
};
- value() : type(value::Undefined) {
+ value() : type(value::Nil) {
debug_inc(countValues);
debug_inc(countEValues);
debug_watchValue();
@@ -239,8 +239,8 @@ public:
if(this == &v)
return true;
switch(type) {
- case value::Undefined:
- return v.type == value::Undefined;
+ case value::Nil:
+ return v.type == value::Nil;
case value::List:
return v.type == value::List && lst()() == v.lst()();
case value::Lambda:
@@ -448,6 +448,18 @@ const string watchValue(const value& v) {
#endif
/**
+ * Write an escaped string value to a stream.
+ */
+ostream& escvwrite(const char* s, ostream& out) {
+ if (*s == '\0')
+ return out;
+ if (*s == '\\' || *s == '"')
+ out << '\\';
+ out << *s;
+ return escvwrite(s + 1, out);
+}
+
+/**
* Write a value to a stream.
*/
ostream& operator<<(ostream& out, const value& v) {
@@ -459,7 +471,9 @@ ostream& operator<<(ostream& out, const value& v) {
case value::Symbol:
return out << v.str()();
case value::String:
- return out << '\"' << v.str()() << '\"';
+ out << '\"';
+ escvwrite(c_str(v.str()()), out);
+ return out << '\"';
case value::Number:
return out << v.num()();
case value::Bool:
@@ -474,7 +488,7 @@ ostream& operator<<(ostream& out, const value& v) {
return out << "gc_ptr::" << p;
}
default:
- return out << "undefined";
+ return out << "nil";
}
}
@@ -489,7 +503,7 @@ const value::ValueType type(const value& v) {
* Returns true if a value is nil.
*/
const bool isNil(const value& v) {
- return type(v) == value::Undefined;
+ return type(v) == value::Nil;
}
/**
diff --git a/sca-cpp/trunk/modules/http/http.hpp b/sca-cpp/trunk/modules/http/http.hpp
index 7050021fb9..1a207669c0 100644
--- a/sca-cpp/trunk/modules/http/http.hpp
+++ b/sca-cpp/trunk/modules/http/http.hpp
@@ -30,6 +30,8 @@
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
+#include <apr.h>
+#include <apr_lib.h>
#include <apr_network_io.h>
#include <apr_portable.h>
#include <apr_poll.h>
@@ -93,6 +95,7 @@ private:
friend CURL* handle(const CURLSession& cs);
friend apr_socket_t* sock(const CURLSession& cs);
+ friend const failable<CURL*> setup(const string& url, const CURLSession& cs);
friend const failable<bool> connect(const string& url, CURLSession& cs);
friend const failable<bool> send(const char* c, const size_t l, const CURLSession& cs);
friend const failable<size_t> recv(char* c, const size_t l, const CURLSession& cs);
@@ -143,6 +146,45 @@ const string apreason(apr_status_t rc) {
}
/**
+ * Escape a URI or a query argument.
+ */
+const char escape_c2x[] = "0123456789ABCDEF";
+
+const string escape(const string& unesc, const char* reserv) {
+ char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(unesc) + 3);
+ const unsigned char* s = (const unsigned char *)c_str(unesc);
+ unsigned char* d = (unsigned char*)copy;
+ unsigned c;
+ while ((c = *s)) {
+ if (!apr_isalnum(c) && !strchr(reserv, c)) {
+ *d++ = '%';
+ *d++ = escape_c2x[c >> 4];
+ *d++ = escape_c2x[c & 0xf];
+ }
+ else {
+ *d++ = (unsigned char)c;
+ }
+ ++s;
+ }
+ *d = '\0';
+ return copy;
+}
+
+const string escapeURI(const string& uri) {
+ debug(uri, "http::escapeURI::uri");
+ const string e = escape(uri, "?$-_.+!*'(),:@&=/~%");
+ debug(e, "http::escapeURI::result");
+ return e;
+}
+
+const string escapeArg(const string& arg) {
+ debug(arg, "http::escapeArg::arg");
+ const string e = escape(arg, "-_.~");
+ debug(e, "http::escapeArg::result");
+ return e;
+}
+
+/**
* Setup a CURL session
*/
const failable<CURL*> setup(const string& url, const CURLSession& cs) {
@@ -177,7 +219,7 @@ const failable<CURL*> setup(const string& url, const CURLSession& cs) {
}
// Set target URL
- curl_easy_setopt(ch, CURLOPT_URL, c_str(url));
+ curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(url)));
return ch;
}
@@ -430,6 +472,22 @@ const failable<value> get(const string& url, const CURLSession& cs) {
debug(val, "http::get::result");
return val;
}
+ if (contains(ct, "application/x-javascript")) {
+ // Read a JSON document enclosed in a javascript function call
+ // Extract the JSON out of the enclosing parenthesis
+ ostringstream os;
+ write(ls, os);
+ const string s = str(os);
+ const size_t fp = find(s, '(');
+ const size_t lp = find_last(s, ')');
+ const list<string> jls = mklist<string>(substr(s, fp + 1, lp - (fp + 1)));
+ debug(jls, "http::get::javascript::content");
+
+ js::JSContext cx;
+ const value val(json::jsonValues(content(json::readJSON(jls, cx))));
+ debug(val, "http::get::result");
+ return val;
+ }
if (contains(ct, "text/xml") || contains(ct, "application/xml") || isXML(ls)) {
// Read an XML document
const value val(elementsToValues(readXML(ls)));
@@ -620,6 +678,23 @@ const failable<size_t> recv(char* c, const size_t l, const CURLSession& cs) {
return recv(c, l, cs);
}
+/**
+ * Converts a list of key value pairs to a query string.
+ */
+ostringstream& queryString(const list<list<value> > args, ostringstream& os) {
+ if (isNil(args))
+ return os;
+ debug(car(args), "http::queryString::arg");
+ os << car(car(args)) << "=" << c_str(cadr(car(args)));
+ if (!isNil(cdr(args)))
+ os << "&";
+ return queryString(cdr(args), os);
+}
+
+const string queryString(const list<list<value> > args) {
+ ostringstream os;
+ return str(queryString(args, os));
+}
/**
* Filter path segment in a list of arguments.
@@ -636,21 +711,11 @@ const bool filterQuery(const value& arg) {
}
/**
- * Converts a list of key value pairs to a query string.
+ * Escape a query string argument.
*/
-ostringstream& queryString(const list<list<value> > args, ostringstream& os) {
- if (isNil(args))
- return os;
- debug(car(args), "http::queryString::arg");
- os << car(car(args)) << "=" << c_str(cadr(car(args)));
- if (!isNil(cdr(args)))
- os << "&";
- return queryString(cdr(args), os);
-}
-
-const string queryString(const list<list<value> > args) {
- ostringstream os;
- return str(queryString(args, os));
+const value escapeQuery(const value& arg) {
+ return arg;
+ //return mklist<value>(car<value>(arg), escapeArg(cadr<value>(arg)));
}
/**
@@ -665,8 +730,7 @@ struct proxy {
if (fun == "get") {
const list<value> lp = filter<value>(filterPath, cadr(args));
debug(lp, "http::queryString::arg");
- const list<value> lq = filter<value>(filterQuery, cadr(args));
- debug(lq, "http::get::query");
+ const list<value> lq = map<value, value>(escapeQuery, filter<value>(filterQuery, cadr(args)));
const value p = path(lp);
const value q = queryString(lq);
const failable<value> val = get(uri + p + (q != ""? string("?") + q : string("")), cs);
diff --git a/sca-cpp/trunk/modules/http/httpd-conf b/sca-cpp/trunk/modules/http/httpd-conf
index cfa3952521..7c9190d08c 100755
--- a/sca-cpp/trunk/modules/http/httpd-conf
+++ b/sca-cpp/trunk/modules/http/httpd-conf
@@ -82,9 +82,9 @@ AddCharset utf-8 .js .css
# Configure cache control
SetEnvIf Request_URI "^/app.html$" must-revalidate
-Header onsuccess merge Cache-Control public
-Header onsuccess merge Cache-Control max-age=31536000
-Header onsuccess merge Cache-Control must-revalidate env=must-revalidate
+Header onsuccess set Cache-Control "max-age=86400" env=!must-revalidate
+Header set Cache-Control "must-revalidate, max-age=0" env=must-revalidate
+Header set Expires "Tue, 01 Jan 1980 00:00:00 GMT" env=must-revalidate
# Set default document root
DocumentRoot $htdocs
diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp
index 0dd7920f65..86a89d8823 100644
--- a/sca-cpp/trunk/modules/http/httpd.hpp
+++ b/sca-cpp/trunk/modules/http/httpd.hpp
@@ -252,6 +252,20 @@ const string unescape(const string& uri) {
}
/**
+ * Unescape a list of key of value pairs representing query args.
+ */
+const list<value> unescapeArg(const list<value> a) {
+ return mklist<value>(car(a), unescape(cadr(a)));
+}
+
+const list<list<value> > unescapeArgs(const list<list<value> > args) {
+ debug(args, "httpd::unescape::args");
+ const list<list<value> > uargs = map<list<value>, list<value>>(unescapeArg, args);
+ debug(uargs, "httpd::unescape::result");
+ return uargs;
+}
+
+/**
* Returns a list of key value pairs from the args in a query string.
*/
const list<value> queryArg(const string& s) {
@@ -328,11 +342,11 @@ const failable<int> writeResult(const failable<list<string> >& ls, const string&
const string ob(str(os));
// 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");
- apr_table_setn(r->headers_out, "Cache-Control", "must-revalidate");
+ // The actual header setup is configured in httpd-conf, based on the must-revalidate env variable
+ apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "must-revalidate"), apr_pstrdup(r->pool, "true"));
// Compute and return an Etag for the returned content
- const string etag(ap_md5(r->pool, (const unsigned char*)c_str(ob)));
+ const string etag(ap_md5_binary(r->pool, (const unsigned char*)c_str(ob), (int)length(ob)));
// Check for an If-None-Match header and just return a 304 not-modified status
// if the Etag matches the Etag presented by the client, to save bandwith
diff --git a/sca-cpp/trunk/modules/js/eval.hpp b/sca-cpp/trunk/modules/js/eval.hpp
index cdb9037bb7..79ae2aec65 100644
--- a/sca-cpp/trunk/modules/js/eval.hpp
+++ b/sca-cpp/trunk/modules/js/eval.hpp
@@ -177,9 +177,10 @@ const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObj
jsval idv;
JS_IdToValue(cx, id, &idv);
if(JSVAL_IS_STRING(idv)) {
- if (isNil(val) && !isList(val))
- return jsPropertiesToValues(propertiesSoFar, o, i, cx);
const string name = JS_GetStringBytes(JSVAL_TO_STRING(idv));
+ if (isNil(val) && !isList(val))
+ return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), val), propertiesSoFar), o, i, cx);
+ //return jsPropertiesToValues(propertiesSoFar, o, i, cx);
if (substr(name, 0, 1) == atsign)
return jsPropertiesToValues(cons<value>(mklist<value>(attribute, c_str(substr(name, 1)), val), propertiesSoFar), o, i, cx);
if (isList(val) && !isJSArray(val))
@@ -257,6 +258,9 @@ const jsval valueToJSVal(const value& val, const js::JSContext& cx) {
return OBJECT_TO_JSVAL(valuesToJSElements(JS_NewArrayObject(cx, 0, NULL), val, 0, cx));
return OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), val, cx));
}
+ case value::Nil: {
+ return JSVAL_NULL;
+ }
default: {
return JSVAL_VOID;
}
diff --git a/sca-cpp/trunk/modules/js/htdocs/jsonutil.js b/sca-cpp/trunk/modules/js/htdocs/jsonutil.js
index 47e3ec4130..8aa291bc89 100644
--- a/sca-cpp/trunk/modules/js/htdocs/jsonutil.js
+++ b/sca-cpp/trunk/modules/js/htdocs/jsonutil.js
@@ -77,6 +77,8 @@ json.jsPropertiesToValues = function(propertiesSoFar, o, i) {
* Converts a JSON val to a value.
*/
json.jsValToValue = function(jsv) {
+ if (jsv == null)
+ return null;
if (isList(jsv))
return json.jsPropertiesToValues(mklist(), jsv, reverse(range(0, jsv.length)));
if (typeof jsv == 'object')
diff --git a/sca-cpp/trunk/modules/scheme/environment.hpp b/sca-cpp/trunk/modules/scheme/environment.hpp
index bfb415a978..303a37cb3c 100644
--- a/sca-cpp/trunk/modules/scheme/environment.hpp
+++ b/sca-cpp/trunk/modules/scheme/environment.hpp
@@ -118,8 +118,11 @@ const value definitionVariable(const value& exp) {
const value definitionValue(const value& exp) {
const list<value> exps(exp);
- if(isSymbol(car(cdr(exps))))
+ if(isSymbol(car(cdr(exps)))) {
+ if (isNil(cdr(cdr(exps))))
+ return value();
return car(cdr(cdr(exps)));
+ }
const list<value> lexps(car(cdr(exps)));
return makeLambda(cdr(lexps), cdr(cdr(exps)));
}
diff --git a/sca-cpp/trunk/modules/scheme/eval-test.cpp b/sca-cpp/trunk/modules/scheme/eval-test.cpp
index 7c4c0c69c4..dd97bc358d 100644
--- a/sca-cpp/trunk/modules/scheme/eval-test.cpp
+++ b/sca-cpp/trunk/modules/scheme/eval-test.cpp
@@ -72,20 +72,30 @@ bool testRead() {
}
bool testWrite() {
- const list<value> i = list<value>()
- + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"
- + (list<value>() + "item"
- + (list<value>() + "name" + "Apple")
- + (list<value>() + "price" + "$2.99")))
- + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c"
- + (list<value>() + "item"
- + (list<value>() + "name" + "Orange")
- + (list<value>() + "price" + "$3.55")));
- const list<value> a = cons<value>("Feed", cons<value>("feed-1234", i));
- ostringstream os;
- writeValue(a, os);
- istringstream is(str(os));
- assert(readValue(is) == a);
+ {
+ const list<value> i = list<value>()
+ + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"
+ + (list<value>() + "item"
+ + (list<value>() + "name" + "Apple")
+ + (list<value>() + "price" + "$2.99")))
+ + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c"
+ + (list<value>() + "item"
+ + (list<value>() + "name" + "Orange")
+ + (list<value>() + "price" + "$3.55")));
+ const list<value> a = cons<value>("Feed", cons<value>("feed-1234", i));
+ ostringstream os;
+ writeValue(a, os);
+ istringstream is(str(os));
+ assert(readValue(is) == a);
+ }
+ {
+ const list<value> i = mklist<value>("x", value());
+ const list<value> a = mklist<value>(i);
+ ostringstream os;
+ writeValue(a, os);
+ istringstream is(str(os));
+ assert(readValue(is) == a);
+ }
return true;
}
diff --git a/sca-cpp/trunk/modules/scheme/io.hpp b/sca-cpp/trunk/modules/scheme/io.hpp
index 6928739d17..8f9d70e7fe 100644
--- a/sca-cpp/trunk/modules/scheme/io.hpp
+++ b/sca-cpp/trunk/modules/scheme/io.hpp
@@ -88,14 +88,14 @@ const bool isQuote(const value& token) {
return token == quoteSymbol;
}
-const value skipComment(istream& in);
+const failable<value> skipComment(istream& in);
const value readQuoted(istream& in);
const value readIdentifier(const char chr, istream& in);
const value readString(istream& in);
const value readNumber(const char chr, istream& in);
const value readValue(istream& in);
-const value readToken(istream& in) {
+const failable<value> readToken(istream& in) {
const char firstChar = readChar(in);
if(isWhitespace(firstChar))
return readToken(in);
@@ -114,12 +114,12 @@ const value readToken(istream& in) {
if(isDigit(firstChar))
return readNumber(firstChar, in);
if(firstChar == -1)
- return value();
+ return mkfailure<value>();
logStream() << "Illegal lexical syntax '" << firstChar << "'" << endl;
return readToken(in);
}
-const value skipComment(istream& in) {
+const failable<value> skipComment(istream& in) {
const char nextChar = readChar(in);
if (nextChar == '\n')
return readToken(in);
@@ -131,8 +131,11 @@ const value readQuoted(istream& in) {
}
const list<value> readList(const list<value>& listSoFar, istream& in) {
- const value token = readToken(in);
- if(isNil(token) || isRightParenthesis(token))
+ const failable<value> ftoken = readToken(in);
+ if (!hasContent(ftoken))
+ return reverse(listSoFar);
+ const value token = content(ftoken);
+ if(isRightParenthesis(token))
return reverse(listSoFar);
if(isLeftParenthesis(token))
return readList(cons(value(readList(list<value> (), in)), listSoFar), in);
@@ -159,14 +162,22 @@ const value readIdentifier(const char chr, istream& in) {
return value((bool)false);
if (val == "true")
return value((bool)true);
+ if (val == "nil")
+ return value();
return val;
}
const list<char> readStringHelper(const list<char>& listSoFar, istream& in) {
const char nextChar = readChar(in);
- if(nextChar != -1 && nextChar != '"')
- return readStringHelper(cons(nextChar, listSoFar), in);
- return reverse(listSoFar);
+ if(nextChar == -1 || nextChar == '"')
+ return reverse(listSoFar);
+ if (nextChar == '\\') {
+ const char escapedChar = readChar(in);
+ if (escapedChar == -1)
+ return reverse(listSoFar);
+ return readStringHelper(cons(escapedChar, listSoFar), in);
+ }
+ return readStringHelper(cons(nextChar, listSoFar), in);
}
const value readString(istream& in) {
@@ -185,17 +196,23 @@ const value readNumber(const char chr, istream& in) {
}
const value readValue(istream& in) {
- const value nextToken = readToken(in);
+ const failable<value> fnextToken = readToken(in);
+ if (!hasContent(fnextToken))
+ return value();
+ const value nextToken = content(fnextToken);
if(isLeftParenthesis(nextToken))
- return readList(list<value> (), in);
+ return readList(list<value>(), in);
return nextToken;
}
const value readValue(const string s) {
istringstream in(s);
- const value nextToken = readToken(in);
+ const failable<value> fnextToken = readToken(in);
+ if (!hasContent(fnextToken))
+ return value();
+ const value nextToken = content(fnextToken);
if(isLeftParenthesis(nextToken))
- return readList(list<value> (), in);
+ return readList(list<value>(), in);
return nextToken;
}
diff --git a/sca-cpp/trunk/modules/wsgi/jsonutil.py b/sca-cpp/trunk/modules/wsgi/jsonutil.py
index ad8f5fcc6b..849b44405b 100644
--- a/sca-cpp/trunk/modules/wsgi/jsonutil.py
+++ b/sca-cpp/trunk/modules/wsgi/jsonutil.py
@@ -57,6 +57,8 @@ def jsPropertiesToValues(propertiesSoFar, o, i):
# Converts a JSON val to a value
def jsValToValue(jsv):
+ if jsv is None:
+ return None
if isinstance(jsv, dict):
return jsPropertiesToValues((), jsv, tuple(jsv.keys()))
if isList(jsv):