summaryrefslogtreecommitdiffstats
path: root/cpp/sca/modules
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/sca/modules')
-rw-r--r--cpp/sca/modules/httpd/Makefile.am4
-rw-r--r--cpp/sca/modules/httpd/mod.cpp278
-rw-r--r--cpp/sca/modules/json/json-test.cpp24
-rw-r--r--cpp/sca/modules/json/json.hpp52
4 files changed, 242 insertions, 116 deletions
diff --git a/cpp/sca/modules/httpd/Makefile.am b/cpp/sca/modules/httpd/Makefile.am
index f7906d09ac..13cb3045f6 100644
--- a/cpp/sca/modules/httpd/Makefile.am
+++ b/cpp/sca/modules/httpd/Makefile.am
@@ -18,7 +18,7 @@
libdir=$(prefix)/lib
lib_LTLIBRARIES = libmod_tuscany.la
-INCLUDES = -I. -I$(top_builddir)/kernel -I${HTTPD_INCLUDE} -I${APR_INCLUDE}
+INCLUDES = -I. -I$(top_builddir)/kernel -I${HTTPD_INCLUDE} -I${APR_INCLUDE} -I${LIBXML2_INCLUDE} -I${LIBMOZJS_INCLUDE}
libmod_tuscany_la_SOURCES = mod.cpp
-libmod_tuscany_la_LIBADD = -lpthread
+libmod_tuscany_la_LIBADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${LIBMOZJS_LIB} -lmozjs
diff --git a/cpp/sca/modules/httpd/mod.cpp b/cpp/sca/modules/httpd/mod.cpp
index 7ecf2840bd..feebca4b5c 100644
--- a/cpp/sca/modules/httpd/mod.cpp
+++ b/cpp/sca/modules/httpd/mod.cpp
@@ -26,6 +26,7 @@
#include <string>
#include <iostream>
#include <sstream>
+#include <fstream>
#include "apr_strings.h"
#include "apr_fnmatch.h"
@@ -48,6 +49,10 @@
#include "list.hpp"
#include "slist.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../json/json.hpp"
+#include "../eval/driver.hpp"
extern "C" {
extern module AP_MODULE_DECLARE_DATA mod_tuscany;
@@ -66,13 +71,38 @@ struct ServerConf {
* Directory configuration.
*/
struct DirConf {
- const char* root;
- const char* path;
- const char* uri;
+ const char* contribution;
const char* component;
+ const char* implementation;
};
/**
+ * Returns the server conf for a request.
+ */
+const ServerConf& serverConf(const request_rec* r) {
+ return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany);
+}
+const std::string home(request_rec* r) {
+ return serverConf(r).home;
+}
+
+/**
+ * Returns the dir conf for a request.
+ */
+const DirConf& dirConf(const request_rec* r) {
+ return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany);
+}
+const std::string contribution(request_rec* r) {
+ return dirConf(r).contribution;
+}
+const std::string component(request_rec* r) {
+ return dirConf(r).component;
+}
+const std::string implementation(request_rec* r) {
+ return dirConf(r).implementation;
+}
+
+/**
* Returns an HTTP request path as a list of strings.
*/
const list<std::string> path(const request_rec* r) {
@@ -83,17 +113,6 @@ const list<std::string> path(const request_rec* r) {
}
/**
- * Returns an HTTP query string as a list of lists of strings.
- */
-const list<list<std::string> > args(const request_rec* r) {
- const char* a = r->args;
- if (a == NULL)
- return list<list<std::string> >();
- const lambda<list<std::string>(std::string, std::string)> tok(tokenize);
- return map(curry(tok, std::string("=")), tokenize("&", a));
-}
-
-/**
* Log HTTP request info to standard out for now, for debugging purposes.
*/
bool logRequests = true;
@@ -109,12 +128,11 @@ const char* optional(const char* s) {
return s;
}
-const bool logRequest(request_rec* r, const ServerConf& sc, const DirConf& dc) {
+const bool logRequest(request_rec* r) {
std::cout << "mod-tuscany..." << std::endl;
- std::cout << "tuscany home: " << sc.home << std::endl;
- std::cout << "tuscany root: " << dc.root << std::endl;
- std::cout << "tuscany path: " << dc.path << std::endl;
- std::cout << "component: " << dc.component << std::endl;
+ std::cout << "tuscany home: " << home(r) << std::endl;
+ std::cout << "contribution: " << contribution(r) << std::endl;
+ std::cout << "component: " << component(r) << std::endl;
std::cout << "protocol: " << optional(r->protocol) << std::endl;
std::cout << "method: " << optional(r->method) << std::endl;
std::cout << "method number: " << r->method_number << std::endl;
@@ -124,41 +142,172 @@ const bool logRequest(request_rec* r, const ServerConf& sc, const DirConf& dc) {
std::cout << "uri: " << optional(r->uri) << std::endl;
std::cout << "path info: " << optional(r->path_info) << std::endl;
std::cout << "path: " << path(r) << std::endl;
- std::cout << "args info: " << optional(r->args) << std::endl;
- std::cout << "args: " << args(r) << std::endl;
+ std::cout << "args: " << optional(r->args) << std::endl;
+ std::cout.flush();
return true;
}
+const value evalLoop(std::istream& is, const value& req, Env& globalEnv) {
+ value in = read(is);
+ if (isNil(in))
+ return eval(req, globalEnv);
+ eval(in, globalEnv);
+ return evalLoop(is, req, globalEnv);
+}
+
+/**
+ * Returns a list of key value pairs from the args in a query string.
+ */
+const list<value> queryArg(std::string s) {
+ const list<std::string> t = tokenize("=", s);
+ return makeList<value>(car(t).c_str(), cadr(t));
+}
+
+const list<list<value> > queryArgs(const request_rec* r) {
+ const char* a = r->args;
+ if (a == NULL)
+ return list<list<value> >();
+ return map<std::string, list<value>>(queryArg, tokenize("&", a));
+}
+
+/**
+ * Returns a list of param values other than the id and method args from a list
+ * of key value pairs.
+ */
+const list<value> queryParams(list<list<value> > a) {
+ if (isNil(a))
+ return list<value>();
+ if (car(a) == value("id") || car(a) == value("method"))
+ return queryParams(cdr(a));
+ return cons(cadr(car(a)), queryParams(cdr(a)));
+}
+
/**
* Handle an HTTP GET request.
*/
const int get(request_rec* r) {
- std::string str("<result>OK</result>");
- if (false) {
- r->status = HTTP_NOT_FOUND;
- return OK;
- }
- // Handle a conditional GET
- std::string etag(ap_md5(r->pool, (const unsigned char*)str.c_str()));
+ // Setup the script evaluator
+ Env globalEnv = setupEnvironment();
+ std::ostringstream nullos;
+ setupEvalOut(nullos);
+
+ // Open the component implementation
+ const std::string impl = contribution(r) + implementation(r);
+ std::ifstream is(impl.c_str(), std::ios_base::in);
+ if (is.fail() || is.bad())
+ return HTTP_NOT_FOUND;
+
+ // Extract the request id, method and params from the query string
+ const list<list<value> > args = queryArgs(r);
+ const value id = cadr(assoc(value("id"), args));
+ const value method = std::string(cadr(assoc(value("method"), args))).c_str();
+ const list<value> params = queryParams(args);
+
+ // Build expr to evaluate
+ const value expr = cons<value>(method, params);
+ std::cout<< "expr: " << expr << std::endl;
+ std::cout.flush();
+
+ // Evaluate the expr
+ const tuscany::value val = evalLoop(is, expr, globalEnv);
+ if (isNil(val))
+ return HTTP_INTERNAL_SERVER_ERROR;
+ std::cout<< "val: " << val << std::endl;
+ std::cout.flush();
+
+ // Convert the expr value to JSON
+ const JSONContext cx;
+ failable<list<std::string>, std::string> jsval = writeJSON(cx, makeList<value>(makeList<value>("id", id), makeList<value>("result", val)));
+ if (!hasValue(jsval))
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ // Send the response
+ ap_set_content_type(r, "application/json-rpc");
+ std::ostringstream os;
+ write(jsval, os);
+ std::string sval = os.str();
+ std::string etag(ap_md5(r->pool, (const unsigned char*)sval.c_str()));
const char* match = apr_table_get(r->headers_in, "If-None-Match");
- if (match != NULL && etag == match) {
+ if (match != NULL && etag == match)
r->status = HTTP_NOT_MODIFIED;
- return OK;
- }
-
- // Send response
- ap_set_content_type(r, "text/xml");
apr_table_setn(r->headers_out, "ETag", etag.c_str());
- ap_rputs(str.c_str(), r);
+ ap_rputs(sval.c_str(), r);
return OK;
}
/**
+ * Read the content of a POST.
+ */
+const list<std::string> read(request_rec* r) {
+ char b[2048];
+ const int n = ap_get_client_block(r, b, 2048);
+ if (n <= 0)
+ return list<std::string>();
+ return cons(std::string(b, n), read(r));
+}
+
+/**
+ * Converts the args received in a POST to a list of key value pairs.
+ */
+const list<list<value> > postArgs(list<value> a) {
+ if (isNil(a))
+ return list<list<value> >();
+ const list<value> l = car(a);
+ return cons(l, postArgs(cdr(a)));
+}
+
+/**
* Handle an HTTP POST request.
*/
const int post(request_rec* r) {
+
+ // Setup the script evaluator
+ Env globalEnv = setupEnvironment();
+ std::ostringstream nullos;
+ setupEvalOut(nullos);
+
+ // Open the component implementation
+ const std::string impl = contribution(r) + implementation(r);
+ std::ifstream is(impl.c_str(), std::ios_base::in);
+ if (is.fail() || is.bad())
+ return HTTP_NOT_FOUND;
+
+ // Read the JSON request
+ const list<std::string> req = read(r);
+ JSONContext cx;
+ const list<value> json = readJSON(cx, req);
+ const list<list<value> > args = postArgs(json);
+
+ // Extract the request id, method and params
+ const value id = cadr(assoc(value("id"), args));
+ const value method = std::string(cadr(assoc(value("method"), args))).c_str();
+ const list<value> params = (list<value>)cadr(assoc(value("params"), args));
+
+ // Build expr to evaluate
+ const value expr = cons<value>(method, params);
+ std::cout<< "expr: " << expr << std::endl;
+ std::cout.flush();
+
+ // Evaluate the expr
+ const tuscany::value val = evalLoop(is, expr, globalEnv);
+ if (isNil(val))
+ return HTTP_INTERNAL_SERVER_ERROR;
+ std::cout<< "val: " << val << std::endl;
+ std::cout.flush();
+
+ // Convert the expr value to JSON
+ failable<list<std::string>, std::string> jsval = writeJSON(cx, makeList<value>(makeList<value>("id", id), makeList<value>("result", val)));
+ if (!hasValue(jsval))
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ // Send the JSON response
+ ap_set_content_type(r, "application/json-rpc");
+ std::ostringstream os;
+ write(jsval, os);
+ ap_rputs(os.str().c_str(), r);
+
return OK;
}
@@ -166,26 +315,6 @@ const int post(request_rec* r) {
* Handle an HTTP PUT request.
*/
const int put(request_rec* r) {
- std::ostringstream sos;
- char buffer[2049];
- for ( ; ; )
- {
- int size = ap_get_client_block(r, buffer, 2048);
- if (size > 0)
- {
- buffer[size] = '\0';
- sos << buffer;
- }
- else if (size == 0)
- {
- break;
- }
- else if (size < 0)
- {
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
- std::string input = sos.str();
return OK;
}
@@ -203,13 +332,9 @@ int handler(request_rec *r) {
if(strcmp(r->handler, "mod_tuscany"))
return DECLINED;
- // Get the server and dir config
- ServerConf& serverConf = *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany);
- DirConf& dirConf = *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany);
-
// Log the request
if(logRequests)
- logRequest(r, serverConf, dirConf);
+ logRequest(r);
// Set up the read policy
const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
@@ -220,10 +345,9 @@ int handler(request_rec *r) {
r->chunked = true;
apr_table_setn(r->headers_out, "Connection", "close");
+ // Handle HTTP method
if (r->header_only)
return OK;
-
- // Handle HTTP method
if(r->method_number == M_GET)
return get(r);
if(r->method_number == M_POST)
@@ -243,31 +367,24 @@ const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) {
conf->home = apr_pstrdup(cmd->pool, arg);
return NULL;
}
-const char *confPath(cmd_parms *cmd, void *c, const char *arg) {
+const char *confContribution(cmd_parms *cmd, void *c, const char *arg) {
DirConf *conf = (DirConf*)c;
- conf->path = apr_pstrdup(cmd->pool, arg);
+ conf->contribution = apr_pstrdup(cmd->pool, arg);
return NULL;
}
-const char *confRoot(cmd_parms *cmd, void *c, const char *arg) {
- DirConf *conf = (DirConf*)c;
- conf->root = apr_pstrdup(cmd->pool, arg);
- return NULL;
-}
-const char *confURI(cmd_parms *cmd, void *c, const char *arg) {
+const char *confComponent(cmd_parms *cmd, void *c, const char *arg) {
DirConf *conf = (DirConf*)c;
- conf->uri = apr_pstrdup(cmd->pool, arg);
+ conf->component = apr_pstrdup(cmd->pool, arg);
return NULL;
}
-const char *confComponent(cmd_parms *cmd, void *c, const char *arg) {
+const char *confImplementation(cmd_parms *cmd, void *c, const char *arg) {
DirConf *conf = (DirConf*)c;
- conf->component = apr_pstrdup(cmd->pool, arg);
+ conf->implementation = apr_pstrdup(cmd->pool, arg);
return NULL;
}
void *makeDirConf(apr_pool_t *p, char *dirspec) {
DirConf* conf = (DirConf*)apr_palloc(p, sizeof(*conf));
- conf->path = "";
- conf->root = "";
- conf->uri = "";
+ conf->contribution = "";
conf->component = "";
return conf;
}
@@ -281,11 +398,10 @@ void* makeServerConf(apr_pool_t *p, server_rec *s) {
* HTTP server module declarations.
*/
const command_rec commands[] = {
- AP_INIT_TAKE1("home", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"),
- AP_INIT_TAKE1("path", (const char*(*)())confPath, NULL, ACCESS_CONF, "Tuscany SCA composite search path"),
- AP_INIT_TAKE1("root", (const char*(*)())confRoot, NULL, ACCESS_CONF, "Tuscany root SCA configuration path"),
- AP_INIT_TAKE1("uri", (const char*(*)())confURI, NULL, ACCESS_CONF, "Tuscany SCA system base URI"),
- AP_INIT_TAKE1("component", (const char*(*)())confComponent, NULL, ACCESS_CONF, "SCA component name"),
+ AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"),
+ AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"),
+ AP_INIT_TAKE1("SCAComponent", (const char*(*)())confComponent, NULL, ACCESS_CONF, "SCA component name"),
+ AP_INIT_TAKE1("SCAImplementation", (const char*(*)())confImplementation, NULL, ACCESS_CONF, "SCA component implementation"),
{NULL}
};
diff --git a/cpp/sca/modules/json/json-test.cpp b/cpp/sca/modules/json/json-test.cpp
index 5a1dcc5d51..1ebece647b 100644
--- a/cpp/sca/modules/json/json-test.cpp
+++ b/cpp/sca/modules/json/json-test.cpp
@@ -34,7 +34,7 @@ namespace tuscany {
bool testJSEval() {
JSONContext cx;
- std::string script("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)");
+ const std::string script("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)");
jsval rval;
assert(JS_EvaluateScript(cx, cx.getGlobal(), script.c_str(), script.length(), "testJSON.js", 1, &rval));
const std::string r(JS_GetStringBytes(JS_ValueToString(cx, rval)));
@@ -48,21 +48,20 @@ std::ostringstream* jsonWriter(std::ostringstream* os, const std::string& s) {
}
bool testJSON() {
- JSONContext cx;
+ const JSONContext cx;
- list<value> phones = makeList<value> (std::string("408-1234"), std::string("650-1234"));
- list<value> l = makeList<value> (makeList<value> ("phones", phones), makeList<value> ("lastName", std::string("test\ttab")), makeList<value> ("firstName", std::string("test1")));
+ const list<value> phones = makeList<value> (std::string("408-1234"), std::string("650-1234"));
+ const list<value> l = makeList<value> (makeList<value> ("phones", phones), makeList<value> ("lastName", std::string("test\ttab")), makeList<value> ("firstName", std::string("test1")));
print(l, std::cout);
std::cout << std::endl;
std::ostringstream os;
- lambda<std::ostringstream*(std::ostringstream*, std::string)> writer(jsonWriter);
- writeJSON(cx, writer, &os, l);
+ writeJSON<std::ostringstream*>(cx, jsonWriter, &os, l);
std::cout << os.str() << std::endl;
std::istringstream is(os.str());
- list<std::string> il = makeStreamList(is);
- list<value> r = readJSON(cx, il);
+ const list<std::string> il = makeStreamList(is);
+ const list<value> r = readJSON(cx, il);
print(r, std::cout);
std::cout << std::endl;
assert(r == l);
@@ -74,6 +73,14 @@ bool testJSON() {
return true;
}
+bool testJSONRPC() {
+ const std::string lm("{\"id\": 1, \"method\": \"system.listMethods\", \"params\": []}");
+ JSONContext cx;
+ const list<value> v = readJSON(cx, makeList(lm));
+ std::cout << v << std::endl;
+ return true;
+}
+
}
int main() {
@@ -81,6 +88,7 @@ int main() {
tuscany::testJSEval();
tuscany::testJSON();
+ tuscany::testJSONRPC();
std::cout << "OK" << std::endl;
diff --git a/cpp/sca/modules/json/json.hpp b/cpp/sca/modules/json/json.hpp
index e9938672c5..f6ed9202c3 100644
--- a/cpp/sca/modules/json/json.hpp
+++ b/cpp/sca/modules/json/json.hpp
@@ -32,6 +32,7 @@
#include <string>
#include "list.hpp"
#include "value.hpp"
+#include "monad.hpp"
namespace tuscany {
@@ -172,8 +173,9 @@ const value jsValToValue(const JSONContext& cx, const jsval& jsv) {
return value((bool)JSVAL_TO_BOOLEAN(jsv));
}
case JSTYPE_NUMBER: {
- jsdouble* jsd = JSVAL_TO_DOUBLE(jsv);
- return value((double)*jsd);
+ jsdouble jsd;
+ JS_ValueToNumber(cx, jsv, &jsd);
+ return value((double)jsd);
}
case JSTYPE_OBJECT: {
JSObject* o = JSVAL_TO_OBJECT(jsv);
@@ -191,39 +193,39 @@ const value jsValToValue(const JSONContext& cx, const jsval& jsv) {
/**
* Consumes JSON strings and populates a JS object.
*/
-bool consumeJSON(const JSONContext& cx, JSONParser* parser, const list<std::string>& ilist) {
+failable<bool, std::string> consumeJSON(const JSONContext& cx, JSONParser* parser, const list<std::string>& ilist) {
if (isNil(ilist))
return true;
JSString* jstr = JS_NewStringCopyZ(cx, car(ilist).c_str());
if(!JS_ConsumeJSONText(cx, parser, JS_GetStringChars(jstr), JS_GetStringLength(jstr)))
- return false;
+ return "JS_ConsumeJSONText failed";
return consumeJSON(cx, parser, cdr(ilist));
}
/**
* Read JSON tokens from list of strings.
*/
-const list<value> readJSON(const JSONContext& cx, const list<std::string>& ilist) {
+const failable<list<value>, std::string> readJSON(const JSONContext& cx, const list<std::string>& ilist) {
jsval val;
JSONParser* parser = JS_BeginJSONParse(cx, &val);
if(parser == NULL)
- return list<value> ();
+ return std::string("JS_BeginJSONParse failed");
- bool ok = consumeJSON(cx, parser, ilist);
+ const failable<bool, std::string> consumed = consumeJSON(cx, parser, ilist);
if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL))
- return list<value> ();
- if(!ok)
- return list<value> ();
+ return std::string("JS_FinishJSONParse failed");
+ if(!hasValue(consumed))
+ return std::string(consumed);
- return jsValToValue(cx, val);
+ return list<value>(jsValToValue(cx, val));
}
/**
* Returns true if a list represents a JS array.
*/
const bool isJSArray(const list<value>& l) {
- if(l == list<value> ())
+ if(isNil(l))
return false;
value v = car(l);
if(isList(v)) {
@@ -240,8 +242,7 @@ const bool isJSArray(const list<value>& l) {
*/
JSObject* valuesToJSElements(const JSONContext& cx, JSObject* a, const list<value>& l, int i) {
const jsval valueToJSVal(const JSONContext& cx, const value& val);
-
- if (l == list<value>())
+ if (isNil(l))
return a;
jsval pv = valueToJSVal(cx, car(l));
JS_SetElement(cx, a, i, &pv);
@@ -253,8 +254,7 @@ JSObject* valuesToJSElements(const JSONContext& cx, JSObject* a, const list<valu
*/
JSObject* valuesToJSProperties(const JSONContext& cx, JSObject* o, const list<value>& l) {
const jsval valueToJSVal(const JSONContext& cx, const value& val);
-
- if(l == list<value> ())
+ if(isNil(l))
return o;
const list<value> p = car(l);
jsval pv = valueToJSVal(cx, cadr(p));
@@ -274,8 +274,7 @@ const jsval valueToJSVal(const JSONContext& cx, const value& val) {
return BOOLEAN_TO_JSVAL((bool)val);
}
case value::Number: {
- jsdouble d = (double)val;
- return DOUBLE_TO_JSVAL(&d);
+ return DOUBLE_TO_JSVAL(JS_NewDouble(cx, (double)val));
}
case value::List: {
if (isJSArray(val)) {
@@ -312,12 +311,13 @@ template<typename R> JSBool writeCallback(const jschar *buf, uint32 len, void *d
}
/**
- * Write a list of values as a JSON document.
+ * Convert a list of values to a JSON document.
*/
-template<typename R> const R writeJSON(const JSONContext& cx, const lambda<R(R, std::string)>& reduce, const R& initial, const list<value>& l) {
+template<typename R> const failable<R, std::string> writeJSON(const JSONContext& cx, const lambda<R(R, std::string)>& reduce, const R& initial, const list<value>& l) {
jsval val = valueToJSVal(cx, l);
JSONWriteContext<R> wcx(cx, reduce, initial);
- JS_Stringify(cx, &val, NULL, JSVAL_NULL, writeCallback<R>, &wcx);
+ if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, writeCallback<R>, &wcx))
+ return std::string("JS_Stringify failed");
return wcx.accum;
}
@@ -326,11 +326,13 @@ const list<std::string> writeJSONList(const list<std::string>& listSoFar, const
}
/**
- * Write a list of values as a JSON document represented as a list of strings.
+ * Convert a list of values to a JSON document represented as a list of strings.
*/
-const list<std::string> writeJSON(const JSONContext& cx, const list<value>& l) {
- lambda<list<std::string>(list<std::string>, std::string)> writer(writeJSONList);
- return reverse(writeJSON(cx, writer, list<std::string>(), l));
+const failable<list<std::string>, std::string> writeJSON(const JSONContext& cx, const list<value>& l) {
+ const failable<list<std::string>, std::string> ls = writeJSON<list<std::string> >(cx, writeJSONList, list<std::string>(), l);
+ if (!hasValue(ls))
+ return ls;
+ return reverse(list<std::string>(ls));
}
}