This commit is contained in:
Benjamin Sergeant 2019-02-28 21:18:27 -08:00
parent 6d56f7223a
commit 285c12775a
9 changed files with 271 additions and 135 deletions

View File

@ -9,15 +9,19 @@
#include "IXWebSocketHttpHeaders.h" #include "IXWebSocketHttpHeaders.h"
#include "IXSocketFactory.h" #include "IXSocketFactory.h"
#include <iostream>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <vector> #include <vector>
#include <cstring>
#include <zlib.h> #include <zlib.h>
namespace ix namespace ix
{ {
const std::string HttpClient::kPost = "POST";
const std::string HttpClient::kGet = "GET";
const std::string HttpClient::kHead = "HEAD";
HttpClient::HttpClient() HttpClient::HttpClient()
{ {
@ -29,32 +33,29 @@ namespace ix
} }
HttpResponse HttpClient::request( HttpResponse HttpClient::request(
const std::string& url,
const std::string& verb, const std::string& verb,
const std::string& body, const std::string& body,
HttpRequestArgs args) const HttpRequestArgs& args,
int redirects)
{ {
uint64_t uploadSize = 0;
uint64_t downloadSize = 0;
int code = 0; int code = 0;
WebSocketHttpHeaders headers; WebSocketHttpHeaders headers;
std::string payload; std::string payload;
std::string protocol, host, path, query; std::string protocol, host, path, query;
int port; int port;
bool websocket = false;
if (!parseUrl(args.url, protocol, host, path, query, port)) if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
{ {
std::stringstream ss; std::stringstream ss;
ss << "Cannot parse url: " << args.url; ss << "Cannot parse url: " << url;
return std::make_tuple(code, headers, payload, ss.str()); return std::make_tuple(code, HttpErrorCode_UrlMalformed,
} headers, payload, ss.str(),
uploadSize, downloadSize);
if (protocol != "http" && protocol != "https")
{
std::stringstream ss;
ss << "Invalid protocol: " << protocol
<< " for url " << args.url
<< " . Supported protocols are http and https";
return std::make_tuple(code, headers, payload, ss.str());
} }
bool tls = protocol == "https"; bool tls = protocol == "https";
@ -63,7 +64,9 @@ namespace ix
if (!_socket) if (!_socket)
{ {
return std::make_tuple(code, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_CannotCreateSocket,
headers, payload, errorMsg,
uploadSize, downloadSize);
} }
// Build request string // Build request string
@ -84,7 +87,7 @@ namespace ix
ss << it.first << ": " << it.second << "\r\n"; ss << it.first << ": " << it.second << "\r\n";
} }
if (verb == "POST") if (verb == kPost)
{ {
ss << "Content-Length: " << body.size() << "\r\n"; ss << "Content-Length: " << body.size() << "\r\n";
@ -102,37 +105,51 @@ namespace ix
} }
std::string req(ss.str()); std::string req(ss.str());
std::string errMsg; std::string errMsg;
std::atomic<bool> requestInitCancellation(false); std::atomic<bool> requestInitCancellation(false);
// Make a cancellation object dealing with connection timeout
auto isCancellationRequested = auto isCancellationRequested =
makeCancellationRequestWithTimeout(args.timeoutSecs, 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)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Cannot connect to url: " << args.url; ss << "Cannot connect to url: " << url;
return std::make_tuple(code, headers, payload, ss.str()); return std::make_tuple(code, HttpErrorCode_CannotConnect,
headers, payload, ss.str(),
uploadSize, downloadSize);
} }
// Make a new cancellation object dealing with transfer timeout
isCancellationRequested =
makeCancellationRequestWithTimeout(args.transferTimeout, requestInitCancellation);
if (args.verbose) if (args.verbose)
{ {
std::cerr << "Sending " << verb << " request " std::stringstream ss;
<< "to " << host << ":" << port << std::endl ss << "Sending " << verb << " request "
<< "request size: " << req.size() << " bytes" << std::endl << "to " << host << ":" << port << std::endl
<< "=============" << std::endl << "request size: " << req.size() << " bytes" << std::endl
<< req << "=============" << std::endl
<< "=============" << std::endl << req
<< std::endl; << "=============" << std::endl
<< std::endl;
log(ss.str(), args);
} }
if (!_socket->writeBytes(req, isCancellationRequested)) if (!_socket->writeBytes(req, isCancellationRequested))
{ {
std::string errorMsg("Cannot send request"); std::string errorMsg("Cannot send request");
return std::make_tuple(code, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_SendError,
headers, payload, errorMsg,
uploadSize, downloadSize);
} }
uploadSize = req.size();
auto lineResult = _socket->readLine(isCancellationRequested); auto lineResult = _socket->readLine(isCancellationRequested);
auto lineValid = lineResult.first; auto lineValid = lineResult.first;
auto line = lineResult.second; auto line = lineResult.second;
@ -140,13 +157,24 @@ namespace ix
if (!lineValid) if (!lineValid)
{ {
std::string errorMsg("Cannot retrieve status line"); std::string errorMsg("Cannot retrieve status line");
return std::make_tuple(code, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_CannotReadStatusLine,
headers, payload, errorMsg,
uploadSize, downloadSize);
}
if (args.verbose)
{
std::stringstream ss;
ss << "Status line " << line;
log(ss.str(), args);
} }
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1) if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
{ {
std::string errorMsg("Cannot parse response code from status line"); std::string errorMsg("Cannot parse response code from status line");
return std::make_tuple(code, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_MissingStatus,
headers, payload, errorMsg,
uploadSize, downloadSize);
} }
auto result = parseHttpHeaders(_socket, isCancellationRequested); auto result = parseHttpHeaders(_socket, isCancellationRequested);
@ -155,39 +183,50 @@ namespace ix
if (!headersValid) if (!headersValid)
{ {
code = 0; // 0 ?
std::string errorMsg("Cannot parse http headers"); std::string errorMsg("Cannot parse http headers");
return std::make_tuple(code, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_HeaderParsingError,
headers, payload, errorMsg,
uploadSize, downloadSize);
} }
// Redirect ? // Redirect ?
// FIXME wrong conditional 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())
{ {
code = 0; // 0 ?
std::string errorMsg("Missing location header for redirect"); std::string errorMsg("Missing location header for redirect");
return std::make_tuple(code, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_MissingLocation,
headers, payload, errorMsg,
uploadSize, downloadSize);
}
if (redirects >= args.maxRedirects)
{
std::stringstream ss;
ss << "Too many redirects: " << redirects;
return std::make_tuple(code, HttpErrorCode_TooManyRedirects,
headers, payload, ss.str(),
uploadSize, downloadSize);
} }
// Recurse // Recurse
std::string location = headers["location"]; std::string location = headers["Location"];
return request(verb, body, args); return request(location, verb, body, args, redirects+1);
} }
if (verb == "HEAD") if (verb == "HEAD")
{ {
return std::make_tuple(code, headers, payload, std::string()); return std::make_tuple(code, HttpErrorCode_Ok,
headers, payload, std::string(),
uploadSize, downloadSize);
} }
// Parse response: // Parse response:
// http://bryce-thomas.blogspot.com/2012/01/technical-parsing-http-to-extract.html if (headers.find("Content-Length") != headers.end())
if (headers.find("content-length") != headers.end())
{ {
ssize_t contentLength = -1; ssize_t contentLength = -1;
ss.str(""); ss.str("");
ss << headers["content-length"]; ss << headers["Content-Length"];
ss >> contentLength; ss >> contentLength;
payload.reserve(contentLength); payload.reserve(contentLength);
@ -198,18 +237,16 @@ namespace ix
char c; char c;
if (!_socket->readByte(&c, isCancellationRequested)) if (!_socket->readByte(&c, isCancellationRequested))
{ {
ss.str(""); return std::make_tuple(code, HttpErrorCode_ReadError,
ss << "Cannot read byte"; headers, payload, "Cannot read byte",
return std::make_tuple(-1, headers, payload, "Cannot read byte"); uploadSize, downloadSize);
} }
payload += c; payload += c;
} }
std::cout << "I WAS HERE" << std::endl;
} }
else if (headers.find("transfer-encoding") != headers.end() && else if (headers.find("Transfer-Encoding") != headers.end() &&
headers["transfer-encoding"] == "chunked") headers["Transfer-Encoding"] == "chunked")
{ {
std::stringstream ss; std::stringstream ss;
@ -220,9 +257,9 @@ namespace ix
if (!lineResult.first) if (!lineResult.first)
{ {
code = 0; // 0 ? return std::make_tuple(code, HttpErrorCode_ChunkReadError,
std::string errorMsg("Cannot read http body"); headers, payload, errorMsg,
return std::make_tuple(code, headers, payload, errorMsg); uploadSize, downloadSize);
} }
uint64_t chunkSize; uint64_t chunkSize;
@ -232,8 +269,10 @@ namespace ix
if (args.verbose) if (args.verbose)
{ {
std::cerr << "Reading " << chunkSize << " bytes" std::stringstream oss;
<< std::endl; oss << "Reading " << chunkSize << " bytes"
<< std::endl;
log(oss.str(), args);
} }
payload.reserve(payload.size() + chunkSize); payload.reserve(payload.size() + chunkSize);
@ -245,9 +284,10 @@ namespace ix
char c; char c;
if (!_socket->readByte(&c, isCancellationRequested)) if (!_socket->readByte(&c, isCancellationRequested))
{ {
ss.str(""); errorMsg = "Cannot read byte";
ss << "Cannot read byte"; return std::make_tuple(code, HttpErrorCode_ChunkReadError,
return std::make_tuple(-1, headers, payload, ss.str()); headers, payload, errorMsg,
uploadSize, downloadSize);
} }
payload += c; payload += c;
@ -257,9 +297,9 @@ namespace ix
if (!lineResult.first) if (!lineResult.first)
{ {
code = 0; // 0 ? return std::make_tuple(code, HttpErrorCode_ChunkReadError,
std::string errorMsg("Cannot read http body"); headers, payload, errorMsg,
return std::make_tuple(code, headers, payload, errorMsg); uploadSize, downloadSize);
} }
if (chunkSize == 0) break; if (chunkSize == 0) break;
@ -271,48 +311,57 @@ namespace ix
} }
else else
{ {
code = 0; // 0 ?
std::string errorMsg("Cannot read http body"); std::string errorMsg("Cannot read http body");
return std::make_tuple(-1, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_CannotReadBody,
headers, payload, errorMsg,
uploadSize, downloadSize);
} }
downloadSize = payload.size();
// If the content was compressed with gzip, decode it // If the content was compressed with gzip, decode it
if (headers["Content-Encoding"] == "gzip") if (headers["Content-Encoding"] == "gzip")
{ {
if (args.verbose) std::cout << "Decoding gzip..." << std::endl;
std::string decompressedPayload; std::string decompressedPayload;
if (!gzipInflate(payload, decompressedPayload)) if (!gzipInflate(payload, decompressedPayload))
{ {
std::string errorMsg("Error decompressing payload"); std::string errorMsg("Error decompressing payload");
return std::make_tuple(-1, headers, payload, errorMsg); return std::make_tuple(code, HttpErrorCode_Gzip,
headers, payload, errorMsg,
uploadSize, downloadSize);
} }
payload = decompressedPayload; payload = decompressedPayload;
} }
return std::make_tuple(code, headers, payload, ""); return std::make_tuple(code, HttpErrorCode_Ok,
headers, payload, std::string(),
uploadSize, downloadSize);
} }
HttpResponse HttpClient::get(HttpRequestArgs args) HttpResponse HttpClient::get(const std::string& url,
const HttpRequestArgs& args)
{ {
return request("GET", std::string(), args); return request(url, kGet, std::string(), args);
} }
HttpResponse HttpClient::head(HttpRequestArgs args) HttpResponse HttpClient::head(const std::string& url,
const HttpRequestArgs& args)
{ {
return request("HEAD", std::string(), args); return request(url, kHead, std::string(), args);
} }
HttpResponse HttpClient::post(const HttpParameters& httpParameters, HttpResponse HttpClient::post(const std::string& url,
HttpRequestArgs args) const HttpParameters& httpParameters,
const HttpRequestArgs& args)
{ {
return request("POST", serializeHttpParameters(httpParameters), args); return request(url, kPost, serializeHttpParameters(httpParameters), args);
} }
HttpResponse HttpClient::post(const std::string& body, HttpResponse HttpClient::post(const std::string& url,
HttpRequestArgs args) const std::string& body,
const HttpRequestArgs& args)
{ {
return request("POST", body, args); return request(url, kPost, body, args);
} }
std::string HttpClient::urlEncode(const std::string& value) std::string HttpClient::urlEncode(const std::string& value)
@ -367,7 +416,7 @@ namespace ix
std::string& out) std::string& out)
{ {
z_stream inflateState; z_stream inflateState;
memset(&inflateState, 0, sizeof(inflateState)); std::memset(&inflateState, 0, sizeof(inflateState));
inflateState.zalloc = Z_NULL; inflateState.zalloc = Z_NULL;
inflateState.zfree = Z_NULL; inflateState.zfree = Z_NULL;
@ -385,7 +434,7 @@ namespace ix
const int kBufferSize = 1 << 14; const int kBufferSize = 1 << 14;
std::unique_ptr<unsigned char[]> compressBuffer = std::unique_ptr<unsigned char[]> compressBuffer =
std::make_unique<unsigned char[]>(kBufferSize); std::make_unique<unsigned char[]>(kBufferSize);
do do
@ -410,4 +459,13 @@ namespace ix
inflateEnd(&inflateState); inflateEnd(&inflateState);
return true; return true;
} }
void HttpClient::log(const std::string& msg,
const HttpRequestArgs& args)
{
if (args.logger)
{
args.logger(msg);
}
}
} }

View File

@ -19,18 +19,48 @@
namespace ix namespace ix
{ {
using HttpResponse = std::tuple<int, WebSocketHttpHeaders, std::string, std::string>; enum HttpErrorCode
{
HttpErrorCode_Ok = 0,
HttpErrorCode_CannotConnect = 1,
HttpErrorCode_Timeout = 2,
HttpErrorCode_Gzip = 3,
HttpErrorCode_UrlMalformed = 4,
HttpErrorCode_CannotCreateSocket = 5,
HttpErrorCode_SendError = 6,
HttpErrorCode_ReadError = 7,
HttpErrorCode_CannotReadStatusLine = 8,
HttpErrorCode_MissingStatus = 9,
HttpErrorCode_HeaderParsingError = 10,
HttpErrorCode_MissingLocation = 11,
HttpErrorCode_TooManyRedirects = 12,
HttpErrorCode_ChunkReadError = 13,
HttpErrorCode_CannotReadBody = 14
};
using HttpResponse = std::tuple<int, // status
HttpErrorCode, // error code
WebSocketHttpHeaders,
std::string, // payload
std::string, // error msg
uint64_t, // upload size
uint64_t>; // download size
using HttpParameters = std::map<std::string, std::string>; using HttpParameters = std::map<std::string, std::string>;
using Logger = std::function<void(const std::string&)>;
struct HttpRequestArgs struct HttpRequestArgs
{ {
std::string url; std::string url;
WebSocketHttpHeaders extraHeaders; WebSocketHttpHeaders extraHeaders;
std::string body; std::string body;
int timeoutSecs; int connectTimeout;
int transferTimeout;
bool followRedirects; bool followRedirects;
int maxRedirects;
bool verbose; bool verbose;
bool compress; bool compress;
Logger logger;
}; };
class HttpClient { class HttpClient {
@ -38,27 +68,39 @@ namespace ix
HttpClient(); HttpClient();
~HttpClient(); ~HttpClient();
HttpResponse get(HttpRequestArgs args); HttpResponse get(const std::string& url,
HttpResponse head(HttpRequestArgs args); const HttpRequestArgs& args);
HttpResponse head(const std::string& url,
const HttpRequestArgs& args);
HttpResponse post(const HttpParameters& httpParameters, HttpResponse post(const std::string& url,
HttpRequestArgs args); const HttpParameters& httpParameters,
HttpResponse post(const std::string& body, const HttpRequestArgs& args);
HttpRequestArgs args); HttpResponse post(const std::string& url,
const std::string& body,
const HttpRequestArgs& args);
private: private:
HttpResponse request(const std::string& verb, HttpResponse request(const std::string& url,
const std::string& verb,
const std::string& body, const std::string& body,
HttpRequestArgs args); const HttpRequestArgs& args,
int redirects = 0);
std::string serializeHttpParameters(const HttpParameters& httpParameters); std::string serializeHttpParameters(const HttpParameters& httpParameters);
std::string urlEncode(const std::string& value); std::string urlEncode(const std::string& value);
void log(const std::string& msg, const HttpRequestArgs& args);
bool gzipInflate( bool gzipInflate(
const std::string& in, const std::string& in,
std::string& out); std::string& out);
std::shared_ptr<Socket> _socket; std::shared_ptr<Socket> _socket;
const static std::string kPost;
const static std::string kGet;
const static std::string kHead;
}; };
} }

View File

@ -6,23 +6,29 @@
#include "IXUrlParser.h" #include "IXUrlParser.h"
#include <regex>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
namespace ix namespace ix
{ {
bool parseUrl(const std::string& url, //
std::string& protocol, // The only difference between those 2 regex is the protocol
std::string& host, //
std::string& path, std::regex UrlParser::_httpRegex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
std::string& query, std::regex UrlParser::_webSocketRegex("(ws|wss)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
int& port)
bool UrlParser::parse(const std::string& url,
std::string& protocol,
std::string& host,
std::string& path,
std::string& query,
int& port,
bool websocket)
{ {
std::regex ex("(ws|wss|http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
std::cmatch what; std::cmatch what;
if (!regex_match(url.c_str(), what, ex)) if (!regex_match(url.c_str(), what,
websocket ? _webSocketRegex : _httpRegex))
{ {
return false; return false;
} }
@ -77,12 +83,12 @@ namespace ix
return true; return true;
} }
void printUrl(const std::string& url) void UrlParser::printUrl(const std::string& url, bool websocket)
{ {
std::string protocol, host, path, query; std::string protocol, host, path, query;
int port {0}; int port {0};
if (!parseUrl(url, protocol, host, path, query, port)) if (!parse(url, protocol, host, path, query, port, websocket))
{ {
return; return;
} }

View File

@ -7,15 +7,25 @@
#pragma once #pragma once
#include <string> #include <string>
#include <regex>
namespace ix namespace ix
{ {
bool parseUrl(const std::string& url, class UrlParser
std::string& protocol, {
std::string& host, public:
std::string& path, static bool parse(const std::string& url,
std::string& query, std::string& protocol,
int& port); std::string& host,
std::string& path,
std::string& query,
int& port,
bool websocket);
void printUrl(const std::string& url); static void printUrl(const std::string& url, bool websocket);
private:
static std::regex _httpRegex;
static std::regex _webSocketRegex;
};
} }

View File

@ -24,13 +24,13 @@ namespace ix
{ {
bool operator() (const unsigned char& c1, const unsigned char& c2) const bool operator() (const unsigned char& c1, const unsigned char& c2) const
{ {
return std::tolower(c1) < std::tolower(c2); return std::tolower(c1) < std::tolower(c2);
} }
}; };
bool operator() (const std::string & s1, const std::string & s2) const bool operator() (const std::string & s1, const std::string & s2) const
{ {
return std::lexicographical_compare return std::lexicographical_compare
(s1.begin(), s1.end(), // source range (s1.begin(), s1.end(), // source range
s2.begin(), s2.end(), // dest range s2.begin(), s2.end(), // dest range
NocaseCompare()); // comparison NocaseCompare()); // comparison

View File

@ -72,8 +72,9 @@ namespace ix
{ {
std::string protocol, host, path, query; std::string protocol, host, path, query;
int port; int port;
bool websocket = true;
if (!parseUrl(url, protocol, host, path, query, port)) if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
{ {
return WebSocketInitResult(false, 0, return WebSocketInitResult(false, 0,
std::string("Could not parse URL ") + url); std::string("Could not parse URL ") + url);

View File

@ -33,7 +33,9 @@ int main(int argc, char** argv)
bool save = false; bool save = false;
bool compress = false; bool compress = false;
int port = 8080; int port = 8080;
int connectTimeOutSeconds = 3; int connectTimeOut = 60;
int transferTimeout = 1800;
int maxRedirects = 5;
CLI::App* sendApp = app.add_subcommand("send", "Send a file"); CLI::App* sendApp = app.add_subcommand("send", "Send a file");
sendApp->add_option("url", url, "Connection url")->required(); sendApp->add_option("url", url, "Connection url")->required();
@ -68,12 +70,14 @@ int main(int argc, char** argv)
httpClientApp->add_option("-F", data, "Form data")->join(); httpClientApp->add_option("-F", data, "Form data")->join();
httpClientApp->add_option("-H", headers, "Header")->join(); httpClientApp->add_option("-H", headers, "Header")->join();
httpClientApp->add_option("--output", output, "Output file"); httpClientApp->add_option("--output", output, "Output file");
httpClientApp->add_flag("-I", headersOnly, "Header"); httpClientApp->add_flag("-I", headersOnly, "Send a HEAD request");
httpClientApp->add_flag("-L", followRedirects, "Header"); httpClientApp->add_flag("-L", followRedirects, "Follow redirects");
httpClientApp->add_option("--max-redirects", maxRedirects, "Max Redirects");
httpClientApp->add_flag("-v", verbose, "Verbose"); httpClientApp->add_flag("-v", verbose, "Verbose");
httpClientApp->add_flag("-O", save, "Save to disk"); httpClientApp->add_flag("-O", save, "Save output to disk");
httpClientApp->add_flag("--compress", compress, "gzip compression"); httpClientApp->add_flag("--compress", compress, "Enable gzip compression");
httpClientApp->add_option("--connect-timeout", connectTimeOutSeconds, "Connection timeout"); httpClientApp->add_option("--connect-timeout", connectTimeOut, "Connection timeout");
httpClientApp->add_option("--transfer-timeout", transferTimeout, "Transfer timeout");
CLI11_PARSE(app, argc, argv); CLI11_PARSE(app, argc, argv);
@ -114,10 +118,10 @@ int main(int argc, char** argv)
} }
else if (app.got_subcommand("curl")) else if (app.got_subcommand("curl"))
{ {
return ix::ws_http_client_main(url, headers, data, return ix::ws_http_client_main(url, headers, data, headersOnly,
headersOnly, connectTimeOutSeconds, connectTimeOut, transferTimeout,
followRedirects, verbose, save, output, followRedirects, maxRedirects, verbose,
compress); save, output, compress);
} }
return 1; return 1;

View File

@ -13,8 +13,10 @@ namespace ix
const std::string& headers, const std::string& headers,
const std::string& data, const std::string& data,
bool headersOnly, bool headersOnly,
int timeoutSecs, int connectTimeout,
int transferTimeout,
bool followRedirects, bool followRedirects,
int maxRedirects,
bool verbose, bool verbose,
bool save, bool save,
const std::string& output, const std::string& output,

View File

@ -86,20 +86,27 @@ namespace ix
const std::string& headersData, const std::string& headersData,
const std::string& data, const std::string& data,
bool headersOnly, bool headersOnly,
int timeoutSecs, int connectTimeout,
int transferTimeout,
bool followRedirects, bool followRedirects,
int maxRedirects,
bool verbose, bool verbose,
bool save, bool save,
const std::string& output, const std::string& output,
bool compress) bool compress)
{ {
HttpRequestArgs args; HttpRequestArgs args;
args.url = url;
args.extraHeaders = parseHeaders(headersData); args.extraHeaders = parseHeaders(headersData);
args.timeoutSecs = timeoutSecs; args.connectTimeout = connectTimeout;
args.transferTimeout = transferTimeout;
args.followRedirects = followRedirects; args.followRedirects = followRedirects;
args.maxRedirects = maxRedirects;
args.verbose = verbose; args.verbose = verbose;
args.compress = compress; args.compress = compress;
args.logger = [](const std::string& msg)
{
std::cout << msg;
};
HttpParameters httpParameters = parsePostParameters(data); HttpParameters httpParameters = parsePostParameters(data);
@ -107,34 +114,40 @@ namespace ix
HttpResponse out; HttpResponse out;
if (headersOnly) if (headersOnly)
{ {
out = httpClient.head(args); out = httpClient.head(url, args);
} }
else if (data.empty()) else if (data.empty())
{ {
out = httpClient.get(args); out = httpClient.get(url, args);
} }
else else
{ {
out = httpClient.post(httpParameters, args); out = httpClient.post(url, httpParameters, args);
} }
auto errorCode = std::get<0>(out); auto statusCode = std::get<0>(out);
auto responseHeaders = std::get<1>(out); auto errorCode = std::get<1>(out);
auto payload = std::get<2>(out); auto responseHeaders = std::get<2>(out);
auto errorMsg = std::get<3>(out); auto payload = std::get<3>(out);
auto errorMsg = std::get<4>(out);
auto uploadSize = std::get<5>(out);
auto downloadSize = std::get<6>(out);
for (auto it : responseHeaders) for (auto it : responseHeaders)
{ {
std::cerr << it.first << ": " << it.second << std::endl; std::cerr << it.first << ": " << it.second << std::endl;
} }
std::cerr << "error code: " << errorCode << std::endl; std::cerr << "Upload size: " << uploadSize << std::endl;
if (errorCode != 200) std::cerr << "Download size: " << downloadSize << std::endl;
std::cerr << "Status: " << statusCode << std::endl;
if (errorCode != HttpErrorCode_Ok)
{ {
std::cerr << "error message: " << errorMsg << std::endl; std::cerr << "error message: " << errorMsg << std::endl;
} }
if (!headersOnly && errorCode == 200) if (!headersOnly && errorCode == HttpErrorCode_Ok)
{ {
if (save || !output.empty()) if (save || !output.empty())
{ {