diff options
author | jsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68> | 2010-12-25 01:49:19 +0000 |
---|---|---|
committer | jsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68> | 2010-12-25 01:49:19 +0000 |
commit | dc15dc32bb3348c760ba3643c083af7e0c8e43fe (patch) | |
tree | 2823b9165d5c3a8a1bc96b3d071f86101bb995b4 /sandbox/sebastien/cpp/apr-2/modules/server | |
parent | 088b2e47386078c79781a448e0b6e458ddaae23c (diff) |
Create a sandbox branch to experiment with latest APR.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1052740 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to '')
24 files changed, 3235 insertions, 0 deletions
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/Makefile.am b/sandbox/sebastien/cpp/apr-2/modules/server/Makefile.am new file mode 100644 index 0000000000..30c89da85d --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/Makefile.am @@ -0,0 +1,55 @@ +# 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. + +INCLUDES = -I${HTTPD_INCLUDE} + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/server + +dist_mod_SCRIPTS = cpp-conf scheme-conf server-conf +moddir = $(prefix)/modules/server + +EXTRA_DIST = domain-test.composite client-test.scm server-test.scm htdocs/*.xml htdocs/*.txt htdocs/*.html + +mod_LTLIBRARIES = libmod_tuscany_eval.la libmod_tuscany_wiring.la +noinst_DATA = libmod_tuscany_eval.so libmod_tuscany_wiring.so + +libmod_tuscany_eval_la_SOURCES = mod-eval.cpp +libmod_tuscany_eval_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_eval.so: + ln -s .libs/libmod_tuscany_eval.so + +libmod_tuscany_wiring_la_SOURCES = mod-wiring.cpp +libmod_tuscany_wiring_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_wiring.so: + ln -s .libs/libmod_tuscany_wiring.so + +noinst_test_LTLIBRARIES = libimpl-test.la +noinst_testdir = `pwd`/tmp +noinst_DATA += libimpl-test.so + +libimpl_test_la_SOURCES = impl-test.cpp +libimpl-test.so: + ln -s .libs/libimpl-test.so + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +dist_noinst_SCRIPTS = httpd-test server-test wiring-test +noinst_PROGRAMS = client-test +TESTS = httpd-test server-test wiring-test + diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/client-test.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.cpp new file mode 100644 index 0000000000..5de7ab6e7b --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.cpp @@ -0,0 +1,39 @@ +/* + * 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$ */ + +/** + * Test HTTP client functions. + */ + +#include "stream.hpp" +#include "string.hpp" +#include "client-test.hpp" + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + tuscany::server::testURI = "http://localhost:8090/test"; + + tuscany::server::testServer(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/client-test.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.hpp new file mode 100644 index 0000000000..c17f012012 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.hpp @@ -0,0 +1,350 @@ +/* + * 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$ */ + +/** + * Test HTTP client functions. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "parallel.hpp" +#include "perf.hpp" +#include "../http/http.hpp" + +namespace tuscany { +namespace server { + +string testURI = "http://localhost:8090/test"; +bool testBlobs = true; + +ostream* curlWriter(const string& s, ostream* os) { + (*os) << s; + return os; +} + +const bool testGet() { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + { + ostringstream os; + const failable<list<ostream*> > r = http::get<ostream*>(curlWriter, &os, "http://localhost:8090/index.html", ch); + assert(hasContent(r)); + assert(contains(str(os), "HTTP/1.1 200") || contains(str(os), "HTTP/1.0 200")); + assert(contains(str(os), "It works")); + } + { + const failable<value> r = http::getcontent("http://localhost:8090/index.html", ch); + assert(hasContent(r)); + assert(contains(car(reverse(list<value>(content(r)))), "It works")); + } + return true; +} + +struct getLoop { + http::CURLSession ch; + getLoop(http::CURLSession& ch) : ch(ch) { + } + const bool operator()() const { + const failable<value> r = http::getcontent("http://localhost:8090/index.html", ch); + assert(hasContent(r)); + assert(contains(car(reverse(list<value>(content(r)))), "It works")); + return true; + } +}; + +const bool testGetPerf() { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + const lambda<bool()> gl = getLoop(ch); + cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl; + return true; +} + +const bool testEval() { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + const value val = content(http::evalExpr(mklist<value>(string("echo"), string("Hello")), testURI, ch)); + assert(val == string("Hello")); + return true; +} + +struct evalLoop { + const string uri; + http::CURLSession ch; + evalLoop(const string& uri, http::CURLSession& ch) : uri(uri), ch(ch) { + } + const bool operator()() const { + const value val = content(http::evalExpr(mklist<value>(string("echo"), string("Hello")), uri, ch)); + assert(val == string("Hello")); + return true; + } +}; + +const value blob(string(2048, 'A')); +const list<value> blobs = mklist(blob, blob); + +struct blobEvalLoop { + const string uri; + http::CURLSession ch; + blobEvalLoop(const string& uri, http::CURLSession& ch) : uri(uri), ch(ch) { + } + const bool operator()() const { + const value val = content(http::evalExpr(mklist<value>(string("echo"), blobs), uri, ch)); + assert(val == blobs); + return true; + } +}; + +const bool testEvalPerf() { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + const lambda<bool()> el = evalLoop(testURI, ch); + cout << "JSON-RPC eval echo test " << time(el, 5, 200) << " ms" << endl; + + if (testBlobs) { + const lambda<bool()> bel = blobEvalLoop(testURI, ch); + cout << "JSON-RPC eval blob test " << time(bel, 5, 200) << " ms" << endl; + } + return true; +} + +bool testPost() { + gc_scoped_pool pool; + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + http::CURLSession ch("", "", ""); + const failable<value> id = http::post(a, testURI, ch); + assert(hasContent(id)); + return true; +} + +struct postLoop { + const string uri; + const value val; + http::CURLSession ch; + postLoop(const string& uri, const value& val, http::CURLSession& ch) : uri(uri), val(val), ch(ch) { + } + const bool operator()() const { + const failable<value> id = http::post(val, uri, ch); + assert(hasContent(id)); + return true; + } +}; + +struct postBlobLoop { + const string uri; + const value val; + http::CURLSession ch; + postBlobLoop(const string& uri, const value& val, http::CURLSession& ch) : uri(uri), val(val), ch(ch) { + } + const bool operator()() const { + gc_scoped_pool pool; + const failable<value> id = http::post(val, uri, ch); + assert(hasContent(id)); + return true; + } +}; + +const bool testPostPerf() { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + { + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const list<value> val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + const lambda<bool()> pl = postLoop(testURI, val, ch); + cout << "ATOMPub POST small test " << time(pl, 5, 200) << " ms" << endl; + } + if (testBlobs) { + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "blob1" + blob) + + (list<value>() + "blob2" + blob) + + (list<value>() + "price" + string("$2.99")); + const list<value> val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + const lambda<bool()> pl = postBlobLoop(testURI, val, ch); + cout << "ATOMPub POST blob test " << time(pl, 5, 200) << " ms" << endl; + } + return true; +} + +#ifdef WANT_THREADS + +const bool postThread(const string& uri, const int count, const value& val) { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + const lambda<bool()> pl = postLoop(uri, val, ch); + time(pl, 0, count); + return true; +} + +const list<future<bool> > startPost(worker& w, const int threads, const lambda<bool()>& l) { + if (threads == 0) + return list<future<bool> >(); + return cons(submit(w, l), startPost(w, threads - 1, l)); +} + +const bool checkPost(const list<future<bool> >& r) { + if (isNil(r)) + return true; + assert(car(r) == true); + return checkPost(cdr(r)); +} + +struct postThreadLoop { + const lambda<bool()> l; + const int threads; + const gc_ptr<worker> w; + postThreadLoop(const lambda<bool()>& l, const int threads) : l(l), threads(threads), w(new (gc_new<worker>()) worker(threads)) { + } + const bool operator()() const { + list<future<bool> > r = startPost(*w, threads, l); + checkPost(r); + return true; + } +}; + +const bool testPostThreadPerf() { + gc_scoped_pool pool; + const int count = 50; + const int threads = 10; + + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const value val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + + const lambda<bool()> pl= curry(lambda<bool(const string, const int, const value)>(postThread), testURI, count, val); + const lambda<bool()> ptl = postThreadLoop(pl, threads); + double t = time(ptl, 0, 1) / (threads * count); + cout << "ATOMPub POST thread test " << t << " ms" << endl; + + return true; +} + +#else + +const bool postProc(const string& uri, const int count, const value& val) { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + const lambda<bool()> pl = postLoop(uri, val, ch); + time(pl, 0, count); + return true; +} + +const list<pid_t> startPost(const int procs, const lambda<bool()>& l) { + if (procs == 0) + return list<pid_t>(); + pid_t pid = fork(); + if (pid == 0) { + assert(l() == true); + exit(0); + } + return cons(pid, startPost(procs - 1, l)); +} + +const bool checkPost(const list<pid_t>& r) { + if (isNil(r)) + return true; + int status; + waitpid(car(r), &status, 0); + assert(status == 0); + return checkPost(cdr(r)); +} + +struct postForkLoop { + const lambda<bool()> l; + const int procs; + postForkLoop(const lambda<bool()>& l, const int procs) : l(l), procs(procs) { + } + const bool operator()() const { + list<pid_t> r = startPost(procs, l); + checkPost(r); + return true; + } +}; + +const bool testPostForkPerf() { + gc_scoped_pool pool; + const int count = 50; + const int procs = 10; + + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const value val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + + const lambda<bool()> pl= curry(lambda<bool(const string, const int, const value)>(postProc), testURI, count, val); + const lambda<bool()> ptl = postForkLoop(pl, procs); + double t = time(ptl, 0, 1) / (procs * count); + cout << "ATOMPub POST fork test " << t << " ms" << endl; + + return true; +} + +#endif + +const bool testPut() { + gc_scoped_pool pool; + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + http::CURLSession ch("", "", ""); + value rc = content(http::put(a, testURI + "/111", ch)); + assert(rc == value(true)); + return true; +} + +const bool testDel() { + gc_scoped_pool pool; + http::CURLSession ch("", "", ""); + value rc = content(http::del(testURI + "/111", ch)); + assert(rc == value(true)); + return true; +} + +const bool testServer() { + tuscany::server::testGet(); + tuscany::server::testPost(); + tuscany::server::testPut(); + tuscany::server::testDel(); + tuscany::server::testEval(); + tuscany::server::testGetPerf(); + tuscany::server::testPostPerf(); +#ifdef WANT_THREADS + tuscany::server::testPostThreadPerf(); +#else + tuscany::server::testPostForkPerf(); +#endif + tuscany::server::testEvalPerf(); + return true; +} + +} +} diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/client-test.scm b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.scm new file mode 100644 index 0000000000..47b799d390 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.scm @@ -0,0 +1,38 @@ +; 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. + +; JSON-RPC test case + +(define (echo x ref) (ref "echo" x)) + +; ATOMPub test case + +(define (get id ref) + (ref "get" id) +) + +(define (post coll entry ref) + (ref "post" coll entry) +) + +(define (put id entry ref) + (ref "put" id entry) +) + +(define (delete id ref) + (ref "delete" id) +) diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/cpp-conf b/sandbox/sebastien/cpp/apr-2/modules/server/cpp-conf new file mode 100755 index 0000000000..086bb49d38 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/cpp-conf @@ -0,0 +1,30 @@ +#!/bin/sh + +# 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. + +# Generate a C++ server conf +here=`readlink -f $0`; here=`dirname $here` +mkdir -p $1 +root=`readlink -f $1` + +cat >>$root/conf/modules.conf <<EOF +# Generated by: cpp-conf $* +# Support for C++ SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_eval.so + +EOF diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/domain-test.composite b/sandbox/sebastien/cpp/apr-2/modules/server/domain-test.composite new file mode 100644 index 0000000000..048c451ef6 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/domain-test.composite @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="scheme-test"> + <t:implementation.scheme script="server-test.scm"/> + <service name="test"> + <t:binding.http uri="test"/> + </service> + </component> + + <component name="cpp-test"> + <implementation.cpp path="." library="libimpl-test"/> + <service name="cpp"> + <t:binding.http uri="cpp"/> + </service> + </component> + + <component name="client-test"> + <t:implementation.scheme script="client-test.scm"/> + <service name="client"> + <t:binding.http uri="client"/> + </service> + <reference name="ref" target="scheme-test"> + <t:binding.http/> + </reference> + </component> + +</composite> diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/index.html b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/index.html new file mode 100644 index 0000000000..1bfb3e30c2 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/index.html @@ -0,0 +1,21 @@ +<!-- + 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. +--> + +<html><body><h1>It works!</h1></body></html> + diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/entry.xml b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/entry.xml new file mode 100644 index 0000000000..6528c793e3 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/entry.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry> diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/feed.xml b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/feed.xml new file mode 100644 index 0000000000..bcb304f9c2 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/feed.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item><name>Orange</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>3.55</price></item></content><link href="222"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item><name>Pear</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>1.55</price></item></content><link href="333"/></entry></feed> diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-request.txt b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-request.txt new file mode 100644 index 0000000000..b4bd07fc46 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-request.txt @@ -0,0 +1 @@ +{"id":1,"method":"echo","params":["Hello"]} diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-result.txt b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-result.txt new file mode 100644 index 0000000000..121bf74902 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-result.txt @@ -0,0 +1 @@ +{"id":1,"result":"Hello"}
\ No newline at end of file diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/wiring/ref.js b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/wiring/ref.js new file mode 100644 index 0000000000..95a84c01a5 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/wiring/ref.js @@ -0,0 +1,574 @@ +/* + * 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. + * + * The JSON-RPC client code is based on Jan-Klaas' JavaScript + * o lait library (jsolait). + * + * $Id: jsonrpc.js,v 1.36.2.3 2006/03/08 15:09:37 mclark Exp $ + * + * Copyright (c) 2003-2004 Jan-Klaas Kollhof + * Copyright (c) 2005 Michael Clark, Metaparadigm Pte Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"). + */ + +/** + * Escape a character. + */ +var JSONClient = new Object(); + +JSONClient.escapeJSONChar = function(c) { + if(c == "\"" || c == "\\") return "\\" + c; + else if (c == "\b") return "\\b"; + else if (c == "\f") return "\\f"; + else if (c == "\n") return "\\n"; + else if (c == "\r") return "\\r"; + else if (c == "\t") return "\\t"; + var hex = c.charCodeAt(0).toString(16); + if(hex.length == 1) return "\\u000" + hex; + else if(hex.length == 2) return "\\u00" + hex; + else if(hex.length == 3) return "\\u0" + hex; + else return "\\u" + hex; +}; + +/** + * Encode a string into JSON format. + */ +JSONClient.escapeJSONString = function(s) { + /* The following should suffice but Safari's regex is broken + (doesn't support callback substitutions) + return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g, + JSONClient.escapeJSONChar) + "\""; + */ + + /* Rather inefficient way to do it */ + var parts = s.split(""); + for(var i = 0; i < parts.length; i++) { + var c = parts[i]; + if(c == '"' || + c == '\\' || + c.charCodeAt(0) < 32 || + c.charCodeAt(0) >= 128) + parts[i] = JSONClient.escapeJSONChar(parts[i]); + } + return "\"" + parts.join("") + "\""; +}; + +/** + * Marshall objects to JSON format. + */ +JSONClient.toJSON = function(o) { + if(o == null) { + return "null"; + } else if(o.constructor == String) { + return JSONClient.escapeJSONString(o); + } else if(o.constructor == Number) { + return o.toString(); + } else if(o.constructor == Boolean) { + return o.toString(); + } else if(o.constructor == Date) { + return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}'; + } else if(o.constructor == Array) { + var v = []; + for(var i = 0; i < o.length; i++) v.push(JSONClient.toJSON(o[i])); + return "[" + v.join(", ") + "]"; + } else { + var v = []; + for(attr in o) { + if(o[attr] == null) v.push("\"" + attr + "\": null"); + else if(typeof o[attr] == "function"); /* skip */ + else v.push(JSONClient.escapeJSONString(attr) + ": " + JSONClient.toJSON(o[attr])); + } + return "{" + v.join(", ") + "}"; + } +}; + +/** + * HTTPBindingClient.Exception. + */ +HTTPBindingClient.Exception = function(code, message, javaStack) { + this.code = code; + var name; + if(javaStack) { + this.javaStack = javaStack; + var m = javaStack.match(/^([^:]*)/); + if(m) name = m[0]; + } + if(name) this.name = name; + else this.name = "HTTPBindingClientException"; + this.message = message; +}; + +HTTPBindingClient.Exception.CODE_REMOTE_EXCEPTION = 490; +HTTPBindingClient.Exception.CODE_ERR_CLIENT = 550; +HTTPBindingClient.Exception.CODE_ERR_PARSE = 590; +HTTPBindingClient.Exception.CODE_ERR_NOMETHOD = 591; +HTTPBindingClient.Exception.CODE_ERR_UNMARSHALL = 592; +HTTPBindingClient.Exception.CODE_ERR_MARSHALL = 593; + +HTTPBindingClient.Exception.prototype = new Error(); +HTTPBindingClient.Exception.prototype.toString = function(code, msg) { + return this.name + ": " + this.message; +}; + +/** + * Default top level exception handler. + */ +HTTPBindingClient.default_ex_handler = function(e) { + alert(e); +}; + +/** + * Client settable variables + */ +HTTPBindingClient.toplevel_ex_handler = HTTPBindingClient.default_ex_handler; +HTTPBindingClient.profile_async = false; +HTTPBindingClient.max_req_active = 1; +HTTPBindingClient.requestId = 1; + +/** + * HTTPBindingClient implementation + */ +HTTPBindingClient.prototype._createApplyMethod = function() { + var fn = function() { + var args = []; + var callback = null; + var methodName = arguments[0]; + for(var i = 1; i < arguments.length; i++) args.push(arguments[i]); + + if(typeof args[args.length - 1] == "function") callback = args.pop(); + + var req = fn.client._makeRequest.call(fn.client, methodName, args, callback); + if(callback == null) { + return fn.client._sendRequest.call(fn.client, req); + } else { + HTTPBindingClient.async_requests.push(req); + HTTPBindingClient.kick_async(); + return req.requestId; + } + }; + fn.client = this; + return fn; +}; + +HTTPBindingClient._getCharsetFromHeaders = function(http) { + try { + var contentType = http.getResponseHeader("Content-type"); + var parts = contentType.split(/\s*;\s*/); + for(var i = 0; i < parts.length; i++) { + if(parts[i].substring(0, 8) == "charset=") + return parts[i].substring(8, parts[i].length); + } + } catch (e) {} + return "UTF-8"; +}; + +/** + * Async queue globals + */ +HTTPBindingClient.async_requests = []; +HTTPBindingClient.async_inflight = {}; +HTTPBindingClient.async_responses = []; +HTTPBindingClient.async_timeout = null; +HTTPBindingClient.num_req_active = 0; + +HTTPBindingClient._async_handler = function() { + HTTPBindingClient.async_timeout = null; + + while(HTTPBindingClient.async_responses.length > 0) { + var res = HTTPBindingClient.async_responses.shift(); + if(res.canceled) continue; + if(res.profile) res.profile.dispatch = new Date(); + try { + res.cb(res.result, res.ex, res.profile); + } catch(e) { + HTTPBindingClient.toplevel_ex_handler(e); + } + } + + while(HTTPBindingClient.async_requests.length > 0 && HTTPBindingClient.num_req_active < HTTPBindingClient.max_req_active) { + var req = HTTPBindingClient.async_requests.shift(); + if(req.canceled) continue; + req.client._sendRequest.call(req.client, req); + } +}; + +HTTPBindingClient.kick_async = function() { + if(HTTPBindingClient.async_timeout == null) + HTTPBindingClient.async_timeout = setTimeout(HTTPBindingClient._async_handler, 0); +}; + +HTTPBindingClient.cancelRequest = function(requestId) { + /* If it is in flight then mark it as canceled in the inflight map + and the XMLHttpRequest callback will discard the reply. */ + if(HTTPBindingClient.async_inflight[requestId]) { + HTTPBindingClient.async_inflight[requestId].canceled = true; + return true; + } + + /* If its not in flight yet then we can just mark it as canceled in + the the request queue and it will get discarded before being sent. */ + for(var i in HTTPBindingClient.async_requests) { + if(HTTPBindingClient.async_requests[i].requestId == requestId) { + HTTPBindingClient.async_requests[i].canceled = true; + return true; + } + } + + /* It may have returned from the network and be waiting for its callback + to be dispatched, so mark it as canceled in the response queue + and the response will get discarded before calling the callback. */ + for(var i in HTTPBindingClient.async_responses) { + if(HTTPBindingClient.async_responses[i].requestId == requestId) { + HTTPBindingClient.async_responses[i].canceled = true; + return true; + } + } + + return false; +}; + +HTTPBindingClient.prototype._makeRequest = function(methodName, args, cb) { + var req = {}; + req.client = this; + req.requestId = HTTPBindingClient.requestId++; + + var obj = {}; + obj.id = req.requestId; + if (this.objectID) + obj.method = ".obj#" + this.objectID + "." + methodName; + else + obj.method = methodName; + obj.params = args; + + if (cb) req.cb = cb; + if (HTTPBindingClient.profile_async) + req.profile = { "submit": new Date() }; + req.data = JSONClient.toJSON(obj); + + return req; +}; + +HTTPBindingClient.prototype._sendRequest = function(req) { + if(req.profile) req.profile.start = new Date(); + + /* Get free http object from the pool */ + var http = HTTPBindingClient.poolGetHTTPRequest(); + HTTPBindingClient.num_req_active++; + + /* Send the request */ + http.open("POST", this.uri, (req.cb != null)); + + /* setRequestHeader is missing in Opera 8 Beta */ + try { + http.setRequestHeader("Content-type", "text/plain"); + } catch(e) {} + + /* Construct call back if we have one */ + if(req.cb) { + var self = this; + http.onreadystatechange = function() { + if(http.readyState == 4) { + http.onreadystatechange = function () {}; + var res = { "cb": req.cb, "result": null, "ex": null}; + if (req.profile) { + res.profile = req.profile; + res.profile.end = new Date(); + } + try { res.result = self._handleResponse(http); } + catch(e) { res.ex = e; } + if(!HTTPBindingClient.async_inflight[req.requestId].canceled) + HTTPBindingClient.async_responses.push(res); + delete HTTPBindingClient.async_inflight[req.requestId]; + HTTPBindingClient.kick_async(); + } + }; + } else { + http.onreadystatechange = function() {}; + } + + HTTPBindingClient.async_inflight[req.requestId] = req; + + try { + http.send(req.data); + } catch(e) { + HTTPBindingClient.poolReturnHTTPRequest(http); + HTTPBindingClient.num_req_active--; + throw new HTTPBindingClient.Exception(HTTPBindingClient.Exception.CODE_ERR_CLIENT, "Connection failed"); + } + + if(!req.cb) return this._handleResponse(http); +}; + +HTTPBindingClient.prototype._handleResponse = function(http) { + /* Get the charset */ + if(!this.charset) { + this.charset = HTTPBindingClient._getCharsetFromHeaders(http); + } + + /* Get request results */ + var status, statusText, data; + try { + status = http.status; + statusText = http.statusText; + data = http.responseText; + } catch(e) { + HTTPBindingClient.poolReturnHTTPRequest(http); + HTTPBindingClient.num_req_active--; + HTTPBindingClient.kick_async(); + throw new HTTPBindingClient.Exception(HTTPBindingClient.Exception.CODE_ERR_CLIENT, "Connection failed"); + } + + /* Return http object to the pool; */ + HTTPBindingClient.poolReturnHTTPRequest(http); + HTTPBindingClient.num_req_active--; + + /* Unmarshall the response */ + if(status != 200) { + throw new HTTPBindingClient.Exception(status, statusText); + } + var obj; + try { + eval("obj = " + data); + } catch(e) { + throw new HTTPBindingClient.Exception(550, "error parsing result"); + } + if(obj.error) + throw new HTTPBindingClient.Exception(obj.error.code, obj.error.msg, obj.error.trace); + var res = obj.result; + + /* Handle CallableProxy */ + if(res && res.objectID && res.JSONRPCType == "CallableReference") + return new HTTPBindingClient(this.uri, res.objectID); + + return res; +}; + + +/** + * XMLHttpRequest wrapper code + */ +HTTPBindingClient.http_spare = []; +HTTPBindingClient.http_max_spare = 8; + +HTTPBindingClient.poolGetHTTPRequest = function() { + if(HTTPBindingClient.http_spare.length > 0) { + return HTTPBindingClient.http_spare.pop(); + } + return HTTPBindingClient.getHTTPRequest(); +}; + +HTTPBindingClient.poolReturnHTTPRequest = function(http) { + if(HTTPBindingClient.http_spare.length >= HTTPBindingClient.http_max_spare) + delete http; + else + HTTPBindingClient.http_spare.push(http); +}; + +HTTPBindingClient.msxmlNames = [ "MSXML2.XMLHTTP.5.0", + "MSXML2.XMLHTTP.4.0", + "MSXML2.XMLHTTP.3.0", + "MSXML2.XMLHTTP", + "Microsoft.XMLHTTP" ]; + +HTTPBindingClient.getHTTPRequest = function() { + /* Mozilla XMLHttpRequest */ + try { + HTTPBindingClient.httpObjectName = "XMLHttpRequest"; + return new XMLHttpRequest(); + } catch(e) {} + + /* Microsoft MSXML ActiveX */ + for (var i=0; i < HTTPBindingClient.msxmlNames.length; i++) { + try { + HTTPBindingClient.httpObjectName = HTTPBindingClient.msxmlNames[i]; + return new ActiveXObject(HTTPBindingClient.msxmlNames[i]); + } catch (e) {} + } + + /* None found */ + HTTPBindingClient.httpObjectName = null; + throw new HTTPBindingClient.Exception(0, "Can't create XMLHttpRequest object"); +}; + + +HTTPBindingClient.prototype.get = function(id, responseFunction) { + var xhr = this.createXMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + var strDocument = xhr.responseText; + var xmlDocument = xhr.responseXML; + if(!xmlDocument || xmlDocument.childNodes.length==0){ + xmlDocument = (new DOMParser()).parseFromString(strDocument, "text/xml"); + } + if (responseFunction != null) responseFunction(xmlDocument); + } else { + alert("get - Error getting data from the server"); + } + } + } + xhr.open("GET", this.uri + '/' + id, true); + xhr.send(null); +}; + +HTTPBindingClient.prototype.post = function (entry, responseFunction) { + var xhr = this.createXMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 201) { + var strDocument = xhr.responseText; + var xmlDocument = xhr.responseXML; + if(!xmlDocument || xmlDocument.childNodes.length==0){ + xmlDocument = (new DOMParser()).parseFromString(strDocument, "text/xml"); + } + if (responseFunction != null) responseFunction(xmlDocument); + } else { + alert("post - Error getting data from the server"); + } + } + } + xhr.open("POST", this.uri, true); + xhr.setRequestHeader("Content-Type", "application/atom+xml"); + xhr.send(entry); +}; + +HTTPBindingClient.prototype.put = function (id, entry, responseFunction) { + var xhr = this.createXMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + var strDocument = xhr.responseText; + var xmlDocument = xhr.responseXML; + if(!xmlDocument || xmlDocument.childNodes.length==0){ + xmlDocument = (new DOMParser()).parseFromString(strDocument, "text/xml"); + } + if (responseFunction != null) responseFunction(xmlDocument); + } else { + alert("put - Error getting data from the server"); + } + } + } + xhr.open("PUT", this.uri + '/' + id, true); + xhr.setRequestHeader("Content-Type", "application/atom+xml"); + xhr.send(entry); +}; + +HTTPBindingClient.prototype.del = function (id, responseFunction) { + var xhr = this.createXMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + if (responseFunction != null) responseFunction(); + } else { + alert("delete - Error getting data from the server"); + } + } + } + xhr.open("DELETE", this.uri + '/' + id, true); + xhr.send(null); +}; + +HTTPBindingClient.prototype.createXMLHttpRequest = function () { + /* Mozilla XMLHttpRequest */ + try { return new XMLHttpRequest(); } catch(e) {} + + /* Microsoft MSXML ActiveX */ + for (var i = 0; i < HTTPBindingClient.msxmlNames.length; i++) { + try { return new ActiveXObject(HTTPBindingClient.msxmlNames[i]); } catch (e) {} + } + alert("XML http request not supported"); + return null; +} + +/** + * Construct an HTTPBindingClient. + */ +function HTTPBindingClient(cname, uri, objectID) { + this.uri = "/references/" + cname + "/" + uri; + this.objectID = objectID; + this.apply = this._createApplyMethod(); + + if (typeof DOMParser == "undefined") { + DOMParser = function () {} + + DOMParser.prototype.parseFromString = function (str, contentType) { + if (typeof ActiveXObject != "undefined") { + var d = new ActiveXObject("MSXML.DomDocument"); + d.loadXML(str); + return d; + } else if (typeof XMLHttpRequest != "undefined") { + var req = new XMLHttpRequest; + req.open("GET", "data:" + (contentType || "application/xml") + + ";charset=utf-8," + encodeURIComponent(str), false); + if (req.overrideMimeType) { + req.overrideMimeType(contentType); + } + req.send(null); + return req.responseXML; + } + } + } +}; + +/** + * Construct a component. + */ +function ClientComponent(name) { + this.name = name; +} + +/** + * Public API. + */ + +/** + * Return a component. + */ +function component(name) { + return new ClientComponent(name); +} + +/** + * Return a reference proxy. + */ +function reference(comp, name) { + return new HTTPBindingClient(comp.name, name); +}; + +/** + * Add proxy functions to a reference proxy. + */ +function defun(ref) { + function defapply(name) { + return function() { + var args = new Array(); + args[0] = name; + for (i = 0, n = arguments.length; i < n; i++) + args[i + 1] = arguments[i]; + return this.apply.apply(this, args); + }; + } + + for (f = 1; f < arguments.length; f++) { + var fn = arguments[f]; + ref[fn]= defapply(fn); + } + return ref; +}; + diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/httpd-test b/sandbox/sebastien/cpp/apr-2/modules/server/httpd-test new file mode 100755 index 0000000000..050becdb24 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/httpd-test @@ -0,0 +1,78 @@ +#!/bin/sh + +# 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. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +../http/httpd-conf tmp localhost 8090 htdocs +./server-conf tmp +./scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/test/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/test/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/test/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/impl-test.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/impl-test.cpp new file mode 100644 index 0000000000..1bd0843745 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/impl-test.cpp @@ -0,0 +1,76 @@ +/* + * 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$ */ + +/** + * Test component implementation. + */ + +#include "string.hpp" + +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" + +namespace tuscany { +namespace server { + +const failable<value> get(unused const list<value>& params) { + return value(mklist<value>("text/html", mklist<value>("Hey"))); +} + +const failable<value> post(unused const list<value>& params) { + return value(mklist<value>(string("123456789"))); +} + +const failable<value> put(unused const list<value>& params) { + return value(true); +} + +const failable<value> del(unused const list<value>& params) { + return value(true); +} + +const failable<value> echo(const list<value>& params) { + return value(car(params)); +} + +} +} + +extern "C" { + +const tuscany::value apply(const tuscany::list<tuscany::value>& params) { + const tuscany::value func(car(params)); + if (func == "get") + return tuscany::server::get(cdr(params)); + if (func == "post") + return tuscany::server::post(cdr(params)); + if (func == "put") + return tuscany::server::put(cdr(params)); + if (func == "delete") + return tuscany::server::del(cdr(params)); + if (func == "echo") + return tuscany::server::echo(cdr(params)); + return tuscany::mkfailure<tuscany::value>(); +} + +} diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/mod-cpp.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-cpp.hpp new file mode 100644 index 0000000000..8cae35e493 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-cpp.hpp @@ -0,0 +1,104 @@ +/* + * 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$ */ + +#ifndef tuscany_modcpp_hpp +#define tuscany_modcpp_hpp + +/** + * Evaluation functions used by mod-eval to evaluate C++ + * component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" + +#include "function.hpp" +#include "list.hpp" +#include "element.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "dynlib.hpp" +#include "../scheme/driver.hpp" + +namespace tuscany { +namespace server { +namespace modcpp { + +/** + * Apply a C++ component implementation function. + */ +const list<value> failableResult(const value& func, const list<value>& v) { + if (isNil(cdr(v))) + return v; + + // Report a failure with an empty reason as 'function not supported' + // Except for the start, and stop functions, which are optional + const value reason = cadr(v); + if (length(reason) == 0) { + if (func == "start" || func == "stop") + return mklist<value>(lambda<value(const list<value>&)>()); + return mklist<value>(value(), string("Function not supported: ") + func); + } + return v; +} + +struct applyImplementation { + const lib ilib; + const lambda<value(const list<value>&)> impl; + const list<value> px; + + applyImplementation(const lib& ilib, const lambda<value(const list<value>&)>& impl, const list<value>& px) : ilib(ilib), impl(impl), px(px) { + } + + const value operator()(const list<value>& params) const { + debug(params, "modeval::cpp::applyImplementation::input"); + + // Apply the component implementation function + const value val = failableResult(car(params), impl(append(params, px))); + + debug(val, "modeval::cpp::applyImplementation::result"); + return val; + } +}; + +/** + * Evaluate a C++ component implementation and convert it to + * an applicable lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) { + + // Configure the implementation's lambda function + const value ipath(attributeValue("path", impl)); + const value iname(attributeValue("library", impl)); + const string fpath(isNil(ipath)? path + iname : path + ipath + "/" + iname); + const lib ilib(*(new (gc_new<lib>()) lib(fpath + dynlibExt))); + const failable<lambda<value(const list<value>&)> > evalf(dynlambda<value(const list<value>&)>("apply", ilib)); + if (!hasContent(evalf)) + return evalf; + const lambda<value(const list<value>&)> l(applyImplementation(ilib, content(evalf), px)); + return l; +} + +} +} +} + +#endif /* tuscany_modcpp_hpp */ diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.cpp new file mode 100644 index 0000000000..a94ccf5bbe --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.cpp @@ -0,0 +1,62 @@ +/* + * 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 eval C++ and Scheme component implementations. + */ + +#include "string.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "mod-eval.hpp" +#include "mod-scheme.hpp" +#include "mod-cpp.hpp" + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Apply a lifecycle start or restart event. + */ +const value applyLifecycle(unused const list<value>& params) { + // Return a nil function as we don't need to handle any subsequent events + return failable<value>(lambda<value(const list<value>&)>()); +} + +/** + * Evaluate a Scheme or C++ component implementation and convert it to an + * applicable lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, unused const lambda<value(const list<value>&)>& lifecycle) { + const string itype(elementName(impl)); + if (contains(itype, ".scheme")) + return modscheme::evalImplementation(path, impl, px); + if (contains(itype, ".cpp")) + return modcpp::evalImplementation(path, impl, px); + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype); +} + +} +} +} diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.hpp new file mode 100644 index 0000000000..b8d3147459 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.hpp @@ -0,0 +1,896 @@ +/* + * 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$ */ + +#ifndef tuscany_modeval_hpp +#define tuscany_modeval_hpp + +/** + * HTTPD module used to eval component implementations. + */ + +#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 "../atom/atom.hpp" +#include "../json/json.hpp" +#include "../scdl/scdl.hpp" +#include "../http/http.hpp" +#include "../http/httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; +} + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s), wiringServerName(""), contributionPath(""), compositeName(""), virtualHostContributionPath(""), virtualHostCompositeName(""), ca(""), cert(""), key("") { + } + + const gc_pool p; + server_rec* server; + lambda<value(const list<value>&)> lifecycle; + string wiringServerName; + string contributionPath; + string compositeName; + string virtualHostContributionPath; + string virtualHostCompositeName; + string ca; + string cert; + string key; + list<value> implementations; + list<value> implTree; +}; + +/** + * 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 != ""; +} + +/** + * Convert a result represented as a content + failure pair to a + * failable monad. + */ +const failable<value> failableResult(const list<value>& v) { + if (isNil(cdr(v))) + return car(v); + return mkfailure<value>(string(cadr(v))); +} + +/** + * Handle an HTTP GET. + */ +const failable<int> get(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::get::uri"); + + // Inspect the query string + const list<list<value> > args = httpd::queryArgs(r); + const list<value> ia = assoc(value("id"), args); + const list<value> ma = assoc(value("method"), args); + + // Evaluate a JSON-RPC request and return a JSON result + if (!isNil(ia) && !isNil(ma)) { + + // Extract the request id, method and params + const value id = cadr(ia); + const value func = c_str(json::funcName(string(cadr(ma)))); + + // Apply the requested function + const failable<value> val = failableResult(impl(cons(func, json::queryParams(args)))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + + // Return JSON result + js::JSContext cx; + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r); + } + + // Evaluate the GET expression + const list<value> path(pathValues(r->uri)); + const failable<value> val = failableResult(impl(cons<value>("get", mklist<value>(cddr(path))))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + const value c = content(val); + + // Write content-type / content-list pair + if (isString(car<value>(c)) && isList(cadr<value>(c))) + return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r); + + // Write ATOM feed or entry + if (isString(car<value>(c)) && isString(cadr<value>(c))) { + if (isNil(cddr(path))) + return httpd::writeResult(atom::writeATOMFeed(atom::feedValuesToElements(c)), "application/atom+xml;type=feed", r); + else + return httpd::writeResult(atom::writeATOMEntry(atom::entryValuesToElements(c)), "application/atom+xml;type=entry", r); + } + + // Write JSON value + js::JSContext cx; + return httpd::writeResult(json::writeJSON(valuesToElements(c), cx), "application/json", r); +} + +/** + * Handle an HTTP POST. + */ +const failable<int> post(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::post::url"); + + // Evaluate a JSON-RPC request and return a JSON result + const string ct = httpd::contentType(r); + if (contains(ct, "application/json-rpc") || contains(ct, "text/plain") || contains(ct, "application/x-www-form-urlencoded")) { + + // Read the JSON request + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + const list<string> ls = httpd::read(r); + debug(ls, "modeval::post::input"); + js::JSContext cx; + const list<value> json = elementsToValues(content(json::readJSON(ls, cx))); + const list<list<value> > args = httpd::postArgs(json); + + // Extract the request id, method and params + const value id = cadr(assoc(value("id"), args)); + const value func = c_str(json::funcName(cadr(assoc(value("method"), args)))); + const list<value> params = (list<value>)cadr(assoc(value("params"), args)); + + // Evaluate the request expression + const failable<value> val = failableResult(impl(cons<value>(func, params))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + + // Return JSON result + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", 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 list<value> path(pathValues(r->uri)); + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + const list<string> ls = httpd::read(r); + debug(ls, "modeval::post::input"); + const value entry = atom::entryValue(content(atom::readATOMEntry(ls))); + + // Evaluate the POST expression + const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(path), entry)))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + + // Return the created resource location + debug(content(val), "modeval::post::location"); + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(httpd::url(r->uri, content(val), r)))); + r->status = HTTP_CREATED; + return OK; + } + + // Unknown content type, wrap the HTTP request struct in a value and pass it to + // the component implementation function + const failable<value> val = failableResult(impl(cons<value>("handle", mklist<value>(httpd::requestValue(r))))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + return (int)content(val); +} + +/** + * Handle an HTTP PUT. + */ +const failable<int> put(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::put::url"); + + // Read the ATOM entry + const list<value> path(pathValues(r->uri)); + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + const list<string> ls = httpd::read(r); + debug(ls, "modeval::put::input"); + const value entry = atom::entryValue(content(atom::readATOMEntry(ls))); + + // Evaluate the PUT expression and update the corresponding resource + const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(path), entry)))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Handle an HTTP DELETE. + */ +const failable<int> del(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::delete::url"); + + // Evaluate an ATOM delete request + const list<value> path(pathValues(r->uri)); + const failable<value> val = failableResult(impl(cons<value>("delete", mklist<value>(cddr(path))))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Translate a component request. + */ +int translate(request_rec *r) { + if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE) + return DECLINED; + if (strncmp(r->uri, "/components/", 12) != 0) + return DECLINED; + r->handler = "mod_tuscany_eval"; + return OK; +} + +/** + * Make an HTTP proxy lambda. + */ +const value mkhttpProxy(const ServerConf& sc, const string& ref, const string& base) { + const string uri = base + ref; + debug(uri, "modeval::mkhttpProxy::uri"); + return lambda<value(const list<value>&)>(http::proxy(uri, sc.ca, sc.cert, sc.key, sc.p)); +} + +/** + * Return a component implementation proxy lambda. + */ +class implProxy { +public: + implProxy(const ServerConf& sc, const value& name) : sc(sc), name(name) { + } + + const value operator()(const list<value>& params) const { + debug(params, "modeval::implProxy::input"); + + // Lookup the component implementation + const list<value> impl(assoctree<value>(name, sc.implTree)); + if (isNil(impl)) + return mkfailure<value>(string("Couldn't find component implementation: ") + name); + + // Call its lambda function + const lambda<value(const list<value>&)> l(cadr<value>(impl)); + const value func = c_str(car(params)); + const failable<value> val = failableResult(l(cons(func, cdr(params)))); + debug(val, "modeval::implProxy::result"); + if (!hasContent(val)) + return value(); + return content(val); + } + +private: + const ServerConf& sc; + const value name; +}; + +const value mkimplProxy(const ServerConf& sc, const value& name) { + debug(name, "modeval::implProxy::impl"); + return lambda<value(const list<value>&)>(implProxy(sc, name)); +} + +/** + * Convert a list of component references to a list of proxy lambdas. + */ +const value mkrefProxy(const ServerConf& sc, const value& ref, const string& base) { + const value target = scdl::target(ref); + debug(ref, "modeval::mkrefProxy::ref"); + debug(target, "modeval::mkrefProxy::target"); + + // Use an HTTP proxy or an internal proxy to the component implementation + if (isNil(target) || httpd::isAbsolute(target)) + return mkhttpProxy(sc, scdl::name(ref), base); + return mkimplProxy(sc, car(pathValues(target))); +} + +const list<value> refProxies(const ServerConf& sc, const list<value>& refs, const string& base) { + if (isNil(refs)) + return refs; + return cons(mkrefProxy(sc, car(refs), base), refProxies(sc, cdr(refs), base)); +} + +/** + * Store current HTTP request for access from property lambda functions. + */ +#ifdef WANT_THREADS +__thread +#endif +request_rec* currentRequest = NULL; + +class ScopedRequest { +public: + ScopedRequest(request_rec* r) { + currentRequest = r; + } + + ~ScopedRequest() { + currentRequest = NULL; + } +}; + +/** + * 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. + */ +struct propProxy { + const value v; + propProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + return v; + } +}; + +struct hostPropProxy { + const value v; + hostPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + return httpd::hostName(currentRequest, v); + } +}; + +struct envPropProxy { + const string name; + const value v; + envPropProxy(const string& name, const value& v) : name(name), v(v) { + } + const value operator()(unused const list<value>& params) const { + const char* env = apr_table_get(currentRequest->subprocess_env, c_str(name)); + if (env == NULL || *env == '\0') + return v; + return string(env); + } +}; + +struct userPropProxy { + const value v; + userPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest->user == NULL) + return v; + return string(currentRequest->user); + } +}; + +const value mkpropProxy(const value& prop) { + if (scdl::name(prop) == "host") + return lambda<value(const list<value>&)>(hostPropProxy(elementValue(prop))); + if (scdl::name(prop) == "user") + return lambda<value(const list<value>&)>(userPropProxy(elementValue(prop))); + if (scdl::name(prop) == "realm") + return lambda<value(const list<value>&)>(envPropProxy("REALM", elementValue(prop))); + if (scdl::name(prop) == "email") + return lambda<value(const list<value>&)>(envPropProxy("EMAIL", elementValue(prop))); + if (scdl::name(prop) == "nickname") + return lambda<value(const list<value>&)>(envPropProxy("NICKNAME", elementValue(prop))); + if (scdl::name(prop) == "fullname") + return lambda<value(const list<value>&)>(envPropProxy("FULLNAME", elementValue(prop))); + if (scdl::name(prop) == "firstname") + return lambda<value(const list<value>&)>(envPropProxy("FIRSTNAME", elementValue(prop))); + if (scdl::name(prop) == "lastname") + return lambda<value(const list<value>&)>(envPropProxy("LASTNAME", elementValue(prop))); + return lambda<value(const list<value>&)>(propProxy(elementValue(prop))); +} + +const list<value> propProxies(const list<value>& props) { + if (isNil(props)) + return props; + return cons(mkpropProxy(car(props)), propProxies(cdr(props))); +} + +/** + * Evaluate a component and convert it to an applicable lambda function. + */ +struct implementationFailure { + const value reason; + implementationFailure(const value& r) : reason(r) { + } + + // Default implementation representing an implementation that + // couldn't be evaluated. Report the evaluation error on all + // subsequent calls expect start/stop. + const value operator()(unused const list<value>& params) const { + const value func = car(params); + if (func == "start" || func == "stop") + return mklist<value>(lambda<value(const list<value>&)>()); + return mklist<value>(value(), reason); + } +}; + +const value evalComponent(ServerConf& sc, const value& comp) { + extern const failable<lambda<value(const list<value>&)> > evalImplementation(const string& cpath, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle); + + const value impl = scdl::implementation(comp); + debug(comp, "modeval::evalComponent::comp"); + debug(impl, "modeval::evalComponent::impl"); + + // Convert component references to configured proxy lambdas + ostringstream base; + base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/"; + const list<value> rpx(refProxies(sc, scdl::references(comp), str(base))); + + // Convert component proxies 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<lambda<value(const list<value>&)> > cimpl(evalImplementation(sc.contributionPath, impl, append(rpx, ppx), sc.lifecycle)); + if (!hasContent(cimpl)) + return lambda<value(const list<value>&)>(implementationFailure(reason(cimpl))); + return content(cimpl); +} + +/** + * Return a list of component-name + configured-implementation pairs. + */ +const list<value> componentToImplementationAssoc(ServerConf& sc, const list<value>& c) { + if (isNil(c)) + return c; + return cons<value>(mklist<value>(scdl::name(car(c)), evalComponent(sc, car(c))), componentToImplementationAssoc(sc, cdr(c))); +} + +/** + * 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))); +} + +/** + * 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 (isNil(impls)) + return list<value>(); + + // Evaluate lifecycle expression against a component implementation lambda + const lambda<value(const list<value>&)> l = cadr<value>(car(impls)); + const failable<value> r = failableResult(l(expr)); + if (!hasContent(r)) + return mkfailure<list<value> >(reason(r)); + const lambda<value(const list<value>&)> rl = content(r); + + // Use the returned lambda function, if any, from now on + const lambda<value(const list<value>&)> al = isNil(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)); +} + +/** + * Configure the components declared in the deployed composite. + */ +const failable<bool> confComponents(ServerConf& sc) { + if (!hasCompositeConf(sc)) + return false; + debug(sc.contributionPath, "modeval::confComponents::contributionPath"); + debug(sc.compositeName, "modeval::confComponents::compositeName"); + if (sc.ca != "") debug(sc.ca, "modeval::confComponents::sslCA"); + if (sc.cert != "") debug(sc.cert, "modeval::confComponents::sslCert"); + if (sc.key != "") debug(sc.key, "modeval::confComponents::sslKey"); + + // Read the components and get their implementation lambda functions + const failable<list<value> > comps = readComponents(sc.contributionPath + sc.compositeName); + if (!hasContent(comps)) + return mkfailure<bool>(reason(comps)); + sc.implementations = componentToImplementationAssoc(sc, content(comps)); + debug(sc.implementations, "modeval::confComponents::implementations"); + return true; +} + +/** + * Start the components declared in the deployed composite. + */ +const failable<bool> startComponents(ServerConf& sc) { + + // Start the components and record the returned implementation lambda functions + debug(sc.implementations, "modeval::startComponents::start"); + const failable<list<value> > impls = applyLifecycleExpr(sc.implementations, mklist<value>("start")); + if (!hasContent(impls)) + return mkfailure<bool>(reason(impls)); + sc.implementations = content(impls); + debug(sc.implementations, "modeval::startComponents::implementations"); + return true; +} + +/** + * Virtual host scoped server configuration. + */ +class VirtualHostConf { +public: + VirtualHostConf(const gc_pool& p, const ServerConf& sc) : sc(sc), vsc(pool(p), sc.server) { + vsc.virtualHostContributionPath = sc.virtualHostContributionPath; + vsc.virtualHostCompositeName = sc.virtualHostCompositeName; + vsc.ca = sc.ca; + vsc.cert = sc.cert; + vsc.key = sc.key; + } + + ~VirtualHostConf() { + extern const failable<bool> virtualHostCleanup(const ServerConf& vsc, const ServerConf& sc); + virtualHostCleanup(vsc, sc); + } + + const ServerConf& sc; + ServerConf vsc; +}; + +/** + * Configure and start the components deployed in a virtual host. + */ +const failable<bool> virtualHostConfig(ServerConf& vsc, const ServerConf& sc, request_rec* r) { + + // Determine the server name and wiring server name + debug(httpd::serverName(vsc.server), "modeval::virtualHostConfig::serverName"); + debug(httpd::serverName(r), "modeval::virtualHostConfig::virtualHostName"); + vsc.wiringServerName = httpd::serverName(r); + debug(vsc.wiringServerName, "modeval::virtualHostConfig::wiringServerName"); + debug(vsc.virtualHostContributionPath, "modwiring::virtualHostConfig::virtualHostContributionPath"); + + // Resolve the configured virtual contribution under + // the virtual host's SCA contribution root + vsc.contributionPath = vsc.virtualHostContributionPath + httpd::subdomain(httpd::hostName(r)) + "/"; + vsc.compositeName = vsc.virtualHostCompositeName; + + // Chdir to the virtual host's contribution + if (chdir(c_str(sc.contributionPath)) != 0) + return mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contributionPath); + + // Configure the deployed components + const failable<bool> cr = confComponents(vsc); + if (!hasContent(cr)) + return cr; + + // Start the configured components + const failable<bool> sr = startComponents(vsc); + if (!hasContent(sr)) + return sr; + + // Store the implementation lambda functions (from both the virtual host and the + // main server) in a tree for fast retrieval + vsc.implTree = mkbtree(sort(append(vsc.implementations, sc.implementations))); + return true; +} + +/** + * Cleanup a virtual host. + */ +const failable<bool> virtualHostCleanup(const ServerConf& vsc, const ServerConf& sc) { + if (!hasCompositeConf(vsc)) + return true; + debug("modeval::virtualHostCleanup"); + + // Stop the component implementations + applyLifecycleExpr(vsc.implementations, mklist<value>("stop")); + + // Chdir back to the main server's contribution + if (chdir(c_str(sc.contributionPath)) != 0) + return mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contributionPath); + return true; +} + +/** + * HTTP request handler. + */ +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_eval")) + return DECLINED; + + gc_scoped_pool pool(r->pool); + ScopedRequest sr(r); + httpdDebugRequest(r, "modeval::handler::input"); + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); + + // Process dynamic virtual host configuration, if any + VirtualHostConf vhc(gc_pool(r->pool), sc); + const bool usevh = hasVirtualCompositeConf(vhc.vsc) && httpd::isVirtualHostRequest(sc.server, r); + if (usevh) { + const failable<bool> cr = virtualHostConfig(vhc.vsc, sc, r); + if (!hasContent(cr)) + return httpd::reportStatus(mkfailure<int>(reason(cr))); + } + + // Get the component implementation lambda + const list<value> path(pathValues(r->uri)); + const list<value> impl(assoctree<value>(cadr(path), usevh? vhc.vsc.implTree : sc.implTree)); + if (isNil(impl)) + return httpd::reportStatus(mkfailure<int>(string("Couldn't find component implementation: ") + cadr(path))); + + // Handle HTTP method + const lambda<value(const list<value>&)> l(cadr<value>(impl)); + if (r->header_only) + return OK; + if(r->method_number == M_GET) + return httpd::reportStatus(get(r, l)); + if(r->method_number == M_POST) + return httpd::reportStatus(post(r, l)); + if(r->method_number == M_PUT) + return httpd::reportStatus(put(r, l)); + if(r->method_number == M_DELETE) + return httpd::reportStatus(del(r, l)); + return HTTP_NOT_IMPLEMENTED; +} + +/** + * Cleanup callback, called when the server is stopped or restarted. + */ +apr_status_t serverCleanup(void* v) { + gc_pool pool; + ServerConf& sc = *(ServerConf*)v; + debug("modeval::serverCleanup"); + + // Stop the component implementations + applyLifecycleExpr(sc.implementations, mklist<value>("stop")); + + // Call the module lifecycle function + if (isNil(sc.lifecycle)) + return APR_SUCCESS; + debug("modeval::serverCleanup::stop"); + sc.lifecycle(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* s) { + if (s == NULL) + return OK; + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval); + debug(httpd::serverName(s), "modeval::postConfigMerge::serverName"); + if (sc.wiringServerName == "") + sc.wiringServerName = mainsc.wiringServerName != ""? mainsc.wiringServerName : httpd::serverName(s); + debug(sc.wiringServerName, "modeval::postConfigMerge::wiringServerName"); + sc.contributionPath = mainsc.contributionPath; + sc.compositeName = mainsc.compositeName; + sc.virtualHostContributionPath = mainsc.virtualHostContributionPath; + sc.virtualHostCompositeName = mainsc.virtualHostCompositeName; + if (sc.ca == "") sc.ca = mainsc.ca; + if (sc.cert == "") sc.cert = mainsc.cert; + if (sc.key == "") sc.key = mainsc.key; + sc.implementations = mainsc.implementations; + sc.implTree = mainsc.implTree; + 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>&); + + gc_scoped_pool pool(p); + + // Get the server configuration and determine the wiring server name + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval); + debug(httpd::serverName(s), "modeval::postConfig::serverName"); + if (sc.wiringServerName == "") sc.wiringServerName = httpd::serverName(s); + debug(sc.wiringServerName, "modeval::postConfig::wiringServerName"); + + // 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.contributionPath)) != 0) { + mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contributionPath); + return -1; + } + + debug("modeval::postConfig::start"); + const failable<value> r = failableResult(applyLifecycle(mklist<value>("start"))); + if (!hasContent(r)) + return -1; + 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; + sc.lifecycle = content(r); + } + + // Configure the deployed components + const failable<bool> res = confComponents(sc); + if (!hasContent(res)) { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + return -1; + } + + // 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); +} + +/** + * Child process initialization. + */ +void childInit(apr_pool_t* p, server_rec* s) { + gc_scoped_pool pool(p); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_eval); + if(psc == NULL) { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } + ServerConf& sc = *psc; + + // Start the components in the child process + const failable<bool> res = startComponents(sc); + if (!hasContent(res)) { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } + + // Store the implementation lambda functions in a tree for fast retrieval + sc.implTree = mkbtree(sort(sc.implementations)); + + // 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* confWiringServerName(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.wiringServerName = arg; + return NULL; +} +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_eval); + 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_eval); + 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_eval); + 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_eval); + sc.virtualHostCompositeName = arg; + return NULL; +} +const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.ca = arg; + return NULL; +} +const char* confCertFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.cert = arg; + return NULL; +} +const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.key = arg; + return NULL; +} +const char* confEnv(unused cmd_parms *cmd, unused void *c, const char *name, const char *value) { + gc_scoped_pool pool(cmd->pool); + setenv(name, value != NULL? value : "", 1); + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("SCAWiringServerName", (const char*(*)())confWiringServerName, NULL, RSRC_CONF, "SCA wiring server name"), + 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 composite location"), + AP_INIT_TAKE12("SCASetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"), + AP_INIT_TAKE1("SCAWiringSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "SCA wiring SSL CA certificate file"), + 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"), + {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_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 diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/mod-scheme.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-scheme.hpp new file mode 100644 index 0000000000..43d7bf4041 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-scheme.hpp @@ -0,0 +1,91 @@ +/* + * 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$ */ + +#ifndef tuscany_modscheme_hpp +#define tuscany_modscheme_hpp + +/** + * Evaluation functions used by mod-eval to evaluate Scheme + * component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../scheme/eval.hpp" + +namespace tuscany { +namespace server { +namespace modscheme { + +/** + * Convert proxy lambdas to evaluator primitive procedures. + */ +const list<value> primitiveProcedures(const list<value>& l) { + if (isNil(l)) + return l; + return cons<value>(mklist<value>(scheme::primitiveSymbol, car(l)), primitiveProcedures(cdr(l))); +} + +/** + * Apply a Scheme component implementation function. + */ +struct applyImplementation { + const value impl; + const list<value> px; + + applyImplementation(const value& impl, const list<value>& px) : impl(impl), px(scheme::quotedParameters(primitiveProcedures(px))) { + } + + const value operator()(const list<value>& params) const { + const value expr = cons<value>(car(params), append(scheme::quotedParameters(cdr(params)), px)); + debug(expr, "modeval::scheme::applyImplementation::input"); + scheme::Env env = scheme::setupEnvironment(); + const value res = scheme::evalScript(expr, impl, env); + const value val = isNil(res)? mklist<value>(value(), string("Could not evaluate expression")) : mklist<value>(res); + debug(val, "modeval::scheme::applyImplementation::result"); + return val; + } +}; + +/** + * Evaluate a Scheme component implementation and convert it to an + * applicable lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) { + const string fpath(path + attributeValue("script", impl)); + ifstream is(fpath); + if (fail(is)) + return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath); + const value script = scheme::readScript(is); + if (isNil(script)) + return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath); + return lambda<value(const list<value>&)>(applyImplementation(script, px)); +} + +} +} +} + +#endif /* tuscany_modscheme_hpp */ diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/mod-wiring.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-wiring.cpp new file mode 100644 index 0000000000..0ab61f5af8 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-wiring.cpp @@ -0,0 +1,472 @@ +/* + * 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 +}; + +} diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/scheme-conf b/sandbox/sebastien/cpp/apr-2/modules/server/scheme-conf new file mode 100755 index 0000000000..cd3c82b280 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/scheme-conf @@ -0,0 +1,30 @@ +#!/bin/sh + +# 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. + +# Generate a Scheme server conf +here=`readlink -f $0`; here=`dirname $here` +mkdir -p $1 +root=`readlink -f $1` + +cat >>$root/conf/modules.conf <<EOF +# Generated by: scheme-conf $* +# Support for Scheme SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_eval.so + +EOF diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/server-conf b/sandbox/sebastien/cpp/apr-2/modules/server/server-conf new file mode 100755 index 0000000000..742d48d614 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/server-conf @@ -0,0 +1,103 @@ +#!/bin/sh + +# 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. + +# Generate a server conf +here=`readlink -f $0`; here=`dirname $here` +mkdir -p $1 +root=`readlink -f $1` + +jsprefix=`readlink -f $here/../js` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` +port=`echo $conf | awk '{ print $7 }' | awk -F "/" '{ print $1 }'` +pport=`echo $conf | awk '{ print $7 }' | awk -F "/" '{ print $2 }'` +if [ "$pport" = "" ]; then + pport=$port +fi +servername="http://$host:$pport" + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$sslconf" != "" ]; then + sslport=`echo $sslconf | awk '{ print $6 }' | awk -F "/" '{ print $1 }'` + sslpport=`echo $sslconf | awk '{ print $6 }' | awk -F "/" '{ print $2 }'` + if [ "$sslpport" = "" ]; then + sslpport=$sslport + fi + servername="https://$host:$sslpport" +fi + +cat >>$root/conf/modules.conf <<EOF +# Generated by: server-conf $* +# Support for SCA component wiring +LoadModule mod_tuscany_wiring $here/libmod_tuscany_wiring.so + +EOF + +cat >>$root/conf/httpd.conf <<EOF +# Generated by: server-conf $* +# Route all wiring through the configured server name +SCAWiringServerName $servername + +# Serve JavaScript client scripts +Alias /component.js $jsprefix/htdocs/component.js +Alias /util.js $jsprefix/htdocs/util.js +Alias /elemutil.js $jsprefix/htdocs/elemutil.js +Alias /xmlutil.js $jsprefix/htdocs/xmlutil.js +Alias /atomutil.js $jsprefix/htdocs/atomutil.js +Alias /ui.js $jsprefix/htdocs/ui.js +Alias /ui.css $jsprefix/htdocs/ui.css +Alias /scdl.js $jsprefix/htdocs/scdl.js +Alias /graph.js $jsprefix/htdocs/graph.js + +<Location /component.js> +AuthType None +Require all granted +</Location> +<Location /scdl.js> +AuthType None +Require all granted +</Location> +<Location /util.js> +AuthType None +Require all granted +</Location> +<Location /ui.js> +AuthType None +Require all granted +</Location> +<Location /ui.css> +AuthType None +Require all granted +</Location> + +EOF + +ssl=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$ssl" != "" ]; then + cat >>$root/conf/httpd.conf <<EOF +# Configure SSL certificates +SCAWiringSSLCACertificateFile "$root/cert/ca.crt" +SCAWiringSSLCertificateFile "$root/cert/server.crt" +SCAWiringSSLCertificateKeyFile "$root/cert/server.key" + +EOF + +fi + diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/server-test b/sandbox/sebastien/cpp/apr-2/modules/server/server-test new file mode 100755 index 0000000000..e53c7f5ef1 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/server-test @@ -0,0 +1,39 @@ +#!/bin/sh + +# 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. + +# Setup +../http/httpd-conf tmp localhost 8090 htdocs +./server-conf tmp +./scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test +./client-test 2>/dev/null +rc=$? + +# Cleanup +../http/httpd-stop tmp +sleep 2 +return $rc diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/server-test.scm b/sandbox/sebastien/cpp/apr-2/modules/server/server-test.scm new file mode 100644 index 0000000000..c23adb7f51 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/server-test.scm @@ -0,0 +1,44 @@ +; 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. + +; JSON-RPC test case + +(define (echo x) x) + +; ATOMPub test case + +(define (get id) + (if (nul id) + '("Sample Feed" "123456789" + ("Item" "111" ((name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) + ("Item" "222" ((name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55))) + ("Item" "333" ((name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55)))) + + (list "Item" (car id) '((name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))) +) + +(define (post collection item) + '("123456789") +) + +(define (put id item) + true +) + +(define (delete id) + true +) diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/wiring-test b/sandbox/sebastien/cpp/apr-2/modules/server/wiring-test new file mode 100755 index 0000000000..e791ec555b --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/server/wiring-test @@ -0,0 +1,78 @@ +#!/bin/sh + +# 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. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +../http/httpd-conf tmp localhost 8090 htdocs +./server-conf tmp +./scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/test/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/test/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/test/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc |