diff options
Diffstat (limited to 'sca-cpp/trunk/modules/http')
-rw-r--r-- | sca-cpp/trunk/modules/http/curl-test.cpp | 6 | ||||
-rw-r--r-- | sca-cpp/trunk/modules/http/curl.hpp | 166 | ||||
-rwxr-xr-x | sca-cpp/trunk/modules/http/http-test | 2 | ||||
-rwxr-xr-x | sca-cpp/trunk/modules/http/httpd-test | 2 | ||||
-rw-r--r-- | sca-cpp/trunk/modules/http/httpd.hpp | 91 |
5 files changed, 148 insertions, 119 deletions
diff --git a/sca-cpp/trunk/modules/http/curl-test.cpp b/sca-cpp/trunk/modules/http/curl-test.cpp index 0a6fbcd8a6..59944546a1 100644 --- a/sca-cpp/trunk/modules/http/curl-test.cpp +++ b/sca-cpp/trunk/modules/http/curl-test.cpp @@ -50,7 +50,7 @@ std::ostringstream* curlWriter(const std::string& s, std::ostringstream* os) { } const bool testGet() { - CURLHandle ch; + CURLSession ch; { std::ostringstream os; const failable<list<std::ostringstream*>, std::string> r = get<std::ostringstream*>(curlWriter, &os, "http://localhost:8090", ch); @@ -66,7 +66,7 @@ const bool testGet() { return true; } -const bool testGetLoop(const int count, CURLHandle& ch) { +const bool testGetLoop(const int count, CURLSession& ch) { if (count == 0) return true; const failable<value, std::string> r = get("http://localhost:8090", ch); @@ -77,7 +77,7 @@ const bool testGetLoop(const int count, CURLHandle& ch) { const bool testGetPerf() { const int count = 50; - CURLHandle ch; + CURLSession ch; struct timeval start; struct timeval end; { diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp index 6c3a3a47dc..fd2e9857d1 100644 --- a/sca-cpp/trunk/modules/http/curl.hpp +++ b/sca-cpp/trunk/modules/http/curl.hpp @@ -30,6 +30,7 @@ #include <curl/types.h> #include <curl/easy.h> #include <string> +#include "gc.hpp" #include "list.hpp" #include "value.hpp" #include "element.hpp" @@ -41,11 +42,6 @@ namespace tuscany { namespace http { /** - * Set to true to log HTTP content. - */ -bool logContent = false; - -/** * CURL library context, one per process. */ class CURLContext { @@ -63,22 +59,45 @@ CURLContext curlContext; /** * Represents a CURL session handle. */ -class CURLHandle { +class CURLSession { public: - CURLHandle() : h(curl_easy_init()) { + CURLSession() : ch(new CURLHandle()) { } - ~CURLHandle() { - curl_easy_cleanup(h); + + ~CURLSession() { } - operator CURL*() const { - return h; + CURLSession(const CURLSession& c) : ch(c.ch) { } + private: - CURL* h; + class CURLHandle { + public: + CURLHandle() : h(curl_easy_init()) { + } + ~CURLHandle() { + curl_easy_cleanup(h); + h = NULL; + } + private: + CURL* h; + + friend CURL* handle(const CURLSession& c); + }; + + const gc_ptr<CURLHandle> ch; + + friend CURL* handle(const CURLSession& c); }; /** + * Returns the CURL handle used by a CURL session. + */ +CURL* handle(const CURLSession& c) { + return c.ch->h; +} + +/** * Context passed to the read callback function. */ class CURLReadContext { @@ -143,9 +162,10 @@ curl_slist* headers(curl_slist* cl, const list<std::string>& h) { return headers(curl_slist_append(cl, std::string(car(h)).c_str()), cdr(h)); } -template<typename R> const failable<list<R>, std::string> apply(const list<list<std::string> >& req, const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const std::string& verb, const CURLHandle& ch) { +template<typename R> const failable<list<R>, std::string> apply(const list<list<std::string> >& req, const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const std::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"); @@ -204,7 +224,9 @@ template<typename R> const failable<list<R>, std::string> apply(const list<list< /** * Evaluate an expression remotely, at the given URL. */ -const failable<value, std::string> evalExpr(const value& expr, const std::string& url, const CURLHandle& ch) { +const failable<value, std::string> evalExpr(const value& expr, const std::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; @@ -212,13 +234,6 @@ const failable<value, std::string> evalExpr(const value& expr, const std::string if (!hasContent(jsreq)) return mkfailure<value, std::string>(reason(jsreq)); - if (logContent) { - std::cout<< "content: " << std::endl; - write(content(jsreq), std::cout); - std::cout<< std::endl; - std::cout.flush(); - } - // POST it to the URL const list<std::string> h = mklist<std::string>("Content-Type: application/json-rpc"); const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(mklist<list<std::string> >(h, content(jsreq)), rcons<std::string>, list<std::string>(), url, "POST", ch); @@ -226,19 +241,58 @@ const failable<value, std::string> evalExpr(const value& expr, const std::string return mkfailure<value, std::string>(reason(res)); // Return result - if (logContent) { - std::cout << "content:" << std::endl; - write(cadr<list<std::string> >(content(res)), std::cout); - std::cout << std::endl; - } - const list<value> val = elementsToValues(content(json::readJSON(cadr<list<std::string> >(content(res)), cx))); - return cadr<value>(cadr<value>(val)); + failable<list<value>, std::string> jsres = json::readJSON(cadr<list<std::string> >(content(res)), cx); + if (!hasContent(jsres)) + return mkfailure<value, std::string>(reason(jsres)); + const list<value> val = elementsToValues(content(jsres)); + + const value rval(cadr<value>(cadr<value>(val))); + debug(rval, "http::evalExpr::result"); + return rval; +} + +/** + * Find and return a header. + */ +const failable<std::string, std::string> header(const std::string& prefix, const list<std::string>& h) { + if (isNil(h)) + return mkfailure<std::string, std::string>(std::string("Couldn't find header: ") + prefix); + const std::string s = car(h); + if (s.find(prefix) != 0) + return header(prefix, cdr(h)); + const std::string l(s.substr(prefix.length())); + return l.substr(0, l.find_first_of("\r\n")); +} + +/** + * Find and return a location header. + */ +const failable<std::string, std::string> location(const list<std::string>& h) { + return header("Location: ", h); +} + +/** + * Convert a location to an entry id. + */ +const failable<value, std::string> entryId(const failable<std::string, std::string> l) { + if (!hasContent(l)) + return mkfailure<value, std::string>(reason(l)); + const std::string ls(content(l)); + return value(ls.substr(ls.find_last_of("/") + 1)); +} + +/** + * Find and return a content-type header. + */ +const failable<std::string, std::string> contentType(const list<std::string>& h) { + return header("Content-Type: ", h); } /** * HTTP GET, return the resource at the given URL. */ -template<typename R> const failable<list<R>, std::string> get(const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const CURLHandle& ch) { +template<typename R> const failable<list<R>, std::string> get(const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const CURLSession& ch) { + debug(url, "http::get::url"); const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>()); return apply(req, reduce, initial, url, "GET", ch); } @@ -246,38 +300,41 @@ template<typename R> const failable<list<R>, std::string> get(const lambda<R(std /** * HTTP GET, return a list of values representing the resource at the given URL. */ -const failable<value, std::string> get(const std::string& url, const CURLHandle& ch) { +const failable<value, std::string> get(const std::string& url, const CURLSession& ch) { + debug(url, "http::get::url"); // Get the contents of the resource at the given URL const failable<list<list<std::string> >, std::string> res = get<list<std::string> >(rcons<std::string>, list<std::string>(), url, ch); if (!hasContent(res)) return mkfailure<value, std::string>(reason(res)); + const list<std::string> ls(reverse(cadr(content(res)))); - const std::string ct; - if (ct.find("application/atom+xml") != std::string::npos) { - // TODO Return an ATOM feed + const std::string ct(content(contentType(car(content(res))))); + if (ct == "application/atom+xml;type=entry") { + const value val(atom::entryValue(content(atom::readEntry(ls)))); + debug(val, "http::get::result"); + return val; } // Return the content as a string value std::ostringstream os; - write(reverse(cadr(content(res))), os); - return value(os.str()); + write(ls, os); + const value val(os.str()); + debug(val, "http::get::result"); + return val; } /** * HTTP POST. */ -const failable<value, std::string> post(const value& val, const std::string& url, const CURLHandle& ch) { +const failable<value, std::string> post(const value& val, const std::string& url, const CURLSession& ch) { // Convert value to an ATOM entry const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); if (!hasContent(entry)) return mkfailure<value, std::string>(reason(entry)); - if (logContent) { - std::cout << "content:" << std::endl; - write(list<std::string>(content(entry)), std::cout); - std::cout << std::endl; - } + debug(url, "http::post::url"); + debug(content(entry), "http::post::input"); // POST it to the URL const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml"); @@ -285,23 +342,24 @@ const failable<value, std::string> post(const value& val, const std::string& url const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "POST", ch); if (!hasContent(res)) return mkfailure<value, std::string>(reason(res)); - return value(true); + + // Return the new entry id from the HTTP location header + const failable<value, std::string> eid(entryId(location(car(content(res))))); + debug(eid, "http::post::result"); + return eid; } /** * HTTP PUT. */ -const failable<value, std::string> put(const value& val, const std::string& url, const CURLHandle& ch) { +const failable<value, std::string> put(const value& val, const std::string& url, const CURLSession& ch) { // Convert value to an ATOM entry const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); if (!hasContent(entry)) return mkfailure<value, std::string>(reason(entry)); - if (logContent) { - std::cout << "content:" << std::endl; - write(list<std::string>(content(entry)), std::cout); - std::cout << std::endl; - } + debug(url, "http::put::url"); + debug(content(entry), "http::put::input"); // PUT it to the URL const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml"); @@ -309,17 +367,23 @@ const failable<value, std::string> put(const value& val, const std::string& url, const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "PUT", ch); if (!hasContent(res)) return mkfailure<value, std::string>(reason(res)); + + debug(true, "http::put::result"); return value(true); } /** * HTTP DELETE. */ -const failable<value, std::string> del(const std::string& url, const CURLHandle& ch) { +const failable<value, std::string> del(const std::string& url, const CURLSession& ch) { + debug(url, "http::delete::url"); + const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>()); const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "DELETE", ch); if (!hasContent(res)) return mkfailure<value, std::string>(reason(res)); + + debug(true, "http::delete::result"); return value(true); } @@ -327,7 +391,7 @@ const failable<value, std::string> del(const std::string& url, const CURLHandle& * HTTP client proxy function. */ struct proxy { - proxy(const std::string& url, const CURLHandle& ch) : url(url), ch(ch) { + proxy(const std::string& url, const CURLSession& ch) : url(url), ch(ch) { } const value operator()(const list<value>& args) const { @@ -338,7 +402,7 @@ struct proxy { } const std::string url; - const CURLHandle& ch; + const CURLSession ch; }; } diff --git a/sca-cpp/trunk/modules/http/http-test b/sca-cpp/trunk/modules/http/http-test index 1ab0da64b9..0aaaec48df 100755 --- a/sca-cpp/trunk/modules/http/http-test +++ b/sca-cpp/trunk/modules/http/http-test @@ -28,5 +28,5 @@ rc=$? # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 2 +sleep 1 return $rc diff --git a/sca-cpp/trunk/modules/http/httpd-test b/sca-cpp/trunk/modules/http/httpd-test index 57c35c5cc9..8e1d681d84 100755 --- a/sca-cpp/trunk/modules/http/httpd-test +++ b/sca-cpp/trunk/modules/http/httpd-test @@ -31,7 +31,7 @@ rc=$? # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 2 +sleep 1 if [ "$rc" = "0" ]; then echo "OK" fi diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp index a9ced05208..05b959f1d2 100644 --- a/sca-cpp/trunk/modules/http/httpd.hpp +++ b/sca-cpp/trunk/modules/http/httpd.hpp @@ -50,18 +50,13 @@ #include "list.hpp" #include "value.hpp" +#include "debug.hpp" namespace tuscany { namespace httpd { /** - * Set to true to log requests and content. - */ -bool logRequests = false; -bool logContent = false; - -/** * Returns a server-scoped module configuration. */ template<typename C> void* makeServerConf(apr_pool_t *p, server_rec *s) { @@ -120,7 +115,7 @@ const std::string path(const list<value>& p) { */ const char* optional(const char* s) { if (s == NULL) - return "(null)"; + return ""; return s; } @@ -128,59 +123,40 @@ const std::string contentType(const request_rec* r) { return optional(apr_table_get(r->headers_in, "Content-Type")); } +#ifdef _DEBUG + /** - * Log HTTP request info. + * Debug log. */ -int logHeader(void* r, const char* key, const char* value) { - std::cout << "header key: " << key << ", value: " << value << std::endl; +int debugHeader(unused void* r, const char* key, const char* value) { + std::cerr << " header key: " << key << ", value: " << value << std::endl; return 1; } -const bool logRequest(request_rec* r, const std::string& msg) { - if (!logRequests) - return true; - std::cout << msg << std::endl; - std::cout << "protocol: " << optional(r->protocol) << std::endl; - std::cout << "method: " << optional(r->method) << std::endl; - std::cout << "method number: " << r->method_number << std::endl; - std::cout << "content type: " << contentType(r) << std::endl; - std::cout << "content encoding: " << optional(r->content_encoding) << std::endl; - apr_table_do(logHeader, r, r->headers_in, NULL); - std::cout << "uri: " << optional(r->unparsed_uri) << std::endl; - std::cout << "path: " << optional(r->uri) << std::endl; - std::cout << "path info: " << optional(r->path_info) << std::endl; - std::cout << "filename: " << optional(r->filename) << std::endl; - std::cout << "path tokens: " << pathTokens(r->uri) << std::endl; - std::cout << "args: " << optional(r->args) << std::endl; - std::cout.flush(); +const bool debugRequest(request_rec* r, const std::string& msg) { + std::cerr << msg << ":" << std::endl; + std::cerr << " protocol: " << optional(r->protocol) << std::endl; + std::cerr << " method: " << optional(r->method) << std::endl; + std::cerr << " method number: " << r->method_number << std::endl; + std::cerr << " content type: " << contentType(r) << std::endl; + std::cerr << " content encoding: " << optional(r->content_encoding) << std::endl; + apr_table_do(debugHeader, r, r->headers_in, NULL); + std::cerr << " uri: " << optional(r->unparsed_uri) << std::endl; + std::cerr << " path: " << optional(r->uri) << std::endl; + std::cerr << " path info: " << optional(r->path_info) << std::endl; + std::cerr << " filename: " << optional(r->filename) << std::endl; + std::cerr << " path tokens: " << pathTokens(r->uri) << std::endl; + std::cerr << " args: " << optional(r->args) << std::endl; return true; } -const bool logValue(const value& v, const std::string& msg) { - if (!logContent) - return true; - std::cout<< msg << ": " << v << std::endl; - std::cout.flush(); - return true; -} +#define httpdDebugRequest(r, msg) httpd::debugRequest(r, msg) -const bool logValue(const failable<value, std::string>& v, const std::string& msg) { - if (!logContent) - return true; - std::cout<< msg << ": " << v << std::endl; - std::cout.flush(); - return true; -} +#else -const bool logStrings(const list<std::string>& ls, const std::string& msg) { - if (!logContent) - return true; - std::cout<< msg << ": " << std::endl; - write(ls, std::cout); - std::cout<< std::endl; - std::cout.flush(); - return true; -} +#define httpdDebugRequest(r, msg) + +#endif /** * Returns a list of key value pairs from the args in a query string. @@ -256,14 +232,6 @@ const char* url(const value& v, request_rec* r) { } /** - * Convert an ATOM entry to a value. - */ -const value feedEntry(const list<value>& e) { - const list<value> v = elementsToValues(mklist<value>(caddr(e))); - return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v)))); -} - -/** * Write an HTTP result. */ const failable<int, std::string> writeResult(const failable<list<std::string>, std::string>& ls, const std::string& ct, request_rec* r) { @@ -271,10 +239,7 @@ const failable<int, std::string> writeResult(const failable<list<std::string>, s return mkfailure<int, std::string>(reason(ls)); std::ostringstream os; write(content(ls), os); - if (logContent) { - std::cout<< "content: " << std::endl << os.str() << std::endl; - std::cout.flush(); - } + debug(os.str(), "httpd::result"); const std::string etag(ap_md5(r->pool, (const unsigned char*)std::string(os.str()).c_str())); const char* match = apr_table_get(r->headers_in, "If-None-Match"); @@ -283,7 +248,7 @@ const failable<int, std::string> writeResult(const failable<list<std::string>, s r->status = HTTP_NOT_MODIFIED; return OK; } - ap_set_content_type(r, ct.c_str()); + ap_set_content_type(r, apr_pstrdup(r->pool, ct.c_str())); ap_rputs(std::string(os.str()).c_str(), r); return OK; } |