diff --git a/ixwebsocket/IXHttpClient.cpp b/ixwebsocket/IXHttpClient.cpp index 5ddde8bc..9b7f732a 100644 --- a/ixwebsocket/IXHttpClient.cpp +++ b/ixwebsocket/IXHttpClient.cpp @@ -14,6 +14,8 @@ #include #include +#include + namespace ix { HttpClient::HttpClient() @@ -71,6 +73,11 @@ namespace ix ss << "User-Agent: ixwebsocket/1.0.0" << "\r\n"; ss << "Accept: */*" << "\r\n"; + if (args.compress) + { + ss << "Accept-Encoding: gzip" << "\r\n"; + } + // Append extra headers for (auto&& it : args.extraHeaders) { @@ -193,11 +200,13 @@ namespace ix { ss.str(""); ss << "Cannot read byte"; - return std::make_tuple(-1, headers, payload, ss.str()); + return std::make_tuple(-1, headers, payload, "Cannot read byte"); } payload += c; } + + std::cout << "I WAS HERE" << std::endl; } else if (headers.find("transfer-encoding") != headers.end() && headers["transfer-encoding"] == "chunked") @@ -264,7 +273,21 @@ namespace ix { code = 0; // 0 ? std::string errorMsg("Cannot read http body"); - return std::make_tuple(code, headers, payload, errorMsg); + return std::make_tuple(-1, headers, payload, errorMsg); + } + + // If the content was compressed with gzip, decode it + if (headers["Content-Encoding"] == "gzip") + { + if (args.verbose) std::cout << "Decoding gzip..." << std::endl; + + std::string decompressedPayload; + if (!gzipInflate(payload, decompressedPayload)) + { + std::string errorMsg("Error decompressing payload"); + return std::make_tuple(-1, headers, payload, errorMsg); + } + payload = decompressedPayload; } return std::make_tuple(code, headers, payload, ""); @@ -338,4 +361,53 @@ namespace ix } return ss.str(); } + + bool HttpClient::gzipInflate( + const std::string& in, + std::string& out) + { + z_stream inflateState; + memset(&inflateState, 0, sizeof(inflateState)); + + inflateState.zalloc = Z_NULL; + inflateState.zfree = Z_NULL; + inflateState.opaque = Z_NULL; + inflateState.avail_in = 0; + inflateState.next_in = Z_NULL; + + if (inflateInit2(&inflateState, 16+MAX_WBITS) != Z_OK) + { + return false; + } + + inflateState.avail_in = (uInt) in.size(); + inflateState.next_in = (unsigned char *)(const_cast(in.data())); + + const int kBufferSize = 1 << 14; + + std::unique_ptr compressBuffer = + std::make_unique(kBufferSize); + + do + { + inflateState.avail_out = (uInt) kBufferSize; + inflateState.next_out = compressBuffer.get(); + + int ret = inflate(&inflateState, Z_SYNC_FLUSH); + + if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) + { + inflateEnd(&inflateState); + return false; + } + + out.append( + reinterpret_cast(compressBuffer.get()), + kBufferSize - inflateState.avail_out + ); + } while (inflateState.avail_out == 0); + + inflateEnd(&inflateState); + return true; + } } diff --git a/ixwebsocket/IXHttpClient.h b/ixwebsocket/IXHttpClient.h index f042e310..e6bd625c 100644 --- a/ixwebsocket/IXHttpClient.h +++ b/ixwebsocket/IXHttpClient.h @@ -30,6 +30,7 @@ namespace ix int timeoutSecs; bool followRedirects; bool verbose; + bool compress; }; class HttpClient { @@ -54,6 +55,10 @@ namespace ix std::string urlEncode(const std::string& value); + bool gzipInflate( + const std::string& in, + std::string& out); + std::shared_ptr _socket; }; } diff --git a/ixwebsocket/IXWebSocketHttpHeaders.cpp b/ixwebsocket/IXWebSocketHttpHeaders.cpp index 7f5d449e..68c3902a 100644 --- a/ixwebsocket/IXWebSocketHttpHeaders.cpp +++ b/ixwebsocket/IXWebSocketHttpHeaders.cpp @@ -57,9 +57,6 @@ namespace ix std::string name(lineStr.substr(0, colon)); std::string value(lineStr.substr(colon + 2, i - colon - 4)); - // Make the name lower case. - std::transform(name.begin(), name.end(), name.begin(), ::tolower); - headers[name] = value; } } diff --git a/ws/ws.cpp b/ws/ws.cpp index 39e2da64..3973a990 100644 --- a/ws/ws.cpp +++ b/ws/ws.cpp @@ -31,6 +31,7 @@ int main(int argc, char** argv) bool followRedirects = false; bool verbose = false; bool save = false; + bool compress = false; int port = 8080; int connectTimeOutSeconds = 3; @@ -71,6 +72,7 @@ int main(int argc, char** argv) httpClientApp->add_flag("-L", followRedirects, "Header"); httpClientApp->add_flag("-v", verbose, "Verbose"); httpClientApp->add_flag("-O", save, "Save to disk"); + httpClientApp->add_flag("--compress", compress, "gzip compression"); httpClientApp->add_option("--connect-timeout", connectTimeOutSeconds, "Connection timeout"); CLI11_PARSE(app, argc, argv); @@ -114,7 +116,8 @@ int main(int argc, char** argv) { return ix::ws_http_client_main(url, headers, data, headersOnly, connectTimeOutSeconds, - followRedirects, verbose, save, output); + followRedirects, verbose, save, output, + compress); } return 1; diff --git a/ws/ws.h b/ws/ws.h index 42b01fdb..85df3ba8 100644 --- a/ws/ws.h +++ b/ws/ws.h @@ -17,7 +17,8 @@ namespace ix bool followRedirects, bool verbose, bool save, - const std::string& output); + const std::string& output, + bool compress); int ws_ping_pong_main(const std::string& url); diff --git a/ws/ws_http_client.cpp b/ws/ws_http_client.cpp index 99697fbf..e6ebbb1d 100644 --- a/ws/ws_http_client.cpp +++ b/ws/ws_http_client.cpp @@ -90,7 +90,8 @@ namespace ix bool followRedirects, bool verbose, bool save, - const std::string& output) + const std::string& output, + bool compress) { HttpRequestArgs args; args.url = url; @@ -98,6 +99,7 @@ namespace ix args.timeoutSecs = timeoutSecs; args.followRedirects = followRedirects; args.verbose = verbose; + args.compress = compress; HttpParameters httpParameters = parsePostParameters(data); @@ -127,24 +129,13 @@ namespace ix } std::cerr << "error code: " << errorCode << std::endl; - if (!errorMsg.empty()) + if (errorCode != 200) { std::cerr << "error message: " << errorMsg << std::endl; } if (!headersOnly && errorCode == 200) { - if (responseHeaders["Content-Type"] != "application/octet-stream") - { - std::cout << "payload: " << payload << std::endl; - } - else - { - std::cerr << "Binary output can mess up your terminal." << std::endl; - std::cerr << "Use the -O flag to save the file to disk." << std::endl; - std::cerr << "You can also use the --output option to specify a filename." << std::endl; - } - if (save || !output.empty()) { // FIMXE we should decode the url first @@ -159,6 +150,19 @@ namespace ix out.write((char*)&payload.front(), payload.size()); out.close(); } + else + { + if (responseHeaders["Content-Type"] != "application/octet-stream") + { + std::cout << "payload: " << payload << std::endl; + } + else + { + std::cerr << "Binary output can mess up your terminal." << std::endl; + std::cerr << "Use the -O flag to save the file to disk." << std::endl; + std::cerr << "You can also use the --output option to specify a filename." << std::endl; + } + } } return 0;