
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1052740 13f79535-47bb-0310-9956-ffa450edef68
472 lines
17 KiB
C++
472 lines
17 KiB
C++
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor 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$ */
|
|
|
|
/**
|
|
* HTTPD module used to wire component references and route requests to
|
|
* target service components.
|
|
*/
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include "string.hpp"
|
|
#include "stream.hpp"
|
|
#include "list.hpp"
|
|
#include "tree.hpp"
|
|
#include "value.hpp"
|
|
#include "monad.hpp"
|
|
#include "../scdl/scdl.hpp"
|
|
#include "../http/httpd.hpp"
|
|
|
|
extern "C" {
|
|
extern module AP_MODULE_DECLARE_DATA mod_tuscany_wiring;
|
|
}
|
|
|
|
namespace tuscany {
|
|
namespace server {
|
|
namespace modwiring {
|
|
|
|
/**
|
|
* Set to true to wire using mod_proxy, false to wire using HTTP client redirects.
|
|
*/
|
|
const bool useModProxy = true;
|
|
|
|
/**
|
|
* Server configuration.
|
|
*/
|
|
class ServerConf {
|
|
public:
|
|
ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s), contributionPath(""), compositeName(""), virtualHostContributionPath(""), virtualHostCompositeName("") {
|
|
}
|
|
|
|
const gc_pool p;
|
|
server_rec* server;
|
|
string contributionPath;
|
|
string compositeName;
|
|
string virtualHostContributionPath;
|
|
string virtualHostCompositeName;
|
|
list<value> references;
|
|
list<value> services;
|
|
};
|
|
|
|
/**
|
|
* Return true if a server contains a composite configuration.
|
|
*/
|
|
const bool hasCompositeConf(const ServerConf& sc) {
|
|
return sc.contributionPath != "" && sc.compositeName != "";
|
|
}
|
|
|
|
/**
|
|
* Return true if a server contains a virtual host composite configuration.
|
|
*/
|
|
const bool hasVirtualCompositeConf(const ServerConf& sc) {
|
|
return sc.virtualHostContributionPath != "" && sc.virtualHostCompositeName != "";
|
|
}
|
|
|
|
/**
|
|
* Route a /references/component-name/reference-name request,
|
|
* to the target of the component reference.
|
|
*/
|
|
int translateReference(const ServerConf& sc, request_rec *r) {
|
|
httpdDebugRequest(r, "modwiring::translateReference::input");
|
|
debug(r->uri, "modwiring::translateReference::uri");
|
|
|
|
// Find the requested component
|
|
const list<value> rpath(pathValues(r->uri));
|
|
const list<value> comp(assoctree(cadr(rpath), sc.references));
|
|
if (isNil(comp))
|
|
return HTTP_NOT_FOUND;
|
|
|
|
// Find the requested reference and target configuration
|
|
const list<value> ref(assoctree<value>(caddr(rpath), cadr(comp)));
|
|
if (isNil(ref))
|
|
return HTTP_NOT_FOUND;
|
|
const string target(cadr(ref));
|
|
debug(target, "modwiring::translateReference::target");
|
|
|
|
// Route to an absolute target URI using mod_proxy or an HTTP client redirect
|
|
const list<value> pathInfo = cdddr(rpath);
|
|
if (httpd::isAbsolute(target)) {
|
|
if (useModProxy) {
|
|
// Build proxy URI
|
|
// current request's protocol scheme, reference target uri and request path info
|
|
string turi = httpd::scheme(r) + substr(target, find(target, "://")) + path(pathInfo);
|
|
r->filename = apr_pstrdup(r->pool, c_str(string("proxy:") + turi));
|
|
debug(r->filename, "modwiring::translateReference::filename");
|
|
r->proxyreq = PROXYREQ_REVERSE;
|
|
r->handler = "proxy-server";
|
|
apr_table_setn(r->notes, "proxy-nocanon", "1");
|
|
return OK;
|
|
}
|
|
|
|
debug(target, "modwiring::translateReference::location");
|
|
r->handler = "mod_tuscany_wiring";
|
|
return httpd::externalRedirect(target, r);
|
|
}
|
|
|
|
// Route to a relative target URI using a local internal redirect
|
|
// /components/, target component name and request path info
|
|
const value tname = substr(target, 0, find(target, '/'));
|
|
const string tpath = path(cons(tname, pathInfo));
|
|
r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/components") + tpath));
|
|
debug(r->filename, "modwiring::translateReference::filename");
|
|
r->handler = "mod_tuscany_wiring";
|
|
return OK;
|
|
}
|
|
|
|
/**
|
|
* Find a leaf matching a request path in a tree of URI paths.
|
|
*/
|
|
const int matchPath(const list<value>& k, const list<value>& p) {
|
|
if (isNil(p))
|
|
return true;
|
|
if (isNil(k))
|
|
return false;
|
|
if (car(k) != car(p))
|
|
return false;
|
|
return matchPath(cdr(k), cdr(p));
|
|
}
|
|
|
|
const list<value> assocPath(const value& k, const list<value>& tree) {
|
|
if (isNil(tree))
|
|
return tree;
|
|
if (matchPath(k, car<value>(car(tree))))
|
|
return car(tree);
|
|
if (k < car<value>(car(tree)))
|
|
return assocPath(k, cadr(tree));
|
|
return assocPath(k, caddr(tree));
|
|
}
|
|
|
|
/**
|
|
* Route a service request to the component providing the requested service.
|
|
*/
|
|
int translateService(const ServerConf& sc, request_rec *r) {
|
|
httpdDebugRequest(r, "modwiring::translateService::input");
|
|
debug(r->uri, "modwiring::translateService::uri");
|
|
|
|
// Find the requested component
|
|
debug(sc.services, "modwiring::translateService::services");
|
|
const list<value> p(pathValues(r->uri));
|
|
const list<value> svc(assocPath(p, sc.services));
|
|
if (isNil(svc))
|
|
return DECLINED;
|
|
debug(svc, "modwiring::translateService::service");
|
|
|
|
// Build a component-name + path-info URI
|
|
const list<value> target(cons<value>(cadr(svc), httpd::pathInfo(p, car(svc))));
|
|
debug(target, "modwiring::translateService::target");
|
|
|
|
// Dispatch to the target component using a local internal redirect
|
|
const string tp(path(target));
|
|
debug(tp, "modwiring::translateService::path");
|
|
const string redir(string("/redirect:/components") + tp);
|
|
debug(redir, "modwiring::translateService::redirect");
|
|
r->filename = apr_pstrdup(r->pool, c_str(redir));
|
|
r->handler = "mod_tuscany_wiring";
|
|
return OK;
|
|
}
|
|
|
|
/**
|
|
* 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(readXML(streamList(is)));
|
|
}
|
|
|
|
/**
|
|
* Return a list of component-name + references pairs. The references are
|
|
* arranged in trees of reference-name + reference-target pairs.
|
|
*/
|
|
const list<value> componentReferenceToTargetTree(const value& c) {
|
|
return mklist<value>(scdl::name(c), mkbtree(sort(scdl::referenceToTargetAssoc(scdl::references(c)))));
|
|
}
|
|
|
|
const list<value> componentReferenceToTargetAssoc(const list<value>& c) {
|
|
if (isNil(c))
|
|
return c;
|
|
return cons<value>(componentReferenceToTargetTree(car(c)), componentReferenceToTargetAssoc(cdr(c)));
|
|
}
|
|
|
|
/**
|
|
* Return a list of service-URI-path + component-name pairs. Service-URI-paths are
|
|
* represented as lists of URI path fragments.
|
|
*/
|
|
const list<value> defaultBindingURI(const string& cn, const string& sn) {
|
|
return mklist<value>(cn, sn);
|
|
}
|
|
|
|
const list<value> bindingToComponentAssoc(const string& cn, const string& sn, const list<value>& b) {
|
|
if (isNil(b))
|
|
return b;
|
|
const value uri(scdl::uri(car(b)));
|
|
if (isNil(uri))
|
|
return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), bindingToComponentAssoc(cn, sn, cdr(b)));
|
|
return cons<value>(mklist<value>(pathValues(c_str(string(uri))), cn), bindingToComponentAssoc(cn, sn, cdr(b)));
|
|
}
|
|
|
|
const list<value> serviceToComponentAssoc(const string& cn, const list<value>& s) {
|
|
if (isNil(s))
|
|
return s;
|
|
const string sn(scdl::name(car(s)));
|
|
const list<value> btoc(bindingToComponentAssoc(cn, sn, scdl::bindings(car(s))));
|
|
if (isNil(btoc))
|
|
return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), serviceToComponentAssoc(cn, cdr(s)));
|
|
return append<value>(btoc, serviceToComponentAssoc(cn, cdr(s)));
|
|
}
|
|
|
|
const list<value> uriToComponentAssoc(const list<value>& c) {
|
|
if (isNil(c))
|
|
return c;
|
|
return append<value>(serviceToComponentAssoc(scdl::name(car(c)), scdl::services(car(c))), uriToComponentAssoc(cdr(c)));
|
|
}
|
|
|
|
/**
|
|
* Configure the components declared in the server's deployment composite.
|
|
*/
|
|
const bool confComponents(ServerConf& sc) {
|
|
if (!hasCompositeConf(sc))
|
|
return true;
|
|
debug(sc.contributionPath, "modwiring::confComponents::contributionPath");
|
|
debug(sc.compositeName, "modwiring::confComponents::compositeName");
|
|
|
|
// Read the component configuration and store the references and service URIs
|
|
// in trees for fast retrieval later
|
|
const failable<list<value> > comps = readComponents(sc.contributionPath + sc.compositeName);
|
|
if (!hasContent(comps))
|
|
return true;
|
|
const list<value> refs = componentReferenceToTargetAssoc(content(comps));
|
|
debug(refs, "modwiring::confComponents::references");
|
|
sc.references = mkbtree(sort(refs));
|
|
|
|
const list<value> svcs = uriToComponentAssoc(content(comps));
|
|
debug(svcs, "modwiring::confComponents::services");
|
|
sc.services = mkbtree(sort(svcs));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Virtual host scoped server configuration.
|
|
*/
|
|
class VirtualHostConf {
|
|
public:
|
|
VirtualHostConf(const gc_pool& p, const ServerConf& ssc) : sc(pool(p), ssc.server) {
|
|
sc.virtualHostContributionPath = ssc.virtualHostContributionPath;
|
|
sc.virtualHostCompositeName = ssc.virtualHostCompositeName;
|
|
}
|
|
|
|
~VirtualHostConf() {
|
|
}
|
|
|
|
ServerConf sc;
|
|
};
|
|
|
|
/**
|
|
* Configure and start the components deployed in a virtual host.
|
|
*/
|
|
const failable<bool> virtualHostConfig(ServerConf& sc, request_rec* r) {
|
|
debug(httpd::serverName(sc.server), "modwiring::virtualHostConfig::serverName");
|
|
debug(httpd::serverName(r), "modwiring::virtualHostConfig::virtualHostName");
|
|
debug(sc.virtualHostContributionPath, "modwiring::virtualHostConfig::virtualHostContributionPath");
|
|
|
|
// Resolve the configured virtual contribution under
|
|
// the virtual host's SCA contribution root
|
|
sc.contributionPath = sc.virtualHostContributionPath + httpd::subdomain(httpd::hostName(r)) + "/";
|
|
sc.compositeName = sc.virtualHostCompositeName;
|
|
|
|
// Configure the wiring for the deployed components
|
|
confComponents(sc);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Translate an HTTP service or reference request and route it
|
|
* to the target component.
|
|
*/
|
|
int translate(request_rec *r) {
|
|
if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
|
|
return DECLINED;
|
|
|
|
// No translation needed for a component or tunnel request
|
|
if (!strncmp(r->uri, "/components/", 12))
|
|
return DECLINED;
|
|
|
|
// Get the server configuration
|
|
gc_scoped_pool pool(r->pool);
|
|
const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring);
|
|
|
|
// Process dynamic virtual host configuration
|
|
VirtualHostConf vhc(gc_pool(r->pool), sc);
|
|
const bool usevh = hasVirtualCompositeConf(vhc.sc) && httpd::isVirtualHostRequest(sc.server, r);
|
|
if (usevh) {
|
|
const failable<bool> cr = virtualHostConfig(vhc.sc, r);
|
|
if (!hasContent(cr))
|
|
return -1;
|
|
}
|
|
|
|
// Translate a component reference request
|
|
if (!strncmp(r->uri, "/references/", 12))
|
|
return translateReference(usevh? vhc.sc: sc, r);
|
|
|
|
// Translate a service request
|
|
return translateService(usevh? vhc.sc : sc, r);
|
|
}
|
|
|
|
/**
|
|
* HTTP request handler, redirect to a target component.
|
|
*/
|
|
int handler(request_rec *r) {
|
|
if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
|
|
return DECLINED;
|
|
if(strcmp(r->handler, "mod_tuscany_wiring"))
|
|
return DECLINED;
|
|
if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0)
|
|
return DECLINED;
|
|
|
|
// Nothing to do for an external redirect
|
|
if (r->status == HTTP_MOVED_TEMPORARILY)
|
|
return OK;
|
|
|
|
// Do an internal redirect
|
|
gc_scoped_pool pool(r->pool);
|
|
httpdDebugRequest(r, "modwiring::handler::input");
|
|
|
|
debug(r->uri, "modwiring::handler::uri");
|
|
debug(r->filename, "modwiring::handler::filename");
|
|
debug(r->path_info, "modwiring::handler::path info");
|
|
if (r->args == NULL)
|
|
return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info)), r);
|
|
return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info), string(r->args)), r);
|
|
}
|
|
|
|
/**
|
|
* Called after all the configuration commands have been run.
|
|
* Process the server configuration and configure the wiring for the deployed components.
|
|
*/
|
|
const int postConfigMerge(const ServerConf& mainsc, server_rec* s) {
|
|
if (s == NULL)
|
|
return OK;
|
|
debug(httpd::serverName(s), "modwiring::postConfigMerge::serverName");
|
|
ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring);
|
|
sc.contributionPath = mainsc.contributionPath;
|
|
sc.compositeName = mainsc.compositeName;
|
|
sc.virtualHostContributionPath = mainsc.virtualHostContributionPath;
|
|
sc.virtualHostCompositeName = mainsc.virtualHostCompositeName;
|
|
sc.references = mainsc.references;
|
|
sc.services = mainsc.services;
|
|
return postConfigMerge(mainsc, s->next);
|
|
}
|
|
|
|
int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) {
|
|
gc_scoped_pool pool(p);
|
|
|
|
// Count the calls to post config, skip the first one as
|
|
// postConfig is always called twice
|
|
const string k("tuscany::modwiring::postConfig");
|
|
const long int count = (long int)httpd::userData(k, s);
|
|
httpd::putUserData(k, (void*)(count + 1), s);
|
|
if (count == 0)
|
|
return OK;
|
|
|
|
// Configure the wiring for the deployed components
|
|
debug(httpd::serverName(s), "modwiring::postConfig::serverName");
|
|
ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring);
|
|
confComponents(sc);
|
|
|
|
// Merge the config into any virtual hosts
|
|
return postConfigMerge(sc, s->next);
|
|
}
|
|
|
|
/**
|
|
* Child process initialization.
|
|
*/
|
|
void childInit(apr_pool_t* p, server_rec* s) {
|
|
gc_scoped_pool pool(p);
|
|
if(ap_get_module_config(s->module_config, &mod_tuscany_wiring) == NULL) {
|
|
cfailure << "[Tuscany] Due to one or more errors mod_tuscany_wiring loading failed. Causing apache to stop loading." << endl;
|
|
exit(APEXIT_CHILDFATAL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configuration commands.
|
|
*/
|
|
const char *confContribution(cmd_parms *cmd, unused void *c, const char *arg) {
|
|
gc_scoped_pool pool(cmd->pool);
|
|
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
|
|
sc.contributionPath = arg;
|
|
return NULL;
|
|
}
|
|
const char *confComposite(cmd_parms *cmd, unused void *c, const char *arg) {
|
|
gc_scoped_pool pool(cmd->pool);
|
|
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
|
|
sc.compositeName = arg;
|
|
return NULL;
|
|
}
|
|
const char *confVirtualContribution(cmd_parms *cmd, unused void *c, const char *arg) {
|
|
gc_scoped_pool pool(cmd->pool);
|
|
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
|
|
sc.virtualHostContributionPath = arg;
|
|
return NULL;
|
|
}
|
|
const char *confVirtualComposite(cmd_parms *cmd, unused void *c, const char *arg) {
|
|
gc_scoped_pool pool(cmd->pool);
|
|
ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
|
|
sc.virtualHostCompositeName = arg;
|
|
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("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution location"),
|
|
AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual host composite location"),
|
|
{NULL, NULL, NULL, 0, NO_ARGS, NULL}
|
|
};
|
|
|
|
void registerHooks(unused apr_pool_t *p) {
|
|
ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
|
|
ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
|
|
ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
|
|
ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
module AP_MODULE_DECLARE_DATA mod_tuscany_wiring = {
|
|
STANDARD20_MODULE_STUFF,
|
|
// dir config and merger
|
|
NULL, NULL,
|
|
// server config and merger
|
|
tuscany::httpd::makeServerConf<tuscany::server::modwiring::ServerConf>, NULL,
|
|
// commands and hooks
|
|
tuscany::server::modwiring::commands, tuscany::server::modwiring::registerHooks
|
|
};
|
|
|
|
}
|