Add HttpClient

This commit is contained in:
Sebastian Messmer 2015-11-24 07:47:29 +01:00
parent 5148a6571e
commit feb806b392
9 changed files with 213 additions and 0 deletions

View File

@ -12,6 +12,8 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE rt)
ENDIF(CMAKE_SYSTEM_NAME)
TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE curl)
ENABLE_STYLE_WARNINGS()
# You can safely delete lines from here...

View File

@ -0,0 +1,47 @@
// Base version taken from https://techoverflow.net/blog/2013/03/15/c-simple-http-download-using-libcurl-easy-api/
#include "CurlHttpClient.h"
#include <sstream>
#include <iostream>
#include <curl/curl.h>
#include <curl/easy.h>
using boost::none;
using boost::optional;
using std::string;
using std::ostringstream;
namespace cpputils {
size_t CurlHttpClient::write_data(void *ptr, size_t size, size_t nmemb, ostringstream *stream) {
stream->write((const char *) ptr, size * nmemb);
return size * nmemb;
}
CurlHttpClient::CurlHttpClient() {
curl = curl_easy_init();
}
CurlHttpClient::~CurlHttpClient() {
curl_easy_cleanup(curl);
}
optional <string> CurlHttpClient::get(const string &url) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
// example.com is redirected, so we tell libcurl to follow redirection
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
ostringstream out;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlHttpClient::write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
// Perform the request, res will get the return code
CURLcode res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK) {
return none;
}
return out.str();
}
}

28
network/CurlHttpClient.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#ifndef MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_HPP
#define MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_HPP
#include "HttpClient.h"
#include "../macros.h"
namespace cpputils {
class CurlHttpClient final : public HttpClient {
public:
CurlHttpClient();
~CurlHttpClient();
boost::optional <std::string> get(const std::string &url) override;
private:
void *curl;
static size_t write_data(void *ptr, size_t size, size_t nmemb, std::ostringstream *stream);
DISALLOW_COPY_AND_ASSIGN(CurlHttpClient);
};
}
#endif

View File

@ -0,0 +1,22 @@
#include "FakeHttpClient.h"
using std::string;
using boost::optional;
using boost::none;
namespace cpputils {
FakeHttpClient::FakeHttpClient(): _sites() {
}
void FakeHttpClient::addWebsite(const string &url, const string &content) {
_sites[url] = content;
}
optional<string> FakeHttpClient::get(const string &url) {
auto found = _sites.find(url);
if (found == _sites.end()) {
return none;
}
return found->second;
}
}

26
network/FakeHttpClient.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef MESSMER_CPPUTILS_NETWORK_FAKEHTTPCLIENT_H
#define MESSMER_CPPUTILS_NETWORK_FAKEHTTPCLIENT_H
#include "HttpClient.h"
#include "../macros.h"
#include <map>
namespace cpputils {
class FakeHttpClient final : public HttpClient {
public:
FakeHttpClient();
void addWebsite(const std::string &url, const std::string &content);
boost::optional<std::string> get(const std::string &url) override;
private:
std::map<std::string, std::string> _sites;
DISALLOW_COPY_AND_ASSIGN(FakeHttpClient);
};
}
#endif

1
network/HttpClient.cpp Normal file
View File

@ -0,0 +1 @@
#include "HttpClient.h"

16
network/HttpClient.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_H
#define MESSMER_CPPUTILS_NETWORK_HTTPCLIENT_H
#include <string>
#include <boost/optional.hpp>
namespace cpputils {
class HttpClient {
public:
virtual ~HttpClient() {}
virtual boost::optional<std::string> get(const std::string& url) = 0;
};
};
#endif

View File

@ -0,0 +1,32 @@
#include <google/gtest/gtest.h>
#include <google/gmock/gmock.h>
#include "../../network/CurlHttpClient.h"
#include "../../pointer/unique_ref_boost_optional_gtest_workaround.h"
using std::string;
using boost::none;
using testing::MatchesRegex;
using namespace cpputils;
TEST(CurlHttpClientTest, InvalidProtocol) {
EXPECT_EQ(none, CurlHttpClient().get("invalid://example.com"));
}
TEST(CurlHttpClientTest, InvalidTld) {
EXPECT_EQ(none, CurlHttpClient().get("http://example.invalidtld"));
}
TEST(CurlHttpClientTest, InvalidDomain) {
EXPECT_EQ(none, CurlHttpClient().get("http://this_is_a_not_existing_domain.com"));
}
TEST(CurlHttpClientTest, ValidHttp) {
string content = CurlHttpClient().get("http://example.com").value();
EXPECT_THAT(content, MatchesRegex(".*Example Domain.*"));
}
TEST(CurlHttpClientTest, ValidHttps) {
string content = CurlHttpClient().get("https://example.com").value();
EXPECT_THAT(content, MatchesRegex(".*Example Domain.*"));
}

View File

@ -0,0 +1,39 @@
#include <google/gtest/gtest.h>
#include "../../network/FakeHttpClient.h"
#include "../../pointer/unique_ref_boost_optional_gtest_workaround.h"
using boost::none;
using namespace cpputils;
TEST(FakeHttpClientTest, Empty) {
EXPECT_EQ(none, FakeHttpClient().get("http://example.com"));
}
TEST(FakeHttpClientTest, Nonexisting) {
FakeHttpClient client;
client.addWebsite("http://existing.com", "content");
EXPECT_EQ(none, client.get("http://notexisting.com"));
}
TEST(FakeHttpClientTest, Existing) {
FakeHttpClient client;
client.addWebsite("http://existing.com", "content");
EXPECT_EQ("content", client.get("http://existing.com").value());
}
TEST(FakeHttpClientTest, TwoExisting) {
FakeHttpClient client;
client.addWebsite("http://firstexisting.com", "first_content");
client.addWebsite("http://secondexisting.com", "second_content");
EXPECT_EQ("first_content", client.get("http://firstexisting.com").value());
EXPECT_EQ("second_content", client.get("http://secondexisting.com").value());
EXPECT_EQ(none, client.get("http://notexisting.com"));
}
TEST(FakeHttpClientTest, Overwriting) {
FakeHttpClient client;
client.addWebsite("http://existing.com", "content");
client.addWebsite("http://existing.com", "new_content");
EXPECT_EQ("new_content", client.get("http://existing.com").value());
}