/* * IXSocketTest.cpp * Author: Benjamin Sergeant * Copyright (c) 2019 Machine Zone. All rights reserved. */ #include "catch.hpp" #include #include #include using namespace ix; TEST_CASE("http_client", "[http]") { SECTION("Connect to a remote HTTP server") { HttpClient httpClient; WebSocketHttpHeaders headers; std::string url("http://httpbin.org/"); auto args = httpClient.createRequest(url); args->extraHeaders = headers; args->connectTimeout = 60; args->transferTimeout = 60; args->followRedirects = true; args->maxRedirects = 10; args->verbose = true; args->compress = true; args->logger = [](const std::string& msg) { std::cout << msg; }; args->onProgressCallback = [](int current, int total) -> bool { std::cerr << "\r" << "Downloaded " << current << " bytes out of " << total; return true; }; auto response = httpClient.get(url, args); for (auto it : response->headers) { std::cerr << it.first << ": " << it.second << std::endl; } std::cerr << "Upload size: " << response->uploadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl; std::cerr << "Status: " << response->statusCode << std::endl; std::cerr << "Error message: " << response->errorMsg << std::endl; REQUIRE(response->errorCode == HttpErrorCode::Ok); REQUIRE(response->statusCode == 200); } #ifdef IXWEBSOCKET_USE_TLS SECTION("Connect to a remote HTTPS server") { HttpClient httpClient; WebSocketHttpHeaders headers; SocketTLSOptions tlsOptions; tlsOptions.caFile = "cacert.pem"; httpClient.setTLSOptions(tlsOptions); std::string url("https://httpbin.org/"); auto args = httpClient.createRequest(url); args->extraHeaders = headers; args->connectTimeout = 60; args->transferTimeout = 60; args->followRedirects = true; args->maxRedirects = 10; args->verbose = true; args->compress = true; args->logger = [](const std::string& msg) { std::cout << msg; }; args->onProgressCallback = [](int current, int total) -> bool { std::cerr << "\r" << "Downloaded " << current << " bytes out of " << total; return true; }; auto response = httpClient.get(url, args); for (auto it : response->headers) { std::cerr << it.first << ": " << it.second << std::endl; } std::cerr << "Upload size: " << response->uploadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl; std::cerr << "Status: " << response->statusCode << std::endl; std::cerr << "Error message: " << response->errorMsg << std::endl; REQUIRE(response->errorCode == HttpErrorCode::Ok); REQUIRE(response->statusCode == 200); } #endif SECTION("Async API, one call") { bool async = true; HttpClient httpClient(async); WebSocketHttpHeaders headers; SocketTLSOptions tlsOptions; tlsOptions.caFile = "cacert.pem"; httpClient.setTLSOptions(tlsOptions); std::string url("http://httpbin.org/"); auto args = httpClient.createRequest(url); args->extraHeaders = headers; args->connectTimeout = 60; args->transferTimeout = 60; args->followRedirects = true; args->maxRedirects = 10; args->verbose = true; args->compress = true; args->logger = [](const std::string& msg) { std::cout << msg; }; args->onProgressCallback = [](int current, int total) -> bool { std::cerr << "\r" << "Downloaded " << current << " bytes out of " << total; return true; }; std::atomic requestCompleted(false); std::atomic statusCode(0); httpClient.performRequest( args, [&requestCompleted, &statusCode](const HttpResponsePtr& response) { std::cerr << "Upload size: " << response->uploadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl; std::cerr << "Status: " << response->statusCode << std::endl; std::cerr << "Error message: " << response->errorMsg << std::endl; // In case of failure, print response->errorMsg statusCode = response->statusCode; requestCompleted = true; }); int wait = 0; while (wait < 5000) { if (requestCompleted) break; std::chrono::duration duration(10); std::this_thread::sleep_for(duration); wait += 10; } std::cerr << "Done" << std::endl; REQUIRE(statusCode == 200); } SECTION("Async API, multiple calls") { bool async = true; HttpClient httpClient(async); WebSocketHttpHeaders headers; std::string url("http://httpbin.org/"); auto args = httpClient.createRequest(url); args->extraHeaders = headers; args->connectTimeout = 60; args->transferTimeout = 60; args->followRedirects = true; args->maxRedirects = 10; args->verbose = true; args->compress = true; args->logger = [](const std::string& msg) { std::cout << msg; }; args->onProgressCallback = [](int current, int total) -> bool { std::cerr << "\r" << "Downloaded " << current << " bytes out of " << total; return true; }; std::atomic requestCompleted(false); std::atomic statusCode0(0); std::atomic statusCode1(0); std::atomic statusCode2(0); for (int i = 0; i < 3; ++i) { httpClient.performRequest( args, [i, &requestCompleted, &statusCode0, &statusCode1, &statusCode2]( const HttpResponsePtr& response) { std::cerr << "Upload size: " << response->uploadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl; std::cerr << "Status: " << response->statusCode << std::endl; std::cerr << "Error message: " << response->errorMsg << std::endl; // In case of failure, print response->errorMsg if (i == 0) { statusCode0 = response->statusCode; } else if (i == 1) { statusCode1 = response->statusCode; } else if (i == 2) { statusCode2 = response->statusCode; requestCompleted = true; } }); } int wait = 0; while (wait < 10000) { if (requestCompleted) break; std::chrono::duration duration(10); std::this_thread::sleep_for(duration); wait += 10; } std::cerr << "Done" << std::endl; REQUIRE(statusCode0 == 200); REQUIRE(statusCode1 == 200); REQUIRE(statusCode2 == 200); } SECTION("Async API, cancel") { bool async = true; HttpClient httpClient(async); WebSocketHttpHeaders headers; SocketTLSOptions tlsOptions; tlsOptions.caFile = "cacert.pem"; httpClient.setTLSOptions(tlsOptions); std::string url("http://httpbin.org/delay/10"); auto args = httpClient.createRequest(url); args->extraHeaders = headers; args->connectTimeout = 60; args->transferTimeout = 60; args->followRedirects = true; args->maxRedirects = 10; args->verbose = true; args->compress = true; args->logger = [](const std::string& msg) { std::cout << msg; }; args->onProgressCallback = [](int current, int total) -> bool { std::cerr << "\r" << "Downloaded " << current << " bytes out of " << total; return true; }; std::atomic requestCompleted(false); std::atomic errorCode(HttpErrorCode::Invalid); httpClient.performRequest( args, [&requestCompleted, &errorCode](const HttpResponsePtr& response) { errorCode = response->errorCode; requestCompleted = true; }); // cancel immediately args->cancel = true; int wait = 0; while (wait < 5000) { if (requestCompleted) break; std::chrono::duration duration(10); std::this_thread::sleep_for(duration); wait += 10; } std::cerr << "Done" << std::endl; REQUIRE(errorCode == HttpErrorCode::Cancelled); } SECTION("Async API, streaming transfer") { bool async = true; HttpClient httpClient(async); WebSocketHttpHeaders headers; SocketTLSOptions tlsOptions; tlsOptions.caFile = "cacert.pem"; httpClient.setTLSOptions(tlsOptions); std::string url("http://speedtest.belwue.net/random-100M"); auto args = httpClient.createRequest(url); args->extraHeaders = headers; args->connectTimeout = 60; args->transferTimeout = 120; args->followRedirects = true; args->maxRedirects = 10; args->verbose = true; args->compress = false; args->logger = [](const std::string& msg) { std::cout << msg; }; args->onProgressCallback = [](int current, int total) -> bool { std::cerr << "\r" << "Downloaded " << current << " bytes out of " << total; return true; }; // compute Adler-32 checksum of received data uint32_t a = 1, b = 0; args->onChunkCallback = [&](const std::string& data) { for (const char c: data) { a = (a + (unsigned char)c) % 65521; b = (b + a) % 65521; } }; std::atomic requestCompleted(false); std::atomic errorCode(HttpErrorCode::Invalid); std::atomic statusCode(0); httpClient.performRequest( args, [&](const HttpResponsePtr& response) { errorCode = response->errorCode; statusCode = response->statusCode; requestCompleted = true; }); int wait = 0; while (wait < 120000) { if (requestCompleted) break; std::chrono::duration duration(10); std::this_thread::sleep_for(duration); wait += 10; } std::cerr << "Done" << std::endl; REQUIRE(errorCode == HttpErrorCode::Ok); REQUIRE(statusCode == 200); // compare checksum with a known good value uint32_t checksum = (b << 16) | a; REQUIRE(checksum == 1440194471); } }