diff options
author | jsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68> | 2009-11-01 05:25:14 +0000 |
---|---|---|
committer | jsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68> | 2009-11-01 05:25:14 +0000 |
commit | 6b94d489977c1cb2eeddded3ee329fe6b9605d5c (patch) | |
tree | f51d8b2373102cb6c8ac9fc0e051b6f1227a414c /cpp/sca/modules/http/curl.hpp | |
parent | 9f187b46ae761e8275362d6c1533e9fe79028c7b (diff) |
Minor refactoring of read/write functions and primitive procs. Added functions to help store data in memcached. Fixes to HTTP support and more tests.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@831640 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to '')
-rw-r--r-- | cpp/sca/modules/http/curl.hpp | 234 |
1 files changed, 207 insertions, 27 deletions
diff --git a/cpp/sca/modules/http/curl.hpp b/cpp/sca/modules/http/curl.hpp index 2ea23010cb..c0d79cef13 100644 --- a/cpp/sca/modules/http/curl.hpp +++ b/cpp/sca/modules/http/curl.hpp @@ -34,11 +34,18 @@ #include "value.hpp" #include "element.hpp" #include "monad.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" namespace tuscany { namespace http { /** + * Set to true to log HTTP content. + */ +bool logContent = false; + +/** * CURL library context, one per process. */ class CURLContext { @@ -72,13 +79,37 @@ private: }; /** + * Context passed to the read callback function. + */ +class CURLReadContext { +public: + CURLReadContext(const list<std::string>& ilist) : ilist(ilist) { + } + list<std::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)) + return 0; + rcx.ilist = fragment(rcx.ilist, size * nmemb); + const std::string s = car(rcx.ilist); + rcx.ilist = cdr(rcx.ilist); + s.copy((char*)ptr, s.length()); + return s.length(); +} + +/** * Context passed to CURL write callback function. */ template<typename R> class CURLWriteContext { public: - CURLWriteContext(const lambda<R(R, std::string)>& reduce, const R& accum) : reduce(reduce), accum(accum) { + CURLWriteContext(const lambda<R(std::string, R)>& reduce, const R& accum) : reduce(reduce), accum(accum) { } - const lambda<R(R, std::string)> reduce; + const lambda<R(std::string, R)> reduce; R accum; }; @@ -88,7 +119,7 @@ public: template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) { CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data)); const size_t realsize = size * nmemb; - wcx.accum = wcx.reduce(wcx.accum, std::string((const char*)ptr, realsize)); + wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum); return realsize; } @@ -98,50 +129,199 @@ template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, template<typename R> size_t headerCallback(void *ptr, size_t size, size_t nmemb, void *data) { CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data)); const size_t realsize = size * nmemb; - wcx.accum = wcx.reduce(wcx.accum, std::string((const char*)ptr, realsize)); + wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum); return realsize; } /** - * HTTP GET, get a resource from a URL. + * 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. */ -template<typename R> const failable<R, int> get(const lambda<R(R, std::string)>& reduce, const R& initial, const std::string& url) { - CURLWriteContext<R> wcx(reduce, initial); +curl_slist* headers(curl_slist* cl, const list<std::string> h) { + if (isNil(h)) + return cl; + 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, CURLHandle& ch) { // Init the curl session - CURLHandle ch; - curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - curl_easy_setopt(ch, CURLOPT_URL, url.c_str()); + 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 + std::ostringstream os; + write(cadr(req), os); + const std::string s = os.str(); + const int sz = s.length(); + if (sz < 1400) + curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true); + + // 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<R> hcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))headerCallback<R>); + curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx); + CURLWriteContext<R> wcx(reduce, initial); curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))writeCallback<R>); curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx); - curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))headerCallback<R>); - curl_easy_setopt(ch, CURLOPT_HEADERDATA, &wcx); - - // Perform the HTTP GET + + // Set the request headers + curl_slist* hl = headers(NULL, car(req)); + if (hl != NULL) + curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); + + // Apply the HTTP verb + curl_easy_setopt(ch, CURLOPT_URL, url.c_str()); + 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 (rc) - return rc; + + if (hl != NULL) + curl_slist_free_all(hl); // Return the HTTP return code or content + if (rc) + return std::string(curl_easy_strerror(rc)); long httprc; curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); - if (httprc != 200) - return httprc; - return wcx.accum; + if (httprc != 200 && httprc != 201) { + std::ostringstream es; + es << "HTTP code " << httprc; + return es.str(); + } + return mklist<R>(hcx.accum, wcx.accum); } /** - * HTTP GET, get a list of values representing a resource from a URL. + * Evaluate an expression remotely, at the given URL. */ -const list<std::string> writeStringList(const list<std::string>& listSoFar, const std::string& s) { - return cons(s, listSoFar); +const failable<value, std::string> evalExpr(const value expr, const std::string& url, CURLHandle& ch) { + + // Convert expression to a JSON-RPC request + json::JSONContext cx; + const failable<list<std::string>, std::string> jsreq = jsonRequest(1, car<value>(expr), cdr<value>(expr), cx); + if (!hasValue(jsreq)) + return std::string(jsreq); + + if (logContent) { + std::cout<< "content: " << std::endl; + write(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, jsreq), rcons<std::string>, list<std::string>(), url, "POST", ch); + if (!hasValue(res)) + return std::string(res); + + // Return result + if (logContent) { + std::cout << "content:" << std::endl; + write(cadr<list<std::string> >(res), std::cout); + std::cout << std::endl; + } + const list<value> val = elementsToValues(json::readJSON(cadr<list<std::string> >(res), cx)); + return cadr<value>(cadr<value>(val)); +} + +/** + * 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, CURLHandle& ch) { + const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>()); + return apply(req, reduce, initial, url, "GET", ch); +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable<value, std::string> get(const std::string& url, CURLHandle& ch) { + + // 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 (!hasValue(res)) + return std::string(res); + const list<list<std::string> > ls = res; + + // TODO Return an ATOM feed + const std::string ct; + if (ct.find("application/atom+xml") != std::string::npos) { + } + + // Return the content as a string value + std::ostringstream os; + write(reverse(cadr(ls)), os); + return value(os.str()); } -const failable<list<std::string>, int> get(const std::string& url) { - const failable<list<std::string>, int> r = get<list<std::string> >(writeStringList, list<std::string>(), url); - if (!hasValue(r)) - return r; - return reverse(list<std::string>(r)); +/** + * HTTP POST. + */ +const failable<value, std::string> post(const value& val, const std::string& url, CURLHandle& ch) { + + // Convert value to an ATOM entry + const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasValue(entry)) + return std::string(entry); + if (logContent) { + std::cout << "content:" << std::endl; + write(list<std::string>(entry), std::cout); + std::cout << std::endl; + } + + // POST it to the URL + const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml"); + const list<list<std::string> > req = mklist<list<std::string> >(h, entry); + const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "POST", ch); + if (!hasValue(res)) + return std::string(res); + return value(true); +} + +/** + * HTTP PUT. + */ +const failable<value, std::string> put(const value& val, const std::string& url, CURLHandle& ch) { + + // Convert value to an ATOM entry + const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasValue(entry)) + return std::string(entry); + if (logContent) { + std::cout << "content:" << std::endl; + write(list<std::string>(entry), std::cout); + std::cout << std::endl; + } + + // POST it to the URL + const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml"); + const list<list<std::string> > req = mklist<list<std::string> >(h, entry); + const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "PUT", ch); + if (!hasValue(res)) + return std::string(res); + return value(true); +} + +/** + * HTTP DELETE. + */ +const failable<value, std::string> del(const std::string& url, CURLHandle& ch) { + 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 (!hasValue(res)) + return std::string(res); + return value(true); } } |