blob: 5c46f1c2244ac74fa633c34cf4b2464ad5f520a3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more provider license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* $Rev$ $Date$ */
#ifndef tuscany_modeval_hpp
#define tuscany_modeval_hpp
/**
* HTTPD module used to eval component implementations.
*/
#include <sys/stat.h>
#include "string.hpp"
#include "stream.hpp"
#include "function.hpp"
#include "list.hpp"
#include "tree.hpp"
#include "value.hpp"
#include "element.hpp"
#include "monad.hpp"
#include "../scheme/io.hpp"
#include "../atom/atom.hpp"
#include "../json/json.hpp"
#include "../scdl/scdl.hpp"
#include "../http/http.hpp"
#include "../http/httpd.hpp"
#include "apr_md5.h"
#include "ap_provider.h"
#include "mod_auth.h"
extern "C" {
extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval;
}
namespace tuscany {
namespace server {
namespace modeval {
/**
* SSL certificate configuration.
*/
class SSLConf {
public:
SSLConf() {
}
gc_mutable_ref<string> ca;
gc_mutable_ref<string> cert;
gc_mutable_ref<string> key;
};
/**
* Virtual host configuration.
*/
class VhostConf {
public:
VhostConf() {
}
gc_mutable_ref<string> domain;
gc_mutable_ref<string> contribPath;
gc_mutable_ref<string> composName;
gc_mutable_ref<string> contributorName;
gc_mutable_ref<value> contributor;
gc_mutable_ref<string> authenticatorName;
gc_mutable_ref<value> authenticator;
};
/**
* Contribution configuration.
*/
class ContribConf {
public:
ContribConf() {
}
gc_mutable_ref<string> contribPath;
gc_mutable_ref<string> composName;
};
/**
* Composite assocs.
*/
class Composite {
public:
Composite() {
}
Composite(const list<value>& refs, const list<value>& svcs, const list<value>& impls) : refs(refs), svcs(svcs), impls(impls) {
}
gc_mutable_ref<list<value> > refs;
gc_mutable_ref<list<value> > svcs;
gc_mutable_ref<list<value> > impls;
};
/**
* Server configuration.
*/
class ServerConf {
public:
ServerConf() {
}
ServerConf(apr_pool_t* const p, const server_rec* s) : p(p), server(s), timeout(0) {
}
const gc_pool p;
const server_rec* server;
gc_mutable_ref<value> lifecycle;
ContribConf contribc;
SSLConf sslc;
int timeout;
VhostConf vhostc;
Composite compos;
};
/**
* Request configuration.
*/
class RequestConf {
public:
RequestConf(apr_pool_t* const p, const request_rec* r) : p(p), request(r), vhost(false), valias(false) {
}
const gc_pool p;
const request_rec* request;
bool vhost;
bool valias;
gc_mutable_ref<list<value> > rpath;
gc_mutable_ref<list<value> > vpath;
gc_mutable_ref<list<value> > impls;
};
/**
* Authentication cache store function.
*/
static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authnCacheStore = NULL;
/**
* Convert a result represented as a (content reason? code?) tuple to a
* failable monad.
*/
const failable<value> failableResult(const list<value>& v) {
if (isNull(cdr(v)))
return car(v);
return mkfailure<value>(string(cadr(v)), isNull(cddr(v))? -1 : (int)caddr(v), false);
}
/**
* Store current HTTP request for access from property and proxy lambda functions.
*/
#ifdef WANT_THREADS
const perthread_ptr<request_rec> currentRequest;
#else
request_rec* currentRequest = NULL;
#endif
class ScopedRequest {
public:
ScopedRequest(request_rec* const r) {
currentRequest = r;
}
~ScopedRequest() {
currentRequest = NULL;
}
};
/**
* Make an HTTP proxy lambda to an absolute URI
*/
const value mkhttpProxy(const string& uri, const int timeout) {
debug(uri, "modeval::mkhttpProxy::uri");
return lvvlambda(http::proxy(uri, emptyString, emptyString, emptyString, emptyString, timeout));
}
/**
* Return a component implementation proxy lambda.
*/
class implProxy {
public:
implProxy(const value& name, const gc_mutable_ref<list<value> >& impls, const SSLConf& sslc, const int timeout) : name(name), impls(impls), sslc(sslc), timeout(timeout) {
}
const value callImpl(const value& cname, const list<value>& aparams) const {
debug(impls, "modeval::implProxy::callImpl::impls");
// Lookup the component implementation
const list<value> impl(rbtreeAssoc<value>(cname, (list<value>)impls));
if (isNull(impl))
return mkfailure<value>(string("Couldn't find component implementation: ") + (string)cname);
// Call its lambda function
const lvvlambda l(cadr<value>(impl));
const value func = c_str(car(aparams));
const failable<value> val = failableResult(l(cons(func, cdr(aparams))));
debug(val, "modeval::implProxy::result");
if (!hasContent(val))
return nilValue;
return content(val);
}
const value operator()(const list<value>& params) const {
debug(name, "modeval::implProxy::name");
debug(params, "modeval::implProxy::input");
// If the reference was 'wiredByImpl' use the first param as target
if (isNull(name)) {
const value uri = cadr(params);
const list<value> aparams = cons<value>(car(params), cddr(params));
debug(uri, "modeval::implProxy::wiredByImpl::uri");
debug(aparams, "modeval::implProxy::wiredByImpl::input");
// Use an HTTP proxy if the target is an absolute :// target
if (http::isAbsolute(uri)) {
const gc_pool p(currentRequest->pool);
// Interpret a uri in the form app://appname, convert it using the scheme,
// top level domain and port number from the current request
if (http::scheme(uri, p) == "app") {
ostringstream appuri;
appuri << httpd::scheme(currentRequest) << "://" << substr(uri, 6) << "." << http::topDomain(httpd::hostName(currentRequest)) << ":" << httpd::port(currentRequest) << "/";
debug(str(appuri), "modeval::implProxy::httpproxy::appuri");
const lvvlambda px = lvvlambda(http::proxy(str(appuri), sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout));
return px(aparams);
}
// Pass our SSL certificate and the cookie from the current request
// only if the target is in the same top level domain
if (http::topDomain(http::hostName(uri, p)) == http::topDomain(httpd::hostName(currentRequest))) {
debug(uri, "modeval::implProxy::httpproxy::samedomain");
const lvvlambda px = lvvlambda(http::proxy(uri, sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout));
return px(aparams);
}
// No SSL certificate or cookie on a cross domain call
debug(uri, "modeval::implProxy::httpproxy::crossdomain");
const lvvlambda px = lvvlambda(http::proxy(uri, emptyString, emptyString, emptyString, emptyString, timeout));
return px(aparams);
}
// Call the component implementation
return callImpl(uri, aparams);
}
// Call the component implementation
return callImpl(name, params);
}
private:
const value name;
const gc_mutable_ref<list<value> >& impls;
const SSLConf& sslc;
const int timeout;
};
const value mkimplProxy(const value& name, const gc_mutable_ref<list<value> >& impls, const SSLConf& sslc, const int timeout) {
debug(name, "modeval::implProxy::impl");
return lvvlambda(implProxy(name, impls, sslc, timeout));
}
/**
* Return a proxy lambda for an unwired reference.
*/
const value mkunwiredProxy(const string& name) {
debug(name, "modeval::mkunwiredProxy::name");
const lvvlambda unwiredProxy = [name](const list<value>& params) -> const value {
debug(name, "modeval::unwiredProxy::name");
debug(params, "modeval::unwiredProxy::params");
// Get function returns a default empty value
if (car(params) == "get") {
debug(nilValue, "modeval::unwiredProxy::result");
return nilValue;
}
// All other functions return a failure
return mkfailure<value>(string("Reference is not wired: ") + name);
};
return unwiredProxy;
}
/**
* Convert a list of component references to a list of proxy lambdas.
*/
const value mkrefProxy(const value& ref, const gc_mutable_ref<list<value> >& impls, const SSLConf& sslc, const int timeout) {
const value target = scdl::target(ref);
const bool wbyimpl = scdl::wiredByImpl(ref);
debug(ref, "modeval::mkrefProxy::ref");
debug(target, "modeval::mkrefProxy::target");
debug(wbyimpl, "modeval::mkrefProxy::wiredByImpl");
// Use an HTTP proxy or an internal proxy to the component implementation
if (wbyimpl)
return mkimplProxy(nilValue, impls, sslc, timeout);
if (isNull(target))
return mkunwiredProxy(scdl::name(ref));
if (http::isAbsolute(target))
return mkhttpProxy(target, timeout);
return mkimplProxy(car(pathValues(target)), impls, sslc, timeout);
}
const list<value> refProxies(const list<value>& refs, const gc_mutable_ref<list<value> >& impls, const SSLConf& sslc, const int timeout) {
if (isNull(refs))
return refs;
return cons(mkrefProxy(car(refs), impls, sslc, timeout), refProxies(cdr(refs), impls, sslc, timeout));
}
/**
* Convert a list of component properties to a list of lambda functions that just return
* the property value. The host, user and email properties are configured with the values
* from the HTTP request, if any.
*/
const lvvlambda mkvaluePropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
return v;
};
}
const lvvlambda mkhostPropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return http::hostName();
const value h = httpd::hostName(currentRequest, v);
debug(h, "modeval::hostPropProxy::value");
return h;
};
}
const lvvlambda mkappPropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return v;
const RequestConf& reqc = httpd::requestConf<RequestConf>(currentRequest, &mod_tuscany_eval);
const value a = isNull((const list<value>)reqc.vpath)? v : car((const list<value>)reqc.vpath);
debug(a, "modeval::appPropProxy::value");
return a;
};
}
const lvvlambda mkpathPropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return v;
const RequestConf& reqc = httpd::requestConf<RequestConf>(currentRequest, &mod_tuscany_eval);
const value p = (const list<value>)reqc.rpath;
debug(p, "modeval::pathPropProxy::value");
return p;
};
}
const lvvlambda mkqueryPropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return v;
const value q = httpd::unescapeArgs(httpd::queryArgs(currentRequest));
debug(q, "modeval::queryPropProxy::value");
return q;
};
}
const lvvlambda mkenvPropProxy(const string& name, const value& v) {
return [name, v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return v;
const char* env = apr_table_get(currentRequest->subprocess_env, c_str(name));
if (env == NULL || *env == '\0')
return v;
debug(name, "modeval::envPropProxy::name");
const value e = string(env);
debug(e, "modeval::envPropProxy::value");
return e;
};
}
const lvvlambda mkrealmPropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return v;
const char* env = apr_table_get(currentRequest->subprocess_env, "REALM");
if (env == NULL)
return v;
const string realm = httpd::realm(string(env));
if (length(realm) == 0)
return v;
const value r = realm;
debug(r, "modeval::realmPropProxy::value");
return r;
};
}
const lvvlambda mktimeoutPropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return v;
const ServerConf& sc = httpd::serverConf<ServerConf>(currentRequest, &mod_tuscany_eval);
const value r = sc.timeout;
debug(r, "modeval::timeoutPropProxy::value");
return r;
};
}
const lvvlambda mkuserPropProxy(const value& v) {
return [v](unused const list<value>& params) -> const value {
if (currentRequest == NULL)
return v;
if (currentRequest->user == NULL)
return v;
const value u = string(currentRequest->user);
debug(u, "modeval::userPropProxy::value");
return u;
};
}
const value mkpropProxy(const value& prop) {
const value n = scdl::name(prop);
const value v = elementHasValue(prop)? elementValue(prop) : emptyStringValue;
if (n == "app")
return mkappPropProxy(v);
if (n == "host")
return mkhostPropProxy(v);
if (n == "path")
return mkpathPropProxy(v);
if (n == "query")
return mkqueryPropProxy(v);
if (n == "user")
return mkuserPropProxy(v);
if (n == "realm")
return mkrealmPropProxy(v);
if (n == "timeout")
return mktimeoutPropProxy(v);
if (n == "email")
return mkenvPropProxy("EMAIL", v);
if (n == "nickname")
return mkenvPropProxy("NICKNAME", v);
if (n == "fullname")
return mkenvPropProxy("FULLNAME", v);
if (n == "firstname")
return mkenvPropProxy("FIRSTNAME", v);
if (n == "lastname")
return mkenvPropProxy("LASTNAME", v);
return mkvaluePropProxy(v);
}
const list<value> propProxies(const list<value>& props) {
if (isNull(props))
return props;
return cons(mkpropProxy(car(props)), propProxies(cdr(props)));
}
/**
* Evaluate a component and convert it to an applicable lambda function.
*/
const value evalComponent(const string& contribPath, const value& comp, const gc_mutable_ref<list<value> >& impls, const lvvlambda& lifecycle, const SSLConf& sslc, const int timeout) {
extern const failable<lvvlambda > evalImplementation(const string& cpath, const value& impl, const list<value>& px, const lvvlambda& lifecycle);
const value impl = scdl::implementation(comp);
debug(comp, "modeval::evalComponent::comp");
debug(impl, "modeval::evalComponent::impl");
// Convert component references to configured proxy lambdas
const list<value> rpx(refProxies(scdl::references(comp), impls, sslc, timeout));
// Convert component properties to configured proxy lambdas
const list<value> ppx(propProxies(scdl::properties(comp)));
// Evaluate the component implementation and convert it to an applicable lambda function
const failable<lvvlambda > cimpl(evalImplementation(contribPath, impl, append(rpx, ppx), lifecycle));
if (!hasContent(cimpl)) {
// Return a lambda function which will report a component evaluation failure
// on all subsequent calls expect start/stop
const value r = reason(cimpl);
const lvvlambda implementationFailure = [r](const list<value>& params) -> const value {
const value func = car(params);
if (func == "start" || func == "stop")
return mklist<value>(lvvlambda());
return mklist<value>(nilValue, r);
};
return implementationFailure;
}
return content(cimpl);
}
/**
* Return a list of component-name + configured-implementation pairs.
*/
const list<value> componentToImplementationAssoc(const list<value>& c, const string& contribPath, const gc_mutable_ref<list<value> >& impls, const lvvlambda& lifecycle, const SSLConf& sslc, const int timeout) {
if (isNull(c))
return c;
return cons<value>(mklist<value>(scdl::name(car(c)),
evalComponent(contribPath, car(c), impls, lifecycle, sslc, timeout)),
componentToImplementationAssoc(cdr(c), contribPath, impls, lifecycle, sslc, timeout));
}
/**
* Read the components declared in a composite.
*/
const failable<list<value> > readComponents(const string& path) {
ifstream is(path);
if (fail(is))
return mkfailure<list<value> >(string("Could not read composite: ") + path);
return scdl::components(content(xml::readElements(streamList(is))));
}
/**
* Get the components returned by a contributor.
*/
const failable<list<value> > getComponents(const lvvlambda& contributor, const string& name) {
const failable<value> val = failableResult(contributor(cons<value>("get", mklist<value>(mklist<value>(name)))));
if (!hasContent(val))
return mkfailure<list<value> >(val);
debug(content(val), "modeval::getComponents::val");
const list<value> valc = assoc<value>(value("content"), cdr<value>(car<value>(content(val))));
if (isNull(valc))
return mkfailure<list<value> >(string("Could not get composite: ") + name);
const list<value> comp = assoc<value>(value("composite"), cdr<value>(valc));
debug(comp, "modeval::getComponents::comp");
if (isNull(comp))
return mkfailure<list<value> >(string("Could not get composite: ") + name);
const failable<list<string> > x = xml::writeElements(car<value>(valuesToElements(mklist<value>(mklist<value>(comp)))));
if (!hasContent(x))
return mkfailure<list<value> >(x);
return scdl::components(content(xml::readElements(content(x))));
}
/**
* Apply a list of component implementations to a start or restart lifecycle expression.
* Return the functions returned by the component implementations.
*/
const failable<list<value> > applyLifecycleExpr(const list<value>& impls, const list<value>& expr) {
if (isNull(impls))
return nilListValue;
// Evaluate lifecycle expression against a component implementation lambda
const lvvlambda l = cadr<value>(car(impls));
const failable<value> r = failableResult(l(expr));
if (!hasContent(r))
return mkfailure<list<value> >(r);
const lvvlambda rl = content(r);
// Use the returned lambda function, if any, from now on
const lvvlambda al = isNull(rl)? l : rl;
// Continue with the rest of the list
const failable<list<value> > nr = applyLifecycleExpr(cdr(impls), expr);
if (!hasContent(nr))
return nr;
return cons<value>(mklist<value>(car<value>(car(impls)), value(al)), content(nr));
}
/**
* 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), mkbrbtree(sort(scdl::referenceToTargetAssoc(scdl::references(c)))));
}
const list<value> componentReferenceToTargetAssoc(const list<value>& c) {
if (isNull(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 (isNull(b))
return b;
const value uri(scdl::uri(car(b)));
if (isNull(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 (isNull(s))
return s;
const string sn(scdl::name(car(s)));
const list<value> btoc(bindingToComponentAssoc(cn, sn, scdl::bindings(car(s))));
if (isNull(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 (isNull(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<Composite> confComponents(const string& contribPath, const string& composName, const value& contributor, const string& vhost, const gc_mutable_ref<list<value> >& impls, const lvvlambda lifecycle, const SSLConf& sslc, const int timeout) {
debug(contribPath, "modeval::confComponents::contribPath");
debug(composName, "modeval::confComponents::composName");
debug(contributor, "modeval::confComponents::contributor");
debug(vhost, "modeval::confComponents::vhost");
debug(impls, "modeval::confComponents::impls");
const failable<list<value> > fcomps = isNull(contributor)?
readComponents(scdl::resourcePath(length(vhost) != 0? contribPath + vhost + "/" : contribPath, composName)) :
getComponents(contributor, vhost);
if (!hasContent(fcomps))
return mkfailure<Composite>(fcomps);
const list<value> comps = content(fcomps);
debug(comps, "modeval::confComponents::comps");
const list<value> refs = mkbrbtree(sort(componentReferenceToTargetAssoc(comps)));
debug(flatten(refs), "modeval::confComponents::refs");
const list<value> svcs = mkbrbtree(sort(uriToComponentAssoc(comps)));
debug(flatten(svcs), "modeval::confComponents::svcs");
const list<value> cimpls = mkbrbtree(sort(componentToImplementationAssoc(comps,
isNull(contributor)? length(vhost) != 0? contribPath + vhost + "/" : contribPath : contribPath,
impls, lifecycle, sslc, timeout)));
debug(flatten(cimpls), "modeval::confComponents::impls");
return Composite(refs, svcs, cimpls);
}
/**
* Start the components declared in a composite.
*/
const failable<list<value> > startComponents(const list<value>& impls) {
debug(flatten(impls), "modeval::startComponents::impls");
const failable<list<value> > fsimpls = applyLifecycleExpr(flatten(impls), mklist<value>("start"));
if (!hasContent(fsimpls))
return mkfailure<list<value> >(fsimpls);
const list<value> simpls = content(fsimpls);
debug(impls, "modeval::startComponents::simpls");
return mkbrbtree(sort(simpls));
}
/**
* Stop the components declared in a composite.
*/
const failable<bool> stopComponents(const list<value>& simpls) {
debug(flatten(simpls), "modeval::stopComponents::simpls");
applyLifecycleExpr(flatten(simpls), mklist<value>("stop"));
return true;
}
/**
* Returns the media type accepted by a client.
*/
const value acceptMediaType(request_rec* const r) {
const char* const xa = apr_table_get(r->headers_in, "X-Accept");
const char* const a = xa != NULL? xa : apr_table_get(r->headers_in, "Accept");
if (a == NULL)
return nilValue;
const string s(a);
if (contains(s, "text/x-scheme"))
return string("scheme");
if (contains(s, "application/json"))
return string("json");
if (contains(s, "text/xml"))
return string("xml");
return nilValue;
}
/**
* Handle an HTTP GET.
*/
const failable<int> get(const list<value>& rpath, request_rec* const r, const lvvlambda& impl) {
debug(r->uri, "modeval::get::uri");
// Inspect the query string
const list<value> args = httpd::unescapeArgs(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 (!isNull(ia) && !isNull(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>(val);
// Return JSON result
return httpd::writeResult(json::jsonResult(id, content(val)), "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>(val);
const value c = content(val);
debug(c, "modeval::get::content");
// Return a nil value as a not found status
if (!isList(c) && isNull(c))
return HTTP_NOT_FOUND;
// Write in the format requested by the client, if any
const list<value> fmt = assoc<value>("format", args);
const value mtype = !isNull(fmt)? cadr(fmt) : acceptMediaType(r);
if (!isNull(mtype)) {
if (mtype == "scheme")
return httpd::writeResult(scheme::writeValue(c), "text/x-scheme; charset=utf-8", r);
if (mtype == "json")
return httpd::writeResult(json::writeValue(c), "application/json; charset=utf-8", r);
if (mtype == "xml")
return httpd::writeResult(xml::writeElements(valuesToElements(c)), "text/xml; charset=utf-8", r);
}
// Write a simple value as a JSON value
if (!isList(c)) {
debug(c, "modeval::get::value");
return httpd::writeResult(json::writeValue(c), "application/json; charset=utf-8", r);
}
// Write an empty list as a JSON value
if (isNull((list<value>)c)) {
debug(nilListValue, "modeval::get::empty");
return httpd::writeResult(json::writeValue(c), "application/json; charset=utf-8", r);
}
// Write content-type / content-list pair
if (isString(car<value>(c)) && !isNull(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 value
if (isSymbol(car<value>(c)) && !isNull(cdr<value>(c))) {
debug(c, "modeval::get::assoc");
return httpd::writeResult(json::writeValue(c), "application/json; charset=utf-8", r);
}
// Write an ATOM feed or entry
const list<value> e = valuesToElements(c);
if (isList(car<value>(e)) && !isNull(car<value>(e))) {
const list<value> el = car<value>(e);
if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNull(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
return httpd::writeResult(json::writeValue(c), "application/json; charset=utf-8", r);
}
/**
* Handle an HTTP POST.
*/
const failable<int> post(const list<value>& rpath, request_rec* const r, const lvvlambda& 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");
const value jsreq = content(json::readValue(ls));
const list<value> args = httpd::postArgs(jsreq);
// 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>(val);
// Return JSON result
return httpd::writeResult(json::jsonResult(id, content(val)), "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 aval = elementsToValues(content(atom::isATOMEntry(ls)? atom::readATOMEntry(ls) : atom::readATOMFeed(ls)));
// Evaluate the POST expression
const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(rpath), aval))));
if (!hasContent(val))
return mkfailure<int>(val);
// Report HTTP status code
const value rval = content(val);
if (isNull(rval) || rval == falseValue)
return HTTP_NOT_FOUND;
if (isNumber(rval))
return (int)rval;
// Return the successfully created resource location
debug(rval, "modeval::post::location");
apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(httpd::url(r->uri, rval, r))));
r->status = HTTP_CREATED;
return OK;
}
// Unknown content type, wrap the HTTP request structure 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>(val);
return (int)content(val);
}
/**
* Handle an HTTP PUT.
*/
const failable<int> put(const list<value>& rpath, request_rec* const r, const lvvlambda& 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 aval = elementsToValues(content(atom::isATOMEntry(ls)? atom::readATOMEntry(ls) : atom::readATOMFeed(ls)));
// Evaluate the PUT expression and update the corresponding resource
const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(rpath), aval))));
if (!hasContent(val))
return mkfailure<int>(val);
// Report HTTP status
const value rval = content(val);
if (isNull(rval) || rval == falseValue)
return HTTP_NOT_FOUND;
if (isNumber(rval))
return (int)rval;
return OK;
}
/**
* Handle an HTTP PATCH.
*/
const failable<int> patch(const list<value>& rpath, request_rec* const r, const lvvlambda& impl) {
debug(r->uri, "modeval::put::patch");
// 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::patch::input");
const value aval = elementsToValues(content(atom::isATOMEntry(ls)? atom::readATOMEntry(ls) : atom::readATOMFeed(ls)));
// Evaluate the PATCH expression and update the corresponding resource
const failable<value> val = failableResult(impl(cons<value>("patch", mklist<value>(cddr(rpath), aval))));
if (!hasContent(val))
return mkfailure<int>(val);
// Report HTTP status
const value rval = content(val);
if (isNull(rval) || rval == falseValue)
return HTTP_NOT_FOUND;
if (isNumber(rval))
return (int)rval;
return OK;
}
/**
* Handle an HTTP DELETE.
*/
const failable<int> del(const list<value>& rpath, request_rec* const r, const lvvlambda& 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>(val);
// Report HTTP status
const value rval = content(val);
if (isNull(rval) || rval == falseValue)
return HTTP_NOT_FOUND;
if (isNumber(rval))
return (int)rval;
return OK;
}
/**
* Proceed to handle a service component request.
*/
int proceedToHandler(request_rec* const r, const int rc) {
r->handler = "mod_tuscany_eval";
return rc;
}
int proceedToHandler(request_rec* const r, const int rc, const bool valias, const list<value>& rpath, const list<value>& vpath, const list<value>& impls) {
r->handler = "mod_tuscany_eval";
r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:") + r->uri));
// Store the selected vhost, path and composite in the request
RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval);
reqc.valias = valias;
reqc.rpath = rpath;
reqc.vpath = vpath;
reqc.impls = impls;
return rc;
}
/**
* Route a component request to the specified component.
*/
int translateComponent(request_rec* const r, const list<value>& rpath, const list<value>& vpath, const list<value>& impls) {
debug(rpath, "modeval::translateComponent::rpath");
debug(flatten(impls), "modeval::translateComponent::impls");
// Find the requested component
if (isNull(cdr(rpath)))
return HTTP_NOT_FOUND;
const list<value> impl(rbtreeAssoc(cadr(rpath), impls));
if (isNull(impl))
return HTTP_NOT_FOUND;
debug(impl, "modeval::translateComponent::impl");
return proceedToHandler(r, OK, false, rpath, vpath, impls);;
}
/**
* Route a /references/component-name/reference-name request,
* to the target of the component reference.
*/
int translateReference(request_rec* const r, const list<value>& rpath, const list<value>& vpath, const list<value>& refs, const list<value>& impls) {
debug(rpath, "modeval::translateReference::rpath");
debug(flatten(refs), "modeval::translateReference::refs");
// Find the requested component
if (isNull(cdr(rpath)))
return HTTP_NOT_FOUND;
const list<value> comp(rbtreeAssoc(cadr(rpath), refs));
if (isNull(comp))
return HTTP_NOT_FOUND;
debug(comp, "modeval::translateReference::comp");
// Find the requested reference and target configuration
const list<value> ref(rbtreeAssoc<value>(caddr(rpath), cadr(comp)));
if (isNull(ref))
return HTTP_NOT_FOUND;
debug(ref, "modeval::translateReference::ref");
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)) {
const string turi = target + (string)path(pathInfo) + (r->args != NULL? string("?") + string(r->args) : emptyString);
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;
}
// Route to a relative target URI using a local internal redirect
// / c / target component name / request path info
const value tname = substr(target, 0, find(target, '/'));
const list<value> redir = cons<value>(string("c"), cons(tname, pathInfo));
debug(redir, "modeval::translateReference::redirect");
return proceedToHandler(r, OK, false, redir, vpath, impls);;
}
/**
* 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 (isNull(p))
return true;
if (isNull(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 (isNull(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(request_rec* const r, const list<value>& rpath, const list<value>& vpath, const list<value>& svcs, const list<value>& impls) {
debug(rpath, "modeval::translateService::rpath");
debug(flatten(svcs), "modeval::translateService::svcs");
// Find the requested component
if (isNull(rpath))
return HTTP_NOT_FOUND;
const list<value> svc(assocPath(rpath, svcs));
if (isNull(svc))
return DECLINED;
debug(svc, "modeval::translateService::svc");
// Dispatch to the target component using a local internal redirect
// / c / target component name / request path info
const list<value> redir = cons<value>(string("c"), cons<value>(cadr(svc), httpd::pathInfo(rpath, car(svc))));
debug(redir, "modeval::translateService::redirect");
return proceedToHandler(r, OK, false, redir, vpath, impls);
}
/**
* Translate a request to the target app and component.
*/
const int translateRequest(request_rec* const r, const list<value>& rpath, const list<value>& vpath, const list<value>& refs, const list<value>& svcs, const list<value>& impls) {
debug(vpath, "modeval::translateRequest::vpath");
debug(rpath, "modeval::translateRequest::rpath");
const string prefix = isNull(rpath)? emptyStringValue : car(rpath);
// Translate a component request
if ((prefix == string("components") || prefix == string("c")) && translateComponent(r, rpath, vpath, impls) == OK)
return proceedToHandler(r, OK);
// Translate a component reference request
if ((prefix == string("references") || prefix == string("r")) && translateReference(r, rpath, vpath, refs, impls) == OK)
return proceedToHandler(r, OK);
// Attempt to translate the request to a service request
if (translateService(r, rpath, vpath, svcs, impls) == OK)
return proceedToHandler(r, OK);
// Attempt to map a request targeting the main host to an actual file
if (isNull(vpath)) {
const failable<request_rec*> fnr = httpd::internalSubRequest(r->uri, r);
if (!hasContent(fnr))
return rcode(fnr);
request_rec* const 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) {
// Found the target file, let the default handler serve it
debug(nr->filename, "modeval::translateRequest::file");
return DECLINED;
}
} else {
// Make sure a document root request ends with a '/' using
// an external redirect
if (isNull(rpath) && r->uri[strlen(r->uri) - 1] != '/') {
const string target = string(r->uri) + string("/") + (r->args != NULL? string("?") + string(r->args) : emptyString);
debug(target, "modeval::translateRequest::location");
return proceedToHandler(r, httpd::externalRedirect(target, r));
}
// If the request didn't match a service, reference or component,
// redirect it to / v / app / path. This will allow mapping to
// the actual app resource using HTTPD aliases.
debug(true, "modeval::translateRequest::valias");
return proceedToHandler(r, OK, true, rpath, vpath, impls);
}
return HTTP_NOT_FOUND;
}
/**
* 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_PATCH && r->method_number != M_DELETE)
return DECLINED;
const gc_scoped_pool sp(r->pool);
debug_httpdRequest(r, "modeval::translate::input");
// Get the server configuration
const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
// Parse the request path
const list<value> rpath = pathValues(r->uri);
// Let default handler handle a resource request
const string prefix = isNull(rpath)? emptyStringValue : car(rpath);
if (prefix == string("vhosts") || prefix == string("v"))
return DECLINED;
// Get the request configuration
RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval);
// If the request is targeting a virtual host, configure the components
// in that virtual host
if (length(sc.vhostc.domain) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNull(sc.vhostc.contributor)) && httpd::isVhostRequest(sc.server, sc.vhostc.domain, r)) {
const string vname = http::subDomain(httpd::hostName(r));
const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, (value)sc.lifecycle, sc.sslc, sc.timeout);
if (!hasContent(fvcompos))
return DECLINED;
const Composite vcompos = content(fvcompos);
// Flag the request as virtual host based
reqc.vhost = true;
// Translate the request
reqc.impls = vcompos.impls;
return translateRequest(r, rpath, mklist<value>(vname), vcompos.refs, vcompos.svcs, reqc.impls);
}
// Translate a request targeting the main host
const int rc = translateRequest(r, rpath, nilListValue, sc.compos.refs, sc.compos.svcs, sc.compos.impls);
if (rc != HTTP_NOT_FOUND)
return rc;
// Attempt to map the first segment of the request path to a virtual host
if (length(prefix) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNull(sc.vhostc.contributor))) {
const string vname = prefix;
const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, (value)sc.lifecycle, sc.sslc, sc.timeout);
if (!hasContent(fvcompos))
return DECLINED;
const Composite vcompos = content(fvcompos);
// Translate the request
reqc.impls = vcompos.impls;
return translateRequest(r, cdr(rpath), mklist<value>(vname), vcompos.refs, vcompos.svcs, reqc.impls);
}
return DECLINED;
}
/**
* Handle a component request.
*/
const int handleRequest(const list<value>& rpath, request_rec* const r, const list<value>& impls) {
debug(rpath, "modeval::handleRequest::path");
// Get the component implementation lambda
const list<value> impl(rbtreeAssoc<value>(cadr(rpath), impls));
if (isNull(impl)) {
mkfailure<int>(string("Couldn't find component implementation: ") + (string)cadr(rpath));
return HTTP_NOT_FOUND;
}
const lvvlambda l(cadr<value>(impl));
// Handle HTTP method
if (r->header_only)
return OK;
if(r->method_number == M_GET)
return httpd::reportStatus(get(rpath, r, l));
if(r->method_number == M_POST)
return httpd::reportStatus(post(rpath, r, l));
if(r->method_number == M_PUT)
return httpd::reportStatus(put(rpath, r, l));
if(r->method_number == M_PATCH)
return httpd::reportStatus(patch(rpath, r, l));
if(r->method_number == M_DELETE)
return httpd::reportStatus(del(rpath, r, l));
return HTTP_NOT_IMPLEMENTED;
}
/**
* HTTP request handler.
*/
int handler(request_rec* r) {
if (r->handler != NULL && r->handler[0] != '\0')
return DECLINED;
// Attempt to translate the request
const int trc = translate(r);
// Pass if we couldn't translate the request
if(trc != OK)
return trc;
if(strcmp(r->handler, "mod_tuscany_eval"))
return DECLINED;
// Create a scope for the current request
const gc_scoped_pool sp(r->pool);
ScopedRequest sr(r);
debug_httpdRequest(r, "modeval::handler::input");
// Get the request configuration
RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval);
// Handle an internal redirect as directed by the translate hook
if (reqc.valias) {
const string redir = path(cons<value>(string("v"), reqc.vhost? (const list<value>)reqc.vpath : nilListValue)) + string(r->uri) + (r->args != NULL? string("?") + string(r->args) : emptyString);
debug(redir, "modeval::handler::internalredirect");
return httpd::internalRedirect(redir, r);
}
if (isNull((const list<value>)reqc.rpath))
return HTTP_NOT_FOUND;
// Get the server configuration
const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
// Handle a request targeting a component in a virtual host
if (!isNull((const list<value>)reqc.vpath)) {
// Start the components in the virtual host
const failable<list<value> > fsimpls = startComponents(reqc.impls);
if (!hasContent(fsimpls))
return HTTP_INTERNAL_SERVER_ERROR;
const list<value> simpls = content(fsimpls);
// Merge the components in the virtual host with the components in the main host
reqc.impls = mkbrbtree(sort(append(flatten((const list<value>)sc.compos.impls), flatten(simpls))));
// Handle the request against the running components
const int rc = handleRequest(reqc.rpath, r, reqc.impls);
// Stop the components in the virtual host
stopComponents(simpls);
return rc;
}
// Handle a request targeting a component in the main host
return handleRequest(reqc.rpath, r, sc.compos.impls);
}
/**
* Call an authenticator component to check a user's password.
*/
authn_status checkAuthnz(request_rec* r, const char* u, const char* p) {
const gc_scoped_pool sp(r->pool);
// Prevent FakeBasicAuth spoofing
const string user = u;
debug(user, "modeval::checkAuthnz::user");
const bool extauth = find(user, "/") != length(user);
if (extauth && substr(user, 0, 1) != "/") {
mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED);
return AUTH_DENIED;
}
// Get the server configuration
const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
if (isNull(sc.vhostc.authenticator)) {
mkfailure<int>("SCA authenticator not configured");
return AUTH_GENERAL_ERROR;
}
// Retrieve the user's password hash
const list<value> uid = extauth? cdr(pathValues(user)) : pathValues(user);
const failable<value> val = failableResult(((value)sc.vhostc.authenticator)(cons<value>("get", mklist<value>(uid))));
if (!hasContent(val) || isNull(content(val))) {
mkfailure<int>(string("SCA authentication check user failed, user not found: ") + user, rcode(val), user != "admin");
return AUTH_USER_NOT_FOUND;
}
debug(content(val), "modeval::checkAuthnz::val");
const value authn = cdr<value>(car<value>(content(val)));
const list<value> acontent = assoc<value>(value("content"), authn);
const list<value> aauthn = isNull(acontent)? nilListValue : assoc<value>(value("authn"), cdr<value>(acontent));
const list<value> ahash = isNull(aauthn)? nilListValue : assoc<value>(value("hash"), cdr<value>(aauthn));
if (isNull(ahash)) {
mkfailure<int>(string("SCA authentication check user failed, hash not found: ") + user, -1, user != "admin");
return AUTH_USER_NOT_FOUND;
}
const string uhash = cadr<value>(ahash);
if (length(uhash) == 0) {
mkfailure<int>(string("SCA authentication check user failed: ") + user);
return AUTH_USER_NOT_FOUND;
}
// Use a fixed hash of the string 'password' for externally authenticated users as they
// don't present an actual password
const string hash = extauth? "$apr1$OPUrN0Kr$/tc96p1r6LdmvB0mly6gg0" : uhash;
// Validate the password against the hash
const apr_status_t rv = apr_password_validate(p, c_str(hash));
if (rv != APR_SUCCESS) {
mkfailure<int>(string("SCA authentication user password check failed: ") + user);
return AUTH_DENIED;
}
// Update the user field of the request with the authenticated user
const list<value> auser = assoc<value>(value("user"), cdr<value>(aauthn));
if (!isNull(auser)) {
debug(c_str(cadr(auser)), "modeval::checkAuthnz::auth_user");
apr_table_set(r->subprocess_env, "AUTHZ_USER", apr_pstrdup(r->pool, c_str(cadr(auser))));
}
return AUTH_GRANTED;
}
/**
* Cleanup callback, called when the server is stopped or restarted.
*/
apr_status_t serverCleanup(void* v) {
const gc_pool pool;
ServerConf& sc = *(ServerConf*)v;
debug("modeval::serverCleanup");
// Stop the component implementations
stopComponents(sc.compos.impls);
// Call the module lifecycle function
if (isNull((value)sc.lifecycle))
return APR_SUCCESS;
const lvvlambda ll = (value)sc.lifecycle;
if (isNull(ll))
return APR_SUCCESS;
debug((value)sc.lifecycle, "modeval::serverCleanup::stop");
ll(mklist<value>("stop"));
return APR_SUCCESS;
}
/**
* Called after all the configuration commands have been run.
* Process the server configuration and configure the deployed components.
*/
const int postConfigMerge(const ServerConf& mainsc, server_rec* const s) {
if (s == NULL)
return OK;
ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval);
debug(httpd::serverName(s), "modeval::postConfigMerge::serverName");
sc.lifecycle = mainsc.lifecycle;
sc.contribc = mainsc.contribc;
sc.vhostc = mainsc.vhostc;
if (sc.sslc.ca == emptyString) sc.sslc.ca = mainsc.sslc.ca;
if (sc.sslc.cert == emptyString) sc.sslc.cert = mainsc.sslc.cert;
if (sc.sslc.key == emptyString) sc.sslc.key = mainsc.sslc.key;
sc.timeout = mainsc.timeout;
sc.compos = mainsc.compos;
return postConfigMerge(mainsc, s->next);
}
int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) {
extern const value applyLifecycle(const list<value>&);
const gc_scoped_pool sp(p);
// 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");
debug(sc.contribc.contribPath, "modeval::postConfig::contribPath");
debug(sc.contribc.composName, "modeval::postConfig::composName");
// Count the calls to post config
const string k("tuscany::modeval::postConfig");
const long int count = (long int)httpd::userData(k, s);
httpd::putUserData(k, (void*)(count + 1), s);
// Count == 0, do nothing as post config is always called twice,
// count == 1 is the first start, count > 1 is a restart
if (count == 0)
return OK;
if (count == 1) {
// Chdir to the deployed contribution
if (chdir(c_str(sc.contribc.contribPath)) != 0) {
mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contribc.contribPath);
return -1;
}
debug("modeval::postConfig::start");
const failable<value> r = failableResult(applyLifecycle(mklist<value>("start")));
if (!hasContent(r))
return -1;
debug(content(r), "modeval::postConfig::setlifecycle");
sc.lifecycle = content(r);
}
if (count > 1) {
debug("modeval::postConfig::restart");
const failable<value> r = failableResult(applyLifecycle(mklist<value>("restart")));
if (!hasContent(r))
return -1;
debug(content(r), "modeval::postConfig::setlifecycle");
sc.lifecycle = content(r);
}
// Configure the deployed components
const failable<Composite> compos = confComponents(sc.contribc.contribPath, sc.contribc.composName, nilValue, emptyString, sc.compos.impls, (value)sc.lifecycle, sc.sslc, sc.timeout);
if (!hasContent(compos)) {
cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
return -1;
}
sc.compos = content(compos);
// Register a cleanup callback, called when the server is stopped or restarted
apr_pool_pre_cleanup_register(p, (void*)&sc, serverCleanup);
// Merge the configuration into the virtual hosts
return postConfigMerge(sc, s->next);
}
/**
* Exit after a failure.
*/
void failureExitChild() {
cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
exit(APEXIT_CHILDFATAL);
}
/**
* Child process initialization.
*/
void childInit(apr_pool_t* p, server_rec* s) {
const gc_scoped_pool sp(p);
ServerConf* const psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_eval);
if(psc == NULL)
failureExitChild();
ServerConf& sc = *psc;
// Start the components in the child process
const failable<list<value> > fsimpls = startComponents(sc.compos.impls);
if (!hasContent(fsimpls))
failureExitChild();
sc.compos.impls = content(fsimpls);
// Get the vhost contributor component implementation lambda
if (length(sc.vhostc.contributorName) != 0) {
const list<value> impl(rbtreeAssoc<value>((string)sc.vhostc.contributorName, (const list<value>)sc.compos.impls));
if (isNull(impl)) {
mkfailure<int>(string("Couldn't find contributor component implementation: ") + sc.vhostc.contributorName);
failureExitChild();
}
sc.vhostc.contributor = cadr<value>(impl);
}
// Get the vhost authenticator component implementation lambda
if (length(sc.vhostc.authenticatorName) != 0) {
const list<value> impl(rbtreeAssoc<value>((string)sc.vhostc.authenticatorName, (const list<value>)sc.compos.impls));
if (isNull(impl)) {
mkfailure<int>(string("Couldn't find authenticator component implementation: ") + sc.vhostc.authenticatorName);
failureExitChild();
}
sc.vhostc.authenticator = cadr<value>(impl);
}
// Merge the updated configuration into the virtual hosts
postConfigMerge(sc, s->next);
// Register a cleanup callback, called when the child is stopped or restarted
apr_pool_pre_cleanup_register(p, (void*)psc, serverCleanup);
}
/**
* Configuration commands.
*/
const char* confContribution(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.contribc.contribPath = arg;
return NULL;
}
const char* confComposite(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.contribc.composName = arg;
return NULL;
}
const char* confVirtualDomain(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.vhostc.domain = arg;
return NULL;
}
const char* confVirtualContribution(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.vhostc.contribPath = arg;
return NULL;
}
const char* confVirtualContributor(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.vhostc.contributorName = arg;
return NULL;
}
const char* confVirtualComposite(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.vhostc.composName = arg;
return NULL;
}
const char* confAuthenticator(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.vhostc.authenticatorName = arg;
return NULL;
}
const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.sslc.ca = arg;
return NULL;
}
const char* confCertFile(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.sslc.cert = arg;
return NULL;
}
const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.sslc.key = arg;
return NULL;
}
const char* confTimeout(cmd_parms *cmd, unused void *c, const char *arg) {
const gc_scoped_pool sp(cmd->pool);
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
sc.timeout = atoi(arg);
return NULL;
}
const char* confEnv(unused cmd_parms *cmd, unused void *c, const char *name, const char *value) {
const gc_scoped_pool sp(cmd->pool);
setenv(name, value != NULL? value : "", 1);
return NULL;
}
/**
* HTTP server module declaration.
*/
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 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_TAKE1("SCAAuthenticator", (const char*(*)())confAuthenticator, NULL, RSRC_CONF, "SCA authenticator component"),
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"),
AP_INIT_TAKE1("SCAWiringSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "SCA wiring SSL certificate file"),
AP_INIT_TAKE1("SCAWiringSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "SCA wiring SSL certificate key file"),
AP_INIT_TAKE1("SCAWiringTimeout", (const char*(*)())confTimeout, NULL, RSRC_CONF, "SCA wiring timeout"),
{NULL, NULL, NULL, 0, NO_ARGS, NULL}
};
const authn_provider AuthnProvider = {
&checkAuthnz,
NULL
};
void retrieveAuthnCacheStore() {
authnCacheStore = APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
}
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_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "component", AUTHN_PROVIDER_VERSION, &AuthnProvider, AP_AUTH_INTERNAL_PER_CONF);
ap_hook_optional_fn_retrieve(retrieveAuthnCacheStore, NULL, NULL, APR_HOOK_MIDDLE);
}
}
}
}
extern "C" {
module AP_MODULE_DECLARE_DATA mod_tuscany_eval = {
STANDARD20_MODULE_STUFF,
// dir config and merger
NULL, NULL,
// server config and merger
tuscany::httpd::makeServerConf<tuscany::server::modeval::ServerConf>, NULL,
// commands and hooks
tuscany::server::modeval::commands, tuscany::server::modeval::registerHooks
};
}
#endif