diff --git a/examples/github_client.cc b/examples/github_client.cc index 952bb4e..e7f4f6b 100644 --- a/examples/github_client.cc +++ b/examples/github_client.cc @@ -21,7 +21,7 @@ bool kSslVerify = true; const std::string kUrlRoot = "https://api.github.com"; // ----------------------------------------------------------------------------- -// JSON helper functions (based on cppjson). +// JSON helper functions (based on jsoncpp). // Parse a string to JSON object. Json::Value StringToJson(const std::string& str) { diff --git a/examples/http_client.cc b/examples/http_client.cc index 8721309..f37d511 100644 --- a/examples/http_client.cc +++ b/examples/http_client.cc @@ -25,34 +25,6 @@ void ExampleBasic() { std::cout << r->content() << std::endl; } -// Use predefined shortcuts. -void ExampleShortcut() { - webcc::HttpClientSession session; - - auto r = session.Get("http://httpbin.org/get", - {"key1", "value1", "key2", "value2"}, - {"Accept", "application/json"}); - - std::cout << r->content() << std::endl; -} - -#if WEBCC_ENABLE_SSL - -// HTTPS is auto-detected from the URL scheme. -void ExampleHttps() { - webcc::HttpClientSession session; - session.set_ssl_verify(kSslVerify); - - auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). - Url("https://httpbin.org/get"). - Query("key1", "value1"). - Header("Accept", "application/json")()); - - std::cout << r->content() << std::endl; -} - -#endif // WEBCC_ENABLE_SSL - // Example for testing Keep-Alive connection. // // Boost.org doesn't support persistent connection so always includes @@ -79,33 +51,6 @@ void ExampleKeepAlive(const std::string& url) { session.Get(url); } -void ExampleCompression() { - webcc::HttpClientSession session; - - auto r = session.Get("http://httpbin.org/gzip"); - - std::cout << r->content() << std::endl; - - r = session.Get("http://httpbin.org/deflate"); - - std::cout << r->content() << std::endl; -} - -// Get an image from HttpBin.org and save to the given file path. -// E.g., ExampleImage("E:\\example.jpg") -void ExampleImage(const std::string& path) { - webcc::HttpClientSession session; - - auto r = session.Get("http://httpbin.org/image/jpeg"); - - // Or - // auto r = session.Get("http://httpbin.org/image", {}, - // {"Accept", "image/jpeg"}); - - std::ofstream ofs(path, std::ios::binary); - ofs << r->content(); -} - int main() { WEBCC_LOG_INIT("", webcc::LOG_CONSOLE); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 627359b..9be4576 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,19 +1,25 @@ # Tests # Common libraries to link. -set(TEST_COMMON_LIBS webcc ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} - "${CMAKE_THREAD_LIBS_INIT}") +set(TEST_LIBS webcc ${Boost_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}") + +if(WEBCC_ENABLE_SSL) + set(TEST_LIBS ${TEST_LIBS} ${OPENSSL_LIBRARIES}) +endif() + if(WIN32) - set(TEST_COMMON_LIBS ${TEST_COMMON_LIBS} zlibstatic crypt32) + set(TEST_LIBS ${TEST_LIBS} zlibstatic crypt32) else() - set(TEST_COMMON_LIBS ${TEST_COMMON_LIBS} ${ZLIB_LIBRARIES}) + set(TEST_LIBS ${TEST_LIBS} ${ZLIB_LIBRARIES}) endif() if(UNIX) # Add `-ldl` for Linux to avoid "undefined reference to `dlopen'". - set(TEST_COMMON_LIBS ${TEST_COMMON_LIBS} ${CMAKE_DL_LIBS}) + set(TEST_LIBS ${TEST_LIBS} ${CMAKE_DL_LIBS}) endif() add_executable(test_logger test_logger.cc) -target_link_libraries(test_logger ${TEST_COMMON_LIBS}) +target_link_libraries(test_logger ${TEST_LIBS}) +add_executable(test_http_client test_http_client.cc) +target_link_libraries(test_http_client gtest jsoncpp ${TEST_LIBS}) diff --git a/test/test_http_client.cc b/test/test_http_client.cc new file mode 100644 index 0000000..6623dc5 --- /dev/null +++ b/test/test_http_client.cc @@ -0,0 +1,230 @@ +#include "gtest/gtest.h" + +#include "boost/algorithm/string.hpp" +#include "json/json.h" + +#include "webcc/http_client_session.h" +#include "webcc/logger.h" + +// ----------------------------------------------------------------------------- + +#if (defined(WIN32) || defined(_WIN64)) +// You need to set environment variable SSL_CERT_FILE properly to enable +// SSL verification. +bool kSslVerify = false; +#else +bool kSslVerify = true; +#endif + +// ----------------------------------------------------------------------------- +// JSON helper functions (based on jsoncpp). + +// Parse a string to JSON object. +Json::Value StringToJson(const std::string& str) { + Json::Value json; + + Json::CharReaderBuilder builder; + std::stringstream stream(str); + std::string errors; + if (!Json::parseFromStream(builder, stream, &json, &errors)) { + std::cerr << errors << std::endl; + } + + return json; +} + +// ----------------------------------------------------------------------------- + +#if 1 + +static void AssertGet(webcc::HttpResponsePtr r) { + Json::Value json = StringToJson(r->content()); + + Json::Value args = json["args"]; + + EXPECT_EQ(2, args.size()); + EXPECT_EQ("value1", args["key1"].asString()); + EXPECT_EQ("value2", args["key2"].asString()); + + Json::Value headers = json["headers"]; + + EXPECT_EQ("application/json", headers["Accept"].asString()); + EXPECT_EQ("gzip, deflate", headers["Accept-Encoding"].asString()); + EXPECT_EQ("httpbin.org", headers["Host"].asString()); +} + +TEST(TestHttpClient, Get_RequestFunc) { + webcc::HttpClientSession session; + + try { + auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). + Url("http://httpbin.org/get"). + Query("key1", "value1"). + Query("key2", "value2"). + Header("Accept", "application/json") + ()); + + AssertGet(r); + + } catch (const webcc::Exception& e) { + std::cerr << e.what() << std::endl; + } +} + +TEST(TestHttpClient, Get_Shortcut) { + webcc::HttpClientSession session; + + try { + // Use predefined shortcuts. + + auto r = session.Get("http://httpbin.org/get", + { "key1", "value1", "key2", "value2" }, + { "Accept", "application/json" }); + + AssertGet(r); + + } catch (const webcc::Exception& e) { + std::cerr << e.what() << std::endl; + } +} + +#if WEBCC_ENABLE_SSL +TEST(TestHttpClient, Get_SSL) { + webcc::HttpClientSession session; + + session.set_ssl_verify(kSslVerify); + + try { + // HTTPS is auto-detected from the URL scheme. + + auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). + Url("https://httpbin.org/get"). + Query("key1", "value1"). + Query("key2", "value2"). + Header("Accept", "application/json") + ()); + + AssertGet(r); + + } catch (const webcc::Exception& e) { + std::cerr << e.what() << std::endl; + } +} +#endif // WEBCC_ENABLE_SSL + +// ----------------------------------------------------------------------------- + +// Test Gzip compressed response. +TEST(TestHttpClient, Compression_Gzip) { + webcc::HttpClientSession session; + + try { + auto r = session.Get("http://httpbin.org/gzip"); + + Json::Value json = StringToJson(r->content()); + + EXPECT_EQ(true, json["gzipped"].asBool()); + + } catch (const webcc::Exception& e) { + std::cerr << e.what() << std::endl; + } +} + +// Test Deflate compressed response. +TEST(TestHttpClient, Compression_Deflate) { + webcc::HttpClientSession session; + + try { + auto r = session.Get("http://httpbin.org/deflate"); + + std::cout << r->content() << std::endl; + + Json::Value json = StringToJson(r->content()); + + EXPECT_EQ(true, json["deflated"].asBool()); + + } catch (const webcc::Exception& e) { + std::cerr << e.what() << std::endl; + } +} + +// ----------------------------------------------------------------------------- + +// Test persistent connections. +TEST(TestHttpClient, KeepAlive) { + webcc::HttpClientSession session; + + std::string url = "http://httpbin.org/get"; + try { + + // Keep-Alive by default. + auto r = session.Get(url); + + using boost::iequals; + + EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Keep-alive")); + + // Close by setting Connection header. + r = session.Get(url, {}, { "Connection", "Close" }); + + EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close")); + + // Close by using request builder. + r = session.Request(webcc::HttpRequestBuilder{}.Get(). + KeepAlive(false) + ()); + + EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close")); + + // Keep-Alive explicitly by using request builder. + r = session.Request(webcc::HttpRequestBuilder{}.Get(). + KeepAlive(true) + ()); + + EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Keep-alive")); + + } catch (const webcc::Exception& e) { + std::cerr << e.what() << std::endl; + } +} + +// ----------------------------------------------------------------------------- + +// Get a JPEG image. +TEST(TestHttpClient, GetImageJpeg) { + webcc::HttpClientSession session; + + std::string url = "http://httpbin.org/get"; + try { + + auto r = session.Get("http://httpbin.org/image/jpeg"); + + // Or + // auto r = session.Get("http://httpbin.org/image", {}, + // {"Accept", "image/jpeg"}); + + //std::ofstream ofs(path, std::ios::binary); + //ofs << r->content(); + + // TODO: Verify the response is a valid JPEG image. + + } catch (const webcc::Exception& e) { + std::cerr << e.what() << std::endl; + } +} + +#endif + +// ----------------------------------------------------------------------------- + +// TODO: Post requests + +// ----------------------------------------------------------------------------- + +int main(int argc, char* argv[]) { + // Set webcc::LOG_CONSOLE to enable logging. + WEBCC_LOG_INIT("", 0); + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/webcc/http_request_builder.cc b/webcc/http_request_builder.cc index 31c85bf..a94e18c 100644 --- a/webcc/http_request_builder.cc +++ b/webcc/http_request_builder.cc @@ -85,37 +85,4 @@ void HttpRequestBuilder::SetContent(HttpRequestPtr request, request->SetContent(std::move(data), true); } -//void HttpRequestBuilder::CreateFormData(std::string* data, -// const std::string& boundary) { -// for (auto& pair : files_) { -// data->append("--" + boundary + kCRLF); -// -// // Content-Disposition header -// data->append("Content-Disposition: form-data"); -// if (!pair.first.empty()) { -// data->append("; name=\"" + pair.first + "\""); -// } -// if (!pair.second.file_name().empty()) { -// data->append("; filename=\"" + pair.second.file_name() + "\""); -// } -// data->append(kCRLF); -// -// // Content-Type header -// if (!pair.second.mime_type().empty()) { -// data->append("Content-Type: " + pair.second.mime_type()); -// data->append(kCRLF); -// } -// -// data->append(kCRLF); -// -// // Payload -// data->append(pair.second.data()); -// -// data->append(kCRLF); -// } -// -// data->append("--" + boundary + "--"); -// data->append(kCRLF); -//} - } // namespace webcc diff --git a/webcc/http_request_builder.h b/webcc/http_request_builder.h index 13f6f22..9dc5736 100644 --- a/webcc/http_request_builder.h +++ b/webcc/http_request_builder.h @@ -94,7 +94,7 @@ public: return *this; } - HttpRequestBuilder& KeepAlive(bool keep_alive) { + HttpRequestBuilder& KeepAlive(bool keep_alive = true) { keep_alive_ = keep_alive; return *this; } @@ -107,8 +107,6 @@ public: private: void SetContent(HttpRequestPtr request, std::string&& data); - - //void CreateFormData(std::string* data, const std::string& boundary); private: std::string method_;