summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2015-08-28 00:36:44 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2015-08-28 00:36:44 +0100
commitf3837b3e0597bf5d7546f6f77374618980e98ca2 (patch)
tree3796683a40709c71c728506f1c483feaf8fa5de0
parentAdd RuntimeContext for easy context switching (diff)
downloadlibadhocutil-f3837b3e0597bf5d7546f6f77374618980e98ca2.tar.bz2
libadhocutil-f3837b3e0597bf5d7546f6f77374618980e98ca2.tar.xz
libadhocutil-f3837b3e0597bf5d7546f6f77374618980e98ca2.zip
Add CurlStreamSource for reading a cURL request as a std::istream
-rw-r--r--libadhocutil/curlStream.cpp81
-rw-r--r--libadhocutil/curlStream.h39
-rw-r--r--libadhocutil/net.ice10
-rw-r--r--libadhocutil/unittests/Jamfile.jam13
-rw-r--r--libadhocutil/unittests/testCurlStream.cpp20
5 files changed, 163 insertions, 0 deletions
diff --git a/libadhocutil/curlStream.cpp b/libadhocutil/curlStream.cpp
new file mode 100644
index 0000000..850dea8
--- /dev/null
+++ b/libadhocutil/curlStream.cpp
@@ -0,0 +1,81 @@
+#include "curlStream.h"
+#include <net.h>
+
+CurlStreamSource::CurlStreamSource(const std::string & url) :
+ curl_handle(curl_easy_init()),
+ curl_headers(nullptr),
+ buflen(0),
+ buf(nullptr),
+ res(CURLE_OK)
+{
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, &CurlStreamSource::recvWrapper);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, this);
+}
+
+CurlStreamSource::~CurlStreamSource()
+{
+ if (curl_headers) {
+ curl_slist_free_all(curl_headers);
+ }
+ if (res != CURLE_OK) {
+ AdHoc::Net::CurlException ce(res, curl_easy_strerror(res), IceUtil::Optional<Ice::Short>());
+ long http_code = 0;
+ if (curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code) == CURLE_OK) {
+ ce.httpcode = http_code;
+ }
+ curl_easy_cleanup(curl_handle);
+ throw ce;
+ }
+ curl_easy_cleanup(curl_handle);
+}
+
+std::streamsize
+CurlStreamSource::read(char * target, std::streamsize targetSize)
+{
+ if (!buflen) {
+ SwapContext();
+ }
+ size_t bytes = std::min<size_t>(buflen, targetSize);
+ memcpy(target, buf, bytes);
+ buflen -= bytes;
+ buf += bytes;
+ return bytes;
+}
+
+void
+CurlStreamSource::setopt(CURLoption opt, const void * & val)
+{
+ curl_easy_setopt(curl_handle, opt, val);
+}
+
+void
+CurlStreamSource::appendHeader(const char * header)
+{
+ curl_headers = curl_slist_append(curl_headers, header);
+}
+
+void
+CurlStreamSource::Callback()
+{
+ if (curl_headers) {
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, curl_headers);
+ }
+ res = curl_easy_perform(curl_handle);
+}
+
+size_t
+CurlStreamSource::recvWrapper(void * data, size_t sz, size_t nm, void * css)
+{
+ return static_cast<CurlStreamSource *>(css)->recv(data, sz * nm);
+}
+
+size_t
+CurlStreamSource::recv(void * data, size_t datalen)
+{
+ buf = (char *)data;
+ buflen = datalen;
+ SwapContext();
+ return datalen;
+}
+
diff --git a/libadhocutil/curlStream.h b/libadhocutil/curlStream.h
new file mode 100644
index 0000000..459a472
--- /dev/null
+++ b/libadhocutil/curlStream.h
@@ -0,0 +1,39 @@
+#ifndef ADHOCUTIL_CURLSTREAM_H
+#define ADHOCUTIL_CURLSTREAM_H
+
+#include <boost/iostreams/stream.hpp>
+#include "runtimeContext.h"
+#include <string>
+#include <curl/curl.h>
+#include "visibility.h"
+
+class DLL_PUBLIC CurlStreamSource : public boost::iostreams::source, RuntimeContext {
+ public:
+ CurlStreamSource(const std::string & url);
+ ~CurlStreamSource();
+
+ CurlStreamSource(const CurlStreamSource &) = delete;
+ void operator=(const CurlStreamSource &) = delete;
+
+ std::streamsize read(char * target, std::streamsize targetSize);
+
+ void setopt(CURLoption opt, const void * & val);
+ void appendHeader(const char * header);
+
+ private:
+ DLL_PRIVATE void Callback() override;
+
+ DLL_PRIVATE static size_t recvWrapper(void * data, size_t sz, size_t nm, void * css);
+ DLL_PRIVATE size_t recv(void * data, size_t datalen);
+
+ CURL * curl_handle;
+ struct curl_slist * curl_headers;
+ size_t buflen;
+ char * buf;
+ CURLcode res;
+};
+
+typedef boost::reference_wrapper<CurlStreamSource> css_ref;
+
+#endif
+
diff --git a/libadhocutil/net.ice b/libadhocutil/net.ice
new file mode 100644
index 0000000..510d749
--- /dev/null
+++ b/libadhocutil/net.ice
@@ -0,0 +1,10 @@
+module AdHoc {
+ module Net {
+ exception CurlException {
+ int resultcode;
+ string message;
+ optional(1) short httpcode;
+ };
+ };
+};
+
diff --git a/libadhocutil/unittests/Jamfile.jam b/libadhocutil/unittests/Jamfile.jam
index 65d8fad..33ff61b 100644
--- a/libadhocutil/unittests/Jamfile.jam
+++ b/libadhocutil/unittests/Jamfile.jam
@@ -17,3 +17,16 @@ run
testContext
;
+run
+ testCurlStream.cpp
+ : : :
+ <define>ROOT=\"$(me)\"
+ <define>BOOST_TEST_DYN_LINK
+ <library>..//adhocutil
+ <library>boost_utf
+ <library>boost_filesystem
+ <library>boost_system
+ :
+ testCurlStream
+ ;
+
diff --git a/libadhocutil/unittests/testCurlStream.cpp b/libadhocutil/unittests/testCurlStream.cpp
new file mode 100644
index 0000000..48e3c27
--- /dev/null
+++ b/libadhocutil/unittests/testCurlStream.cpp
@@ -0,0 +1,20 @@
+#define BOOST_TEST_MODULE CurlStream
+#include <boost/test/unit_test.hpp>
+
+#include "curlStream.h"
+#include "definedDirs.h"
+
+BOOST_AUTO_TEST_CASE( fetch_file )
+{
+ auto url = "file://" + RootDir.string() + "/testCurlStream.cpp";
+ CurlStreamSource css(url);
+ boost::iostreams::stream<css_ref> curlstrm(boost::ref(css));
+ std::string tok;
+ curlstrm >> tok;
+ BOOST_REQUIRE_EQUAL("#define", tok);
+ curlstrm >> tok;
+ BOOST_REQUIRE_EQUAL("BOOST_TEST_MODULE", tok);
+ curlstrm >> tok;
+ BOOST_REQUIRE_EQUAL("CurlStream", tok);
+}
+