
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1517413 13f79535-47bb-0310-9956-ffa450edef68
1617 lines
60 KiB
C++
1617 lines
60 KiB
C++
/*
|
|
* 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
|