From e195d3217826d61ab0d7ca7e2032271f5066a7df Mon Sep 17 00:00:00 2001 From: B Stack Date: Mon, 15 Jul 2019 08:13:44 -0400 Subject: add upstream 10.14 --- zen/http.cpp | 75 ++++++++++++++++++++++++++++++++++--------------------- zen/http.h | 15 ++++++++--- zen/socket.h | 5 ++-- zen/warn_static.h | 8 +++--- 4 files changed, 65 insertions(+), 38 deletions(-) (limited to 'zen') diff --git a/zen/http.cpp b/zen/http.cpp index f8538c93..43c9dcbf 100644 --- a/zen/http.cpp +++ b/zen/http.cpp @@ -7,6 +7,7 @@ #include "http.h" #include "socket.h" + #include "open_ssl.h" using namespace zen; @@ -14,11 +15,15 @@ using namespace zen; class HttpInputStream::Impl { public: - Impl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError - const std::vector>* postParams) : //issue POST if bound, GET otherwise + Impl(const Zstring& url, + const std::vector>* postParams, //issue POST if bound, GET otherwise + bool disableGetCache /*not relevant for POST (= never cached)*/, + const Zstring& userAgent, + const Zstring* caCertFilePath /*optional: enable certificate validation*/, + const IOCallback& notifyUnbufferedIO) : //throw SysError notifyUnbufferedIO_(notifyUnbufferedIO) { - ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ ); + ZEN_ON_SCOPE_FAIL(cleanup(); /*destructor call would lead to member double clean-up!!!*/); const Zstring urlFmt = afterFirst(url, Zstr("://"), IF_MISSING_RETURN_NONE); const Zstring server = beforeFirst(urlFmt, Zstr('/'), IF_MISSING_RETURN_ALL); @@ -33,12 +38,15 @@ public: throw SysError(L"URL uses unexpected protocol."); }(); - assert(!useTls); //not supported by our plain socket! - (void)useTls; - - socket_ = std::make_unique(server, Zstr("http")); //throw SysError - //HTTP default port: 80, see %WINDIR%\system32\drivers\etc\services + if (useTls) //HTTP default port: 443, see %WINDIR%\system32\drivers\etc\services + { + socket_ = std::make_unique(server, Zstr("https")); //throw SysError + tlsCtx_ = std::make_unique(socket_->get(), server, caCertFilePath); //throw SysError + } + else //HTTP default port: 80, see %WINDIR%\system32\drivers\etc\services + socket_ = std::make_unique(server, Zstr("http")); //throw SysError + //we don't support "chunked transfer encoding" => HTTP 1.0 std::map headers; headers["Host" ] = utfTo(server); //only required for HTTP/1.1 headers["User-Agent"] = utfTo(userAgent); @@ -46,12 +54,11 @@ public: const std::string postBuf = postParams ? xWwwFormUrlEncode(*postParams) : ""; - if (!postParams) //HTTP GET + if (!postParams /*HTTP GET*/ && disableGetCache) headers["Pragma"] = "no-cache"; //HTTP 1.0 only! superseeded by "Cache-Control" - //consider internetIsAlive() test; not relevant for POST (= never cached) else //HTTP POST { - headers["Content-type"] = "application/x-www-form-urlencoded"; + headers["Content-Type"] = "application/x-www-form-urlencoded"; headers["Content-Length"] = numberTo(postBuf.size()); } @@ -64,9 +71,13 @@ public: //send request for (size_t bytesToSend = msg.size(); bytesToSend > 0;) - bytesToSend -= tryWriteSocket(socket_->get(), &*(msg.end() - bytesToSend), bytesToSend); //throw SysError + bytesToSend -= tlsCtx_ ? + tlsCtx_->tryWrite( &*(msg.end() - bytesToSend), bytesToSend) : //throw SysError + tryWriteSocket(socket_->get(), &*(msg.end() - bytesToSend), bytesToSend); //throw SysError - shutdownSocketSend(socket_->get()); //throw SysError + //shutdownSocketSend(socket_->get()); //throw SysError + //NO! Sending TCP FIN before receiving response (aka "TCP Half Closed") is not always supported! e.g. Cloudflare server will immediately end connection: recv() returns 0. + //"clients SHOULD NOT half-close their TCP connections": https://github.com/httpwg/http-core/issues/22 //receive response: std::string headBuf; @@ -164,7 +175,9 @@ private: return 0; bytesToRead = static_cast(std::min(static_cast(bytesToRead), contentRemaining_)); //[!] contentRemaining_ > 4 GB possible! } - const size_t bytesReceived = tryReadSocket(socket_->get(), buffer, bytesToRead); //throw SysError; may return short, only 0 means EOF! + const size_t bytesReceived = tlsCtx_ ? + tlsCtx_->tryRead( buffer, bytesToRead) : //throw SysError; may return short, only 0 means EOF! + tryReadSocket (socket_->get(), buffer, bytesToRead); // if (contentRemaining_ >= 0) contentRemaining_ -= bytesReceived; @@ -174,14 +187,15 @@ private: return bytesReceived; //"zero indicates end of file" } - Impl (const Impl&) = delete; - Impl& operator=(const Impl&) = delete; - void cleanup() { } - std::unique_ptr socket_; //*bound* after constructor has run + Impl (const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + std::unique_ptr socket_; //*bound* after constructor has run + std::unique_ptr tlsCtx_; //optional: support HTTPS int statusCode_ = 0; std::map responseHeaders_; @@ -208,14 +222,17 @@ std::string HttpInputStream::readAll() { return bufferedLoad(*pimpl namespace { -std::unique_ptr sendHttpRequestImpl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError - const std::vector>* postParams) //issue POST if bound, GET otherwise +std::unique_ptr sendHttpRequestImpl(const Zstring& url, + const std::vector>* postParams /*issue POST if bound, GET otherwise*/, + const Zstring& userAgent, + const Zstring* caCertFilePath /*optional: enable certificate validation*/, + const IOCallback& notifyUnbufferedIO) //throw SysError { Zstring urlRed = url; //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop." for (int redirects = 0; redirects < 6; ++redirects) { - auto response = std::make_unique(urlRed, userAgent, notifyUnbufferedIO, postParams); //throw SysError + auto response = std::make_unique(urlRed, postParams, false /*disableGetCache*/, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection const int statusCode = response->getStatusCode(); @@ -310,16 +327,16 @@ std::vector> zen::xWwwFormUrlDecode(const st } -HttpInputStream zen::sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, - const std::vector>& postParams) //throw SysError +HttpInputStream zen::sendHttpPost(const Zstring& url, const std::vector>& postParams, + const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError { - return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, &postParams); //throw SysError + return sendHttpRequestImpl(url, &postParams, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError } -HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO) //throw SysError +HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const Zstring* caCertFilePath, const IOCallback& notifyUnbufferedIO) //throw SysError { - return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, nullptr); //throw SysError + return sendHttpRequestImpl(url, nullptr /*postParams*/, userAgent, caCertFilePath, notifyUnbufferedIO); //throw SysError } @@ -328,9 +345,11 @@ bool zen::internetIsAlive() //noexcept try { auto response = std::make_unique(Zstr("http://www.google.com/"), + nullptr /*postParams*/, + true /*disableGetCache*/, Zstr("FreeFileSync"), - nullptr /*notifyUnbufferedIO*/, - nullptr /*postParams*/); //throw SysError + nullptr /*caCertFilePath*/, + nullptr /*notifyUnbufferedIO*/); //throw SysError const int statusCode = response->getStatusCode(); //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!! diff --git a/zen/http.h b/zen/http.h index 5d84be2c..2fafa574 100644 --- a/zen/http.h +++ b/zen/http.h @@ -15,7 +15,7 @@ namespace zen { /* - thread-safe! (Window/Linux/macOS) - - HTTPS supported only for Windows + - Linux/macOS: init OpenSSL before use! */ class HttpInputStream { @@ -36,9 +36,16 @@ private: }; -HttpInputStream sendHttpGet (const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError -HttpInputStream sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/, // - const std::vector>& postParams); +HttpInputStream sendHttpGet(const Zstring& url, + const Zstring& userAgent, + const Zstring* caCertFilePath /*optional: enable certificate validation*/, + const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError + +HttpInputStream sendHttpPost(const Zstring& url, + const std::vector>& postParams, + const Zstring& userAgent, + const Zstring* caCertFilePath /*optional: enable certificate validation*/, + const IOCallback& notifyUnbufferedIO /*throw X*/); bool internetIsAlive(); //noexcept std::string xWwwFormUrlEncode(const std::vector>& paramPairs); diff --git a/zen/socket.h b/zen/socket.h index 0b4c823d..7ca0c93f 100644 --- a/zen/socket.h +++ b/zen/socket.h @@ -105,7 +105,7 @@ size_t tryReadSocket(SocketType socket, void* buffer, size_t bytesToRead) //thro THROW_LAST_SYS_ERROR_WSA(L"recv"); if (static_cast(bytesReceived) > bytesToRead) //better safe than sorry - throw SysError(L"HttpInputStream::tryRead: buffer overflow."); + throw SysError(L"recv: buffer overflow."); return bytesReceived; //"zero indicates end of file" } @@ -138,10 +138,11 @@ size_t tryWriteSocket(SocketType socket, const void* buffer, size_t bytesToWrite } +//initiate termination of connection by sending TCP FIN package inline void shutdownSocketSend(SocketType socket) //throw SysError { - if (::shutdown(socket, SHUT_WR) != 0) + if (::shutdown(socket, SHUT_WR) != 0) THROW_LAST_SYS_ERROR_WSA(L"shutdown"); } diff --git a/zen/warn_static.h b/zen/warn_static.h index fb8fbb95..17e7cf25 100644 --- a/zen/warn_static.h +++ b/zen/warn_static.h @@ -8,10 +8,10 @@ #define WARN_STATIC_H_08724567834560832745 /* -Portable Compile-Time Warning ------------------------------ -Usage: - warn_static("my message") + Portable Compile-Time Warning + ----------------------------- + Usage: + warn_static("my message") */ #define ZEN_STATIC_WARNING_STRINGIZE(NUM) #NUM -- cgit