#include #include "gtest/gtest.h" #include "boost/algorithm/string.hpp" #include "boost/filesystem/operations.hpp" #include "json/json.h" #include "webcc/client_session.h" #include "webcc/logger.h" // ----------------------------------------------------------------------------- // JSON helper functions (based on jsoncpp). // Parse a string to JSON object. static 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; } // ----------------------------------------------------------------------------- TEST(ClientTest, Head_RequestFunc) { webcc::ClientSession session; try { auto r = session.Request(webcc::RequestBuilder{}. Head("http://httpbin.org/get") ()); EXPECT_EQ(webcc::Status::kOK, r->status()); EXPECT_EQ("OK", r->reason()); EXPECT_EQ("", r->data()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } TEST(ClientTest, Head_Shortcut) { webcc::ClientSession session; try { auto r = session.Head("http://httpbin.org/get"); EXPECT_EQ(webcc::Status::kOK, r->status()); EXPECT_EQ("OK", r->reason()); EXPECT_EQ("", r->data()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // Force Accept-Encoding to be "identity" so that HttpBin.org will include // a Content-Length header in the response. // This tests that the response with Content-Length while no body could be // correctly parsed. TEST(ClientTest, Head_AcceptEncodingIdentity) { webcc::ClientSession session; try { auto r = session.Request(webcc::RequestBuilder{}. Head("http://httpbin.org/get"). Header("Accept-Encoding", "identity") ()); EXPECT_EQ(webcc::Status::kOK, r->status()); EXPECT_EQ("OK", r->reason()); EXPECT_TRUE(r->HasHeader(webcc::headers::kContentLength)); EXPECT_EQ("", r->data()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // ----------------------------------------------------------------------------- static void AssertGet(webcc::ResponsePtr r) { EXPECT_EQ(webcc::Status::kOK, r->status()); EXPECT_EQ("OK", r->reason()); Json::Value json = StringToJson(r->data()); 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("httpbin.org", headers["Host"].asString()); #if WEBCC_ENABLE_GZIP EXPECT_EQ("gzip, deflate", headers["Accept-Encoding"].asString()); #else EXPECT_EQ("identity", headers["Accept-Encoding"].asString()); #endif // WEBCC_ENABLE_GZIP } TEST(ClientTest, Get_RequestFunc) { webcc::ClientSession session; try { auto r = session.Request(webcc::RequestBuilder{}. Get("http://httpbin.org/get"). Query("key1", "value1"). Query("key2", "value2"). Header("Accept", "application/json") ()); AssertGet(r); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } TEST(ClientTest, Get_Shortcut) { webcc::ClientSession session; try { auto r = session.Get("http://httpbin.org/get", { "key1", "value1", "key2", "value2" }, { "Accept", "application/json" }); AssertGet(r); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } #if WEBCC_ENABLE_SSL TEST(ClientTest, Get_SSL) { webcc::ClientSession session; try { // HTTPS is auto-detected from the URL scheme. auto r = session.Request(webcc::RequestBuilder{}. Get("https://httpbin.org/get"). Query("key1", "value1"). Query("key2", "value2"). Header("Accept", "application/json") ()); AssertGet(r); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } #endif // WEBCC_ENABLE_SSL // ----------------------------------------------------------------------------- #if WEBCC_ENABLE_GZIP // Test Gzip compressed response. TEST(ClientTest, Compression_Gzip) { webcc::ClientSession session; try { auto r = session.Get("http://httpbin.org/gzip"); Json::Value json = StringToJson(r->data()); EXPECT_EQ(true, json["gzipped"].asBool()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // Test Deflate compressed response. TEST(ClientTest, Compression_Deflate) { webcc::ClientSession session; try { auto r = session.Get("http://httpbin.org/deflate"); Json::Value json = StringToJson(r->data()); EXPECT_EQ(true, json["deflated"].asBool()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // Test trying to compress the request. // TODO TEST(ClientTest, Compression_Request) { webcc::ClientSession session; try { const std::string data = "{'name'='Adam', 'age'=20}"; // This doesn't really compress the body! auto r = session.Request(webcc::RequestBuilder{}. Post("http://httpbin.org/post"). Body(data).Json(). Gzip() ()); //Json::Value json = StringToJson(r->data()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } #endif // WEBCC_ENABLE_GZIP // ----------------------------------------------------------------------------- // Test persistent (keep-alive) connections. // // NOTE: // Boost.org doesn't support persistent connection and always includes // "Connection: Close" header in the response. // Both Google and GitHub support persistent connection but they don't like // to include "Connection: Keep-Alive" header in the responses. // URLs: // "http://httpbin.org/get"; // "https://www.boost.org/LICENSE_1_0.txt"; // "https://www.google.com"; // "https://api.github.com/events"; // TEST(ClientTest, KeepAlive) { webcc::ClientSession 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::RequestBuilder{}. Get(url).KeepAlive(false) ()); EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close")); // Keep-Alive explicitly by using request builder. r = session.Request(webcc::RequestBuilder{}. Get(url).KeepAlive(true) ()); EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Keep-alive")); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // ----------------------------------------------------------------------------- // Get a JPEG image (without streaming). TEST(ClientTest, GetImageJpeg_NoStream) { webcc::ClientSession session; try { auto r = session.Get("http://httpbin.org/image/jpeg"); // TODO: Verify the response is a valid JPEG image. //std::ofstream ofs(, std::ios::binary); //ofs << r->data(); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // ----------------------------------------------------------------------------- // Streaming TEST(ClientTest, Stream_GetImageJpeg) { webcc::ClientSession session; try { auto r = session.Request(webcc::RequestBuilder{}. Get("http://httpbin.org/image/jpeg")(), true); auto file_body = r->file_body(); EXPECT_TRUE(!!file_body); EXPECT_TRUE(!file_body->path().empty()); // Backup the path of the temp file. const webcc::Path ori_path = file_body->path(); const webcc::Path new_path("./wolf.jpeg"); bool moved = file_body->Move(new_path); EXPECT_TRUE(moved); EXPECT_TRUE(boost::filesystem::exists(new_path)); // The file in the original path should not exist any more. EXPECT_TRUE(!boost::filesystem::exists(ori_path)); // After move, the original path should be reset. EXPECT_TRUE(file_body->path().empty()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // Test whether the streamed file will be deleted or not at the end if it's // not moved to another path by the user. TEST(ClientTest, Stream_GetImageJpeg_NoMove) { webcc::ClientSession session; try { webcc::Path ori_path; { auto r = session.Request(webcc::RequestBuilder{}. Get("http://httpbin.org/image/jpeg")(), true); auto file_body = r->file_body(); EXPECT_TRUE(!!file_body); EXPECT_TRUE(!file_body->path().empty()); // Backup the path of the temp file. ori_path = file_body->path(); } // The temp file should be deleted. EXPECT_TRUE(!boost::filesystem::exists(ori_path)); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } // ----------------------------------------------------------------------------- TEST(ClientTest, Post_RequestFunc) { webcc::ClientSession session; try { const std::string data = "{'name'='Adam', 'age'=20}"; auto r = session.Request(webcc::RequestBuilder{}. Post("http://httpbin.org/post"). Body(data).Json() ()); EXPECT_EQ(webcc::Status::kOK, r->status()); EXPECT_EQ("OK", r->reason()); Json::Value json = StringToJson(r->data()); EXPECT_EQ(data, json["data"].asString()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } TEST(ClientTest, Post_Shortcut) { webcc::ClientSession session; try { const std::string data = "{'name'='Adam', 'age'=20}"; auto r = session.Post("http://httpbin.org/post", std::string(data), true); EXPECT_EQ(webcc::Status::kOK, r->status()); EXPECT_EQ("OK", r->reason()); Json::Value json = StringToJson(r->data()); EXPECT_EQ(data, json["data"].asString()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } #if (WEBCC_ENABLE_GZIP && WEBCC_ENABLE_SSL) // NOTE: Most servers don't support compressed requests! TEST(ClientTest, Post_Gzip) { webcc::ClientSession session; try { // Use Boost.org home page as the POST data. auto r1 = session.Get("https://www.boost.org/"); const std::string& data = r1->data(); auto r2 = session.Request(webcc::RequestBuilder{}. Post("http://httpbin.org/post"). Body(data).Gzip() ()); EXPECT_EQ(webcc::Status::kOK, r2->status()); EXPECT_EQ("OK", r2->reason()); } catch (const webcc::Error& error) { std::cerr << error << std::endl; } } #endif // (WEBCC_ENABLE_GZIP && WEBCC_ENABLE_SSL) // ----------------------------------------------------------------------------- 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(); }