From f3837b3e0597bf5d7546f6f77374618980e98ca2 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 28 Aug 2015 00:36:44 +0100 Subject: Add CurlStreamSource for reading a cURL request as a std::istream --- libadhocutil/curlStream.cpp | 81 +++++++++++++++++++++++++++++++ libadhocutil/curlStream.h | 39 +++++++++++++++ libadhocutil/net.ice | 10 ++++ libadhocutil/unittests/Jamfile.jam | 13 +++++ libadhocutil/unittests/testCurlStream.cpp | 20 ++++++++ 5 files changed, 163 insertions(+) create mode 100644 libadhocutil/curlStream.cpp create mode 100644 libadhocutil/curlStream.h create mode 100644 libadhocutil/net.ice create mode 100644 libadhocutil/unittests/testCurlStream.cpp 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 + +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()); + 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(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(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 +#include "runtimeContext.h" +#include +#include +#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 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 + : : : + ROOT=\"$(me)\" + BOOST_TEST_DYN_LINK + ..//adhocutil + boost_utf + boost_filesystem + 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 + +#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 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); +} + -- cgit v1.2.3