chunk encoding / simple redirect support / -I option

This commit is contained in:
Benjamin Sergeant 2019-02-25 21:50:42 -08:00
parent b563541b14
commit 069eccf415
3 changed files with 107 additions and 32 deletions

View File

@ -117,7 +117,7 @@ namespace ix
ss << "\r\n"; ss << "\r\n";
} }
std::string request(ss.str()); std::string req(ss.str());
int timeoutSecs = 10; int timeoutSecs = 10;
@ -138,14 +138,14 @@ namespace ix
{ {
std::cout << "Sending " << verb << " request " std::cout << "Sending " << verb << " request "
<< "to " << host << ":" << port << std::endl << "to " << host << ":" << port << std::endl
<< "request size: " << request.size() << " bytes" << "request size: " << req.size() << " bytes"
<< "=============" << std::endl << "=============" << std::endl
<< request << req
<< "=============" << std::endl << "=============" << std::endl
<< std::endl; << std::endl;
} }
if (!_socket->writeBytes(request, isCancellationRequested)) if (!_socket->writeBytes(req, isCancellationRequested))
{ {
code = 0; // 0 ? code = 0; // 0 ?
std::string errorMsg("Cannot send request"); std::string errorMsg("Cannot send request");
@ -187,34 +187,102 @@ namespace ix
return std::make_tuple(code, headers, payload, errorMsg); return std::make_tuple(code, headers, payload, errorMsg);
} }
// Parse response: // Redirect ?
// http://bryce-thomas.blogspot.com/2012/01/technical-parsing-http-to-extract.html if (code == 301)
if (headers.find("content-length") == headers.end())
{ {
code = 0; // 0 ? if (headers.find("location") == headers.end())
std::string errorMsg("No content length header");
return std::make_tuple(code, headers, payload, errorMsg);
}
ssize_t contentLength = -1;
ss.str("");
ss << headers["content-length"];
ss >> contentLength;
payload.reserve(contentLength);
// FIXME: very inefficient way to read bytes, but it works...
for (int i = 0; i < contentLength; ++i)
{
char c;
if (!_socket->readByte(&c, isCancellationRequested))
{ {
ss.str(""); code = 0; // 0 ?
ss << "Cannot read byte"; std::string errorMsg("Missing location header for redirect");
return std::make_tuple(-1, headers, payload, ss.str()); return std::make_tuple(code, headers, payload, errorMsg);
} }
payload += c; std::string location = headers["location"];
return request(location, verb, extraHeaders, httpParameters, verbose);
}
// Parse response:
// http://bryce-thomas.blogspot.com/2012/01/technical-parsing-http-to-extract.html
if (headers.find("content-length") != headers.end())
{
ssize_t contentLength = -1;
ss.str("");
ss << headers["content-length"];
ss >> contentLength;
payload.reserve(contentLength);
// FIXME: very inefficient way to read bytes, but it works...
for (int i = 0; i < contentLength; ++i)
{
char c;
if (!_socket->readByte(&c, isCancellationRequested))
{
ss.str("");
ss << "Cannot read byte";
return std::make_tuple(-1, headers, payload, ss.str());
}
payload += c;
}
}
else if (headers.find("transfer-encoding") != headers.end() &&
headers["transfer-encoding"] == "chunked")
{
std::stringstream ss;
while (true)
{
lineResult = _socket->readLine(isCancellationRequested);
line = lineResult.second;
if (!lineResult.first)
{
code = 0; // 0 ?
std::string errorMsg("Cannot read http body");
return std::make_tuple(code, headers, payload, errorMsg);
}
uint64_t chunkSize;
ss.str("");
ss << std::hex << line;
ss >> chunkSize;
payload.reserve(payload.size() + chunkSize);
// Read another line
for (uint64_t i = 0; i < chunkSize; ++i)
{
char c;
if (!_socket->readByte(&c, isCancellationRequested))
{
ss.str("");
ss << "Cannot read byte";
return std::make_tuple(-1, headers, payload, ss.str());
}
payload += c;
}
lineResult = _socket->readLine(isCancellationRequested);
if (!lineResult.first)
{
code = 0; // 0 ?
std::string errorMsg("Cannot read http body");
return std::make_tuple(code, headers, payload, errorMsg);
}
if (chunkSize == 0) break;
}
}
else
{
code = 0; // 0 ?
std::string errorMsg("Cannot read http body");
return std::make_tuple(code, headers, payload, errorMsg);
} }
return std::make_tuple(code, headers, payload, ""); return std::make_tuple(code, headers, payload, "");

View File

@ -19,7 +19,8 @@ namespace ix
{ {
int ws_http_client_main(const std::string& url, int ws_http_client_main(const std::string& url,
const std::string& headers, const std::string& headers,
const std::string& data); const std::string& data,
bool headersOnly);
int ws_ping_pong_main(const std::string& url); int ws_ping_pong_main(const std::string& url);
@ -51,6 +52,7 @@ int main(int argc, char** argv)
std::string user; std::string user;
std::string data; std::string data;
std::string headers; std::string headers;
bool headersOnly = false;
int port = 8080; int port = 8080;
CLI::App* sendApp = app.add_subcommand("send", "Send a file"); CLI::App* sendApp = app.add_subcommand("send", "Send a file");
@ -85,6 +87,7 @@ int main(int argc, char** argv)
httpClientApp->add_option("-d", data, "Form data")->join(); httpClientApp->add_option("-d", data, "Form data")->join();
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_flag("-I", headersOnly, "Header");
CLI11_PARSE(app, argc, argv); CLI11_PARSE(app, argc, argv);
@ -126,7 +129,7 @@ int main(int argc, char** argv)
else if (app.got_subcommand("http_client")) else if (app.got_subcommand("http_client"))
{ {
std::cout << "data: " << data << std::endl; std::cout << "data: " << data << std::endl;
return ix::ws_http_client_main(url, headers, data); return ix::ws_http_client_main(url, headers, data, headersOnly);
} }
return 1; return 1;

View File

@ -67,7 +67,8 @@ namespace ix
int ws_http_client_main(const std::string& url, int ws_http_client_main(const std::string& url,
const std::string& headersData, const std::string& headersData,
const std::string& data) const std::string& data,
bool headersOnly)
{ {
HttpParameters httpParameters = parsePostParameters(data); HttpParameters httpParameters = parsePostParameters(data);
WebSocketHttpHeaders headers = parseHeaders(headersData); WebSocketHttpHeaders headers = parseHeaders(headersData);
@ -99,7 +100,10 @@ namespace ix
std::cout << "error message: " << errorMsg << std::endl; std::cout << "error message: " << errorMsg << std::endl;
} }
std::cout << "payload: " << payload << std::endl; if (!headersOnly)
{
std::cout << "payload: " << payload << std::endl;
}
return 0; return 0;
} }