From bd0fdbf902f8ca8e7e352582efe938e1d6743dd1 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Mon, 16 Nov 2009 06:57:41 +0000 Subject: Cleaning up SVN structure, moving sca trunk to sca-cpp/trunk. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@880633 13f79535-47bb-0310-9956-ffa450edef68 --- sca-cpp/trunk/modules/http/curl.hpp | 348 ++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 sca-cpp/trunk/modules/http/curl.hpp (limited to 'sca-cpp/trunk/modules/http/curl.hpp') diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp new file mode 100644 index 0000000000..5ee3a090b0 --- /dev/null +++ b/sca-cpp/trunk/modules/http/curl.hpp @@ -0,0 +1,348 @@ +/* + * 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 "list.hpp" +#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 { +public: + CURLContext() { + curl_global_init(CURL_GLOBAL_ALL); + } + ~CURLContext() { + curl_global_cleanup(); + } +}; + +CURLContext curlContext; + +/** + * Represents a CURL session handle. + */ +class CURLHandle { +public: + CURLHandle() : h(curl_easy_init()) { + } + ~CURLHandle() { + curl_easy_cleanup(h); + } + + operator CURL*() const { + return h; + } +private: + CURL* 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; + 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 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(std::string((const char*)ptr, realsize), wcx.accum); + return realsize; +} + +/** + * Called by CURL to write received header data. + */ +template size_t headerCallback(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(std::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, std::string(car(h)).c_str()), cdr(h)); +} + +template const failable, std::string> apply(const list >& req, const lambda& reduce, const R& initial, const std::string& url, const std::string& verb, const CURLHandle& ch) { + + // Init the curl session + 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 hcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))headerCallback); + 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); + + // 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 (hl != NULL) + curl_slist_free_all(hl); + + // Return the HTTP return code or content + if (rc) + return mkfailure, std::string>(curl_easy_strerror(rc)); + long httprc; + curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); + if (httprc != 200 && httprc != 201) { + std::ostringstream es; + es << "HTTP code " << httprc; + return mkfailure, std::string>(es.str()); + } + return mklist(hcx.accum, wcx.accum); +} + +/** + * Evaluate an expression remotely, at the given URL. + */ +const failable evalExpr(const value& expr, const std::string& url, const CURLHandle& ch) { + + // Convert expression to a JSON-RPC request + json::JSONContext cx; + const failable, std::string> jsreq = jsonRequest(1, car(expr), cdr(expr), cx); + if (!hasValue(jsreq)) + return mkfailure(reason(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 h = mklist("Content-Type: application/json-rpc"); + const failable >, std::string> res = apply >(mklist >(h, jsreq), rcons, list(), url, "POST", ch); + if (!hasValue(res)) + return mkfailure(reason(res)); + + // Return result + if (logContent) { + std::cout << "content:" << std::endl; + write(cadr >(res), std::cout); + std::cout << std::endl; + } + const list val = elementsToValues(json::readJSON(cadr >(res), cx)); + return cadr(cadr(val)); +} + +/** + * HTTP GET, return the resource at the given URL. + */ +template const failable, std::string> get(const lambda& reduce, const R& initial, const std::string& url, const CURLHandle& ch) { + 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 get(const std::string& url, const CURLHandle& ch) { + + // Get the contents of the resource at the given URL + const failable >, std::string> res = get >(rcons, list(), url, ch); + if (!hasValue(res)) + return mkfailure(reason(res)); + const list > ls = res; + + const std::string ct; + if (ct.find("application/atom+xml") != std::string::npos) { + // TODO Return an ATOM feed + } + + // Return the content as a string value + std::ostringstream os; + write(reverse(cadr(ls)), os); + return value(os.str()); +} + +/** + * HTTP POST. + */ +const failable post(const value& val, const std::string& url, const CURLHandle& ch) { + + // Convert value to an ATOM entry + const failable, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasValue(entry)) + return mkfailure(reason(entry)); + if (logContent) { + std::cout << "content:" << std::endl; + write(list(entry), std::cout); + std::cout << std::endl; + } + + // POST it to the URL + const list h = mklist("Content-Type: application/atom+xml"); + const list > req = mklist >(h, entry); + const failable >, std::string> res = apply >(req, rcons, list(), url, "POST", ch); + if (!hasValue(res)) + return mkfailure(reason(res)); + return value(true); +} + +/** + * HTTP PUT. + */ +const failable put(const value& val, const std::string& url, const CURLHandle& ch) { + + // Convert value to an ATOM entry + const failable, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasValue(entry)) + return mkfailure(reason(entry)); + if (logContent) { + std::cout << "content:" << std::endl; + write(list(entry), std::cout); + std::cout << std::endl; + } + + // PUT it to the URL + const list h = mklist("Content-Type: application/atom+xml"); + const list > req = mklist >(h, entry); + const failable >, std::string> res = apply >(req, rcons, list(), url, "PUT", ch); + if (!hasValue(res)) + return mkfailure(reason(res)); + return value(true); +} + +/** + * HTTP DELETE. + */ +const failable del(const std::string& url, const CURLHandle& ch) { + const list > req = mklist(list(), list()); + const failable >, std::string> res = apply >(req, rcons, list(), url, "DELETE", ch); + if (!hasValue(res)) + return mkfailure(reason(res)); + return value(true); +} + +/** + * HTTP client proxy function. + */ +struct proxy { + proxy(const std::string& url, const CURLHandle& ch) : url(url), ch(ch) { + } + + const value operator()(const list& args) const { + failable val = evalExpr(args, url, ch); + if (!hasValue(val)) + return value(); + return val; + } + + const std::string url; + const CURLHandle& ch; +}; + +} +} + +#endif /* tuscany_curl_hpp */ -- cgit v1.2.3