summaryrefslogtreecommitdiffstats
path: root/sca-cpp/trunk/modules/server/mod-eval.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp/trunk/modules/server/mod-eval.hpp')
-rw-r--r--sca-cpp/trunk/modules/server/mod-eval.hpp918
1 files changed, 594 insertions, 324 deletions
diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp
index 62d91c6236..999a5793ad 100644
--- a/sca-cpp/trunk/modules/server/mod-eval.hpp
+++ b/sca-cpp/trunk/modules/server/mod-eval.hpp
@@ -52,50 +52,61 @@ namespace server {
namespace modeval {
/**
+ * Set to true to wire using mod_proxy, false to wire using HTTP client redirects.
+ */
+const bool useModProxy = true;
+
+/**
* Server configuration.
*/
class ServerConf {
public:
- ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s), contributionPath(""), compositeName(""), virtualHostDomain(""), virtualHostContributionPath(""), virtualHostCompositeName(""), ca(""), cert(""), key("") {
+ ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) {
}
- ServerConf(apr_pool_t* p, const ServerConf& ssc, const string& name) : p(p), server(ssc.server), lifecycle(ssc.lifecycle), contributionPath(ssc.virtualHostContributionPath + name + "/"), compositeName(ssc.virtualHostCompositeName), virtualHostDomain(""), virtualHostContributionPath(""), virtualHostCompositeName(""), ca(ssc.ca), cert(ssc.cert), key(ssc.key) {
+ ServerConf(apr_pool_t* p, const ServerConf& ssc, const string& name) : p(p), server(ssc.server), lifecycle(ssc.lifecycle), vhostName(name), compositeName(ssc.vhostCompositeName), ca(ssc.ca), cert(ssc.cert), key(ssc.key), vhostContributor(ssc.vhostContributor) {
+ contributionPath = length(ssc.vhostContributionPath) != 0? ssc.vhostContributionPath + name + "/" : ssc.contributionPath;
}
const gc_pool p;
server_rec* server;
lambda<value(const list<value>&)> lifecycle;
+ string vhostName;
string contributionPath;
string compositeName;
- string virtualHostDomain;
- string virtualHostContributionPath;
- string virtualHostCompositeName;
+ string vhostDomain;
+ string vhostContributionPath;
+ string vhostCompositeName;
+ string vhostContributorName;
string ca;
string cert;
string key;
+ list<value> references;
+ list<value> services;
list<value> implementations;
list<value> implTree;
+ value vhostContributor;
};
/**
- * Return true if a server contains a composite configuration.
+ * Return true if a server contains a contribution configuration.
*/
-const bool hasCompositeConf(const ServerConf& sc) {
- return sc.contributionPath != "" && sc.compositeName != "";
+const bool hasContributionConf(const ServerConf& sc) {
+ return length(sc.contributionPath) != 0;
}
/**
* Return true if a server contains a virtual host domain configuration.
*/
-const bool hasVirtualDomainConf(const ServerConf& sc) {
- return sc.virtualHostDomain != "";
+const bool hasVhostDomainConf(const ServerConf& sc) {
+ return length(sc.vhostDomain) != 0;
}
/**
- * Return true if a server contains a virtual host composite configuration.
+ * Return true if a server contains a virtual host contribution configuration.
*/
-const bool hasVirtualCompositeConf(const ServerConf& sc) {
- return sc.virtualHostContributionPath != "" && sc.virtualHostCompositeName != "";
+const bool hasVhostContributionConf(const ServerConf& sc) {
+ return length(sc.vhostContributionPath) != 0 || length(sc.vhostContributorName) != 0;
}
/**
@@ -129,262 +140,6 @@ public:
};
/**
- * Handle an HTTP GET.
- */
-const failable<int> get(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
- debug(r->uri, "modeval::get::uri");
-
- // Inspect the query string
- const list<list<value> > args = httpd::queryArgs(r);
- const list<value> ia = assoc(value("id"), args);
- const list<value> ma = assoc(value("method"), args);
-
- // Evaluate a JSON-RPC request and return a JSON result
- if (!isNil(ia) && !isNil(ma)) {
-
- // Extract the request id, method and params
- const value id = cadr(ia);
- const value func = c_str(json::funcName(string(cadr(ma))));
-
- // Apply the requested function
- const failable<value> val = failableResult(impl(cons(func, json::queryParams(args))));
- if (!hasContent(val))
- return mkfailure<int>(reason(val));
-
- // Return JSON result
- js::JSContext cx;
- return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", r);
- }
-
- // Evaluate the GET expression
- const list<value> params(append<value>(cddr(rpath), mkvalues(args)));
- const failable<value> val = failableResult(impl(cons<value>("get", mklist<value>(params))));
- if (!hasContent(val))
- return mkfailure<int>(reason(val));
- const value c = content(val);
- debug(c, "modeval::get::content");
-
- // Check if the client requested a specific format
- const list<value> fmt = assoc<value>("format", args);
-
- // Write as a scheme value if requested by the client
- if (!isNil(fmt) && cadr(fmt) == "scheme")
- return httpd::writeResult(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8", r);
-
- // Write a simple value as a JSON value
- if (!isList(c)) {
- js::JSContext cx;
- if (isSymbol(c)) {
- const list<value> lc = mklist<value>(mklist<value>("name", value(string(c))));
- debug(lc, "modeval::get::symbol");
- return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
- }
- const list<value> lc = mklist<value>(mklist<value>("value", c));
- debug(lc, "modeval::get::value");
- return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
- }
-
- // Write an empty list as a JSON empty value
- if (isNil((list<value>)c)) {
- js::JSContext cx;
- debug(list<value>(), "modeval::get::empty");
- return httpd::writeResult(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8", r);
- }
-
- // Write content-type / content-list pair
- if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c)))
- return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r);
-
- // Write an assoc value as a JSON result
- if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) {
- js::JSContext cx;
- const list<value> lc = mklist<value>(c);
- debug(lc, "modeval::get::assoc");
- debug(valuesToElements(lc), "modeval::get::assoc::element");
- return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
- }
-
- // Write value as JSON if requested by the client
- if (!isNil(fmt) && cadr(fmt) == "json") {
- js::JSContext cx;
- return httpd::writeResult(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8", r);
- }
-
- // Convert list of values to element values
- const list<value> e = valuesToElements(c);
- debug(e, "modeval::get::elements");
-
- // Write an ATOM feed or entry
- if (isList(car<value>(e)) && !isNil(car<value>(e))) {
- const list<value> el = car<value>(e);
- if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) {
- if (cadr<value>(el) == atom::feed)
- return httpd::writeResult(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8", r);
- if (cadr<value>(el) == atom::entry)
- return httpd::writeResult(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8", r);
- }
- }
-
- // Write any other compound value as a JSON value
- js::JSContext cx;
- return httpd::writeResult(json::writeJSON(e, cx), "application/json; charset=utf-8", r);
-}
-
-/**
- * Handle an HTTP POST.
- */
-const failable<int> post(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
- debug(r->uri, "modeval::post::url");
-
- // Evaluate a JSON-RPC request and return a JSON result
- const string ct = httpd::contentType(r);
- if (contains(ct, "application/json-rpc") || contains(ct, "text/plain") || contains(ct, "application/x-www-form-urlencoded")) {
-
- // Read the JSON request
- const int rc = httpd::setupReadPolicy(r);
- if(rc != OK)
- return rc;
- const list<string> ls = httpd::read(r);
- debug(ls, "modeval::post::input");
- js::JSContext cx;
- const list<value> json = elementsToValues(content(json::readJSON(ls, cx)));
- const list<list<value> > args = httpd::postArgs(json);
-
- // Extract the request id, method and params
- const value id = cadr(assoc(value("id"), args));
- const value func = c_str(json::funcName(cadr(assoc(value("method"), args))));
- const list<value> params = (list<value>)cadr(assoc(value("params"), args));
-
- // Evaluate the request expression
- const failable<value> val = failableResult(impl(cons<value>(func, params)));
- if (!hasContent(val))
- return mkfailure<int>(reason(val));
-
- // Return JSON result
- return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", r);
- }
-
- // Evaluate an ATOM POST request and return the location of the corresponding created resource
- if (contains(ct, "application/atom+xml")) {
-
- // Read the ATOM entry
- const int rc = httpd::setupReadPolicy(r);
- if(rc != OK)
- return rc;
- const list<string> ls = httpd::read(r);
- debug(ls, "modeval::post::input");
- const value entry = elementsToValues(content(atom::readATOMEntry(ls)));
-
- // Evaluate the POST expression
- const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(rpath), entry))));
- if (!hasContent(val))
- return mkfailure<int>(reason(val));
-
- // Return the created resource location
- debug(content(val), "modeval::post::location");
- apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(httpd::url(r->uri, content(val), r))));
- r->status = HTTP_CREATED;
- return OK;
- }
-
- // Unknown content type, wrap the HTTP request struct in a value and pass it to
- // the component implementation function
- const failable<value> val = failableResult(impl(cons<value>("handle", mklist<value>(httpd::requestValue(r)))));
- if (!hasContent(val))
- return mkfailure<int>(reason(val));
- return (int)content(val);
-}
-
-/**
- * Handle an HTTP PUT.
- */
-const failable<int> put(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
- debug(r->uri, "modeval::put::url");
-
- // Read the ATOM entry
- const int rc = httpd::setupReadPolicy(r);
- if(rc != OK)
- return rc;
- const list<string> ls = httpd::read(r);
- debug(ls, "modeval::put::input");
- const value entry = elementsToValues(content(atom::readATOMEntry(ls)));
-
- // Evaluate the PUT expression and update the corresponding resource
- const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(rpath), entry))));
- if (!hasContent(val))
- return mkfailure<int>(reason(val));
- if (val == value(false))
- return HTTP_NOT_FOUND;
- return OK;
-}
-
-/**
- * Handle an HTTP DELETE.
- */
-const failable<int> del(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
- debug(r->uri, "modeval::delete::url");
-
- // Evaluate an ATOM delete request
- const failable<value> val = failableResult(impl(cons<value>("delete", mklist<value>(cddr(rpath)))));
- if (!hasContent(val))
- return mkfailure<int>(reason(val));
- if (val == value(false))
- return HTTP_NOT_FOUND;
- return OK;
-}
-
-/**
- * Translate a component request.
- */
-const int translateRequest(const ServerConf& sc, const list<value>& rpath, request_rec *r) {
- debug(rpath, "modeval::translateRequest::path");
- if (isNil(rpath))
- return DECLINED;
-
- // Translate a component request
- const value c = car(rpath);
- if (c == string("components") || c == string("c")) {
- r->handler = "mod_tuscany_eval";
- return OK;
- }
-
- // Translate a request targeting a virtual host or virtual app
- if (hasVirtualCompositeConf(sc) && !isNil(cdr(rpath))) {
- const string cp = sc.virtualHostContributionPath + string(c) + "/" + sc.virtualHostCompositeName;
- struct stat st;
- const int s = stat(c_str(cp), &st);
- if (s != -1) {
- const value d = cadr(rpath);
- if (d == string("components") || d == string("c")) {
- r->handler = "mod_tuscany_eval";
- return OK;
- }
- }
- }
-
- return DECLINED;
-}
-
-/**
- * Translate a component request.
- */
-int translate(request_rec *r) {
- if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
- return DECLINED;
-
- // Create a scoped memory pool
- gc_scoped_pool pool(r->pool);
-
- httpdDebugRequest(r, "modeval::translate::input");
-
- // Get the server configuration
- const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
-
- // Translate the request
- return translateRequest(sc, pathValues(r->uri), r);
-}
-
-/**
* Make an HTTP proxy lambda to a component reference.
*/
const value mkhttpProxy(const ServerConf& sc, const string& ref, const string& base) {
@@ -573,7 +328,7 @@ struct appPropProxy {
appPropProxy(const value& v) : v(v) {
}
const value operator()(unused const list<value>& params) const {
- const char* n = apr_table_get(currentRequest->headers_in, "X-Request-AppName");
+ const char* n = apr_table_get(currentRequest->notes, "X-Request-AppName");
const value a = n != NULL? value(string(n)) : v;
debug(a, "modeval::appPropProxy::value");
return a;
@@ -584,7 +339,7 @@ struct pathPropProxy {
pathPropProxy(unused const value& v) {
}
const value operator()(unused const list<value>& params) const {
- const char* u = apr_table_get(currentRequest->headers_in, "X-Request-URI");
+ const char* u = apr_table_get(currentRequest->notes, "X-Request-URI");
const value v = u != NULL? pathValues(string(u)) : list<value>();
debug(v, "modeval::pathPropProxy::value");
return v;
@@ -743,21 +498,79 @@ const failable<list<value> > applyLifecycleExpr(const list<value>& impls, const
}
/**
+ * Return a list of component-name + references pairs. The references are
+ * arranged in trees of reference-name + reference-target pairs.
+ */
+const list<value> componentReferenceToTargetTree(const value& c) {
+ return mklist<value>(scdl::name(c), mkbtree(sort(scdl::referenceToTargetAssoc(scdl::references(c)))));
+}
+
+const list<value> componentReferenceToTargetAssoc(const list<value>& c) {
+ if (isNil(c))
+ return c;
+ return cons<value>(componentReferenceToTargetTree(car(c)), componentReferenceToTargetAssoc(cdr(c)));
+}
+
+/**
+ * Return a list of service-URI-path + component-name pairs. Service-URI-paths are
+ * represented as lists of URI path fragments.
+ */
+const list<value> defaultBindingURI(const string& cn, const string& sn) {
+ return mklist<value>(cn, sn);
+}
+
+const list<value> bindingToComponentAssoc(const string& cn, const string& sn, const list<value>& b) {
+ if (isNil(b))
+ return b;
+ const value uri(scdl::uri(car(b)));
+ if (isNil(uri))
+ return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), bindingToComponentAssoc(cn, sn, cdr(b)));
+ return cons<value>(mklist<value>(pathValues(c_str(string(uri))), cn), bindingToComponentAssoc(cn, sn, cdr(b)));
+}
+
+const list<value> serviceToComponentAssoc(const string& cn, const list<value>& s) {
+ if (isNil(s))
+ return s;
+ const string sn(scdl::name(car(s)));
+ const list<value> btoc(bindingToComponentAssoc(cn, sn, scdl::bindings(car(s))));
+ if (isNil(btoc))
+ return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), serviceToComponentAssoc(cn, cdr(s)));
+ return append<value>(btoc, serviceToComponentAssoc(cn, cdr(s)));
+}
+
+const list<value> uriToComponentAssoc(const list<value>& c) {
+ if (isNil(c))
+ return c;
+ return append<value>(serviceToComponentAssoc(scdl::name(car(c)), scdl::services(car(c))), uriToComponentAssoc(cdr(c)));
+}
+
+/**
* Configure the components declared in the deployed composite.
*/
const failable<bool> confComponents(ServerConf& sc) {
- if (!hasCompositeConf(sc))
+ if (!hasContributionConf(sc))
return false;
debug(sc.contributionPath, "modeval::confComponents::contributionPath");
+ debug(sc.contributionPath, "modeval::confComponents::contributorName");
debug(sc.compositeName, "modeval::confComponents::compositeName");
if (sc.ca != "") debug(sc.ca, "modeval::confComponents::sslCA");
if (sc.cert != "") debug(sc.cert, "modeval::confComponents::sslCert");
if (sc.key != "") debug(sc.key, "modeval::confComponents::sslKey");
- // Read the components and get their implementation lambda functions
+ // Read the components and get their services, references and implementation
+ // lambda functions
const failable<list<value> > comps = readComponents(scdl::resourcePath(sc.contributionPath, sc.compositeName));
if (!hasContent(comps))
return mkfailure<bool>(reason(comps));
+
+ const list<value> refs = componentReferenceToTargetAssoc(content(comps));
+ debug(refs, "modeval::confComponents::references");
+ sc.references = mkbtree(sort(refs));
+
+ const list<value> svcs = uriToComponentAssoc(content(comps));
+ debug(svcs, "modeval::confComponents::services");
+ sc.services = mkbtree(sort(svcs));
+
sc.implementations = componentToImplementationAssoc(sc, content(comps));
debug(sc.implementations, "modeval::confComponents::implementations");
return true;
@@ -781,21 +594,36 @@ const failable<bool> startComponents(ServerConf& sc) {
/**
* Configure and start the components deployed in a virtual host.
*/
-const failable<bool> virtualHostConfig(ServerConf& sc, const ServerConf& ssc, request_rec* r) {
- debug(httpd::serverName(ssc.server), "modeval::virtualHostConfig::serverName");
- debug(httpd::serverName(r), "modeval::virtualHostConfig::virtualHostName");
- debug(ssc.virtualHostContributionPath, "modwiring::virtualHostConfig::virtualHostContributionPath");
- debug(sc.contributionPath, "modeval::virtualHostConfig::contributionPath");
-
- // Chdir to the virtual host's contribution
- if (chdir(c_str(sc.contributionPath)) != 0)
- return mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contributionPath);
+const failable<bool> vhostConfig(ServerConf& sc, const ServerConf& ssc, request_rec* r) {
+ debug(httpd::serverName(ssc.server), "modeval::vhostConfig::serverName");
+ debug(httpd::serverName(r), "modeval::vhostConfig::vhostName");
+ debug(ssc.vhostContributionPath, "modeval::vhostConfig::vhostContributionPath");
+ debug(sc.contributionPath, "modeval::vhostConfig::contributionPath");
// Configure the deployed components
const failable<bool> cr = confComponents(sc);
if (!hasContent(cr))
return cr;
+ // Store the virtual host configuration in the request config
+
+ return true;
+}
+
+/**
+ * Start the components deployed in a virtual host.
+ */
+const failable<bool> vhostStart(ServerConf& sc, const ServerConf& ssc, request_rec* r) {
+ debug(httpd::serverName(ssc.server), "modeval::vhostStart::serverName");
+ debug(httpd::serverName(r), "modeval::vhostStart::vhostName");
+ debug(ssc.vhostContributionPath, "modeval::vhostStart::vhostContributionPath");
+ debug(sc.contributionPath, "modeval::vhostStart::contributionPath");
+
+ // Configure the components deployed in a virtual host
+ const failable<bool> cr = vhostConfig(sc, ssc, r);
+ if (!hasContent(cr))
+ return cr;
+
// Start the configured components
const failable<bool> sr = startComponents(sc);
if (!hasContent(sr))
@@ -808,23 +636,441 @@ const failable<bool> virtualHostConfig(ServerConf& sc, const ServerConf& ssc, re
}
/**
- * Cleanup a virtual host.
+ * Stop a virtual host.
*/
-const failable<bool> virtualHostCleanup(const ServerConf& sc, const ServerConf& ssc) {
- if (!hasCompositeConf(sc))
+const failable<bool> vhostStop(const ServerConf& sc, unused const ServerConf& ssc) {
+ if (!hasContributionConf(sc))
return true;
- debug("modeval::virtualHostCleanup");
+ debug("modeval::vhostStop");
// Stop the component implementations
applyLifecycleExpr(sc.implementations, mklist<value>("stop"));
- // Chdir back to the main server's contribution
- if (chdir(c_str(ssc.contributionPath)) != 0)
- return mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + ssc.contributionPath);
return true;
}
/**
+ * Handle an HTTP GET.
+ */
+const failable<int> get(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::get::uri");
+
+ // Inspect the query string
+ const list<list<value> > args = httpd::queryArgs(r);
+ const list<value> ia = assoc(value("id"), args);
+ const list<value> ma = assoc(value("method"), args);
+
+ // Evaluate a JSON-RPC request and return a JSON result
+ if (!isNil(ia) && !isNil(ma)) {
+
+ // Extract the request id, method and params
+ const value id = cadr(ia);
+ const value func = c_str(json::funcName(string(cadr(ma))));
+
+ // Apply the requested function
+ const failable<value> val = failableResult(impl(cons(func, json::queryParams(args))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+
+ // Return JSON result
+ js::JSContext cx;
+ return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", r);
+ }
+
+ // Evaluate the GET expression
+ const list<value> params(append<value>(cddr(rpath), mkvalues(args)));
+ const failable<value> val = failableResult(impl(cons<value>("get", mklist<value>(params))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ const value c = content(val);
+ debug(c, "modeval::get::content");
+
+ // Check if the client requested a specific format
+ const list<value> fmt = assoc<value>("format", args);
+
+ // Write as a scheme value if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "scheme")
+ return httpd::writeResult(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8", r);
+
+ // Write a simple value as a JSON value
+ if (!isList(c)) {
+ js::JSContext cx;
+ if (isSymbol(c)) {
+ const list<value> lc = mklist<value>(mklist<value>("name", value(string(c))));
+ debug(lc, "modeval::get::symbol");
+ return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
+ }
+ const list<value> lc = mklist<value>(mklist<value>("value", c));
+ debug(lc, "modeval::get::value");
+ return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Write an empty list as a JSON empty value
+ if (isNil((list<value>)c)) {
+ js::JSContext cx;
+ debug(list<value>(), "modeval::get::empty");
+ return httpd::writeResult(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Write content-type / content-list pair
+ if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c)))
+ return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r);
+
+ // Write an assoc value as a JSON result
+ if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) {
+ js::JSContext cx;
+ const list<value> lc = mklist<value>(c);
+ debug(lc, "modeval::get::assoc");
+ debug(valuesToElements(lc), "modeval::get::assoc::element");
+ return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Write value as JSON if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "json") {
+ js::JSContext cx;
+ return httpd::writeResult(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Convert list of values to element values
+ const list<value> e = valuesToElements(c);
+ debug(e, "modeval::get::elements");
+
+ // Write an ATOM feed or entry
+ if (isList(car<value>(e)) && !isNil(car<value>(e))) {
+ const list<value> el = car<value>(e);
+ if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) {
+ if (cadr<value>(el) == atom::feed)
+ return httpd::writeResult(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8", r);
+ if (cadr<value>(el) == atom::entry)
+ return httpd::writeResult(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8", r);
+ }
+ }
+
+ // Write any other compound value as a JSON value
+ js::JSContext cx;
+ return httpd::writeResult(json::writeJSON(e, cx), "application/json; charset=utf-8", r);
+}
+
+/**
+ * Handle an HTTP POST.
+ */
+const failable<int> post(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::post::uri");
+
+ // Evaluate a JSON-RPC request and return a JSON result
+ const string ct = httpd::contentType(r);
+ if (contains(ct, "application/json-rpc") || contains(ct, "text/plain") || contains(ct, "application/x-www-form-urlencoded")) {
+
+ // Read the JSON request
+ const int rc = httpd::setupReadPolicy(r);
+ if(rc != OK)
+ return rc;
+ const list<string> ls = httpd::read(r);
+ debug(ls, "modeval::post::input");
+ js::JSContext cx;
+ const list<value> json = elementsToValues(content(json::readJSON(ls, cx)));
+ const list<list<value> > args = httpd::postArgs(json);
+
+ // Extract the request id, method and params
+ const value id = cadr(assoc(value("id"), args));
+ const value func = c_str(json::funcName(cadr(assoc(value("method"), args))));
+ const list<value> params = (list<value>)cadr(assoc(value("params"), args));
+
+ // Evaluate the request expression
+ const failable<value> val = failableResult(impl(cons<value>(func, params)));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+
+ // Return JSON result
+ return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", r);
+ }
+
+ // Evaluate an ATOM POST request and return the location of the corresponding created resource
+ if (contains(ct, "application/atom+xml")) {
+
+ // Read the ATOM entry
+ const int rc = httpd::setupReadPolicy(r);
+ if(rc != OK)
+ return rc;
+ const list<string> ls = httpd::read(r);
+ debug(ls, "modeval::post::input");
+ const value entry = elementsToValues(content(atom::readATOMEntry(ls)));
+
+ // Evaluate the POST expression
+ const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(rpath), entry))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+
+ // Return the created resource location
+ debug(content(val), "modeval::post::location");
+ apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(httpd::url(r->uri, content(val), r))));
+ r->status = HTTP_CREATED;
+ return OK;
+ }
+
+ // Unknown content type, wrap the HTTP request struct in a value and pass it to
+ // the component implementation function
+ const failable<value> val = failableResult(impl(cons<value>("handle", mklist<value>(httpd::requestValue(r)))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ return (int)content(val);
+}
+
+/**
+ * Handle an HTTP PUT.
+ */
+const failable<int> put(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::put::uri");
+
+ // Read the ATOM entry
+ const int rc = httpd::setupReadPolicy(r);
+ if(rc != OK)
+ return rc;
+ const list<string> ls = httpd::read(r);
+ debug(ls, "modeval::put::input");
+ const value entry = elementsToValues(content(atom::readATOMEntry(ls)));
+
+ // Evaluate the PUT expression and update the corresponding resource
+ const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(rpath), entry))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
+ return OK;
+}
+
+/**
+ * Handle an HTTP DELETE.
+ */
+const failable<int> del(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::delete::uri");
+
+ // Evaluate an ATOM delete request
+ const failable<value> val = failableResult(impl(cons<value>("delete", mklist<value>(cddr(rpath)))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
+ return OK;
+}
+
+/**
+ * Route a /references/component-name/reference-name request,
+ * to the target of the component reference.
+ */
+int translateReference(const ServerConf& sc, request_rec *r, const list<value>& rpath, const list<value>& apath) {
+ httpdDebugRequest(r, "modeval::translateReference::input");
+ debug(r->uri, "modeval::translateReference::uri");
+ debug(apath, "modeval::translateReference::apath");
+ debug(rpath, "modeval::translateReference::rpath");
+
+ // Find the requested component
+ if (isNil(cdr(rpath)))
+ return HTTP_NOT_FOUND;
+ const list<value> comp(assoctree(cadr(rpath), sc.references));
+ if (isNil(comp))
+ return HTTP_NOT_FOUND;
+
+ // Find the requested reference and target configuration
+ const list<value> ref(assoctree<value>(caddr(rpath), cadr(comp)));
+ if (isNil(ref))
+ return HTTP_NOT_FOUND;
+ const string target(cadr(ref));
+ debug(target, "modeval::translateReference::target");
+
+ // Route to an absolute target URI using mod_proxy or an HTTP client redirect
+ const list<value> pathInfo = cdddr(rpath);
+ if (http::isAbsolute(target)) {
+ if (useModProxy) {
+ // Build proxy URI
+ string turi = target + path(pathInfo) + (r->args != NULL? string("?") + r->args : string(""));
+ const string proxy(string("proxy:") + turi);
+ debug(proxy, "modeval::translateReference::proxy");
+ r->filename = apr_pstrdup(r->pool, c_str(proxy));
+ r->proxyreq = PROXYREQ_REVERSE;
+ r->handler = "proxy-server";
+ apr_table_setn(r->notes, "proxy-nocanon", "1");
+ return OK;
+ }
+
+ debug(target, "modeval::translateReference::location");
+ r->handler = "mod_tuscany_eval";
+ return httpd::externalRedirect(target, r);
+ }
+
+ // Route to a relative target URI using a local internal redirect
+ // /components/, target component name and request path info
+ const value tname = substr(target, 0, find(target, '/'));
+ const string redir = path(append(apath, cons<value>(string("c"), cons(tname, pathInfo))));
+ debug(redir, "modeval::translateReference::redirect");
+ r->uri = apr_pstrdup(r->pool, c_str(redir));
+ r->handler = "mod_tuscany_eval";
+ return OK;
+}
+
+/**
+ * Find a leaf matching a request path in a tree of URI paths.
+ */
+const int matchPath(const list<value>& k, const list<value>& p) {
+ if (isNil(p))
+ return true;
+ if (isNil(k))
+ return false;
+ if (car(k) != car(p))
+ return false;
+ return matchPath(cdr(k), cdr(p));
+}
+
+const list<value> assocPath(const value& k, const list<value>& tree) {
+ if (isNil(tree))
+ return tree;
+ if (matchPath(k, car<value>(car(tree))))
+ return car(tree);
+ if (k < car<value>(car(tree)))
+ return assocPath(k, cadr(tree));
+ return assocPath(k, caddr(tree));
+}
+
+/**
+ * Route a service request to the component providing the requested service.
+ */
+int translateService(const ServerConf& sc, request_rec *r, const list<value>& rpath, const list<value>& apath) {
+ httpdDebugRequest(r, "modeval::translateService::input");
+ debug(r->uri, "modeval::translateService::uri");
+
+ // Find the requested component
+ debug(sc.services, "modeval::translateService::services");
+ const list<value> svc(assocPath(rpath, sc.services));
+ if (isNil(svc))
+ return DECLINED;
+ debug(svc, "modeval::translateService::service");
+
+ // Build a component-name + path-info URI
+ const list<value> target(append(apath, cons<value>(string("c"), cons<value>(cadr(svc), httpd::pathInfo(rpath, car(svc))))));
+ debug(target, "modeval::translateService::target");
+
+ // Dispatch to the target component using a local internal redirect
+ const string redir(path(target));
+ debug(redir, "modeval::translateService::redirect");
+ r->uri = apr_pstrdup(r->pool, c_str(redir));
+ r->handler = "mod_tuscany_eval";
+ return OK;
+}
+
+/**
+ * Translate a request to the target app and component.
+ */
+const int translateRequest(const ServerConf& sc, request_rec* r, const list<value>& rpath, const list<value>& apath) {
+ debug(apath, "modeval::translateRequest::apath");
+ debug(rpath, "modeval::translateRequest::rpath");
+ if (isNil(apath) && isNil(rpath))
+ return DECLINED;
+
+ if (!isNil(rpath)) {
+
+ // If the request is targeting a virtual host, use the corresponding
+ // virtual host configuration
+ if (isNil(apath)) {
+ if (hasVhostDomainConf(sc) && hasVhostContributionConf(sc) && httpd::isVhostRequest(sc.server, sc.vhostDomain, r)) {
+ const string aname = httpd::hostName(r);
+ ServerConf vsc(r->pool, sc, aname);
+ if (!hasContent(vhostConfig(vsc, sc, r)))
+ return DECLINED;
+ return translateRequest(vsc, r, rpath, mklist<value>(aname));
+ }
+ }
+
+ // Let default handler handle a resource request
+ const value prefix = car(rpath);
+ if (prefix == string("vhosts") || prefix == string("v"))
+ return DECLINED;
+
+ // Let our handler handle a component request
+ if (prefix == string("components") || prefix == string("c")) {
+ r->handler = "mod_tuscany_eval";
+ return OK;
+ }
+
+ // Translate a component reference request
+ if (prefix == string("references") || prefix == string("r"))
+ return translateReference(sc, r, rpath, apath);
+
+ // Attempt to translate the request to a service request
+ if (translateService(sc, r, rpath, apath) == OK)
+ return OK;
+
+ // Attempt to map the request to an actual file
+ if (isNil(apath)) {
+ const failable<request_rec*, int> fnr = httpd::internalSubRequest(r->uri, r);
+ if (!hasContent(fnr))
+ return HTTP_INTERNAL_SERVER_ERROR;
+ request_rec* nr = content(fnr);
+ nr->uri = r->filename;
+ const int tr = ap_core_translate(nr);
+ if (tr != OK)
+ return tr;
+ if (ap_directory_walk(nr) == OK && ap_file_walk(nr) == OK && nr->finfo.filetype != APR_NOFILE) {
+ debug(nr->filename, "modeval::translateRequest::file");
+ return DECLINED;
+ }
+
+ // If the request is targeting a virtual app, use the corresponding
+ // virtual host configuration
+ if (hasVhostContributionConf(sc)) {
+ const string cp = sc.vhostContributionPath + string(prefix) + "/" + sc.vhostCompositeName;
+ ServerConf vsc(r->pool, sc, string(prefix));
+ if (!hasContent(vhostConfig(vsc, sc, r)))
+ return DECLINED;
+ return translateRequest(vsc, r, cdr(rpath), mklist<value>(car(rpath)));
+ }
+ }
+ }
+
+ // If we're in a virtual app and the request didn't match a service,
+ // reference or component, redirect it to /v/<uri>. This will allow
+ // mapping to the actual app resources using HTTPD aliases.
+ if (!isNil(apath)) {
+ if (isNil(rpath) && r->uri[strlen(r->uri) -1] != '/') {
+
+ // Make sure a document root request ends with a '/', using
+ // an external redirect
+ const string target = string(r->uri) + string("/") + (r->args != NULL? string("?") + r->args : string(""));;
+ debug(target, "modeval::translateRequest::location");
+ r->handler = "mod_tuscany_eval";
+ return httpd::externalRedirect(target, r);
+ }
+
+ // Do an internal redirect to /v/<uri>
+ const string redir = string("/redirect:") + string("/v") + string(r->uri);
+ debug(redir, "modeval::translateRequest::redirect");
+ r->filename = apr_pstrdup(r->pool, c_str(redir));
+ r->handler = "mod_tuscany_eval";
+ return OK;
+ }
+
+ return DECLINED;
+}
+
+/**
+ * Translate a request.
+ */
+int translate(request_rec *r) {
+ if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
+ return DECLINED;
+
+ // Create a scoped memory pool
+ gc_scoped_pool pool(r->pool);
+
+ httpdDebugRequest(r, "modeval::translate::input");
+
+ // Get the server configuration
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
+
+ // Translate the request
+ return translateRequest(sc, r, pathValues(r->uri), list<value>());
+}
+
+/**
* Handle a component request.
*/
const int handleRequest(const ServerConf& sc, const list<value>& rpath, request_rec *r) {
@@ -832,45 +1078,44 @@ const int handleRequest(const ServerConf& sc, const list<value>& rpath, request_
if (isNil(cdr(rpath)))
return HTTP_NOT_FOUND;
- // Handle a request targeting a virtual host or virtual app
- if (hasVirtualCompositeConf(sc)) {
- if (hasVirtualDomainConf(sc) && httpd::isVirtualHostRequest(sc.server, sc.virtualHostDomain, r)) {
+ if (hasVhostContributionConf(sc)) {
+
+ // Handle a request targeting a component in a virtual host
+ if (hasVhostDomainConf(sc) && httpd::isVhostRequest(sc.server, sc.vhostDomain, r)) {
// Determine the app name from the host sub-domain name, and
- // store it in a header
+ // store it in a request note
const string app = http::subDomain(httpd::hostName(r));
- apr_table_setn(r->headers_in, "X-Request-AppName", apr_pstrdup(r->pool, c_str(app)));
+ apr_table_setn(r->notes, "X-Request-AppName", apr_pstrdup(r->pool, c_str(app)));
ServerConf vsc(r->pool, sc, app);
- if (!hasContent(virtualHostConfig(vsc, sc, r)))
+ if (!hasContent(vhostStart(vsc, sc, r)))
return HTTP_INTERNAL_SERVER_ERROR;
const int rc = handleRequest(vsc, rpath, r);
- virtualHostCleanup(vsc, sc);
+ vhostStop(vsc, sc);
return rc;
}
+ // Handle a request targeting a component in a virtual app
const value c = car(rpath);
if (c != string("components") && c != string("c")) {
- // Determine the app name from the request URI path
+ // Determine the app name from the request URI path and
+ // store it in a request note
const string app = string(c);
- const string cp = sc.virtualHostContributionPath + app + "/" + sc.virtualHostCompositeName;
- struct stat st;
- const int s = stat(c_str(cp), &st);
- if (s != -1) {
- // Store the app name in a header
- apr_table_setn(r->headers_in, "X-Request-AppName", apr_pstrdup(r->pool, c_str(app)));
- ServerConf vsc(r->pool, sc, app);
- if (!hasContent(virtualHostConfig(vsc, sc, r)))
- return HTTP_INTERNAL_SERVER_ERROR;
- const int rc = handleRequest(vsc, cdr(rpath), r);
- virtualHostCleanup(vsc, sc);
- return rc;
- }
+ const string cp = sc.vhostContributionPath + app + "/" + sc.vhostCompositeName;
+ apr_table_setn(r->notes, "X-Request-AppName", apr_pstrdup(r->pool, c_str(app)));
+
+ ServerConf vsc(r->pool, sc, app);
+ if (!hasContent(vhostStart(vsc, sc, r)))
+ return HTTP_INTERNAL_SERVER_ERROR;
+ const int rc = handleRequest(vsc, cdr(rpath), r);
+ vhostStop(vsc, sc);
+ return rc;
}
}
- // Store the request uri path in a header
- apr_table_setn(r->headers_in, "X-Request-URI", apr_pstrdup(r->pool, c_str(path(rpath))));
+ // Store the request uri path in a request note
+ apr_table_setn(r->notes, "X-Request-URI", apr_pstrdup(r->pool, c_str(path(rpath))));
// Get the component implementation lambda
const list<value> impl(assoctree<value>(cadr(rpath), sc.implTree));
@@ -903,11 +1148,20 @@ int handler(request_rec *r) {
if(strcmp(r->handler, "mod_tuscany_eval"))
return DECLINED;
- // Create a scoped memory pool
- gc_scoped_pool pool(r->pool);
+ // Nothing to do for an external redirect
+ if (r->status == HTTP_MOVED_TEMPORARILY)
+ return OK;
+ // Handle an internal redirect as directed by the translate hook
+ if (r->filename != NULL && !strncmp(r->filename, "/redirect:", 10)) {
+ if (r->args == NULL)
+ return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info)), r);
+ return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info), string(r->args)), r);
+ }
+
+
+ // Create a scope for the current request
ScopedRequest sr(r);
- httpdDebugRequest(r, "modeval::handler::input");
// Get the server configuration
const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
@@ -946,16 +1200,21 @@ const int postConfigMerge(const ServerConf& mainsc, server_rec* s) {
ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval);
debug(httpd::serverName(s), "modeval::postConfigMerge::serverName");
sc.lifecycle = mainsc.lifecycle;
+ sc.vhostName = mainsc.vhostName;
sc.contributionPath = mainsc.contributionPath;
sc.compositeName = mainsc.compositeName;
- sc.virtualHostDomain = mainsc.virtualHostDomain;
- sc.virtualHostContributionPath = mainsc.virtualHostContributionPath;
- sc.virtualHostCompositeName = mainsc.virtualHostCompositeName;
+ sc.vhostDomain = mainsc.vhostDomain;
+ sc.vhostContributionPath = mainsc.vhostContributionPath;
+ sc.vhostCompositeName = mainsc.vhostCompositeName;
+ sc.vhostContributorName = mainsc.vhostContributorName;
if (sc.ca == "") sc.ca = mainsc.ca;
if (sc.cert == "") sc.cert = mainsc.cert;
if (sc.key == "") sc.key = mainsc.key;
+ sc.references = mainsc.references;
+ sc.services = mainsc.services;
sc.implementations = mainsc.implementations;
sc.implTree = mainsc.implTree;
+ sc.vhostContributor = mainsc.vhostContributor;
return postConfigMerge(mainsc, s->next);
}
@@ -964,7 +1223,7 @@ int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp,
gc_scoped_pool pool(p);
- // Get the server configuration and determine the wiring server name
+ // Get the server configuration and determine the server name
ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval);
debug(httpd::serverName(s), "modeval::postConfig::serverName");
@@ -1037,6 +1296,10 @@ void childInit(apr_pool_t* p, server_rec* s) {
// Store the implementation lambda functions in a tree for fast retrieval
sc.implTree = mkbtree(sort(sc.implementations));
+ // Create a proxy for the vhost contributor if needed
+ if (length(sc.vhostContributorName) != 0)
+ sc.vhostContributor = mkimplProxy(sc, sc.vhostContributorName);
+
// Merge the updated configuration into the virtual hosts
postConfigMerge(sc, s->next);
@@ -1062,19 +1325,25 @@ const char* confComposite(cmd_parms *cmd, unused void *c, const char *arg) {
const char* confVirtualDomain(cmd_parms *cmd, unused void *c, const char *arg) {
gc_scoped_pool pool(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
- sc.virtualHostDomain = arg;
+ sc.vhostDomain = arg;
return NULL;
}
const char* confVirtualContribution(cmd_parms *cmd, unused void *c, const char *arg) {
gc_scoped_pool pool(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
- sc.virtualHostContributionPath = arg;
+ sc.vhostContributionPath = arg;
+ return NULL;
+}
+const char* confVirtualContributor(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
+ sc.vhostContributorName = arg;
return NULL;
}
const char* confVirtualComposite(cmd_parms *cmd, unused void *c, const char *arg) {
gc_scoped_pool pool(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
- sc.virtualHostCompositeName = arg;
+ sc.vhostCompositeName = arg;
return NULL;
}
const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) {
@@ -1108,7 +1377,8 @@ const command_rec commands[] = {
AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, RSRC_CONF, "SCA contribution location"),
AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, RSRC_CONF, "SCA composite location"),
AP_INIT_TAKE1("SCAVirtualDomain", (const char*(*)())confVirtualDomain, NULL, RSRC_CONF, "SCA virtual host domain"),
- AP_INIT_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution location"),
+ AP_INIT_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution path"),
+ AP_INIT_TAKE1("SCAVirtualContributor", (const char*(*)())confVirtualContributor, NULL, RSRC_CONF, "SCA virtual host contributor component"),
AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual composite location"),
AP_INIT_TAKE12("SCASetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"),
AP_INIT_TAKE1("SCAWiringSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "SCA wiring SSL CA certificate file"),
@@ -1122,7 +1392,7 @@ void registerHooks(unused apr_pool_t *p) {
ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
- ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST);
+ ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_LAST);
}
}