fix command line tools

This commit is contained in:
Benjamin Sergeant 2019-06-05 15:52:31 -07:00
parent 3257ad1363
commit c7cb743a69
6 changed files with 105 additions and 109 deletions

View File

@ -13,7 +13,6 @@
#include <iomanip> #include <iomanip>
#include <vector> #include <vector>
#include <cstring> #include <cstring>
#include <iostream>
#include <zlib.h> #include <zlib.h>
@ -96,20 +95,18 @@ namespace ix
if (_stop) return; if (_stop) return;
HttpResponsePtr response = request(args->url, args->verb, args->body, *args); HttpResponsePtr response = request(args->url, args->verb, args->body, args);
onResponseCallback(response); onResponseCallback(response);
if (_stop) return; if (_stop) return;
} }
std::cout << "HttpClient::run() finished" << std::endl;
} }
HttpResponsePtr HttpClient::request( HttpResponsePtr HttpClient::request(
const std::string& url, const std::string& url,
const std::string& verb, const std::string& verb,
const std::string& body, const std::string& body,
const HttpRequestArgs& args, HttpRequestArgsPtr args,
int redirects) int redirects)
{ {
uint64_t uploadSize = 0; uint64_t uploadSize = 0;
@ -146,13 +143,13 @@ namespace ix
ss << verb << " " << path << " HTTP/1.1\r\n"; ss << verb << " " << path << " HTTP/1.1\r\n";
ss << "Host: " << host << "\r\n"; ss << "Host: " << host << "\r\n";
if (args.compress) if (args->compress)
{ {
ss << "Accept-Encoding: gzip" << "\r\n"; ss << "Accept-Encoding: gzip" << "\r\n";
} }
// Append extra headers // Append extra headers
for (auto&& it : args.extraHeaders) for (auto&& it : args->extraHeaders)
{ {
ss << it.first << ": " << it.second << "\r\n"; ss << it.first << ": " << it.second << "\r\n";
} }
@ -174,7 +171,7 @@ namespace ix
ss << "Content-Length: " << body.size() << "\r\n"; ss << "Content-Length: " << body.size() << "\r\n";
// Set default Content-Type if unspecified // Set default Content-Type if unspecified
if (args.extraHeaders.find("Content-Type") == args.extraHeaders.end()) if (args->extraHeaders.find("Content-Type") == args->extraHeaders.end())
{ {
ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n"; ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
} }
@ -192,7 +189,7 @@ namespace ix
// Make a cancellation object dealing with connection timeout // Make a cancellation object dealing with connection timeout
auto isCancellationRequested = auto isCancellationRequested =
makeCancellationRequestWithTimeout(args.connectTimeout, requestInitCancellation); makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation);
bool success = _socket->connect(host, port, errMsg, isCancellationRequested); bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
if (!success) if (!success)
@ -206,9 +203,9 @@ namespace ix
// Make a new cancellation object dealing with transfer timeout // Make a new cancellation object dealing with transfer timeout
isCancellationRequested = isCancellationRequested =
makeCancellationRequestWithTimeout(args.transferTimeout, requestInitCancellation); makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation);
if (args.verbose) if (args->verbose)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Sending " << verb << " request " ss << "Sending " << verb << " request "
@ -244,7 +241,7 @@ namespace ix
uploadSize, downloadSize); uploadSize, downloadSize);
} }
if (args.verbose) if (args->verbose)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Status line " << line; ss << "Status line " << line;
@ -272,7 +269,7 @@ namespace ix
} }
// Redirect ? // Redirect ?
if ((code >= 301 && code <= 308) && args.followRedirects) if ((code >= 301 && code <= 308) && args->followRedirects)
{ {
if (headers.find("Location") == headers.end()) if (headers.find("Location") == headers.end())
{ {
@ -282,7 +279,7 @@ namespace ix
uploadSize, downloadSize); uploadSize, downloadSize);
} }
if (redirects >= args.maxRedirects) if (redirects >= args->maxRedirects)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Too many redirects: " << redirects; ss << "Too many redirects: " << redirects;
@ -314,7 +311,7 @@ namespace ix
payload.reserve(contentLength); payload.reserve(contentLength);
auto chunkResult = _socket->readBytes(contentLength, auto chunkResult = _socket->readBytes(contentLength,
args.onProgressCallback, args->onProgressCallback,
isCancellationRequested); isCancellationRequested);
if (!chunkResult.first) if (!chunkResult.first)
{ {
@ -347,7 +344,7 @@ namespace ix
ss << std::hex << line; ss << std::hex << line;
ss >> chunkSize; ss >> chunkSize;
if (args.verbose) if (args->verbose)
{ {
std::stringstream oss; std::stringstream oss;
oss << "Reading " << chunkSize << " bytes" oss << "Reading " << chunkSize << " bytes"
@ -359,7 +356,7 @@ namespace ix
// Read a chunk // Read a chunk
auto chunkResult = _socket->readBytes((size_t) chunkSize, auto chunkResult = _socket->readBytes((size_t) chunkSize,
args.onProgressCallback, args->onProgressCallback,
isCancellationRequested); isCancellationRequested);
if (!chunkResult.first) if (!chunkResult.first)
{ {
@ -417,47 +414,47 @@ namespace ix
} }
HttpResponsePtr HttpClient::get(const std::string& url, HttpResponsePtr HttpClient::get(const std::string& url,
const HttpRequestArgs& args) HttpRequestArgsPtr args)
{ {
return request(url, kGet, std::string(), args); return request(url, kGet, std::string(), args);
} }
HttpResponsePtr HttpClient::head(const std::string& url, HttpResponsePtr HttpClient::head(const std::string& url,
const HttpRequestArgs& args) HttpRequestArgsPtr args)
{ {
return request(url, kHead, std::string(), args); return request(url, kHead, std::string(), args);
} }
HttpResponsePtr HttpClient::del(const std::string& url, HttpResponsePtr HttpClient::del(const std::string& url,
const HttpRequestArgs& args) HttpRequestArgsPtr args)
{ {
return request(url, kDel, std::string(), args); return request(url, kDel, std::string(), args);
} }
HttpResponsePtr HttpClient::post(const std::string& url, HttpResponsePtr HttpClient::post(const std::string& url,
const HttpParameters& httpParameters, const HttpParameters& httpParameters,
const HttpRequestArgs& args) HttpRequestArgsPtr args)
{ {
return request(url, kPost, serializeHttpParameters(httpParameters), args); return request(url, kPost, serializeHttpParameters(httpParameters), args);
} }
HttpResponsePtr HttpClient::post(const std::string& url, HttpResponsePtr HttpClient::post(const std::string& url,
const std::string& body, const std::string& body,
const HttpRequestArgs& args) HttpRequestArgsPtr args)
{ {
return request(url, kPost, body, args); return request(url, kPost, body, args);
} }
HttpResponsePtr HttpClient::put(const std::string& url, HttpResponsePtr HttpClient::put(const std::string& url,
const HttpParameters& httpParameters, const HttpParameters& httpParameters,
const HttpRequestArgs& args) HttpRequestArgsPtr args)
{ {
return request(url, kPut, serializeHttpParameters(httpParameters), args); return request(url, kPut, serializeHttpParameters(httpParameters), args);
} }
HttpResponsePtr HttpClient::put(const std::string& url, HttpResponsePtr HttpClient::put(const std::string& url,
const std::string& body, const std::string& body,
const HttpRequestArgs& args) const HttpRequestArgsPtr args)
{ {
return request(url, kPut, body, args); return request(url, kPut, body, args);
} }
@ -559,11 +556,11 @@ namespace ix
} }
void HttpClient::log(const std::string& msg, void HttpClient::log(const std::string& msg,
const HttpRequestArgs& args) HttpRequestArgsPtr args)
{ {
if (args.logger) if (args->logger)
{ {
args.logger(msg); args->logger(msg);
} }
} }
} }

View File

@ -98,32 +98,32 @@ namespace ix
HttpClient(bool async = false); HttpClient(bool async = false);
~HttpClient(); ~HttpClient();
HttpResponsePtr get(const std::string& url, const HttpRequestArgs& args); HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr head(const std::string& url, const HttpRequestArgs& args); HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr del(const std::string& url, const HttpRequestArgs& args); HttpResponsePtr del(const std::string& url, HttpRequestArgsPtr args);
HttpResponsePtr post(const std::string& url, HttpResponsePtr post(const std::string& url,
const HttpParameters& httpParameters, const HttpParameters& httpParameters,
const HttpRequestArgs& args); HttpRequestArgsPtr args);
HttpResponsePtr post(const std::string& url, HttpResponsePtr post(const std::string& url,
const std::string& body, const std::string& body,
const HttpRequestArgs& args); HttpRequestArgsPtr args);
HttpResponsePtr put(const std::string& url, HttpResponsePtr put(const std::string& url,
const HttpParameters& httpParameters, const HttpParameters& httpParameters,
const HttpRequestArgs& args); HttpRequestArgsPtr args);
HttpResponsePtr put(const std::string& url, HttpResponsePtr put(const std::string& url,
const std::string& body, const std::string& body,
const HttpRequestArgs& args); HttpRequestArgsPtr args);
HttpResponsePtr request(const std::string& url, HttpResponsePtr request(const std::string& url,
const std::string& verb, const std::string& verb,
const std::string& body, const std::string& body,
const HttpRequestArgs& args, HttpRequestArgsPtr args,
int redirects = 0); int redirects = 0);
// Async API // Async API
HttpRequestArgsPtr createRequest(const std::string& url, HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
const std::string& verb = HttpClient::kGet); const std::string& verb = HttpClient::kGet);
bool performRequest(HttpRequestArgsPtr request, bool performRequest(HttpRequestArgsPtr request,
@ -140,7 +140,7 @@ namespace ix
const static std::string kPut; const static std::string kPut;
private: private:
void log(const std::string& msg, const HttpRequestArgs& args); void log(const std::string& msg, HttpRequestArgsPtr args);
bool gzipInflate(const std::string& in, std::string& out); bool gzipInflate(const std::string& in, std::string& out);

View File

@ -10,8 +10,8 @@ set (CMAKE_CXX_STANDARD 14)
if (NOT WIN32) if (NOT WIN32)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../third_party/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH})
find_package(Sanitizers) find_package(Sanitizers)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") set(CMAKE_LD_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
option(USE_TLS "Add TLS support" ON) option(USE_TLS "Add TLS support" ON)
endif() endif()

View File

@ -15,30 +15,30 @@ TEST_CASE("http client", "[http]")
{ {
SECTION("Connect to a remote HTTP server") SECTION("Connect to a remote HTTP server")
{ {
std::string url("http://httpbin.org/"); HttpClient httpClient;
WebSocketHttpHeaders headers; WebSocketHttpHeaders headers;
HttpRequestArgs args; std::string url("http://httpbin.org/");
args.extraHeaders = headers; auto args = httpClient.createRequest(url);
args.connectTimeout = 60;
args.transferTimeout = 60; args->extraHeaders = headers;
args.followRedirects = true; args->connectTimeout = 60;
args.maxRedirects = 10; args->transferTimeout = 60;
args.verbose = true; args->followRedirects = true;
args.compress = true; args->maxRedirects = 10;
args.logger = [](const std::string& msg) args->verbose = true;
args->compress = true;
args->logger = [](const std::string& msg)
{ {
std::cout << msg; std::cout << msg;
}; };
args.onProgressCallback = [](int current, int total) -> bool args->onProgressCallback = [](int current, int total) -> bool
{ {
std::cerr << "\r" << "Downloaded " std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total; << current << " bytes out of " << total;
return true; return true;
}; };
HttpClient httpClient;
auto response = httpClient.get(url, args); auto response = httpClient.get(url, args);
for (auto it : response->headers) for (auto it : response->headers)
@ -56,30 +56,30 @@ TEST_CASE("http client", "[http]")
SECTION("Connect to a remote HTTPS server") SECTION("Connect to a remote HTTPS server")
{ {
std::string url("https://httpbin.org/"); HttpClient httpClient;
WebSocketHttpHeaders headers; WebSocketHttpHeaders headers;
HttpRequestArgs args; std::string url("https://httpbin.org/");
args.extraHeaders = headers; auto args = httpClient.createRequest(url);
args.connectTimeout = 60;
args.transferTimeout = 60; args->extraHeaders = headers;
args.followRedirects = true; args->connectTimeout = 60;
args.maxRedirects = 10; args->transferTimeout = 60;
args.verbose = true; args->followRedirects = true;
args.compress = true; args->maxRedirects = 10;
args.logger = [](const std::string& msg) args->verbose = true;
args->compress = true;
args->logger = [](const std::string& msg)
{ {
std::cout << msg; std::cout << msg;
}; };
args.onProgressCallback = [](int current, int total) -> bool args->onProgressCallback = [](int current, int total) -> bool
{ {
std::cerr << "\r" << "Downloaded " std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total; << current << " bytes out of " << total;
return true; return true;
}; };
HttpClient httpClient;
auto response = httpClient.get(url, args); auto response = httpClient.get(url, args);
for (auto it : response->headers) for (auto it : response->headers)
@ -144,7 +144,6 @@ TEST_CASE("http client", "[http]")
wait += 10; wait += 10;
} }
std::cerr << "Done" << std::endl; std::cerr << "Done" << std::endl;
REQUIRE(statusCode == 200); REQUIRE(statusCode == 200);
} }

View File

@ -141,42 +141,42 @@ namespace ix
bool SentryClient::send(const Json::Value& msg, bool SentryClient::send(const Json::Value& msg,
bool verbose) bool verbose)
{ {
HttpRequestArgs args; auto args = _httpClient.createRequest();
args.extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader(); args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
args.connectTimeout = 60; args->connectTimeout = 60;
args.transferTimeout = 5 * 60; args->transferTimeout = 5 * 60;
args.followRedirects = true; args->followRedirects = true;
args.verbose = verbose; args->verbose = verbose;
args.logger = [](const std::string& msg) args->logger = [](const std::string& msg)
{ {
std::cout << msg; std::cout << msg;
}; };
std::string body = computePayload(msg); std::string body = computePayload(msg);
HttpResponse response = _httpClient.post(_url, body, args); HttpResponsePtr response = _httpClient.post(_url, body, args);
if (verbose) if (verbose)
{ {
for (auto it : response.headers) for (auto it : response->headers)
{ {
std::cerr << it.first << ": " << it.second << std::endl; std::cerr << it.first << ": " << it.second << std::endl;
} }
std::cerr << "Upload size: " << response.uploadSize << std::endl; std::cerr << "Upload size: " << response->uploadSize << std::endl;
std::cerr << "Download size: " << response.downloadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl;
std::cerr << "Status: " << response.statusCode << std::endl; std::cerr << "Status: " << response->statusCode << std::endl;
if (response.errorCode != HttpErrorCode::Ok) if (response->errorCode != HttpErrorCode::Ok)
{ {
std::cerr << "error message: " << response.errorMsg << std::endl; std::cerr << "error message: " << response->errorMsg << std::endl;
} }
if (response.headers["Content-Type"] != "application/octet-stream") if (response->headers["Content-Type"] != "application/octet-stream")
{ {
std::cerr << "payload: " << response.payload << std::endl; std::cerr << "payload: " << response->payload << std::endl;
} }
} }
return response.statusCode == 200; return response->statusCode == 200;
} }
} // namespace ix } // namespace ix

View File

@ -95,19 +95,20 @@ namespace ix
const std::string& output, const std::string& output,
bool compress) bool compress)
{ {
HttpRequestArgs args; HttpClient httpClient;
args.extraHeaders = parseHeaders(headersData); auto args = httpClient.createRequest();
args.connectTimeout = connectTimeout; args->extraHeaders = parseHeaders(headersData);
args.transferTimeout = transferTimeout; args->connectTimeout = connectTimeout;
args.followRedirects = followRedirects; args->transferTimeout = transferTimeout;
args.maxRedirects = maxRedirects; args->followRedirects = followRedirects;
args.verbose = verbose; args->maxRedirects = maxRedirects;
args.compress = compress; args->verbose = verbose;
args.logger = [](const std::string& msg) args->compress = compress;
args->logger = [](const std::string& msg)
{ {
std::cout << msg; std::cout << msg;
}; };
args.onProgressCallback = [](int current, int total) -> bool args->onProgressCallback = [](int current, int total) -> bool
{ {
std::cerr << "\r" << "Downloaded " std::cerr << "\r" << "Downloaded "
<< current << " bytes out of " << total; << current << " bytes out of " << total;
@ -116,8 +117,7 @@ namespace ix
HttpParameters httpParameters = parsePostParameters(data); HttpParameters httpParameters = parsePostParameters(data);
HttpClient httpClient; HttpResponsePtr response;
HttpResponse response;
if (headersOnly) if (headersOnly)
{ {
response = httpClient.head(url, args); response = httpClient.head(url, args);
@ -133,21 +133,21 @@ namespace ix
std::cerr << std::endl; std::cerr << std::endl;
for (auto it : response.headers) for (auto it : response->headers)
{ {
std::cerr << it.first << ": " << it.second << std::endl; std::cerr << it.first << ": " << it.second << std::endl;
} }
std::cerr << "Upload size: " << response.uploadSize << std::endl; std::cerr << "Upload size: " << response->uploadSize << std::endl;
std::cerr << "Download size: " << response.downloadSize << std::endl; std::cerr << "Download size: " << response->downloadSize << std::endl;
std::cerr << "Status: " << response.statusCode << std::endl; std::cerr << "Status: " << response->statusCode << std::endl;
if (response.errorCode != HttpErrorCode::Ok) if (response->errorCode != HttpErrorCode::Ok)
{ {
std::cerr << "error message: " << response.errorMsg << std::endl; std::cerr << "error message: " << response->errorMsg << std::endl;
} }
if (!headersOnly && response.errorCode == HttpErrorCode::Ok) if (!headersOnly && response->errorCode == HttpErrorCode::Ok)
{ {
if (save || !output.empty()) if (save || !output.empty())
{ {
@ -160,14 +160,14 @@ namespace ix
std::cout << "Writing to disk: " << filename << std::endl; std::cout << "Writing to disk: " << filename << std::endl;
std::ofstream out(filename); std::ofstream out(filename);
out.write((char*)&response.payload.front(), response.payload.size()); out.write((char*)&response->payload.front(), response->payload.size());
out.close(); out.close();
} }
else else
{ {
if (response.headers["Content-Type"] != "application/octet-stream") if (response->headers["Content-Type"] != "application/octet-stream")
{ {
std::cout << "payload: " << response.payload << std::endl; std::cout << "payload: " << response->payload << std::endl;
} }
else else
{ {