diff options
Diffstat (limited to '')
-rw-r--r-- | sca-cpp/trunk/modules/http/http.hpp | 358 |
1 files changed, 180 insertions, 178 deletions
diff --git a/sca-cpp/trunk/modules/http/http.hpp b/sca-cpp/trunk/modules/http/http.hpp index 408b9fdee5..1153f61840 100644 --- a/sca-cpp/trunk/modules/http/http.hpp +++ b/sca-cpp/trunk/modules/http/http.hpp @@ -71,86 +71,91 @@ public: */ class CURLSession { public: - CURLSession() : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(false), ca(""), cert(""), key(""), cookie(""), timeout(0) { + CURLSession() : p(), h(*(new (gc_new<handles>()) handles())), owner(false), ca(emptyString), cert(emptyString), key(emptyString), cookie(emptyString), timeout(0) { } - CURLSession(const string& ca, const string& cert, const string& key, const string& cookie, const int timeout) : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(true), ca(ca), cert(cert), key(key), cookie(cookie), timeout(timeout) { + CURLSession(const string& ca, const string& cert, const string& key, const string& cookie, const int timeout) : p(), h(*(new (gc_new<handles>()) handles())), owner(true), ca(ca), cert(cert), key(key), cookie(cookie), timeout(timeout) { } - 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), cookie(c.cookie), timeout(c.timeout) { + CURLSession(const CURLSession& c) : p(c.p), h(c.h), owner(false), ca(c.ca), cert(c.cert), key(c.key), cookie(c.cookie), timeout(c.timeout) { } - const CURLSession& operator=(const CURLSession& c) { - if(this == &c) - return *this; - 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; - cookie = c.cookie; - timeout = c.timeout; - return *this; - } + CURLSession& operator=(const CURLSession& c) = delete; ~CURLSession() { if (!owner) return; - if (h == NULL) + if (h.h == NULL) return; - curl_easy_cleanup(h); + debug(h.h, "http::~CURLSession::cleanup::h"); + curl_easy_cleanup(h.h); } private: - CURL* h; - gc_child_pool p; - apr_socket_t* sock; - apr_pollset_t* wpollset; - apr_pollfd_t* wpollfd; - apr_pollset_t* rpollset; - apr_pollfd_t* rpollfd; - bool owner; - - friend CURL* handle(const CURLSession& cs); - friend apr_socket_t* sock(const CURLSession& cs); - friend const failable<CURL*> setup(const string& url, CURLSession& cs); - friend const failable<bool> cleanup(CURLSession& cs); - friend const failable<bool> connect(const string& url, CURLSession& cs); - friend const failable<bool> send(const char* c, const size_t l, CURLSession& cs); - friend const failable<size_t> recv(char* c, const size_t l, CURLSession& cs); + class handles { + public: + handles() : h(NULL), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL) { + } + + handles(const handles& c) : h(c.h), sock(c.sock), wpollset(c.wpollset), wpollfd(c.wpollfd), rpollset(c.rpollset), rpollfd(c.rpollfd) { + } + + private: + CURL* h; + apr_socket_t* sock; + apr_pollset_t* wpollset; + apr_pollfd_t* wpollfd; + apr_pollset_t* rpollset; + apr_pollfd_t* rpollfd; + + friend class CURLSession; + friend CURL* const handle(const CURLSession& cs); + friend apr_socket_t* const sock(const CURLSession& cs); + friend const failable<CURL*> setup(const string& url, const CURLSession& cs); + friend const failable<bool> cleanup(const CURLSession& cs); + friend const failable<bool> connect(const string& url, const CURLSession& cs); + friend const failable<bool> send(const char* const c, const size_t l, const CURLSession& cs); + friend const failable<size_t> recv(char* const c, const size_t l, const CURLSession& cs); + }; + + const gc_child_pool p; + handles& h; + const bool owner; + + friend CURL* const handle(const CURLSession& cs); + friend apr_socket_t* const sock(const CURLSession& cs); + friend const failable<CURL*> setup(const string& url, const CURLSession& cs); + friend const failable<bool> cleanup(const CURLSession& cs); + friend const failable<bool> connect(const string& url, const CURLSession& cs); + friend const failable<bool> send(const char* const c, const size_t l, const CURLSession& cs); + friend const failable<size_t> recv(char* const c, const size_t l, const CURLSession& cs); public: - string ca; - string cert; - string key; - string cookie; - int timeout; + const string ca; + const string cert; + const string key; + const string cookie; + const int timeout; }; /** * Returns the CURL handle used by a CURL session. */ -CURL* handle(const CURLSession& cs) { - return cs.h; +CURL* const handle(const CURLSession& cs) { + return cs.h.h; } /** * Return an apr_socket_t for the socket used by a CURL session. */ -apr_socket_t* sock(const CURLSession& cs) { - return cs.sock; +apr_socket_t* const sock(const CURLSession& cs) { + return cs.h.sock; } /** * Convert a socket fd to an apr_socket_t. */ -apr_socket_t* sock(const int sd, const gc_pool& p) { +apr_socket_t* const sock(const int sd, const gc_pool& p) { int fd = sd; apr_socket_t* s = NULL; apr_os_sock_put(&s, &fd, pool(p)); @@ -160,14 +165,14 @@ apr_socket_t* sock(const int sd, const gc_pool& p) { /** * Convert a CURL return code to an error string. */ -const string curlreason(CURLcode rc) { +const string curlreason(const CURLcode rc) { return curl_easy_strerror(rc); } /** * Convert an APR status to an error string. */ -const string apreason(apr_status_t rc) { +const string apreason(const apr_status_t rc) { char buf[256]; return apr_strerror(rc, buf, sizeof(buf)); } @@ -178,7 +183,7 @@ const string apreason(apr_status_t rc) { const char escape_c2x[] = "0123456789ABCDEF"; const string escape(const string& unesc, const char* reserv) { - char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(unesc) + 3); + char* const copy = (char*)apr_palloc(gc_current_pool(), 3 * length(unesc) + 3); const unsigned char* s = (const unsigned char *)c_str(unesc); unsigned char* d = (unsigned char*)copy; unsigned c; @@ -225,9 +230,9 @@ const string hostName(const string& uri, const gc_pool& p) { apr_uri_t u; const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u); if (rc != APR_SUCCESS) - return ""; + return emptyString; if (u.hostname == NULL) - return ""; + return emptyString; return u.hostname; } @@ -238,9 +243,9 @@ const string scheme(const string& uri, const gc_pool& p) { apr_uri_t u; const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u); if (rc != APR_SUCCESS) - return ""; + return emptyString; if (u.scheme == NULL) - return ""; + return emptyString; return u.scheme; } @@ -262,13 +267,14 @@ const string topDomain(const string& host) { /** * Setup a CURL session */ -const failable<CURL*> setup(const string& url, CURLSession& cs) { +const failable<CURL*> setup(const string& url, const CURLSession& cs) { // Init CURL session - if (cs.h != NULL) + if (cs.h.h != NULL) cleanup(cs); - cs.h = curl_easy_init(); - CURL* ch = cs.h; + cs.h.h = curl_easy_init(); + debug(cs.h.h, "http::setup::init::h"); + CURL* const ch = cs.h.h; curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0"); #ifdef WANT_MAINTAINER_CURL_VERBOSE curl_easy_setopt(ch, CURLOPT_VERBOSE, true); @@ -279,34 +285,35 @@ const failable<CURL*> setup(const string& url, CURLSession& cs) { curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, true); curl_easy_setopt(ch, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(ch, CURLOPT_DNS_USE_GLOBAL_CACHE, 0); curl_easy_setopt(ch, CURLOPT_TIMEOUT, cs.timeout); // Setup SSL options - if (cs.ca != "") { + if (cs.ca != emptyString) { debug(cs.ca, "http::setup::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 != "") { + if (cs.cert != emptyString) { debug(cs.cert, "http::setup::cert"); curl_easy_setopt(ch, CURLOPT_SSLCERT, c_str(cs.cert)); curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); } - if (cs.key != "") { + if (cs.key != emptyString) { debug(cs.key, "http::setup::key"); curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key)); curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM"); } - if (cs.cookie != "") { + if (cs.cookie != emptyString) { debug(cs.cookie, "http::setup::cookie"); curl_easy_setopt(ch, CURLOPT_COOKIE, c_str(cs.cookie)); } // Set up HTTP basic auth if requested apr_uri_t u; - apr_pool_t* p = gc_current_pool(); + apr_pool_t* const p = gc_current_pool(); const apr_status_t prc = apr_uri_parse(p, c_str(url), &u); if (prc == APR_SUCCESS) { if (u.user != NULL) { @@ -336,11 +343,12 @@ const failable<CURL*> setup(const string& url, CURLSession& cs) { /** * Cleanup a CURL session */ -const failable<bool> cleanup(CURLSession& cs) { - if (cs.h == NULL) +const failable<bool> cleanup(const CURLSession& cs) { + if (cs.h.h == NULL) return true; - curl_easy_cleanup(cs.h); - cs.h = NULL; + debug(cs.h.h, "http::cleanup::cleanup::h"); + curl_easy_cleanup(cs.h.h); + cs.h.h = NULL; return true; } @@ -351,15 +359,16 @@ class CURLReadContext { public: CURLReadContext(const list<string>& ilist) : ilist(ilist) { } - list<string> ilist; + + gc_mutable_ref<list<string> > 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<CURLReadContext*>(data); - if (isNil(rcx.ilist)) + CURLReadContext& rcx = *(CURLReadContext*)data; + if (isNil((const list<string>)rcx.ilist)) return 0; const list<string> f(fragment(rcx.ilist, size * nmemb)); const string s = car(f); @@ -373,17 +382,18 @@ size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) { */ template<typename R> class CURLWriteContext { public: - CURLWriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) { + CURLWriteContext(const lambda<const R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) { } - const lambda<R(const string&, const R)> reduce; - R accum; + + const lambda<const R(const string&, const R)> reduce; + gc_mutable_ref<R> accum; }; /** * Called by CURL to write received data. */ template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) { - CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data)); + CURLWriteContext<R>& wcx = *(CURLWriteContext<R>*)data; const size_t realsize = size * nmemb; wcx.accum = wcx.reduce(string((const char*)ptr, realsize), wcx.accum); return realsize; @@ -393,13 +403,13 @@ template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, * 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<string>& h) { +curl_slist* headers(curl_slist* const cl, const list<string>& h) { if (isNil(h)) return cl; return headers(curl_slist_append(cl, c_str(string(car(h)))), cdr(h)); } -template<typename R> const failable<list<R> > apply(const list<list<string> >& hdr, const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const string& verb, CURLSession& cs) { +template<typename R> const failable<list<R> > apply(const list<list<string> >& hdr, const lambda<const R(const string&, const R)>& reduce, const R& initial, const string& url, const string& verb, const CURLSession& cs) { debug(url, "http::apply::url"); debug(verb, "http::apply::verb"); @@ -409,12 +419,12 @@ template<typename R> const failable<list<R> > apply(const list<list<string> >& h cleanup(cs); return mkfailure<list<R>>(fch); } - CURL* ch = content(fch); + CURL* const ch = content(fch); // Set the request headers curl_slist* hl = headers(NULL, car(hdr)); - if (hl != NULL) - curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); + hl = curl_slist_append(hl, "X-Accept: text/x-scheme; charset=utf-8"); + curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); // Convert request body to a string // TODO use HTTP chunking instead @@ -462,9 +472,7 @@ template<typename R> const failable<list<R> > apply(const list<list<string> >& h curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); if (httprc != 200 && httprc != 201) { cleanup(cs); - ostringstream es; - es << "HTTP code " << httprc; - return mkfailure<list<R> >(str(es)); + return mkfailure<list<R> >(string("HTTP code not 200"), (int)httprc, (httprc != 301 && httprc != 302 && httprc != 404)); } cleanup(cs); @@ -474,13 +482,12 @@ template<typename R> const failable<list<R> > apply(const list<list<string> >& h /** * Evaluate an expression remotely, at the given URL. */ -const failable<value> evalExpr(const value& expr, const string& url, CURLSession& cs) { +const failable<value> 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 - js::JSContext cx; - const failable<list<string> > jsreq = json::jsonRequest(1, car<value>(expr), cdr<value>(expr), cx); + const failable<list<string> > jsreq = json::jsonRequest(1, car<value>(expr), cdr<value>(expr)); if (!hasContent(jsreq)) return mkfailure<value>(jsreq); @@ -491,7 +498,7 @@ const failable<value> evalExpr(const value& expr, const string& url, CURLSession return mkfailure<value>(res); // Parse and return JSON-RPC result - const failable<value> rval = json::jsonResultValue(cadr<list<string> >(content(res)), cx); + const failable<value> rval = json::jsonResultValue(cadr<list<string> >(content(res))); debug(rval, "http::evalExpr::result"); if (!hasContent(rval)) return mkfailure<value>(rval); @@ -501,7 +508,7 @@ const failable<value> evalExpr(const value& expr, const string& url, CURLSession /** * Find and return a header. */ -const maybe<string> header(const char* prefix, const list<string>& h) { +const maybe<string> header(const char* const prefix, const list<string>& h) { if (isNil(h)) return maybe<string>(); const string s = car(h); @@ -516,7 +523,7 @@ const maybe<string> header(const char* prefix, const list<string>& h) { */ const string location(const list<string>& h) { const maybe<string> l = header("Location: ", h); - return hasContent(l)? content(l) : ""; + return hasContent(l)? content(l) : emptyString; } /** @@ -524,7 +531,7 @@ const string location(const list<string>& h) { */ const value entryId(const failable<string> l) { if (!hasContent(l)) - return list<value>(); + return nilListValue; const string ls(content(l)); return value(mklist<value>(string(substr(ls, find_last(ls, '/') + 1)))); } @@ -534,13 +541,13 @@ const value entryId(const failable<string> l) { */ const string contentType(const list<string>& h) { const maybe<string> ct = header("Content-Type: ", h); - return hasContent(ct)? content(ct) : ""; + return hasContent(ct)? content(ct) : emptyString; } /** * HTTP GET, return the resource at the given URL. */ -template<typename R> const failable<list<R> > get(const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, CURLSession& cs) { +template<typename R> const failable<list<R> > get(const lambda<const R(const string&, const R)>& reduce, const R& initial, const string& url, const CURLSession& cs) { debug(url, "http::get::url"); const list<list<string> > req = mklist(list<string>(), list<string>()); return apply(req, reduce, initial, url, "GET", cs); @@ -549,7 +556,7 @@ template<typename R> const failable<list<R> > get(const lambda<R(const string&, /** * HTTP GET, return a list of values representing the resource at the given URL. */ -const failable<value> getcontent(const string& url, CURLSession& cs) { +const failable<value> getcontent(const string& url, const CURLSession& cs) { debug(url, "http::get::url"); // Get the contents of the resource at the given URL @@ -576,6 +583,15 @@ const failable<value> responseValue(const list<list<string> > res) { const list<string> ls(reverse(cadr(res))); debug(ls, "http::responseValue::content"); + if (contains(ct, "text/x-scheme")) { + // Read a Scheme value + ostringstream os; + write(ls, os); + istringstream is(str(os)); + const value val(content(scheme::readValue(is))); + debug(val, "http::responseValue::result"); + return val; + } if (atom::isATOMEntry(ls)) { // Read an ATOM entry const value val(elementsToValues(content(atom::readATOMEntry(ls)))); @@ -596,8 +612,7 @@ const failable<value> responseValue(const list<list<string> > res) { } if (contains(ct, "text/javascript") || contains(ct, "application/json") || json::isJSON(ls)) { // Read a JSON document - js::JSContext cx; - const value val(json::jsonValues(content(json::readJSON(ls, cx)))); + const value val(content(json::readValue(ls))); debug(val, "http::responseValue::result"); return val; } @@ -612,14 +627,13 @@ const failable<value> responseValue(const list<list<string> > res) { const list<string> jls = mklist<string>(substr(s, fp + 1, lp - (fp + 1))); debug(jls, "http::responseValue::javascript::content"); - js::JSContext cx; - const value val(json::jsonValues(content(json::readJSON(jls, cx)))); + const value val(content(json::readValue(jls))); debug(val, "http::responseValue::result"); return val; } - if (contains(ct, "text/xml") || contains(ct, "application/xml") || isXML(ls)) { + if (contains(ct, "text/xml") || contains(ct, "application/xml") || xml::isXML(ls)) { // Read an XML document - const value val(elementsToValues(readXML(ls))); + const value val(elementsToValues(content(xml::readElements(ls)))); debug(val, "http::responseValue::result"); return val; } @@ -633,7 +647,7 @@ const failable<value> responseValue(const list<list<string> > res) { /** * HTTP GET, return a list of values representing the resource at the given URL. */ -const failable<value> get(const string& url, CURLSession& cs) { +const failable<value> get(const string& url, const CURLSession& cs) { debug(url, "http::get::url"); // Get the contents of the resource at the given URL @@ -661,58 +675,41 @@ const failable<list<list<string> > > writeRequest(const failable<list<string> >& */ const failable<list<list<string> > > contentRequest(const value& c, unused const string& url) { - // Check if the client requested a specific format - //TODO derive that from given URL - const list<value> fmt = assoc<value>("format", list<value>()); - - // Write as a scheme value if requested by the client - if (!isNil(fmt) && cadr(fmt) == "scheme") - return writeRequest(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8"); + // Write in the format requested by the client, if any + const list<value> fmt = assoc<value>("format", nilListValue); + if (!isNil(fmt)) { + if (cadr(fmt) == "scheme") + return writeRequest(scheme::writeValue(c), "text/x-scheme; charset=utf-8"); + if (cadr(fmt) == "json") + return writeRequest(json::writeValue(c), "application/json; charset=utf-8"); + if (cadr(fmt) == "xml") + return writeRequest(xml::writeElements(valuesToElements(c)), "text/xml; charset=utf-8"); + } // Write a simple value as a JSON value if (!isList(c)) { - js::JSContext cx; - if (isSymbol(c)) { - const list<value> lc = mklist<value>(mklist<value>("name", value(string(c)))); - debug(lc, "http::contentRequest::symbol"); - return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8"); - } - const list<value> lc = mklist<value>(mklist<value>("value", c)); - debug(lc, "http::contentRequest::value"); - return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8"); + debug(c, "http::contentRequest::value"); + return writeRequest(json::writeValue(c), "application/json; charset=utf-8"); } - // Write an empty list as a JSON empty value + // Write an empty list as a JSON value if (isNil((list<value>)c)) { - js::JSContext cx; - debug(list<value>(), "http::contentRequest::empty"); - return writeRequest(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8"); + debug(nilListValue, "http::contentRequest::empty"); + return writeRequest(json::writeValue(c), "application/json; charset=utf-8"); } // Write content-type / content-list pair if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c))) return writeRequest(convertValues<string>(cadr<value>(c)), car<value>(c)); - // Write an assoc value as JSON + // Write an assoc value as a JSON value if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) { - js::JSContext cx; - const list<value> lc = mklist<value>(c); - debug(lc, "http::contentRequest::assoc"); - debug(valuesToElements(lc), "http::contentRequest::assoc::element"); - return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8"); - } - - // Write value as JSON if requested by the client - if (!isNil(fmt) && cadr(fmt) == "json") { - js::JSContext cx; - return writeRequest(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8"); + debug(c, "http::contentRequest::assoc"); + return writeRequest(json::writeValue(c), "application/json; charset=utf-8"); } - // Convert list of values to element values - const list<value> e = valuesToElements(c); - debug(e, "http::contentRequest::elements"); - // Write an ATOM feed or entry + const list<value> e = valuesToElements(c); if (isList(car<value>(e)) && !isNil(car<value>(e))) { const list<value> el = car<value>(e); if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) { @@ -724,14 +721,13 @@ const failable<list<list<string> > > contentRequest(const value& c, unused const } // Write any other compound value as a JSON value - js::JSContext cx; - return writeRequest(json::writeJSON(e, cx), "application/json; charset=utf-8"); + return writeRequest(json::writeValue(c), "application/json; charset=utf-8"); } /** * HTTP POST. */ -const failable<value> post(const value& val, const string& url, CURLSession& cs) { +const failable<value> post(const value& val, const string& url, const CURLSession& cs) { debug(url, "http::post::url"); // Convert value to a content request @@ -760,7 +756,7 @@ const failable<value> post(const value& val, const string& url, CURLSession& cs) /** * HTTP PUT. */ -const failable<value> put(const value& val, const string& url, CURLSession& cs) { +const failable<value> put(const value& val, const string& url, const CURLSession& cs) { debug(url, "http::put::url"); // Convert value to a content request @@ -775,13 +771,13 @@ const failable<value> put(const value& val, const string& url, CURLSession& cs) return mkfailure<value>(res); debug(true, "http::put::result"); - return value(true); + return trueValue; } /** * HTTP PATCH. */ -const failable<value> patch(const value& val, const string& url, CURLSession& cs) { +const failable<value> patch(const value& val, const string& url, const CURLSession& cs) { debug(url, "http::put::patch"); // Convert value to a content request @@ -796,13 +792,13 @@ const failable<value> patch(const value& val, const string& url, CURLSession& cs return mkfailure<value>(res); debug(true, "http::patch::result"); - return value(true); + return trueValue; } /** * HTTP DELETE. */ -const failable<value, string> del(const string& url, CURLSession& cs) { +const failable<value, string> del(const string& url, const CURLSession& cs) { debug(url, "http::delete::url"); const list<list<string> > req = mklist(list<string>(), list<string>()); @@ -811,7 +807,7 @@ const failable<value, string> del(const string& url, CURLSession& cs) { return mkfailure<value>(res); debug(true, "http::delete::result"); - return value(true); + return trueValue; } /** @@ -827,7 +823,7 @@ const string hostName() { /** * 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* const pollfd(apr_socket_t* const s, const int e, const gc_pool& p) { apr_pollfd_t* pfd = gc_new<apr_pollfd_t>(p); pfd->p = pool(p); pfd->desc_type = APR_POLL_SOCKET; @@ -841,7 +837,7 @@ apr_pollfd_t* pollfd(apr_socket_t* s, const int e, const gc_pool& p) { /** * Connect to a URL. */ -const failable<bool> connect(const string& url, CURLSession& cs) { +const failable<bool> connect(const string& url, const CURLSession& cs) { debug(url, "http::connect::url"); // Setup the CURL session @@ -850,7 +846,7 @@ const failable<bool> connect(const string& url, CURLSession& cs) { cleanup(cs); return mkfailure<bool>(fch); } - CURL* ch = content(fch); + CURL* const ch = content(fch); // Connect curl_easy_setopt(ch, CURLOPT_CONNECT_ONLY, true); @@ -867,23 +863,23 @@ const failable<bool> connect(const string& url, CURLSession& cs) { cleanup(cs); return mkfailure<bool>(string(curl_easy_strerror(grc))); } - cs.sock = sock(sd, cs.p); + cs.h.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); + const apr_status_t rpcrc = apr_pollset_create(&cs.h.rpollset, 1, pool(cs.p), 0); if (rpcrc != APR_SUCCESS) { cleanup(cs); return mkfailure<bool>(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); + cs.h.rpollfd = pollfd(cs.h.sock, APR_POLLIN, cs.p); + apr_pollset_add(cs.h.rpollset, cs.h.rpollfd); + const apr_status_t wpcrc = apr_pollset_create(&cs.h.wpollset, 1, pool(cs.p), 0); if (wpcrc != APR_SUCCESS) { cleanup(cs); return mkfailure<bool>(apreason(wpcrc)); } - cs.wpollfd = pollfd(cs.sock, APR_POLLOUT, cs.p); - apr_pollset_add(cs.wpollset, cs.wpollfd); + cs.h.wpollfd = pollfd(cs.h.sock, APR_POLLOUT, cs.p); + apr_pollset_add(cs.h.wpollset, cs.h.wpollfd); return true; } @@ -891,11 +887,11 @@ const failable<bool> connect(const string& url, CURLSession& cs) { /** * Send an array of chars. */ -const failable<bool> send(const char* c, const size_t l, CURLSession& cs) { +const failable<bool> send(const char* const c, const size_t l, const CURLSession& cs) { // Send the data size_t wl = 0; - const CURLcode rc = curl_easy_send(cs.h, c, (size_t)l, &wl); + const CURLcode rc = curl_easy_send(cs.h.h, c, (size_t)l, &wl); if (rc == CURLE_OK && wl == (size_t)l) return true; if (rc != CURLE_AGAIN) { @@ -906,7 +902,7 @@ const failable<bool> send(const char* c, const size_t l, CURLSession& cs) { // 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); + const apr_status_t pollrc = apr_pollset_poll(cs.h.wpollset, -1, &pollcount, &pollfds); if (pollrc != APR_SUCCESS) return mkfailure<bool>(apreason(pollrc)); @@ -917,11 +913,11 @@ const failable<bool> send(const char* c, const size_t l, CURLSession& cs) { /** * Receive an array of chars. */ -const failable<size_t> recv(char* c, const size_t l, CURLSession& cs) { +const failable<size_t> recv(char* const c, const size_t l, const CURLSession& cs) { // Receive data size_t rl; - const CURLcode rc = curl_easy_recv(cs.h, c, (size_t)l, &rl); + const CURLcode rc = curl_easy_recv(cs.h.h, c, (size_t)l, &rl); if (rc == CURLE_OK) return (size_t)rl; if (rc == 1) @@ -934,7 +930,7 @@ const failable<size_t> recv(char* c, const size_t l, CURLSession& cs) { // 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); + const apr_status_t pollrc = apr_pollset_poll(cs.h.rpollset, -1, &pollcount, &pollfds); if (pollrc != APR_SUCCESS) { cleanup(cs); return mkfailure<size_t>(apreason(pollrc)); @@ -990,8 +986,9 @@ const value escapeQuery(const value& arg) { /** * HTTP client proxy function. */ -struct proxy { - proxy(const string& uri, const string& ca, const string& cert, const string& key, const string& cookie, const int timeout, const gc_pool& p) : p(p), uri(uri), ca(ca), cert(cert), key(key), cookie(cookie), cs(*(new (gc_new<CURLSession>(p)) CURLSession(ca, cert, key, cookie, timeout))) { +class proxy { +public: + proxy(const string& uri, const string& ca, const string& cert, const string& key, const string& cookie, const int timeout) : uri(uri), cs(mksession(ca, cert, key, cookie, timeout)) { } const value operator()(const list<value>& args) const { @@ -1001,39 +998,44 @@ struct proxy { const list<value> lp = filter<value>(filterPath, cadr(args)); debug(lp, "http::proxy::path"); const list<value> lq = map<value, value>(escapeQuery, filter<value>(filterQuery, cadr(args))); - debug(lp, "http::proxy::query"); - const value p = path(lp); + debug(lq, "http::proxy::query"); const value q = queryString(lq); - const failable<value> val = get(uri + p + (q != ""? string("?") + q : string("")), cs); + const failable<value> val = get(uri + (string)path(lp) + (q != emptyString? string("?") + (string)q : emptyString), *cs); return content(val); } if (fun == "post") { - const failable<value> val = post(caddr(args), uri + path(cadr(args)), cs); + const failable<value> val = post(caddr(args), uri + (string)path(cadr(args)), *cs); return content(val); } if (fun == "put") { - const failable<value> val = put(caddr(args), uri + path(cadr(args)), cs); + const failable<value> val = put(caddr(args), uri + (string)path(cadr(args)), *cs); return content(val); } if (fun == "patch") { - const failable<value> val = patch(caddr(args), uri + path(cadr(args)), cs); + const failable<value> val = patch(caddr(args), uri + (string)path(cadr(args)), *cs); return content(val); } if (fun == "delete") { - const failable<value> val = del(uri + path(cadr(args)), cs); + const failable<value> val = del(uri + (string)path(cadr(args)), *cs); return content(val); } - const failable<value> val = evalExpr(args, uri, cs); + const failable<value> val = evalExpr(args, uri, *cs); return content(val); } - const gc_pool p; +private: const string uri; - const string ca; - const string cert; - const string key; - const string cookie; - CURLSession& cs; + const perthread_ptr<http::CURLSession> cs; + + + const perthread_ptr<http::CURLSession> mksession(const string& ca, const string& cert, const string& key, const string& cookie, const int timeout) { + const gc_pool cp = gc_current_pool(); + const lambda<const gc_ptr<http::CURLSession>()> newsession = [ca, cert, key, cookie, timeout, cp]() -> const gc_ptr<http::CURLSession> { + const gc_scoped_pool sp(pool(cp)); + return new (gc_new<http::CURLSession>()) http::CURLSession(ca, cert, key, cookie, timeout); + }; + return *(new (gc_new<perthread_ptr<http::CURLSession> >()) perthread_ptr<CURLSession>(newsession)); + } }; } |