From d6451b81703c809abcd0f51e74abdba7c732b513 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Sat, 14 Aug 2010 18:46:26 +0000 Subject: Some refactoring of the HTTP support, tunnel Memcached requests over HTTPS and add HTTPS config to store-cluster sample. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@985561 13f79535-47bb-0310-9956-ffa450edef68 --- sca-cpp/trunk/modules/http/Makefile.am | 15 +- sca-cpp/trunk/modules/http/curl-connect.cpp | 98 ++++ sca-cpp/trunk/modules/http/curl-get.cpp | 13 +- sca-cpp/trunk/modules/http/curl-test.cpp | 2 +- sca-cpp/trunk/modules/http/curl.hpp | 456 ----------------- sca-cpp/trunk/modules/http/http.hpp | 625 +++++++++++++++++++++++ sca-cpp/trunk/modules/http/httpd-addr | 54 ++ sca-cpp/trunk/modules/http/httpd-auth-conf | 2 + sca-cpp/trunk/modules/http/httpd-conf | 19 +- sca-cpp/trunk/modules/http/httpd-ssl-conf | 42 +- sca-cpp/trunk/modules/http/httpd.hpp | 169 +++--- sca-cpp/trunk/modules/http/mod-ssltunnel.cpp | 364 +++++++++++++ sca-cpp/trunk/modules/http/proxy-member-conf | 3 +- sca-cpp/trunk/modules/http/proxy-ssl-conf | 4 +- sca-cpp/trunk/modules/http/proxy-ssl-member-conf | 3 +- sca-cpp/trunk/modules/http/ssl-ca-conf | 32 +- sca-cpp/trunk/modules/http/ssl-cert-conf | 23 +- sca-cpp/trunk/modules/http/ssl-cert-find | 26 + sca-cpp/trunk/modules/http/ssl-ls | 25 - sca-cpp/trunk/modules/http/tunnel-ssl-conf | 56 ++ sca-cpp/trunk/modules/http/vhost-conf | 15 +- sca-cpp/trunk/modules/http/vhost-ssl-conf | 16 +- sca-cpp/trunk/modules/json/json.hpp | 13 + sca-cpp/trunk/modules/server/client-test.hpp | 2 +- sca-cpp/trunk/modules/server/mod-eval.hpp | 9 +- sca-cpp/trunk/modules/server/mod-wiring.cpp | 17 +- sca-cpp/trunk/modules/server/server-conf | 22 +- 27 files changed, 1455 insertions(+), 670 deletions(-) create mode 100644 sca-cpp/trunk/modules/http/curl-connect.cpp delete mode 100644 sca-cpp/trunk/modules/http/curl.hpp create mode 100644 sca-cpp/trunk/modules/http/http.hpp create mode 100755 sca-cpp/trunk/modules/http/httpd-addr create mode 100644 sca-cpp/trunk/modules/http/mod-ssltunnel.cpp create mode 100755 sca-cpp/trunk/modules/http/ssl-cert-find delete mode 100755 sca-cpp/trunk/modules/http/ssl-ls create mode 100755 sca-cpp/trunk/modules/http/tunnel-ssl-conf (limited to 'sca-cpp/trunk/modules') diff --git a/sca-cpp/trunk/modules/http/Makefile.am b/sca-cpp/trunk/modules/http/Makefile.am index 03f5c234f5..b17f774da2 100644 --- a/sca-cpp/trunk/modules/http/Makefile.am +++ b/sca-cpp/trunk/modules/http/Makefile.am @@ -20,7 +20,7 @@ INCLUDES = -I${HTTPD_INCLUDE} incl_HEADERS = *.hpp incldir = $(prefix)/include/modules/http -dist_mod_SCRIPTS = httpd-conf httpd-start httpd-stop httpd-restart ssl-ca-conf ssl-cert-conf httpd-ssl-conf httpd-auth-conf proxy-conf proxy-ssl-conf proxy-member-conf proxy-ssl-member-conf vhost-conf vhost-ssl-conf +dist_mod_SCRIPTS = httpd-conf httpd-start httpd-stop httpd-restart ssl-ca-conf ssl-cert-conf ssl-cert-find httpd-ssl-conf httpd-auth-conf proxy-conf proxy-ssl-conf proxy-member-conf proxy-ssl-member-conf vhost-conf vhost-ssl-conf tunnel-ssl-conf moddir=$(prefix)/modules/http curl_test_SOURCES = curl-test.cpp @@ -29,6 +29,17 @@ curl_test_LDFLAGS = -lxml2 -lcurl -lmozjs curl_get_SOURCES = curl-get.cpp curl_get_LDFLAGS = -lxml2 -lcurl -lmozjs +curl_connect_SOURCES = curl-connect.cpp +curl_connect_LDFLAGS = -lxml2 -lcurl -lmozjs + +mod_LTLIBRARIES = libmod_tuscany_ssltunnel.la +noinst_DATA = libmod_tuscany_ssltunnel.so + +libmod_tuscany_ssltunnel_la_SOURCES = mod-ssltunnel.cpp +libmod_tuscany_ssltunnel_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_ssltunnel.so: + ln -s .libs/libmod_tuscany_ssltunnel.so + mod_DATA = httpd.prefix httpd-apachectl.prefix httpd-modules.prefix curl.prefix nobase_dist_mod_DATA = conf/* @@ -44,6 +55,6 @@ curl.prefix: $(top_builddir)/config.status echo ${CURL_PREFIX} >curl.prefix dist_noinst_SCRIPTS = httpd-test http-test proxy-test -noinst_PROGRAMS = curl-test curl-get +noinst_PROGRAMS = curl-test curl-get curl-connect TESTS = httpd-test http-test proxy-test diff --git a/sca-cpp/trunk/modules/http/curl-connect.cpp b/sca-cpp/trunk/modules/http/curl-connect.cpp new file mode 100644 index 0000000000..8957fb01b0 --- /dev/null +++ b/sca-cpp/trunk/modules/http/curl-connect.cpp @@ -0,0 +1,98 @@ +/* + * 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$ */ + +/** + * HTTP connect command line test tool. + */ + +#include +#include +#include "stream.hpp" +#include "string.hpp" +#include "perf.hpp" +#include "http.hpp" + +namespace tuscany { +namespace http { + +const bool testConnect(const string& url, const string& ca = "", const string& cert = "", const string& key = "") { + gc_scoped_pool p; + + CURLSession cs(ca, cert, key); + const failable crc = connect(url, cs); + assert(hasContent(crc)); + + apr_pollset_t* pollset; + apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0); + assert(cprc == APR_SUCCESS); + apr_socket_t* csock = sock(0, p); + const apr_pollfd_t* cpollfd = pollfd(csock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p); + apr_pollset_add(pollset, cpollfd); + apr_socket_t* tsock = sock(cs); + const apr_pollfd_t* tpollfd = pollfd(tsock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p); + apr_pollset_add(pollset, tpollfd); + + const apr_pollfd_t* pollfds; + apr_int32_t pollcount; + for(;;) { + apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds); + assert(pollrc == APR_SUCCESS); + + for (; pollcount > 0; pollcount--, pollfds++) { + if (pollfds->rtnevents & APR_POLLIN) { + char data[8192]; + if (pollfds->desc.s == csock) { + const int rl = ::read(0, data, sizeof(data)); + if (rl == -1) + return false; + if (rl > 0) { + const failable src = http::send(data, rl, cs); + assert(hasContent(src)); + } + } + else { + const failable frl = http::recv(data, sizeof(data), cs); + assert(hasContent(frl)); + const int rl = content(frl); + if (rl == 0) + return true; + const int wl = ::write(0, data, rl); + assert(wl == rl); + } + continue; + } + assert(!(pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL))); + } + } + return true; +} + +} +} + +int main(unused const int argc, const char** argv) { + if (argc > 2) + tuscany::http::testConnect(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4])); + else + tuscany::http::testConnect(tuscany::string(argv[1])); + return 0; +} + diff --git a/sca-cpp/trunk/modules/http/curl-get.cpp b/sca-cpp/trunk/modules/http/curl-get.cpp index cbd693092a..4d5f1837fc 100644 --- a/sca-cpp/trunk/modules/http/curl-get.cpp +++ b/sca-cpp/trunk/modules/http/curl-get.cpp @@ -20,20 +20,20 @@ /* $Rev$ $Date$ */ /** - * HTTP client command line test tool. + * HTTP GET command line test tool. */ #include #include "stream.hpp" #include "string.hpp" #include "perf.hpp" -#include "curl.hpp" +#include "http.hpp" namespace tuscany { namespace http { -const bool testGet(const string& url) { - CURLSession ch; +const bool testGet(const string& url, const string& ca = "", const string& cert = "", const string& key = "") { + CURLSession ch(ca, cert, key); const failable val = get(url, ch); assert(hasContent(val)); cout << val << endl; @@ -44,7 +44,10 @@ const bool testGet(const string& url) { } int main(unused const int argc, const char** argv) { - tuscany::http::testGet(tuscany::string(argv[1])); + if (argc > 2) + tuscany::http::testGet(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4])); + else + tuscany::http::testGet(tuscany::string(argv[1])); return 0; } diff --git a/sca-cpp/trunk/modules/http/curl-test.cpp b/sca-cpp/trunk/modules/http/curl-test.cpp index c9b85ad962..1d1b07bff4 100644 --- a/sca-cpp/trunk/modules/http/curl-test.cpp +++ b/sca-cpp/trunk/modules/http/curl-test.cpp @@ -27,7 +27,7 @@ #include "stream.hpp" #include "string.hpp" #include "perf.hpp" -#include "curl.hpp" +#include "http.hpp" namespace tuscany { namespace http { diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp deleted file mode 100644 index 61f40eb475..0000000000 --- a/sca-cpp/trunk/modules/http/curl.hpp +++ /dev/null @@ -1,456 +0,0 @@ -/* - * 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_curl_hpp -#define tuscany_curl_hpp - -/** - * CURL HTTP client functions. - */ - -#include -#include -#include -#include -#include "string.hpp" -#include "gc.hpp" -#include "list.hpp" -#include "value.hpp" -#include "element.hpp" -#include "monad.hpp" -#include "parallel.hpp" -#include "../atom/atom.hpp" -#include "../rss/rss.hpp" -#include "../json/json.hpp" - -namespace tuscany { -namespace http { - -/** - * CURL library runtime, one per process. - */ -class CURLRuntime { -public: - CURLRuntime() { - curl_global_init(CURL_GLOBAL_ALL); - } -} curlRuntime; - -/** - * Represents a CURL session handle. - */ -class CURLSession { -public: - CURLSession(const string& ca = "", const string& cert = "", const string& key = "") : h(curl_easy_init()), owner(true), ca(ca), cert(cert), key(key) { - } - - CURLSession(const CURLSession& c) : h(c.h), owner(false), ca(c.ca), cert(c.cert), key(c.key) { - } - - ~CURLSession() { - if (!owner) - return; - if (h == NULL) - return; - curl_easy_cleanup(h); - } - -private: - CURL* h; - const bool owner; - - friend CURL* handle(const CURLSession& c); - -public: - const string ca; - const string cert; - const string key; -}; - -/** - * Returns the CURL handle used by a CURL session. - */ -CURL* handle(const CURLSession& c) { - return c.h; -} - -/** - * Context passed to the read callback function. - */ -class CURLReadContext { -public: - CURLReadContext(const list& ilist) : ilist(ilist) { - } - list ilist; -}; - -/** - * Called by CURL to read data to send. - */ -size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) { - CURLReadContext& rcx = *static_cast(data); - if (isNil(rcx.ilist)) - return 0; - const list f(fragment(rcx.ilist, size * nmemb)); - const string s = car(f); - rcx.ilist = cdr(f); - memcpy(ptr, c_str(s), length(s)); - return length(s); -} - -/** - * Context passed to CURL write callback function. - */ -template class CURLWriteContext { -public: - CURLWriteContext(const lambda& reduce, const R& accum) : reduce(reduce), accum(accum) { - } - const lambda reduce; - R accum; -}; - -/** - * Called by CURL to write received data. - */ -template size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) { - CURLWriteContext& wcx = *(static_cast*> (data)); - const size_t realsize = size * nmemb; - wcx.accum = wcx.reduce(string((const char*)ptr, realsize), wcx.accum); - return realsize; -} - -/** - * Apply an HTTP verb to a list containing a list of headers and a list of content, and - * a reduce function used to process the response. - */ -curl_slist* headers(curl_slist* cl, const list& h) { - if (isNil(h)) - return cl; - return headers(curl_slist_append(cl, c_str(string(car(h)))), cdr(h)); -} - -template const failable > apply(const list >& hdr, const lambda& reduce, const R& initial, const string& url, const string& verb, const CURLSession& cs) { - - // Init the curl session - CURL* ch = handle(cs); - curl_easy_reset(ch); - curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0"); - - //TODO use HTTP chunking, for now just convert request to a single string - ostringstream os; - write(cadr(hdr), os); - const string s = str(os); - const int sz = length(s); - - // Setup the read, header and write callbacks - CURLReadContext rcx(mklist(s)); - curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback); - curl_easy_setopt(ch, CURLOPT_READDATA, &rcx); - CURLWriteContext hcx(reduce, initial); - curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback)); - curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx); - CURLWriteContext wcx(reduce, initial); - curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback)); - curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx); - - // Setup protocol options - curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true); - curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, true); - curl_easy_setopt(ch, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); - - // Setup SSL options - if (cs.ca != "") { - debug(cs.ca, "http::apply::ca"); - curl_easy_setopt(ch, CURLOPT_CAINFO, c_str(cs.ca)); - curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, true); - curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 2); - } - if (cs.cert != "") { - debug(cs.cert, "http::apply::cert"); - curl_easy_setopt(ch, CURLOPT_SSLCERT, c_str(cs.cert)); - curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); - } - if (cs.key != "") { - debug(cs.key, "http::apply::key"); - curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key)); - curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM"); - } - - // Set the request headers - curl_slist* hl = headers(NULL, car(hdr)); - if (hl != NULL) - curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); - - // Apply the HTTP verb - curl_easy_setopt(ch, CURLOPT_URL, c_str(url)); - if (verb == "POST") { - curl_easy_setopt(ch, CURLOPT_POST, true); - curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz); - } else if (verb == "PUT") { - curl_easy_setopt(ch, CURLOPT_UPLOAD, true); - curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz); - } else if (verb == "DELETE") - curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE"); - const CURLcode rc = curl_easy_perform(ch); - - if (hl != NULL) - curl_slist_free_all(hl); - - // Return the HTTP return code or content - if (rc) - return mkfailure >(string(curl_easy_strerror(rc))); - long httprc; - curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); - if (httprc != 200 && httprc != 201) { - ostringstream es; - es << "HTTP code " << httprc; - return mkfailure >(str(es)); - } - return mklist(hcx.accum, wcx.accum); -} - -/** - * Evaluate an expression remotely, at the given URL. - */ -const failable evalExpr(const value& expr, const string& url, const CURLSession& ch) { - debug(url, "http::evalExpr::url"); - debug(expr, "http::evalExpr::input"); - - // Convert expression to a JSON-RPC request - json::JSONContext cx; - const failable > jsreq = json::jsonRequest(1, car(expr), cdr(expr), cx); - if (!hasContent(jsreq)) - return mkfailure(reason(jsreq)); - - // POST it to the URL - const list h = mklist("Content-Type: application/json-rpc"); - const failable > > res = apply >(mklist >(h, content(jsreq)), rcons, list(), url, "POST", ch); - if (!hasContent(res)) - return mkfailure(reason(res)); - - // Parse and return JSON-RPC result - const failable rval = json::jsonResultValue(cadr >(content(res)), cx); - if (!hasContent(rval)) - return mkfailure(reason(rval)); - debug(content(rval), "http::evalExpr::result"); - return content(rval); -} - -/** - * Find and return a header. - */ -const failable header(const char* prefix, const list& h) { - if (isNil(h)) - return mkfailure(string("Couldn't find header: ") + prefix); - const string s = car(h); - if (find(s, prefix) != 0) - return header(prefix, cdr(h)); - const string l(substr(s, length(prefix))); - return substr(l, 0, find_first_of(l, "\r\n")); -} - -/** - * Find and return a location header. - */ -const failable location(const list& h) { - return header("Location: ", h); -} - -/** - * Convert a location to an entry id. - */ -const failable entryId(const failable l) { - if (!hasContent(l)) - return mkfailure(reason(l)); - const string ls(content(l)); - return value(mklist(string(substr(ls, find_last(ls, '/') + 1)))); -} - -/** - * Find and return a content-type header. - */ -const failable contentType(const list& h) { - return header("Content-Type: ", h); -} - -/** - * HTTP GET, return the resource at the given URL. - */ -template const failable > get(const lambda& reduce, const R& initial, const string& url, const CURLSession& ch) { - debug(url, "http::get::url"); - const list > req = mklist(list(), list()); - return apply(req, reduce, initial, url, "GET", ch); -} - -/** - * HTTP GET, return a list of values representing the resource at the given URL. - */ -const failable getcontent(const string& url, const CURLSession& ch) { - debug(url, "http::get::url"); - - // Get the contents of the resource at the given URL - const failable > > res = get>(rcons, list(), url, ch); - if (!hasContent(res)) - return mkfailure(reason(res)); - const list ls(reverse(cadr(content(res)))); - - // Return the content as a list of values - const value val(mkvalues(ls)); - debug(val, "http::get::result"); - return val; -} - -/** - * HTTP GET, return a list of values representing the resource at the given URL. - */ -const failable get(const string& url, const CURLSession& ch) { - debug(url, "http::get::url"); - - // Get the contents of the resource at the given URL - const failable > > res = get >(rcons, list(), url, ch); - if (!hasContent(res)) - return mkfailure(reason(res)); - const list ls(reverse(cadr(content(res)))); - - const string ct(content(contentType(car(content(res))))); - if (ct == "application/atom+xml;type=entry") { - // Read an ATOM entry - const value val(atom::entryValue(content(atom::readATOMEntry(ls)))); - debug(val, "http::get::result"); - return val; - } - if (ct == "application/atom+xml;type=feed" || atom::isATOMFeed(ls)) { - // Read an ATOM feed - const value val(atom::feedValues(content(atom::readATOMFeed(ls)))); - debug(val, "http::get::result"); - return val; - } - if (ct == "application/rss+xml" || rss::isRSSFeed(ls)) { - // Read an RSS feed - const value val(rss::feedValues(content(rss::readRSSFeed(ls)))); - debug(val, "http::get::result"); - return val; - } - - // Return the content as a list of values - const value val(mkvalues(ls)); - debug(val, "http::get::result"); - return val; -} - -/** - * HTTP POST. - */ -const failable post(const value& val, const string& url, const CURLSession& ch) { - - // Convert value to an ATOM entry - const failable > entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); - if (!hasContent(entry)) - return mkfailure(reason(entry)); - debug(url, "http::post::url"); - debug(content(entry), "http::post::input"); - - // POST it to the URL - const list h = mklist("Content-Type: application/atom+xml"); - const list > req = mklist >(h, content(entry)); - const failable > > res = apply>(req, rcons, list(), url, "POST", ch); - if (!hasContent(res)) - return mkfailure(reason(res)); - - // Return the new entry id from the HTTP location header - const failable eid(entryId(location(car(content(res))))); - debug(eid, "http::post::result"); - return eid; -} - -/** - * HTTP PUT. - */ -const failable put(const value& val, const string& url, const CURLSession& ch) { - - // Convert value to an ATOM entry - const failable > entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); - if (!hasContent(entry)) - return mkfailure(reason(entry)); - debug(url, "http::put::url"); - debug(content(entry), "http::put::input"); - - // PUT it to the URL - const list h = mklist("Content-Type: application/atom+xml"); - const list > req = mklist >(h, content(entry)); - const failable > > res = apply >(req, rcons, list(), url, "PUT", ch); - if (!hasContent(res)) - return mkfailure(reason(res)); - - debug(true, "http::put::result"); - return value(true); -} - -/** - * HTTP DELETE. - */ -const failable del(const string& url, const CURLSession& ch) { - debug(url, "http::delete::url"); - - const list > req = mklist(list(), list()); - const failable > > res = apply >(req, rcons, list(), url, "DELETE", ch); - if (!hasContent(res)) - return mkfailure(reason(res)); - - debug(true, "http::delete::result"); - return value(true); -} - -/** - * Returns the current host name. - */ -const string hostname() { - char h[256]; - if (gethostname(h, 256) == -1) - return "localhost"; - return h; -} - -/** - * HTTP client proxy function. - */ -struct proxy { - proxy(const string& uri, const string& ca, const string& cert, const string& key) : uri(uri), ca(ca), cert(cert), key(key) { - } - - const value operator()(const list& args) const { - CURLSession cs(ca, cert, key); - failable val = evalExpr(args, uri, cs); - if (!hasContent(val)) - return value(); - return content(val); - } - - const string uri; - const string ca; - const string cert; - const string key; -}; - -} -} - -#endif /* tuscany_curl_hpp */ diff --git a/sca-cpp/trunk/modules/http/http.hpp b/sca-cpp/trunk/modules/http/http.hpp new file mode 100644 index 0000000000..b6de790028 --- /dev/null +++ b/sca-cpp/trunk/modules/http/http.hpp @@ -0,0 +1,625 @@ +/* + * 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_http_hpp +#define tuscany_http_hpp + +/** + * CURL HTTP client functions. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "string.hpp" +#include "gc.hpp" +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "parallel.hpp" +#include "../atom/atom.hpp" +#include "../rss/rss.hpp" +#include "../json/json.hpp" + +namespace tuscany { +namespace http { + +/** + * CURL library runtime, one per process. + */ +class CURLRuntime { +public: + CURLRuntime() { + curl_global_init(CURL_GLOBAL_ALL); + } +} curlRuntime; + +/** + * Represents a CURL session handle. + */ +class CURLSession { +public: + CURLSession(const string& ca = "", const string& cert = "", const string& key = "") : h(curl_easy_init()), p(gc_pool(mkpool())), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(true), ca(ca), cert(cert), key(key) { + } + + CURLSession(const CURLSession& c) : h(c.h), p(c.p), sock(c.sock), wpollset(c.wpollset), wpollfd(c.wpollfd), rpollset(c.rpollset), rpollfd(c.rpollfd), owner(false), ca(c.ca), cert(c.cert), key(c.key) { + } + + ~CURLSession() { + if (!owner) + return; + if (h == NULL) + return; + curl_easy_cleanup(h); + destroy(p); + } + +private: + CURL* h; + gc_pool p; + apr_socket_t* sock; + apr_pollset_t* wpollset; + apr_pollfd_t* wpollfd; + apr_pollset_t* rpollset; + apr_pollfd_t* rpollfd; + const bool owner; + + friend CURL* handle(const CURLSession& cs); + friend apr_socket_t* sock(const CURLSession& cs); + friend const failable connect(const string& url, CURLSession& cs); + friend const failable send(const char* c, const int l, const CURLSession& cs); + friend const failable recv(char* c, const int l, const CURLSession& cs); + +public: + const string ca; + const string cert; + const string key; +}; + +/** + * Returns the CURL handle used by a CURL session. + */ +CURL* handle(const CURLSession& cs) { + return cs.h; +} + +/** + * Return an apr_socket_t for the socket used by a CURL session. + */ +apr_socket_t* sock(const CURLSession& cs) { + return cs.sock; +} + +/** + * Convert a socket fd to an apr_socket_t. + */ +apr_socket_t* sock(const int sd, const gc_pool& p) { + int fd = sd; + apr_socket_t* s = NULL; + apr_os_sock_put(&s, &fd, pool(p)); + return s; +} + +/** + * Convert a CURL return code to an error string. + */ +const string curlreason(CURLcode rc) { + return curl_easy_strerror(rc); +} + +/** + * Convert an APR status to an error string. + */ +const string apreason(apr_status_t rc) { + char buf[256]; + return apr_strerror(rc, buf, sizeof(buf)); +} + +/** + * Setup a CURL session + */ +const failable setup(const string& url, const CURLSession& cs) { + + // Init CURL session + CURL* ch = handle(cs); + curl_easy_reset(ch); + curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0"); + + // Setup protocol options + curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true); + curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, true); + curl_easy_setopt(ch, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + + // Setup SSL options + if (cs.ca != "") { + debug(cs.ca, "http::apply::ca"); + curl_easy_setopt(ch, CURLOPT_CAINFO, c_str(cs.ca)); + curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, true); + curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 2); + } else + curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, false); + if (cs.cert != "") { + debug(cs.cert, "http::apply::cert"); + curl_easy_setopt(ch, CURLOPT_SSLCERT, c_str(cs.cert)); + curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); + } + if (cs.key != "") { + debug(cs.key, "http::apply::key"); + curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key)); + curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM"); + } + + // Set target URL + curl_easy_setopt(ch, CURLOPT_URL, c_str(url)); + + return ch; +} + +/** + * Context passed to the read callback function. + */ +class CURLReadContext { +public: + CURLReadContext(const list& ilist) : ilist(ilist) { + } + list ilist; +}; + +/** + * Called by CURL to read data to send. + */ +size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) { + CURLReadContext& rcx = *static_cast(data); + if (isNil(rcx.ilist)) + return 0; + const list f(fragment(rcx.ilist, size * nmemb)); + const string s = car(f); + rcx.ilist = cdr(f); + memcpy(ptr, c_str(s), length(s)); + return length(s); +} + +/** + * Context passed to CURL write callback function. + */ +template class CURLWriteContext { +public: + CURLWriteContext(const lambda& reduce, const R& accum) : reduce(reduce), accum(accum) { + } + const lambda reduce; + R accum; +}; + +/** + * Called by CURL to write received data. + */ +template size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) { + CURLWriteContext& wcx = *(static_cast*> (data)); + const size_t realsize = size * nmemb; + wcx.accum = wcx.reduce(string((const char*)ptr, realsize), wcx.accum); + return realsize; +} + +/** + * Apply an HTTP verb to a list containing a list of headers and a list of content, and + * a reduce function used to process the response. + */ +curl_slist* headers(curl_slist* cl, const list& h) { + if (isNil(h)) + return cl; + return headers(curl_slist_append(cl, c_str(string(car(h)))), cdr(h)); +} + +template const failable > apply(const list >& hdr, const lambda& reduce, const R& initial, const string& url, const string& verb, const CURLSession& cs) { + + // Setup the CURL session + const failable fch = setup(url, cs); + if (!hasContent(fch)) + return mkfailure>(reason(fch)); + CURL* ch = content(fch); + + // Set the request headers + curl_slist* hl = headers(NULL, car(hdr)); + if (hl != NULL) + curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); + + // Convert request body to a string + // TODO use HTTP chunking instead + ostringstream os; + write(cadr(hdr), os); + const string s = str(os); + const int sz = length(s); + + // Setup the read, write header and write data callbacks + CURLReadContext rcx(mklist(s)); + curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback); + curl_easy_setopt(ch, CURLOPT_READDATA, &rcx); + CURLWriteContext hcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback)); + curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx); + CURLWriteContext wcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback)); + curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx); + + // Apply the HTTP verb + if (verb == "POST") { + curl_easy_setopt(ch, CURLOPT_POST, true); + curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz); + } else if (verb == "PUT") { + curl_easy_setopt(ch, CURLOPT_UPLOAD, true); + curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz); + } else if (verb == "DELETE") + curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + const CURLcode rc = curl_easy_perform(ch); + + // Free the headers + if (hl != NULL) + curl_slist_free_all(hl); + + // Return the HTTP return code or content + if (rc) + return mkfailure >(string(curl_easy_strerror(rc))); + long httprc; + curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); + if (httprc != 200 && httprc != 201) { + ostringstream es; + es << "HTTP code " << httprc; + return mkfailure >(str(es)); + } + return mklist(hcx.accum, wcx.accum); +} + +/** + * Evaluate an expression remotely, at the given URL. + */ +const failable evalExpr(const value& expr, const string& url, const CURLSession& cs) { + debug(url, "http::evalExpr::url"); + debug(expr, "http::evalExpr::input"); + + // Convert expression to a JSON-RPC request + json::JSONContext cx; + const failable > jsreq = json::jsonRequest(1, car(expr), cdr(expr), cx); + if (!hasContent(jsreq)) + return mkfailure(reason(jsreq)); + + // POST it to the URL + const list h = mklist("Content-Type: application/json-rpc"); + const failable > > res = apply >(mklist >(h, content(jsreq)), rcons, list(), url, "POST", cs); + if (!hasContent(res)) + return mkfailure(reason(res)); + + // Parse and return JSON-RPC result + const failable rval = json::jsonResultValue(cadr >(content(res)), cx); + if (!hasContent(rval)) + return mkfailure(reason(rval)); + debug(content(rval), "http::evalExpr::result"); + return content(rval); +} + +/** + * Find and return a header. + */ +const failable header(const char* prefix, const list& h) { + if (isNil(h)) + return mkfailure(string("Couldn't find header: ") + prefix); + const string s = car(h); + if (find(s, prefix) != 0) + return header(prefix, cdr(h)); + const string l(substr(s, length(prefix))); + return substr(l, 0, find_first_of(l, "\r\n")); +} + +/** + * Find and return a location header. + */ +const failable location(const list& h) { + return header("Location: ", h); +} + +/** + * Convert a location to an entry id. + */ +const failable entryId(const failable l) { + if (!hasContent(l)) + return mkfailure(reason(l)); + const string ls(content(l)); + return value(mklist(string(substr(ls, find_last(ls, '/') + 1)))); +} + +/** + * Find and return a content-type header. + */ +const failable contentType(const list& h) { + return header("Content-Type: ", h); +} + +/** + * HTTP GET, return the resource at the given URL. + */ +template const failable > get(const lambda& reduce, const R& initial, const string& url, const CURLSession& cs) { + debug(url, "http::get::url"); + const list > req = mklist(list(), list()); + return apply(req, reduce, initial, url, "GET", cs); +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable getcontent(const string& url, const CURLSession& cs) { + debug(url, "http::get::url"); + + // Get the contents of the resource at the given URL + const failable > > res = get>(rcons, list(), url, cs); + if (!hasContent(res)) + return mkfailure(reason(res)); + const list ls(reverse(cadr(content(res)))); + + // Return the content as a list of values + const value val(mkvalues(ls)); + debug(val, "http::get::result"); + return val; +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable get(const string& url, const CURLSession& cs) { + debug(url, "http::get::url"); + + // Get the contents of the resource at the given URL + const failable > > res = get >(rcons, list(), url, cs); + if (!hasContent(res)) + return mkfailure(reason(res)); + const list ls(reverse(cadr(content(res)))); + + const string ct(content(contentType(car(content(res))))); + if (ct == "application/atom+xml;type=entry") { + // Read an ATOM entry + const value val(atom::entryValue(content(atom::readATOMEntry(ls)))); + debug(val, "http::get::result"); + return val; + } + if (ct == "application/atom+xml;type=feed" || atom::isATOMFeed(ls)) { + // Read an ATOM feed + const value val(atom::feedValues(content(atom::readATOMFeed(ls)))); + debug(val, "http::get::result"); + return val; + } + if (ct == "application/rss+xml" || rss::isRSSFeed(ls)) { + // Read an RSS feed + const value val(rss::feedValues(content(rss::readRSSFeed(ls)))); + debug(val, "http::get::result"); + return val; + } + + // Return the content as a list of values + const value val(mkvalues(ls)); + debug(val, "http::get::result"); + return val; +} + +/** + * HTTP POST. + */ +const failable post(const value& val, const string& url, const CURLSession& cs) { + + // Convert value to an ATOM entry + const failable > entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasContent(entry)) + return mkfailure(reason(entry)); + debug(url, "http::post::url"); + debug(content(entry), "http::post::input"); + + // POST it to the URL + const list h = mklist("Content-Type: application/atom+xml"); + const list > req = mklist >(h, content(entry)); + const failable > > res = apply>(req, rcons, list(), url, "POST", cs); + if (!hasContent(res)) + return mkfailure(reason(res)); + + // Return the new entry id from the HTTP location header + const failable eid(entryId(location(car(content(res))))); + debug(eid, "http::post::result"); + return eid; +} + +/** + * HTTP PUT. + */ +const failable put(const value& val, const string& url, const CURLSession& cs) { + + // Convert value to an ATOM entry + const failable > entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasContent(entry)) + return mkfailure(reason(entry)); + debug(url, "http::put::url"); + debug(content(entry), "http::put::input"); + + // PUT it to the URL + const list h = mklist("Content-Type: application/atom+xml"); + const list > req = mklist >(h, content(entry)); + const failable > > res = apply >(req, rcons, list(), url, "PUT", cs); + if (!hasContent(res)) + return mkfailure(reason(res)); + + debug(true, "http::put::result"); + return value(true); +} + +/** + * HTTP DELETE. + */ +const failable del(const string& url, const CURLSession& cs) { + debug(url, "http::delete::url"); + + const list > req = mklist(list(), list()); + const failable > > res = apply >(req, rcons, list(), url, "DELETE", cs); + if (!hasContent(res)) + return mkfailure(reason(res)); + + debug(true, "http::delete::result"); + return value(true); +} + +/** + * Returns the current host name. + */ +const string hostname() { + char h[256]; + if (gethostname(h, 256) == -1) + return "localhost"; + return h; +} + +/** + * Create an APR pollfd for a socket. + */ +apr_pollfd_t* pollfd(apr_socket_t* s, const int e, const gc_pool& p) { + apr_pollfd_t* pfd = gc_new(p); + pfd->p = pool(p); + pfd->desc_type = APR_POLL_SOCKET; + pfd->reqevents = (apr_int16_t)e; + pfd->rtnevents = (apr_int16_t)e; + pfd->desc.s = s; + pfd->client_data = NULL; + return pfd; +} + +/** + * Connect to a URL. + */ +const failable connect(const string& url, CURLSession& cs) { + + // Setup the CURL session + const failable fch = setup(url, cs); + if (!hasContent(fch)) + return mkfailure(reason(fch)); + CURL* ch = content(fch); + + // Connect + curl_easy_setopt(ch, CURLOPT_CONNECT_ONLY, true); + const CURLcode rc = curl_easy_perform(ch); + if (rc) + return mkfailure(string(curl_easy_strerror(rc))); + + // Convert the connected socket to an apr_socket_t + int sd; + const CURLcode grc = curl_easy_getinfo(ch, CURLINFO_LASTSOCKET, &sd); + if (grc) + return mkfailure(string(curl_easy_strerror(grc))); + cs.sock = sock(sd, cs.p); + + // Create pollsets and pollfds which can be used to poll the socket + apr_status_t rpcrc = apr_pollset_create(&cs.rpollset, 1, pool(cs.p), 0); + if (rpcrc != APR_SUCCESS) + return mkfailure(apreason(rpcrc)); + cs.rpollfd = pollfd(cs.sock, APR_POLLIN, cs.p); + apr_pollset_add(cs.rpollset, cs.rpollfd); + apr_status_t wpcrc = apr_pollset_create(&cs.wpollset, 1, pool(cs.p), 0); + if (wpcrc != APR_SUCCESS) + return mkfailure(apreason(wpcrc)); + cs.wpollfd = pollfd(cs.sock, APR_POLLOUT, cs.p); + apr_pollset_add(cs.wpollset, cs.wpollfd); + + return true; +} + +/** + * Send an array of chars. + */ +const failable send(const char* c, const int l, const CURLSession& cs) { + + // Send the data + size_t wl = 0; + const CURLcode rc = curl_easy_send(cs.h, c, (size_t)l, &wl); + if (rc == CURLE_OK && wl == (size_t)l) + return true; + if (rc != CURLE_AGAIN) + return mkfailure(curlreason(rc)); + + // If the socket was not ready, wait for it to become ready + const apr_pollfd_t* pollfds; + apr_int32_t pollcount; + apr_status_t pollrc = apr_pollset_poll(cs.wpollset, -1, &pollcount, &pollfds); + if (pollrc != APR_SUCCESS) + return mkfailure(apreason(pollrc)); + + // Send what's left + return send(c + wl, (int)((size_t)l - wl), cs); +} + +/** + * Receive an array of chars. + */ +const failable recv(char* c, const int l, const CURLSession& cs) { + + // Receive data + size_t rl; + const CURLcode rc = curl_easy_recv(cs.h, c, (size_t)l, &rl); + if (rc == CURLE_OK) + return (int)rl; + if (rc == 1) + return 0; + if (rc != CURLE_AGAIN) + return mkfailure(curlreason(rc)); + + // If the socket was not ready, wait for it to become ready + const apr_pollfd_t* pollfds; + apr_int32_t pollcount; + apr_status_t pollrc = apr_pollset_poll(cs.rpollset, -1, &pollcount, &pollfds); + if (pollrc != APR_SUCCESS) + return mkfailure(apreason(pollrc)); + + // Receive again + return recv(c, l, cs); +} + +/** + * HTTP client proxy function. + */ +struct proxy { + proxy(const string& uri, const string& ca, const string& cert, const string& key) : uri(uri), ca(ca), cert(cert), key(key) { + } + + const value operator()(const list& args) const { + CURLSession cs(ca, cert, key); + failable val = evalExpr(args, uri, cs); + if (!hasContent(val)) + return value(); + return content(val); + } + + const string uri; + const string ca; + const string cert; + const string key; +}; + +} +} + +#endif /* tuscany_http_hpp */ diff --git a/sca-cpp/trunk/modules/http/httpd-addr b/sca-cpp/trunk/modules/http/httpd-addr new file mode 100755 index 0000000000..62fc775ea7 --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd-addr @@ -0,0 +1,54 @@ +#!/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. + +# Parse a string in the form ip-addr:local-port/public-port +addr=`echo $2 | awk -F "/" '{ print $1 }'` +ip=`echo $addr | awk -F ":" '{ print $1 }'` +port=`echo $addr | awk -F ":" '{ print $2 }'` +if [ "$port" = "" ]; then + port=$ip + ip="" + listen=$port + vhost="*:$port" +else + listen="$ip:$port" + vhost="$ip:$port" +fi +pport=`echo $2 | awk -F "/" '{ print $2 }'` +if [ "$pport" = "" ]; then + pport=$port +fi + +# Return the requested part +if [ "$1" = "ip" ]; then + echo $ip +fi +if [ "$1" = "port" ]; then + echo $port +fi +if [ "$1" = "pport" ]; then + echo $pport +fi +if [ "$1" = "listen" ]; then + echo $listen +fi +if [ "$1" = "vhost" ]; then + echo $vhost +fi +return 0 diff --git a/sca-cpp/trunk/modules/http/httpd-auth-conf b/sca-cpp/trunk/modules/http/httpd-auth-conf index cfe81f778a..ffe745db9d 100755 --- a/sca-cpp/trunk/modules/http/httpd-auth-conf +++ b/sca-cpp/trunk/modules/http/httpd-auth-conf @@ -20,8 +20,10 @@ # Generate a minimal HTTPD SSL configuration here=`readlink -f $0`; here=`dirname $here` root=`readlink -f $1` + conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` host=`echo $conf | awk '{ print $6 }'` + httpd_prefix=`cat $here/httpd.prefix` # Generate basic authentication configuration diff --git a/sca-cpp/trunk/modules/http/httpd-conf b/sca-cpp/trunk/modules/http/httpd-conf index 2cbf5120e9..09d78351ff 100755 --- a/sca-cpp/trunk/modules/http/httpd-conf +++ b/sca-cpp/trunk/modules/http/httpd-conf @@ -21,16 +21,19 @@ here=`readlink -f $0`; here=`dirname $here` mkdir -p $1 root=`readlink -f $1` + host=$2 -port=`echo $3 | awk -F "/" '{ print $1 }'` -pport=`echo $3 | awk -F "/" '{ print $2 }'` -if [ "$pport" = "" ]; then - pport=$port -fi +port=`$here/httpd-addr port $3` +pport=`$here/httpd-addr pport $3` +listen=`$here/httpd-addr listen $3` +vhost=`$here/httpd-addr vhost $3` + mkdir -p $4 htdocs=`readlink -f $4` + user=`id -un` group=`id -gn` + modules_prefix=`cat $here/httpd-modules.prefix` mkdir -p $root @@ -74,6 +77,8 @@ LoadModule log_config_module ${modules_prefix}/modules/mod_log_config.so LoadModule vhost_alias_module ${modules_prefix}/modules/mod_vhost_alias.so +LoadModule mod_tuscany_ssltunnel $here/libmod_tuscany_ssltunnel.so + # Basic security precautions User $user Group $group @@ -124,10 +129,10 @@ Allow from all # Listen on HTTP port -Listen $port +Listen $listen # Setup HTTP virtual host - + ServerName http://$host:$pport Include conf/svhost.conf diff --git a/sca-cpp/trunk/modules/http/httpd-ssl-conf b/sca-cpp/trunk/modules/http/httpd-ssl-conf index f36da55b12..7b57539f5d 100755 --- a/sca-cpp/trunk/modules/http/httpd-ssl-conf +++ b/sca-cpp/trunk/modules/http/httpd-ssl-conf @@ -20,20 +20,21 @@ # Generate a minimal HTTPD SSL configuration here=`readlink -f $0`; here=`dirname $here` root=`readlink -f $1` + 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 }'` -sslport=`echo $2 | awk -F "/" '{ print $1 }'` -sslpport=`echo $2 | awk -F "/" '{ print $2 }'` -if [ "$sslpport" = "" ]; then - sslpport=$sslport -fi + +sslpport=`$here/httpd-addr pport $2` +ssllisten=`$here/httpd-addr listen $2` +sslvhost=`$here/httpd-addr vhost $2` + htdocs=`echo $conf | awk '{ print $8 }'` htdocs=`readlink -f $htdocs` + httpd_prefix=`cat $here/httpd.prefix` # Extract organization name from our CA certificate -org=`openssl x509 -noout -subject -nameopt multiline -in $root/conf/ca.crt | grep organizationName | awk -F "= " '{ print $2 }'` +org=`openssl x509 -noout -subject -nameopt multiline -in $root/cert/ca.crt | grep organizationName | awk -F "= " '{ print $2 }'` # Generate HTTPD configuration cat >>$root/conf/httpd.conf <>$root/conf/httpd.conf < RewriteEngine on -RewriteCond %{SERVER_PORT} !^$sslpport$ +RewriteCond %{HTTPS} !^on$ RewriteRule .* https://%{SERVER_NAME}:$sslpport%{REQUEST_URI} [R,L] @@ -56,10 +57,10 @@ SSLRandomSeed startup builtin SSLRandomSeed connect builtin # Listen on HTTPS port -Listen $sslport +Listen $ssllisten # HTTPS virtual host - + ServerName https://$host:$sslpport Include conf/svhost-ssl.conf @@ -156,6 +157,7 @@ else # - another valid form of authentication as per the Satisfy directive SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 and ( \ ( %{SSL_CLIENT_I_DN_O} == "$org" and %{SSL_CLIENT_S_DN_OU} == "server" ) or \ +( %{SSL_CLIENT_I_DN_O} == "$org" and %{SSL_CLIENT_S_DN_OU} == "tunnel" ) or \ ( %{SSL_CLIENT_I_DN_O} == "$org" and %{SSL_CLIENT_S_DN_OU} == "proxy" and \ %{HTTP:X-Forwarded-SSL-Issuer-DN-O} == "$org" and %{HTTP:X-Forwarded-SSL-Client-DN-OU} == "server" ) or \ %{REQUEST_URI} =~ m/^.(login|logout|openid|unprotected).*$/ ) @@ -176,6 +178,10 @@ RewriteCond %{SSL:SSL_CLIENT_I_DN_O} "$org" RewriteCond %{SSL:SSL_CLIENT_S_DN_OU} "server" RewriteRule .* - [E=SSL_REMOTE_USER:%{SSL:SSL_CLIENT_S_DN}] +RewriteCond %{SSL:SSL_CLIENT_I_DN_O} "$org" +RewriteCond %{SSL:SSL_CLIENT_S_DN_OU} "tunnel" +RewriteRule .* - [E=SSL_REMOTE_USER:%{SSL:SSL_CLIENT_S_DN}] + RewriteCond %{SSL:SSL_CLIENT_I_DN_O} "$org" RewriteCond %{SSL:SSL_CLIENT_S_DN_OU} "proxy" RewriteCond %{HTTP:X-Forwarded-SSL-Issuer-DN-O} "$org" @@ -196,10 +202,10 @@ cat >$root/conf/svhost-ssl.conf <$root/conf/dvhost-ssl.conf < +#include +#include #define APR_WANT_STRFUNC -#include "apr_want.h" - -#include "httpd.h" -#include "http_config.h" -#include "http_core.h" -#include "http_request.h" -#include "http_protocol.h" -#include "http_log.h" -#include "http_main.h" -#include "util_script.h" -#include "util_md5.h" -#include "http_config.h" -#include "ap_mpm.h" -#include "mod_core.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "string.hpp" #include "stream.hpp" @@ -131,66 +132,13 @@ const bool isVirtualHostRequest(const server_rec* s, request_rec* r) { /** * Return the content type of a request. */ -const char* optional(const char* s) { - if (s == NULL) - return ""; - return s; -} - const string contentType(const request_rec* r) { - return optional(apr_table_get(r->headers_in, "Content-Type")); -} - -#ifdef WANT_MAINTAINER_MODE - -/** - * Debug log. - */ -int debugHeader(unused void* r, const char* key, const char* value) { - cerr << " header key: " << key << ", value: " << value << endl; - return 1; -} - -int debugEnv(unused void* r, const char* key, const char* value) { - cerr << " var key: " << key << ", value: " << value << endl; - return 1; -} - -int debugNote(unused void* r, const char* key, const char* value) { - cerr << " note key: " << key << ", value: " << value << endl; - return 1; -} - -const bool debugRequest(request_rec* r, const string& msg) { - cerr << msg << ":" << endl; - cerr << " server: " << optional(r->server->server_hostname) << endl; - cerr << " protocol: " << optional(r->protocol) << endl; - cerr << " method: " << optional(r->method) << endl; - cerr << " method number: " << r->method_number << endl; - cerr << " content type: " << contentType(r) << endl; - cerr << " content encoding: " << optional(r->content_encoding) << endl; - apr_table_do(debugHeader, r, r->headers_in, NULL); - cerr << " unparsed uri: " << optional(r->unparsed_uri) << endl; - cerr << " uri: " << optional(r->uri) << endl; - cerr << " path info: " << optional(r->path_info) << endl; - cerr << " filename: " << optional(r->filename) << endl; - cerr << " uri tokens: " << pathTokens(r->uri) << endl; - cerr << " args: " << optional(r->args) << endl; - cerr << " user: " << optional(r->user) << endl; - cerr << " auth type: " << optional(r->ap_auth_type) << endl; - apr_table_do(debugEnv, r, r->subprocess_env, NULL); - apr_table_do(debugEnv, r, r->notes, NULL); - return true; + const char* ct = apr_table_get(r->headers_in, "Content-Type"); + if (ct == NULL) + return ""; + return ct; } -#define httpdDebugRequest(r, msg) httpd::debugRequest(r, msg) - -#else - -#define httpdDebugRequest(r, msg) - -#endif - /** * Return the remaining part of a uri after the given path (aka the path info.) */ @@ -215,19 +163,6 @@ const list > queryArgs(const request_rec* r) { return map>(queryArg, tokenize("&", a)); } -/** - * Returns a list of param values other than the id and method args from a list - * of key value pairs. - */ -const list queryParams(const list >& a) { - if (isNil(a)) - return list(); - const list p = car(a); - if (car(p) == value("id") || car(p) == value("method")) - return queryParams(cdr(a)); - return cons(cadr(p), queryParams(cdr(a))); -} - /** * Converts the args received in a POST to a list of key value pairs. */ @@ -303,7 +238,7 @@ const failable writeResult(const failable >& ls, const string& } /** - * Report request execution status. + * Report a request execution status. */ const int reportStatus(const failable& rc) { if (!hasContent(rc)) @@ -337,7 +272,7 @@ const value requestValue(request_rec* r) { } /** - * Update filters in an HTTPD redirect request. + * Update request filters in an HTTPD redirect request. * Similar to httpd/modules/http/http_request.c::update_r_in_filters. */ const bool redirectFilters(ap_filter_t* f, request_rec* from, request_rec* to) { @@ -468,6 +403,62 @@ const void* userData(const string& k, const server_rec* s) { return v; } +#ifdef WANT_MAINTAINER_MODE + +/** + * Debug log. + */ +const char* debugOptional(const char* s) { + if (s == NULL) + return ""; + return s; +} + +int debugHeader(unused void* r, const char* key, const char* value) { + cerr << " header key: " << key << ", value: " << value << endl; + return 1; +} + +int debugEnv(unused void* r, const char* key, const char* value) { + cerr << " var key: " << key << ", value: " << value << endl; + return 1; +} + +int debugNote(unused void* r, const char* key, const char* value) { + cerr << " note key: " << key << ", value: " << value << endl; + return 1; +} + +const bool debugRequest(request_rec* r, const string& msg) { + cerr << msg << ":" << endl; + cerr << " server: " << debugOptional(r->server->server_hostname) << endl; + cerr << " protocol: " << debugOptional(r->protocol) << endl; + cerr << " method: " << debugOptional(r->method) << endl; + cerr << " method number: " << r->method_number << endl; + cerr << " content type: " << contentType(r) << endl; + cerr << " content encoding: " << debugOptional(r->content_encoding) << endl; + apr_table_do(debugHeader, r, r->headers_in, NULL); + cerr << " unparsed uri: " << debugOptional(r->unparsed_uri) << endl; + cerr << " uri: " << debugOptional(r->uri) << endl; + cerr << " path info: " << debugOptional(r->path_info) << endl; + cerr << " filename: " << debugOptional(r->filename) << endl; + cerr << " uri tokens: " << pathTokens(r->uri) << endl; + cerr << " args: " << debugOptional(r->args) << endl; + cerr << " user: " << debugOptional(r->user) << endl; + cerr << " auth type: " << debugOptional(r->ap_auth_type) << endl; + apr_table_do(debugEnv, r, r->subprocess_env, NULL); + apr_table_do(debugEnv, r, r->notes, NULL); + return true; +} + +#define httpdDebugRequest(r, msg) httpd::debugRequest(r, msg) + +#else + +#define httpdDebugRequest(r, msg) + +#endif + } } diff --git a/sca-cpp/trunk/modules/http/mod-ssltunnel.cpp b/sca-cpp/trunk/modules/http/mod-ssltunnel.cpp new file mode 100644 index 0000000000..5337a4cac2 --- /dev/null +++ b/sca-cpp/trunk/modules/http/mod-ssltunnel.cpp @@ -0,0 +1,364 @@ +/* + * 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 tunnel traffic over an HTTPS connection. + */ + +#include + +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "httpd.hpp" +#include "http.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel; +} + +namespace tuscany { +namespace httpd { +namespace modssltunnel { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(server_rec* s) : server(s) { + } + server_rec* server; + string pass; + string host; + string path; + string ca; + string cert; + string key; +}; + +extern "C" { +extern module AP_DECLARE_DATA core_module; +} + +/** + * Process the module configuration. + */ +int M_SSLTUNNEL; +int postConfigParse(ServerConf& mainsc, apr_pool_t* p, server_rec* s) { + if (s == NULL) + return OK; + ServerConf& sc = httpd::serverConf(s, &mod_tuscany_ssltunnel); + debug(httpd::serverName(s), "modwiring::postConfigParse::serverName"); + + // Merge configuration from main server + if (length(sc.ca) == 0 && length(mainsc.ca) !=0) + sc.ca = mainsc.ca; + if (length(sc.cert) == 0 && length(mainsc.cert) !=0) + sc.cert = mainsc.cert; + if (length(sc.key) == 0 && length(mainsc.key) !=0) + sc.key = mainsc.key; + + // Parse the configured TunnelPass URI + if (length(sc.pass) != 0) { + apr_uri_t uri; + apr_status_t prc = apr_uri_parse(p, c_str(sc.pass), &uri); + if (prc != APR_SUCCESS) { + mkfailure("Couldn't parse TunnelPass: " + sc.pass + ", " + http::apreason(prc)); + return prc; + } + sc.host = uri.hostname; + sc.path = uri.path; + } + return postConfigParse(mainsc, p, 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); + ServerConf& sc = httpd::serverConf(s, &mod_tuscany_ssltunnel); + debug(httpd::serverName(s), "modwiring::postConfig::serverName"); + + // Register the SSLTUNNEL method + M_SSLTUNNEL = ap_method_register(p, "SSLTUNNEL"); + + // Parse the configured TunnelPass URI + return postConfigParse(sc, p, s); +} + +/** + * Close a connection. + */ +extern "C" { + AP_DECLARE(void) ap_lingering_close(conn_rec *c); +} + +const int close(conn_rec* conn) { + debug("modssltunnel::close"); + ap_lingering_close(conn); + return OK; +} + +/** + * Abort a connection. + */ +const int abort(unused conn_rec* conn, const string& reason) { + debug("modssltunnel::abort"); + return httpd::reportStatus(mkfailure(reason)); +} + +/** + * Tunnel traffic from a client connection to a target URL. + */ +int tunnel(conn_rec* conn, http::CURLSession& cs, const string& url, const string& preamble, const gc_pool& p, unused ap_filter_t* ifilter, ap_filter_t* ofilter) { + + // Get client connection socket + apr_socket_t* csock = (apr_socket_t*)ap_get_module_config(conn->conn_config, &core_module); + + // Open connection to target + const failable crc = http::connect(url, cs); + if (!hasContent(crc)) + return abort(conn, reason(crc)); + apr_socket_t* tsock = http::sock(cs); + + // Send preamble string + if (length(preamble) != 0) { + debug(preamble, "modssltunnel::tunnel::sendToTarget"); + const failable src = http::send(c_str(preamble), length(preamble), cs); + if (!hasContent(src)) + return abort(conn, string("Couldn't send to target: ") + reason(src)); + } + + // Create input/output bucket brigades + apr_bucket_brigade* ib = apr_brigade_create(pool(p), conn->bucket_alloc); + apr_bucket_brigade* ob = apr_brigade_create(pool(p), conn->bucket_alloc); + + // Create a pollset for the client and target sockets + apr_pollset_t* pollset; + apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0); + if (cprc != APR_SUCCESS) + return abort(conn, http::apreason(cprc)); + const apr_pollfd_t* cpollfd = http::pollfd(csock, APR_POLLIN, p); + apr_pollset_add(pollset, cpollfd); + const apr_pollfd_t* tpollfd = http::pollfd(tsock, APR_POLLIN, p); + apr_pollset_add(pollset, tpollfd); + + // Relay traffic in both directions until end of stream + const apr_pollfd_t* pollfds = cpollfd; + apr_int32_t pollcount = 1; + for(;;) { + for (; pollcount > 0; pollcount--, pollfds++) { + if (pollfds->rtnevents & APR_POLLIN) { + if (pollfds->desc.s == csock) { + + // Receive buckets from client + const apr_status_t getrc = ap_get_brigade(conn->input_filters, ib, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); + if (getrc != APR_SUCCESS) + return OK; + + for (apr_bucket* bucket = APR_BRIGADE_FIRST(ib); bucket != APR_BRIGADE_SENTINEL(ib); bucket = APR_BUCKET_NEXT(bucket)) { + if (APR_BUCKET_IS_FLUSH(bucket)) + continue; + + // Client connection closed + if (APR_BUCKET_IS_EOS(bucket)) + return close(conn); + + const char *data; + apr_size_t rl; + apr_bucket_read(bucket, &data, &rl, APR_BLOCK_READ); + if (rl > 0) { + debug(string(data, rl), "modssltunnel::tunnel::sendToTarget"); + + // Send to target + const failable src = http::send(data, rl, cs); + if (!hasContent(src)) + return abort(conn, string("Couldn't send to target: ") + reason(src)); + } + } + apr_brigade_cleanup(ib); + } else { + + // Receive from target + char data[8192]; + const failable frl = http::recv(data, sizeof(data), cs); + if (!hasContent(frl)) + return abort(conn, string("Couldn't receive from target") + reason(frl)); + const int rl = content(frl); + + // Target connection closed + if (rl == 0) + return close(conn); + + // Send bucket to client + APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_transient_create(data, rl, conn->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_flush_create(conn->bucket_alloc)); + if (ap_pass_brigade(ofilter, ob) != APR_SUCCESS) + return abort(conn, "Couldn't send data bucket to client"); + apr_brigade_cleanup(ob); + } + } + + // Error + if (pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) { + if (pollfds->desc.s == csock) + return abort(conn, "Couldn't receive from client"); + else + return abort(conn, "Couldn't receive from target"); + } + } + + // Poll the client and target sockets + debug("modssltunnel::tunnel::poll"); + apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds); + if (pollrc != APR_SUCCESS) + return abort(conn, "Couldn't poll sockets"); + debug(pollcount, "modssltunnel::tunnel::pollfds"); + } + + // Close client connection + return close(conn); +} + +/** + * Return the first connection filter in a list of filters. + */ +ap_filter_t* connectionFilter(ap_filter_t* f) { + if (f == NULL) + return f; + if (f->frec->ftype < AP_FTYPE_CONNECTION) + return connectionFilter(f->next); + return f; +} + +/** + * Process a client connection and relay it to a tunnel. + */ +int processConnection(conn_rec *conn) { + // Only allow configured virtual hosts + if (!conn->base_server->is_virtual) + return DECLINED; + if (ap_get_module_config(conn->base_server->module_config, &mod_tuscany_ssltunnel) == NULL) + return DECLINED; + + gc_scoped_pool(conn->pool); + const ServerConf& sc = httpd::serverConf(conn->base_server, &mod_tuscany_ssltunnel); + if (length(sc.pass) == 0) + return DECLINED; + debug(sc.pass, "modssltunnel::processConnection::pass"); + + // Create the target connection + http::CURLSession cs(sc.ca, sc.cert, sc.key); + + // Run the tunnel + const string preamble = string("SSLTUNNEL ") + sc.path + string(" HTTP/1.1\r\nHost: ") + sc.host + string("\r\n\r\n"); + debug(preamble, "modssltunnel::processConnection::preamble"); + return tunnel(conn, cs, sc.pass, preamble, gc_pool(conn->pool), connectionFilter(conn->input_filters), connectionFilter(conn->output_filters)); +} + +/** + * Tunnel a SSLTUNNEL request to a target host/port. + */ +int handler(request_rec* r) { + if (r->method_number != M_SSLTUNNEL) + return DECLINED; + + // Only allow HTTPS + if (strcmp(r->server->server_scheme, "https")) + return DECLINED; + + // Build the target URL + debug(r->uri, "modssltunnel::handler::uri"); + const list path(pathValues(r->uri)); + const string url = string(cadr(path)) + ":" + caddr(path); + debug(url, "modssltunnel::handler::target"); + + // Create the target connection + http::CURLSession cs; + + // Run the tunnel + return tunnel(r->connection, cs, url, "", gc_pool(r->pool), connectionFilter(r->proto_input_filters), connectionFilter(r->proto_output_filters)); +} + +/** + * Configuration commands. + */ +const char* confTunnelPass(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf(cmd, &mod_tuscany_ssltunnel); + sc.pass = 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(cmd, &mod_tuscany_ssltunnel); + 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(cmd, &mod_tuscany_ssltunnel); + 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(cmd, &mod_tuscany_ssltunnel); + sc.key = arg; + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("TunnelPass", (const char*(*)())confTunnelPass, NULL, RSRC_CONF, "Tunnel server name"), + AP_INIT_TAKE1("TunnelSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "Tunnel SSL CA certificate file"), + AP_INIT_TAKE1("TunnelSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "Tunnel SSL certificate file"), + AP_INIT_TAKE1("TunnelSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "Tunnel 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_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_process_connection(processConnection, NULL, NULL, APR_HOOK_MIDDLE); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel = { + STANDARD20_MODULE_STUFF, + // dir config and merger + NULL, NULL, + // server config and merger + tuscany::httpd::makeServerConf, NULL, + // commands and hooks + tuscany::httpd::modssltunnel::commands, tuscany::httpd::modssltunnel::registerHooks +}; + +} diff --git a/sca-cpp/trunk/modules/http/proxy-member-conf b/sca-cpp/trunk/modules/http/proxy-member-conf index 73c448c065..43f31c97b0 100755 --- a/sca-cpp/trunk/modules/http/proxy-member-conf +++ b/sca-cpp/trunk/modules/http/proxy-member-conf @@ -20,8 +20,9 @@ # Add a proxy balancer member here=`readlink -f $0`; here=`dirname $here` root=`readlink -f $1` + host=$2 -port=`echo $3 | awk -F "/" '{ print $1 }'` +port=`$here/httpd-addr port $3` cat >>$root/conf/vhost.conf <>$root/conf/vhost-ssl.conf <>$root/conf/vhost-ssl.conf <$root/conf/openssl-ca.conf <$root/cert/openssl-ca.conf < $root/conf/ca-serial -touch $root/conf/ca-database +rm -rf $root/cert/*.crt $root/cert/*.pem $root/cert/hash +rm -f $root/cert/ca-database +echo 1000 > $root/cert/ca-serial +touch $root/cert/ca-database # Generate the certification authority certificate -openssl req -new -x509 -config $root/conf/openssl-ca.conf -out $root/conf/ca.crt -keyout $root/conf/ca.key +openssl req -new -x509 -config $root/cert/openssl-ca.conf -out $root/cert/ca.crt -keyout $root/cert/ca.key + +# Add to the hash directory and rehash +mkdir -p $root/cert/hash +cp $root/cert/ca.crt $root/cert/hash +c_rehash $root/cert/hash diff --git a/sca-cpp/trunk/modules/http/ssl-cert-conf b/sca-cpp/trunk/modules/http/ssl-cert-conf index 8b6208a449..57c4522535 100755 --- a/sca-cpp/trunk/modules/http/ssl-cert-conf +++ b/sca-cpp/trunk/modules/http/ssl-cert-conf @@ -21,6 +21,7 @@ here=`readlink -f $0`; here=`dirname $here` mkdir -p $1 root=`readlink -f $1` + host=$2 if [ "$3" != "" ]; then certname=$3 @@ -29,14 +30,14 @@ else fi # Don't regenerate the certificate if it already exists -if [ -f $root/conf/$certname.crt ]; then +if [ -f $root/cert/$certname.crt ]; then return 0 fi # Generate openssl configuration -mkdir -p $root/conf +mkdir -p $root/cert umask 0007 -cat >$root/conf/openssl-cert-$certname.conf <$root/cert/openssl-cert-$certname.conf <> $root/conf/$certname.pem +openssl x509 -in $root/cert/$certname.crt -out $root/cert/$certname.pem +cat $root/cert/$certname.key >> $root/cert/$certname.pem + +# Add to the hash directory and rehash +mkdir -p $root/cert/hash +cp $root/cert/$certname.crt $root/cert/hash +cp $root/cert/$certname.pem $root/cert/hash +c_rehash $root/cert/hash diff --git a/sca-cpp/trunk/modules/http/ssl-cert-find b/sca-cpp/trunk/modules/http/ssl-cert-find new file mode 100755 index 0000000000..b5aefb8e38 --- /dev/null +++ b/sca-cpp/trunk/modules/http/ssl-cert-find @@ -0,0 +1,26 @@ +#!/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. + +# List certificate files, useful to distribute them to another host +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +cd $root +find -regex '.*\.\(\(crt\)\|\(pem\)\|\(p12\)\|\(key\)\|\(0\)\)' 2>/dev/null + diff --git a/sca-cpp/trunk/modules/http/ssl-ls b/sca-cpp/trunk/modules/http/ssl-ls deleted file mode 100755 index 71d40719ca..0000000000 --- a/sca-cpp/trunk/modules/http/ssl-ls +++ /dev/null @@ -1,25 +0,0 @@ -#!/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 test certification authority certificate -here=`readlink -f $0`; here=`dirname $here` -root=`readlink -f $1` - -ls $root/conf/*.crt $root/conf/*.key $root/conf/*.pem $root/conf/*.p12 2>/dev/null - diff --git a/sca-cpp/trunk/modules/http/tunnel-ssl-conf b/sca-cpp/trunk/modules/http/tunnel-ssl-conf new file mode 100755 index 0000000000..cdcb79e4e7 --- /dev/null +++ b/sca-cpp/trunk/modules/http/tunnel-ssl-conf @@ -0,0 +1,56 @@ +#!/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 an SSL tunnel configuration +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +port=`$here/httpd-addr port $2` +sslhost=$3 +sslport=$4 +tport=$5 + +httpd_prefix=`cat $here/httpd.prefix` + +# Generate HTTPD configuration +cat >>$root/conf/httpd.conf < +ServerName http://localhost:$port + +TunnelPass https://$sslhost:$sslport/tunnel/localhost/$tport + +# Declare SSL certificates used in this virtual host +#TunnelSSLCACertificateFile "$root/cert/ca.crt" +TunnelSSLCertificateFile "$root/cert/tunnel.crt" +TunnelSSLCertificateKeyFile "$root/cert/tunnel.key" + + + +EOF + diff --git a/sca-cpp/trunk/modules/http/vhost-conf b/sca-cpp/trunk/modules/http/vhost-conf index 4f563b673e..9dff38d6e7 100755 --- a/sca-cpp/trunk/modules/http/vhost-conf +++ b/sca-cpp/trunk/modules/http/vhost-conf @@ -20,22 +20,23 @@ # Generate mass dynamic virtual hosting configuration here=`readlink -f $0`; here=`dirname $here` root=`readlink -f $1` + 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 +addr=`echo $conf | awk '{ print $7 }'` +port=`$here/httpd-addr port $addr` +pport=`$here/httpd-addr pport $addr` +vhost=`$here/httpd-addr vhost $addr` + htdocs=`echo $conf | awk '{ print $8 }'` htdocs=`readlink -f $htdocs` cat >>$root/conf/httpd.conf < + ServerName http://vhost.$host:$pport ServerAlias *.$host VirtualDocumentRoot $htdocs/domains/%1/ diff --git a/sca-cpp/trunk/modules/http/vhost-ssl-conf b/sca-cpp/trunk/modules/http/vhost-ssl-conf index e6801248c4..433f26e6b3 100755 --- a/sca-cpp/trunk/modules/http/vhost-ssl-conf +++ b/sca-cpp/trunk/modules/http/vhost-ssl-conf @@ -20,24 +20,26 @@ # Generate mass dynamic virtual hosting configuration here=`readlink -f $0`; here=`dirname $here` root=`readlink -f $1` + conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` host=`echo $conf | awk '{ print $6 }'` + sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` -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 +ssladdr=`echo $sslconf | awk '{ print $6 }'` +sslport=`$here/httpd-addr port $ssladdr` +sslpport=`$here/httpd-addr pport $ssladdr` +sslvhost=`$here/httpd-addr vhost $ssladdr` + htdocs=`echo $conf | awk '{ print $8 }'` htdocs=`readlink -f $htdocs` cat >>$root/conf/httpd.conf < + ServerName https://vhost.$host:$sslpport ServerAlias *.$host VirtualDocumentRoot $htdocs/domains/%1/ diff --git a/sca-cpp/trunk/modules/json/json.hpp b/sca-cpp/trunk/modules/json/json.hpp index 1d966d3f67..4e02cbf2c9 100644 --- a/sca-cpp/trunk/modules/json/json.hpp +++ b/sca-cpp/trunk/modules/json/json.hpp @@ -397,6 +397,19 @@ const string funcName(const string& f) { return f; } +/** + * Returns a list of param values other than the id and method args from a list + * of key value pairs. + */ +const list queryParams(const list >& a) { + if (isNil(a)) + return list(); + const list p = car(a); + if (car(p) == value("id") || car(p) == value("method")) + return queryParams(cdr(a)); + return cons(cadr(p), queryParams(cdr(a))); +} + } } diff --git a/sca-cpp/trunk/modules/server/client-test.hpp b/sca-cpp/trunk/modules/server/client-test.hpp index 9b37a73803..f2fdb60877 100644 --- a/sca-cpp/trunk/modules/server/client-test.hpp +++ b/sca-cpp/trunk/modules/server/client-test.hpp @@ -31,7 +31,7 @@ #include "string.hpp" #include "parallel.hpp" #include "perf.hpp" -#include "../http/curl.hpp" +#include "../http/http.hpp" namespace tuscany { namespace server { diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp index 0aff56f59d..0d90cb7ceb 100644 --- a/sca-cpp/trunk/modules/server/mod-eval.hpp +++ b/sca-cpp/trunk/modules/server/mod-eval.hpp @@ -37,7 +37,7 @@ #include "../atom/atom.hpp" #include "../json/json.hpp" #include "../scdl/scdl.hpp" -#include "../http/curl.hpp" +#include "../http/http.hpp" #include "../http/httpd.hpp" extern "C" { @@ -113,7 +113,7 @@ const failable get(request_rec* r, const lambda&)>& const value func = c_str(json::funcName(string(cadr(ma)))); // Apply the requested function - const failable val = failableResult(impl(cons(func, httpd::queryParams(args)))); + const failable val = failableResult(impl(cons(func, json::queryParams(args)))); if (!hasContent(val)) return mkfailure(reason(val)); @@ -250,6 +250,8 @@ const failable del(request_rec* r, const lambda&)>& * 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"; @@ -544,8 +546,11 @@ const failable virtualHostCleanup(const ServerConf& sc) { * 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"); diff --git a/sca-cpp/trunk/modules/server/mod-wiring.cpp b/sca-cpp/trunk/modules/server/mod-wiring.cpp index 964f7efc82..7ed47c7f48 100644 --- a/sca-cpp/trunk/modules/server/mod-wiring.cpp +++ b/sca-cpp/trunk/modules/server/mod-wiring.cpp @@ -299,13 +299,15 @@ const failable virtualHostConfig(ServerConf& sc, request_rec* r) { * to the target component. */ int translate(request_rec *r) { - gc_scoped_pool pool(r->pool); + 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 request + // 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(r, &mod_tuscany_wiring); // Process dynamic virtual host configuration, if any @@ -329,14 +331,17 @@ int translate(request_rec *r) { * HTTP request handler, redirect to a target component. */ int handler(request_rec *r) { - gc_scoped_pool pool(r->pool); + 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; + + gc_scoped_pool pool(r->pool); httpdDebugRequest(r, "modwiring::handler::input"); // Do an internal redirect - if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0) - return DECLINED; debug(r->uri, "modwiring::handler::uri"); debug(r->filename, "modwiring::handler::filename"); debug(r->path_info, "modwiring::handler::path info"); @@ -364,7 +369,7 @@ const int postConfigMerge(const ServerConf& mainsc, server_rec* s) { return postConfigMerge(mainsc, s->next); } -int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) { +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 diff --git a/sca-cpp/trunk/modules/server/server-conf b/sca-cpp/trunk/modules/server/server-conf index 4a318c6fce..7c72233ca9 100755 --- a/sca-cpp/trunk/modules/server/server-conf +++ b/sca-cpp/trunk/modules/server/server-conf @@ -57,27 +57,11 @@ ssl=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` if [ "$ssl" != "" ]; then cat >>$root/conf/httpd.conf <>$root/conf/httpd.conf <