From 7efa590aa304c12c16f2a686de24c83117a87279 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Wed, 10 Jul 2019 11:34:04 -0400 Subject: [PATCH 01/41] Support CURL based HTTP client --- Release/CMakeLists.txt | 3 +- Release/cmake/cpprest_find_curl.cmake | 17 + Release/cmake/cpprestsdk-config.in.cmake | 4 + Release/src/CMakeLists.txt | 12 + Release/src/http/client/http_client_curl.cpp | 2379 +++++++++++ Release/src/http/client/http_client_impl.h | 2 - .../src/http/client/winhttppal/winhttppal.cpp | 3804 +++++++++++++++++ .../src/http/client/winhttppal/winhttppal.h | 426 ++ .../http/client/client_construction.cpp | 2 +- .../http/client/request_uri_tests.cpp | 6 +- 10 files changed, 6648 insertions(+), 7 deletions(-) create mode 100644 Release/cmake/cpprest_find_curl.cmake create mode 100644 Release/src/http/client/http_client_curl.cpp create mode 100644 Release/src/http/client/winhttppal/winhttppal.cpp create mode 100644 Release/src/http/client/winhttppal/winhttppal.h diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 4b6433a93a..8d68817bd9 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -61,6 +61,7 @@ endif() include(cmake/cpprest_find_boost.cmake) include(cmake/cpprest_find_zlib.cmake) +include(cmake/cpprest_find_curl.cmake) include(cmake/cpprest_find_openssl.cmake) include(cmake/cpprest_find_websocketpp.cmake) include(cmake/cpprest_find_brotli.cmake) @@ -164,7 +165,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS) elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") message("-- Setting gcc options") - set(WARNINGS -Wall -Wextra -Wunused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code) + set(WARNINGS -Wall -Wextra -Wunused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wno-unused-parameter -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code) set(LD_FLAGS "${LD_FLAGS} -Wl,-z,defs") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") diff --git a/Release/cmake/cpprest_find_curl.cmake b/Release/cmake/cpprest_find_curl.cmake new file mode 100644 index 0000000000..bc572e233c --- /dev/null +++ b/Release/cmake/cpprest_find_curl.cmake @@ -0,0 +1,17 @@ +function(cpprest_find_curl) + if(TARGET cpprestsdk_curl_internal) + return() + endif() + + if(NOT CURL_LIBRARY OR NOT CURL_INCLUDE_DIRS) + find_package(CURL REQUIRED) + endif() + + add_library(cpprestsdk_curl_internal INTERFACE) + if(TARGET CURL::CURL) + target_link_libraries(cpprestsdk_curl_internal INTERFACE CURL::CURL) + else() + target_link_libraries(cpprestsdk_curl_internal INTERFACE "$") + target_include_directories(cpprestsdk_curl_internal INTERFACE "$") + endif() +endfunction() diff --git a/Release/cmake/cpprestsdk-config.in.cmake b/Release/cmake/cpprestsdk-config.in.cmake index 8b5e8a6ff3..9e7db64dfb 100644 --- a/Release/cmake/cpprestsdk-config.in.cmake +++ b/Release/cmake/cpprestsdk-config.in.cmake @@ -11,6 +11,10 @@ if(@CPPREST_USES_OPENSSL@) find_dependency(OpenSSL) endif() +if(@CPPREST_USES_CURL@) + find_dependency(CURL) +endif() + if(@CPPREST_USES_BOOST@ AND OFF) if(UNIX) find_dependency(Boost COMPONENTS random system thread filesystem chrono atomic date_time regex) diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt index 119b07afe2..82eed60672 100644 --- a/Release/src/CMakeLists.txt +++ b/Release/src/CMakeLists.txt @@ -131,6 +131,13 @@ if(CPPREST_HTTP_CLIENT_IMPL STREQUAL "asio") target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_ASIO) target_sources(cpprest PRIVATE http/client/http_client_asio.cpp http/client/x509_cert_utilities.cpp) target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal) +elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "curl") + cpprest_find_boost() + cpprest_find_openssl() + cpprest_find_curl() + target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_CURL) + target_sources(cpprest PRIVATE http/client/http_client_curl.cpp http/client/winhttppal/winhttppal.cpp http/client/x509_cert_utilities.cpp) + target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal cpprestsdk_curl_internal) elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttp") target_link_libraries(cpprest PRIVATE httpapi.lib @@ -237,6 +244,7 @@ if(CPPREST_INSTALL) set(CPPREST_USES_ZLIB OFF) set(CPPREST_USES_BROTLI OFF) set(CPPREST_USES_OPENSSL OFF) + set(CPPREST_USES_CURL OFF) set(CPPREST_TARGETS cpprest) if(TARGET cpprestsdk_boost_internal) @@ -255,6 +263,10 @@ if(CPPREST_INSTALL) list(APPEND CPPREST_TARGETS cpprestsdk_openssl_internal) set(CPPREST_USES_OPENSSL ON) endif() + if(TARGET cpprestsdk_curl_internal) + list(APPEND CPPREST_TARGETS cpprestsdk_curl_internal) + set(CPPREST_USES_CURL ON) + endif() if(TARGET cpprestsdk_websocketpp_internal) list(APPEND CPPREST_TARGETS cpprestsdk_websocketpp_internal) endif() diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp new file mode 100644 index 0000000000..c0a4321121 --- /dev/null +++ b/Release/src/http/client/http_client_curl.cpp @@ -0,0 +1,2379 @@ +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * HTTP Library: Client-side APIs. + * + * This file contains the implementation for Windows Desktop, based on WinHTTP. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ +#include "stdafx.h" + +#include "../common/x509_cert_utilities.h" +#include "../common/internal_http_helpers.h" +#include "cpprest/http_headers.h" +#include "http_client_impl.h" +#include "winhttppal/winhttppal.h" +#include + +namespace +{ +struct security_failure_message +{ + std::uint32_t flag; + const char* text; +}; + +CPPREST_CONSTEXPR security_failure_message g_security_failure_messages[] = { + {WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED, + "WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED failed to check revocation status."}, + {WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT, + "WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT SSL certificate is invalid."}, + {WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED, + "WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED SSL certificate was revoked."}, + {WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA, "WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA SSL invalid CA."}, + {WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID, + "WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID SSL common name does not match."}, + {WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID, + "WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID SLL certificate is expired."}, + {WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR, + "WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR internal error."}, +}; + +std::string generate_security_failure_message(std::uint32_t flags) +{ + std::string result("SSL Error:"); + for (const auto& message : g_security_failure_messages) + { + if (flags & message.flag) + { + result.push_back(' '); + result.append(message.text); + } + } + + return result; +} + +} // unnamed namespace +namespace web +{ +namespace http +{ +namespace client +{ +namespace details +{ +// Helper function to query for the size of header values. +static void query_header_length(HINTERNET request_handle, DWORD header, DWORD& length) +{ + WinHttpQueryHeaders(request_handle, + header, + WINHTTP_HEADER_NAME_BY_INDEX, + WINHTTP_NO_OUTPUT_BUFFER, + &length, + WINHTTP_NO_HEADER_INDEX); +} + +// Helper function to get the status code from a WinHTTP response. +static http::status_code parse_status_code(HINTERNET request_handle) +{ + DWORD length = 0; + query_header_length(request_handle, WINHTTP_QUERY_STATUS_CODE, length); + utility::string_t buffer; + buffer.resize(length); + WinHttpQueryHeaders(request_handle, + WINHTTP_QUERY_STATUS_CODE, + WINHTTP_HEADER_NAME_BY_INDEX, + &buffer[0], + &length, + WINHTTP_NO_HEADER_INDEX); + return (unsigned short)atoi((LPCTSTR)(buffer.c_str())); +} + +// Helper function to trim leading and trailing null characters from a string. +static void trim_nulls(utility::string_t& str) +{ + size_t index; + for (index = 0; index < str.size() && str[index] == 0; ++index) + ; + str.erase(0, index); + for (index = str.size(); index > 0 && str[index - 1] == 0; --index) + ; + str.erase(index); +} + +// Helper function to get the reason phrase from a WinHTTP response. +static utility::string_t parse_reason_phrase(HINTERNET request_handle) +{ + utility::string_t phrase; + DWORD length = 0; + + query_header_length(request_handle, WINHTTP_QUERY_STATUS_TEXT, length); + phrase.resize(length); + WinHttpQueryHeaders(request_handle, + WINHTTP_QUERY_STATUS_TEXT, + WINHTTP_HEADER_NAME_BY_INDEX, + &phrase[0], + &length, + WINHTTP_NO_HEADER_INDEX); + // WinHTTP reports back the wrong length, trim any null characters. + trim_nulls(phrase); + return phrase; +} + + +static void parse_headers_string(_Inout_z_ TCHAR* headersStr, web::http::http_headers& headers) +{ + TCHAR* line = strtok (headersStr, "\r\n"); + while (line != nullptr) + { + const utility::string_t header_line(line); + const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":")); + if (colonIndex != utility::string_t::npos) + { + utility::string_t key = header_line.substr(0, colonIndex); + utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1); + web::http::details::trim_whitespace(key); + web::http::details::trim_whitespace(value); + headers.add(key, value); + } + line = strtok(nullptr, "\r\n"); + } +} + +/// +/// Parses a string containing HTTP headers. +/// +static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ TCHAR* headersStr, http_response& response) +{ + // Clear the header map for each new response; otherwise, the header values will be combined. + response.headers().clear(); + + // Status code and reason phrase. + response.set_status_code(parse_status_code(request_handle)); + response.set_reason_phrase(parse_reason_phrase(request_handle)); + + parse_headers_string(headersStr, response.headers()); +} + +// Helper function to build error messages. +static std::string build_error_msg(unsigned long code, const std::string& location) +{ + std::string msg(location); + msg.append(": "); + msg.append(std::to_string(code)); + return msg; +} + +// Helper function to build an error message from a WinHTTP async result. +static std::string build_error_msg(_In_ WINHTTP_ASYNC_RESULT* error_result) +{ + switch (error_result->dwResult) + { + case API_RECEIVE_RESPONSE: return build_error_msg(error_result->dwError, "WinHttpReceiveResponse"); + case API_QUERY_DATA_AVAILABLE: return build_error_msg(error_result->dwError, "WinHttpQueryDataAvaliable"); + case API_READ_DATA: return build_error_msg(error_result->dwError, "WinHttpReadData"); + case API_WRITE_DATA: return build_error_msg(error_result->dwError, "WinHttpWriteData"); + case API_SEND_REQUEST: return build_error_msg(error_result->dwError, "WinHttpSendRequest"); + default: return build_error_msg(error_result->dwError, "Unknown WinHTTP Function"); + } +} + + +class memory_holder +{ + uint8_t* m_externalData; + std::vector m_internalData; + size_t m_size; + +public: + memory_holder() : m_externalData(nullptr), m_size(0) {} + + void allocate_space(size_t length) + { + if (length > m_internalData.size()) + { + m_internalData.resize(length); + } + m_externalData = nullptr; + } + + inline void reassign_to(_In_opt_ uint8_t* block, size_t length) + { + assert(block != nullptr); + m_externalData = block; + m_size = length; + } + + inline bool is_internally_allocated() const { return m_externalData == nullptr; } + + inline uint8_t* get() { return is_internally_allocated() ? &m_internalData[0] : m_externalData; } + + inline size_t size() const { return is_internally_allocated() ? m_internalData.size() : m_size; } +}; + +// Possible ways a message body can be sent/received. +enum msg_body_type +{ + no_body, + content_length_chunked, + transfer_encoding_chunked +}; + +// Additional information necessary to track a WinHTTP request. +class winhttp_request_context final : public request_context +{ +public: + // Factory function to create requests on the heap. + static std::shared_ptr create_request_context( + const std::shared_ptr<_http_client_communicator>& client, const http_request& request) + { + std::shared_ptr ret(new winhttp_request_context(client, request)); + ret->m_self_reference = ret; + return std::move(ret); + } + + ~winhttp_request_context() { cleanup(); } + + void allocate_request_space(_In_opt_ uint8_t* block, size_t length) + { + if (block == nullptr) + m_body_data.allocate_space(length); + else + m_body_data.reassign_to(block, length); + } + + void allocate_reply_space(_In_opt_ uint8_t* block, size_t length) + { + if (block == nullptr) + m_body_data.allocate_space(length); + else + m_body_data.reassign_to(block, length); + } + + bool is_externally_allocated() const { return !m_body_data.is_internally_allocated(); } + + HINTERNET m_request_handle; + std::weak_ptr* + m_request_handle_context; // owned by m_request_handle to be delete'd by WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING + + bool m_proxy_authentication_tried; + bool m_server_authentication_tried; + + msg_body_type m_bodyType; + + utility::size64_t m_remaining_to_write; + + std::char_traits::pos_type m_startingPosition; + + // If the user specified that to guarantee data buffering of request data, in case of challenged authentication + // requests, etc... Then if the request stream buffer doesn't support seeking we need to copy the body chunks as it + // is sent. + concurrency::streams::istream m_readStream; + std::unique_ptr>> m_readBufferCopy; + virtual concurrency::streams::streambuf _get_readbuffer() { return m_readStream.streambuf(); } + + // This self reference will keep us alive until finish() is called. + std::shared_ptr m_self_reference; + memory_holder m_body_data; + + // Compress/decompress-related processing state lives here + class compression_state + { + public: + compression_state() + : m_acquired(nullptr) + , m_bytes_read(0) + , m_bytes_processed(0) + , m_needs_flush(false) + , m_started(false) + , m_done(false) + , m_chunked(false) + , m_chunk_bytes(0) + { + } + + compression_state(const compression_state&) = delete; + compression_state(compression_state&& other) + : m_buffer(std::move(other.m_buffer)) + , m_acquired(other.m_acquired) + , m_bytes_read(other.m_bytes_read) + , m_bytes_processed(other.m_bytes_processed) + , m_needs_flush(other.m_needs_flush) + , m_started(other.m_started) + , m_done(other.m_done) + , m_chunked(other.m_chunked) + , m_chunk_bytes(other.m_chunk_bytes) + , m_chunk(std::move(other.m_chunk)) + { + } + compression_state& operator=(const compression_state&) = delete; + compression_state& operator=(compression_state&& other) + { + m_buffer = std::move(other.m_buffer); + m_acquired = other.m_acquired; + m_bytes_read = other.m_bytes_read; + m_bytes_processed = other.m_bytes_processed; + m_needs_flush = other.m_needs_flush; + m_started = other.m_started; + m_done = other.m_done; + m_chunked = other.m_chunked; + m_chunk_bytes = other.m_chunk_bytes; + m_chunk = std::move(other.m_chunk); + return *this; + } + + // Minimal state for on-the-fly decoding of "chunked" encoded data + class _chunk_helper + { + public: + _chunk_helper() + : m_bytes_remaining(0) + , m_chunk_size(true) + , m_chunk_delim(false) + , m_expect_linefeed(false) + , m_ignore(false) + , m_trailer(false) + { + } + + // Returns true if the end of chunked data has been reached, specifically whether the 0-length + // chunk and its trailing delimiter has been processed. Otherwise, offset and length bound the + // portion of buffer that represents a contiguous (and possibly partial) chunk of consumable + // data; offset+length is the total number of bytes processed from the buffer on this pass. + bool process_buffer(uint8_t* buffer, size_t buffer_size, size_t& offset, size_t& length) + { + bool done = false; + size_t n = 0; + size_t l = 0; + + while (n < buffer_size) + { + if (m_ignore) + { + if (m_expect_linefeed) + { + _ASSERTE(m_chunk_delim && m_trailer); + if (buffer[n] != '\n') + { + // The data stream does not conform to "chunked" encoding + throw http_exception(status_codes::BadRequest, "Transfer-Encoding malformed trailer"); + } + + // Look for further trailer fields or the end of the stream + m_expect_linefeed = false; + m_trailer = false; + } + else if (buffer[n] == '\r') + { + if (!m_trailer) + { + // We're at the end of the data we need to ignore + _ASSERTE(m_chunk_size || m_chunk_delim); + m_ignore = false; + m_chunk_delim = false; // this is only set if we're at the end of the message + } // else we're at the end of a trailer field + m_expect_linefeed = true; + } + else if (m_chunk_delim) + { + // We're processing (and ignoring) a trailer field + m_trailer = true; + } + } + else if (m_expect_linefeed) + { + // We've already seen a carriage return; confirm the linefeed + if (buffer[n] != '\n') + { + // The data stream does not conform to "chunked" encoding + throw http_exception(status_codes::BadRequest, "Transfer-Encoding malformed delimiter"); + } + if (m_chunk_size) + { + if (!m_bytes_remaining) + { + // We're processing the terminating "empty" chunk; there's + // no data, we just need to confirm the final chunk delimiter, + // possibly ignoring a trailer part along the way + m_ignore = true; + m_chunk_delim = true; + } // else we move on to the chunk data itself + m_chunk_size = false; + } + else + { + // Now we move on to the next chunk size + _ASSERTE(!m_bytes_remaining); + if (m_chunk_delim) + { + // We expect a chunk size next + m_chunk_size = true; + } + else + { + // We just processed the end-of-input delimiter + done = true; + } + m_chunk_delim = false; + } + m_expect_linefeed = false; + } + else if (m_chunk_delim) + { + // We're processing a post-chunk delimiter + if (buffer[n] != '\r') + { + // The data stream does not conform to "chunked" encoding + throw http_exception(status_codes::BadRequest, + "Transfer-Encoding malformed chunk delimiter"); + } + + // We found the carriage return; look for the linefeed + m_expect_linefeed = true; + } + else if (m_chunk_size) + { + // We're processing an ASCII hexadecimal chunk size + if (buffer[n] >= 'a' && buffer[n] <= 'f') + { + m_bytes_remaining *= 16; + m_bytes_remaining += 10 + buffer[n] - 'a'; + } + else if (buffer[n] >= 'A' && buffer[n] <= 'F') + { + m_bytes_remaining *= 16; + m_bytes_remaining += 10 + buffer[n] - 'A'; + } + else if (buffer[n] >= '0' && buffer[n] <= '9') + { + m_bytes_remaining *= 16; + m_bytes_remaining += buffer[n] - '0'; + } + else if (buffer[n] == '\r') + { + // We've reached the end of the size, and there's no chunk extension + m_expect_linefeed = true; + } + else if (buffer[n] == ';') + { + // We've reached the end of the size, and there's a chunk extension; + // we don't support extensions, so we ignore them per RFC + m_ignore = true; + } + else + { + // The data stream does not conform to "chunked" encoding + throw http_exception(status_codes::BadRequest, + "Transfer-Encoding malformed chunk size or extension"); + } + } + else + { + if (m_bytes_remaining) + { + // We're at the offset of a chunk of consumable data; let the caller process it + l = std::min(m_bytes_remaining, buffer_size - n); + m_bytes_remaining -= l; + if (!m_bytes_remaining) + { + // We're moving on to the post-chunk delimiter + m_chunk_delim = true; + } + } + else + { + // We've previously processed the terminating empty chunk and its + // trailing delimiter; skip the entire buffer, and inform the caller + n = buffer_size; + done = true; + } + + // Let the caller process the result + break; + } + + // Move on to the next byte + n++; + } + + offset = n; + length = l; + return buffer_size ? done : (!m_bytes_remaining && !m_chunk_size && !m_chunk_delim); + } + + private: + size_t m_bytes_remaining; // the number of bytes remaining in the chunk we're currently processing + bool m_chunk_size; // if true, we're processing a chunk size or its trailing delimiter + bool m_chunk_delim; // if true, we're processing a delimiter between a chunk and the next chunk's size + bool m_expect_linefeed; // if true, we're processing a delimiter, and we've already seen its carriage return + bool m_ignore; // if true, we're processing a chunk extension or trailer, which we don't support + bool m_trailer; // if true, we're processing (and ignoring) a trailer field; m_ignore is also true + }; + + std::vector m_buffer; // we read data from the stream into this before compressing + uint8_t* m_acquired; // we use this in place of m_buffer if the stream has directly-accessible data available + size_t m_bytes_read; // we most recently read this many bytes, which may be less than m_buffer.size() + size_t m_bytes_processed; // we've compressed this many bytes of m_bytes_read so far + bool m_needs_flush; // we've read and compressed all bytes, but the compressor still has compressed bytes to + // give us + bool m_started; // we've sent at least some number of bytes to m_decompressor + bool m_done; // we've read, compressed, and consumed all bytes + bool m_chunked; // if true, we need to decode and decompress a transfer-encoded message + size_t m_chunk_bytes; // un-decompressed bytes remaining in the most-recently-obtained data from m_chunk + std::unique_ptr<_chunk_helper> m_chunk; + } m_compression_state; + + void cleanup() + { + if (m_compression_state.m_acquired != nullptr) + { + // We may still hold a piece of the buffer if we encountered an exception; release it here + if (m_decompressor) + { + _get_writebuffer().commit(0); + } + else + { + _get_readbuffer().release(m_compression_state.m_acquired, m_compression_state.m_bytes_processed); + } + m_compression_state.m_acquired = nullptr; + } + + if (m_request_handle != nullptr) + { + WinHttpCloseHandle(m_request_handle); + } + } + + void install_custom_cn_check(const utility::string_t& customHost) + { + m_customCnCheck = customHost; + utility::details::inplace_tolower(m_customCnCheck); + } + + void on_send_request_validate_cn() + { + // we do the validation inside curl + } + +protected: + virtual void finish() override + { + request_context::finish(); + assert(m_self_reference != nullptr); + auto dereference_self = std::move(m_self_reference); + // As the stack frame cleans up, this will be deleted if no other references exist. + } + +private: + utility::string_t m_customCnCheck; + std::vector m_cachedEncodedCert; + + // Can only create on the heap using factory function. + winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) + : request_context(client, request) + , m_request_handle(nullptr) + , m_proxy_authentication_tried(false) + , m_server_authentication_tried(false) + , m_bodyType(no_body) + , m_remaining_to_write(0) + , m_startingPosition(std::char_traits::eof()) + , m_readStream(request.body()) + , m_body_data() + { + } +}; + + +#if 0 +static DWORD ChooseAuthScheme(DWORD dwSupportedSchemes) +{ + // It is the server's responsibility only to accept + // authentication schemes that provide a sufficient + // level of security to protect the servers resources. + // + // The client is also obligated only to use an authentication + // scheme that adequately protects its username and password. + // + if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE) + return WINHTTP_AUTH_SCHEME_NEGOTIATE; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM) + return WINHTTP_AUTH_SCHEME_NTLM; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT) + return WINHTTP_AUTH_SCHEME_PASSPORT; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST) + return WINHTTP_AUTH_SCHEME_DIGEST; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_BASIC) + return WINHTTP_AUTH_SCHEME_BASIC; + else + return 0; +} +#endif + +// Small RAII helper to ensure that the fields of this struct are always +// properly freed. +struct proxy_info : WINHTTP_PROXY_INFO +{ + proxy_info() { memset(this, 0, sizeof(WINHTTP_PROXY_INFO)); } + + ~proxy_info() + { + if (lpszProxy) ::GlobalFree(lpszProxy); + if (lpszProxyBypass) ::GlobalFree(lpszProxyBypass); + } +}; + +struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG +{ + ie_proxy_config() { memset(this, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG)); } + + ~ie_proxy_config() + { + if (lpszAutoConfigUrl) ::GlobalFree(lpszAutoConfigUrl); + if (lpszProxy) ::GlobalFree(lpszProxy); + if (lpszProxyBypass) ::GlobalFree(lpszProxyBypass); + } +}; + + +// WinHTTP client. +class winhttp_client final : public _http_client_communicator +{ +public: + winhttp_client(http::uri address, http_client_config client_config) + : _http_client_communicator(std::move(address), std::move(client_config)) + , m_opened(false) + , m_hSession(nullptr) + , m_hConnection(nullptr) + , m_secure(m_uri.scheme() == _XPLATSTR("https")) + { + } + + winhttp_client(const winhttp_client&) = delete; + winhttp_client& operator=(const winhttp_client&) = delete; + + // Closes session. + ~winhttp_client() + { + if (m_hConnection != nullptr) + { + WinHttpCloseHandle(m_hConnection); + } + + if (m_hSession != nullptr) + { + // Unregister the callback. + WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0); + + WinHttpCloseHandle(m_hSession); + } + } + + virtual pplx::task propagate(http_request request) override + { + auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this()); + auto context = details::winhttp_request_context::create_request_context(self, request); + + // Use a task to externally signal the final result and completion of the task. + auto result_task = pplx::create_task(context->m_request_completion); + + // Asynchronously send the response with the HTTP client implementation. + this->async_send_request(context); + + return result_task; + } + +protected: + // Open session and connection with the server. + unsigned long open() + { + if (m_opened) + { + return 0; + } + + pplx::extensibility::scoped_critical_section_t l(m_client_lock); + if (m_opened) + { + return 0; + } + + // This object have lifetime greater than proxy_name and proxy_bypass + // which may point to its elements. + ie_proxy_config proxyIE; + + DWORD access_type; + LPCTSTR proxy_name; + LPCTSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS; + utility::string_t proxy_str; + http::uri uri; + + const auto& config = client_config(); + + if (config.proxy().is_disabled()) + { + access_type = WINHTTP_ACCESS_TYPE_NO_PROXY; + proxy_name = WINHTTP_NO_PROXY_NAME; + } + else if (config.proxy().is_default() || config.proxy().is_auto_discovery()) + { + // Use the default WinHTTP proxy by default. + access_type = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + proxy_name = WINHTTP_NO_PROXY_NAME; + m_proxy_auto_config = true; + access_type = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; + } + else + { + _ASSERTE(config.proxy().is_specified()); + access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY; + // WinHttpOpen cannot handle trailing slash in the name, so here is some string gymnastics to keep + // WinHttpOpen happy proxy_str is intentionally declared at the function level to avoid pointing to the + // string in the destructed object + uri = config.proxy().address(); + if (uri.is_port_default()) + { + proxy_name = uri.host().c_str(); + } + else + { + proxy_str = uri.host(); + if (uri.port() > 0) + { + proxy_str.push_back(_XPLATSTR(':')); + proxy_str.append(::utility::conversions::details::to_string_t(uri.port())); + } + + proxy_name = proxy_str.c_str(); + } + } + + // Open session. + m_hSession = WinHttpOpen(NULL, access_type, proxy_name, proxy_bypass, WINHTTP_FLAG_ASYNC); + if (!m_hSession) + { + printf("%s:%d\n", __func__, __LINE__); + return GetLastError(); + } + + // Set timeouts. + int milliseconds = static_cast(config.timeout().count()); + milliseconds = std::max(milliseconds, 1); + if (!WinHttpSetTimeouts(m_hSession, milliseconds, milliseconds, milliseconds, milliseconds)) + { + printf("%s:%d\n", __func__, __LINE__); + return GetLastError(); + } + + if (config.guarantee_order()) + { + // Set max connection to use per server to 1. + DWORD maxConnections = 1; + if (!WinHttpSetOption( + m_hSession, WINHTTP_OPTION_MAX_CONNS_PER_SERVER, &maxConnections, sizeof(maxConnections))) + { + printf("%s:%d\n", __func__, __LINE__); + return GetLastError(); + } + } + + // Enable TLS 1.1 and 1.2 + BOOL win32_result(FALSE); + + DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2); + win32_result = ::WinHttpSetOption( + m_hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols)); + if (FALSE == win32_result) + { + printf("%s:%d\n", __func__, __LINE__); + return GetLastError(); + } + + config._invoke_nativesessionhandle_options(m_hSession); + + // Register asynchronous callback. + if (WINHTTP_INVALID_STATUS_CALLBACK == + WinHttpSetStatusCallback(m_hSession, + &winhttp_client::completion_callback, + WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_HANDLES | + WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, + 0)) + { + printf("%s:%d\n", __func__, __LINE__); + return GetLastError(); + } + + // Open connection. + unsigned int port = m_uri.is_port_default() + ? (m_secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT) + : m_uri.port(); + m_hConnection = WinHttpConnect(m_hSession, m_uri.host().c_str(), (INTERNET_PORT)port, 0); + + if (m_hConnection == nullptr) + { + printf("%s:%d\n", __func__, __LINE__); + return GetLastError(); + } + + m_opened = true; + return S_OK; + } + + // Start sending request. + void send_request(_In_ const std::shared_ptr& request) + { + // First see if we need to be opened. + unsigned long error = open(); + if (error != 0) + { + // DO NOT TOUCH the this pointer after completing the request + // This object could be freed along with the request as it could + // be the last reference to this object + request->report_error(error, _XPLATSTR("Open failed")); + return; + } + + http_request& msg = request->m_request; + http_headers& headers = msg.headers(); + std::shared_ptr winhttp_context = + std::static_pointer_cast(request); + std::weak_ptr weak_winhttp_context = winhttp_context; + + proxy_info info; + bool proxy_info_required = false; + + const auto& method = msg.method(); + + // stop injection of headers via method + // resource should be ok, since it's been encoded + // and host won't resolve + if (!::web::http::details::validate_method(method)) + { + request->report_exception(http_exception("The method string is invalid.")); + return; + } + + if (m_proxy_auto_config) + { + WINHTTP_AUTOPROXY_OPTIONS autoproxy_options; + memset(&autoproxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS)); + + if (m_proxy_auto_config_url.empty()) + { + autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } + else + { + autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; + autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str(); + } + + autoproxy_options.fAutoLogonIfChallenged = TRUE; + +#if 0 + auto result = WinHttpGetProxyForUrl(m_hSession, m_uri.to_string().c_str(), &autoproxy_options, &info); + if (result) + { + proxy_info_required = true; + } + else + { + // Failure to download the auto-configuration script is not fatal. Fall back to the default proxy. + } +#endif + } + + // Need to form uri path, query, and fragment for this request. + // Make sure to keep any path that was specified with the uri when the http_client was created. + const utility::string_t encoded_resource = + http::uri_builder(m_uri).append(msg.relative_uri()).to_uri().resource().to_string(); + + // Open the request. + winhttp_context->m_request_handle_context = new std::weak_ptr(winhttp_context); + + winhttp_context->m_request_handle = + WinHttpOpenRequest(m_hConnection, + msg.method().c_str(), + encoded_resource.c_str(), + nullptr, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + WINHTTP_FLAG_ESCAPE_DISABLE | (m_secure ? WINHTTP_FLAG_SECURE : 0)); + if (winhttp_context->m_request_handle == nullptr) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + delete winhttp_context->m_request_handle_context; + winhttp_context->m_request_handle_context = 0; + request->report_error(errorCode, build_error_msg(errorCode, "WinHttpOpenRequest")); + return; + } + + if (!WinHttpSetOption(winhttp_context->m_request_handle, + WINHTTP_OPTION_CONTEXT_VALUE, + &winhttp_context->m_request_handle_context, + sizeof(void*))) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + delete winhttp_context->m_request_handle_context; + winhttp_context->m_request_handle_context = 0; + request->report_error(errorCode, build_error_msg(errorCode, "WinHttpSetOption request context")); + return; + } + + if (proxy_info_required) + { + auto result = WinHttpSetOption( + winhttp_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO)); + if (!result) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + request->report_error(errorCode, build_error_msg(errorCode, "Setting proxy options")); + return; + } + } + + // If credentials are specified, use autologon policy: WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH + // => default credentials are not used. + // Else, the default autologon policy WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM will be used. + if (client_config().credentials().is_set()) + { + DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; + + auto result = WinHttpSetOption( + winhttp_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data)); + if (!result) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + request->report_error( + errorCode, + build_error_msg(errorCode, "Setting autologon policy to WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH")); + return; + } + } + + // Check to turn off server certificate verification. + DWORD ignoredCertificateValidationSteps = 0; + if (client_config().validate_certificates()) + { + // Revocation is enabled by default in CURL + // check if the user has overridden the desired Common Name with the host header + const auto hostHeader = headers.find(_XPLATSTR("Host")); + if (hostHeader != headers.end()) + { + const auto& requestHost = hostHeader->second; + if (!utility::details::str_iequal(requestHost, m_uri.host())) + { + winhttp_context->install_custom_cn_check(requestHost); + ignoredCertificateValidationSteps = SECURITY_FLAG_IGNORE_CERT_CN_INVALID; + } + } + } + else + { + ignoredCertificateValidationSteps = + SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | + SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; + } + + if (ignoredCertificateValidationSteps && !WinHttpSetOption(winhttp_context->m_request_handle, + WINHTTP_OPTION_SECURITY_FLAGS, + &ignoredCertificateValidationSteps, + sizeof(ignoredCertificateValidationSteps))) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + request->report_error(errorCode, + build_error_msg(errorCode, "Setting ignore server certificate verification")); + return; + } + + size_t content_length; + try + { + content_length = msg._get_impl()->_get_content_length_and_set_compression(); + } + catch (...) + { + request->report_exception(std::current_exception()); + return; + } + if (content_length > 0) + { + if (msg.method() == http::methods::GET || msg.method() == http::methods::HEAD) + { + request->report_exception(http_exception("get_with_body_err_msg")); + return; + } + + // There is a request body that needs to be transferred. + if (content_length == std::numeric_limits::max()) + { + // The content length is not set and the application set a stream. This is an + // indication that we will use transfer encoding chunked. We still want to + // know that stream's effective length if possible for memory efficiency. + winhttp_context->m_bodyType = transfer_encoding_chunked; + winhttp_context->m_remaining_to_write = msg._get_impl()->_get_stream_length(); + } + else + { + // While we won't be transfer-encoding the data, we will write it in portions. + winhttp_context->m_bodyType = content_length_chunked; + winhttp_context->m_remaining_to_write = content_length; + } + } + + utility::string_t flattened_headers = web::http::details::flatten_http_headers(headers); + if (winhttp_context->m_request.method() == http::methods::GET) + { + // Prepare to request a compressed response from the server if necessary. + flattened_headers += winhttp_context->get_compression_header(); + } + + // Add headers. + if (!flattened_headers.empty()) + { + if (!WinHttpAddRequestHeaders(winhttp_context->m_request_handle, + flattened_headers.c_str(), + static_cast(flattened_headers.length()), + WINHTTP_ADDREQ_FLAG_ADD)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + request->report_error(errorCode, build_error_msg(errorCode, "WinHttpAddRequestHeaders")); + return; + } + } + + // Register for notification on cancellation to abort this request. + if (msg._cancellation_token() != pplx::cancellation_token::none()) + { + // cancellation callback is unregistered when request is completed. + winhttp_context->m_cancellationRegistration = + msg._cancellation_token().register_callback([weak_winhttp_context]() { + // Call the WinHttpSendRequest API after WinHttpCloseHandle will give invalid handle error and we + // throw this exception. Call the cleanup to make the m_request_handle as nullptr, otherwise, + // Application Verifier will give AV exception on m_request_handle. + auto lock = weak_winhttp_context.lock(); + if (!lock) return; + lock->cleanup(); + }); + } + + // Call the callback function of user customized options. + try + { + client_config().invoke_nativehandle_options(winhttp_context->m_request_handle); + } + catch (...) + { + request->report_exception(std::current_exception()); + return; + } + + // Only need to cache the request body if user specified and the request stream doesn't support seeking. + if (winhttp_context->m_bodyType != no_body && client_config().buffer_request() && + !winhttp_context->_get_readbuffer().can_seek()) + { + winhttp_context->m_readBufferCopy = + ::utility::details::make_unique<::concurrency::streams::container_buffer>>(); + } + + _start_request_send(winhttp_context, content_length); + + return; + } + +private: + void _start_request_send(const std::shared_ptr& winhttp_context, size_t content_length) + { + DWORD totalLength; + if (winhttp_context->m_bodyType == no_body) + { + totalLength = 0; + } + else + { + // Capture the current read position of the stream. + auto rbuf = winhttp_context->_get_readbuffer(); + + // Record starting position in case request is challenged for authorization + // and needs to seek back to where reading is started from. + winhttp_context->m_startingPosition = rbuf.getpos(std::ios_base::in); + + // If we find ourselves here, we either don't know how large the message + totalLength = winhttp_context->m_bodyType == content_length_chunked ? (DWORD)content_length + : WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH; + } + + const auto requestSuccess = WinHttpSendRequest(winhttp_context->m_request_handle, + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + nullptr, + 0, + totalLength, + (DWORD_PTR)winhttp_context->m_request_handle_context); + if (!requestSuccess) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + winhttp_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest")); + } + } + + // Helper function to query/read next part of response data from winhttp. + static void read_next_response_chunk(winhttp_request_context* pContext, DWORD bytesRead, bool firstRead = false) + { + const bool defaultChunkSize = pContext->m_http_client->client_config().is_default_chunksize(); + + // If user specified a chunk size then read in chunks instead of using query data available. + if (defaultChunkSize) + { + if (!WinHttpQueryDataAvailable(pContext->m_request_handle, nullptr)) + { + auto errorCode = GetLastError(); + pContext->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryDataAvaliable")); + } + } + else + { + // If bytes read is less than the chunk size this request is done. + // Is it really, though? The WinHttpReadData docs suggest that less can be returned regardless... + const size_t chunkSize = pContext->m_http_client->client_config().chunksize(); + std::unique_ptr& decompressor = pContext->m_decompressor; + if (!decompressor && bytesRead < chunkSize && !firstRead) + { + pContext->complete_request(pContext->m_downloaded); + } + else + { + uint8_t* buffer; + + if (decompressor) + { + // m_buffer holds the compressed data; we'll decompress into the caller's buffer later + if (pContext->m_compression_state.m_buffer.capacity() < chunkSize) + { + pContext->m_compression_state.m_buffer.reserve(chunkSize); + } + buffer = pContext->m_compression_state.m_buffer.data(); + } + else + { + auto writebuf = pContext->_get_writebuffer(); + pContext->allocate_reply_space(writebuf.alloc(chunkSize), chunkSize); + buffer = pContext->m_body_data.get(); + } + + if (!WinHttpReadData(pContext->m_request_handle, buffer, static_cast(chunkSize), nullptr)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + pContext->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData")); + } + } + } + } + + static void _transfer_encoding_chunked_write_data(_In_ winhttp_request_context* p_request_context) + { + size_t chunk_size; + std::unique_ptr& compressor = p_request_context->m_request.compressor(); + + // Set the chunk size up front; we need it before the lambda functions come into scope + if (compressor) + { + // We could allocate less than a chunk for the compressed data here, though that + // would result in more trips through this path for not-so-compressible data... + if (p_request_context->m_body_data.size() > http::details::chunked_encoding::additional_encoding_space) + { + // If we've previously allocated space for the compressed data, don't reduce it + chunk_size = + p_request_context->m_body_data.size() - http::details::chunked_encoding::additional_encoding_space; + } + else if (p_request_context->m_remaining_to_write != std::numeric_limits::max()) + { + // Choose a semi-intelligent size based on how much total data is left to compress + chunk_size = std::min(static_cast(p_request_context->m_remaining_to_write) + 128, + p_request_context->m_http_client->client_config().chunksize()); + } + else + { + // Just base our allocation on the chunk size, since we don't have any other data available + chunk_size = p_request_context->m_http_client->client_config().chunksize(); + } + } + else + { + // We're not compressing; use the smaller of the remaining data (if known) and the configured (or default) + // chunk size + chunk_size = std::min(static_cast(p_request_context->m_remaining_to_write), + p_request_context->m_http_client->client_config().chunksize()); + } + p_request_context->allocate_request_space( + nullptr, chunk_size + http::details::chunked_encoding::additional_encoding_space); + + auto after_read = [p_request_context, chunk_size, &compressor](pplx::task op) { + size_t bytes_read; + try + { + bytes_read = op.get(); + // If the read buffer for copying exists then write to it. + if (p_request_context->m_readBufferCopy) + { + // We have raw memory here writing to a memory stream so it is safe to wait + // since it will always be non-blocking. + if (!compressor) + { + p_request_context->m_readBufferCopy + ->putn_nocopy( + &p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset], + bytes_read) + .wait(); + } + } + } + catch (...) + { + p_request_context->report_exception(std::current_exception()); + return; + } + + _ASSERTE(bytes_read != static_cast(-1)); + + size_t offset = http::details::chunked_encoding::add_chunked_delimiters( + p_request_context->m_body_data.get(), + chunk_size + http::details::chunked_encoding::additional_encoding_space, + bytes_read); + + if (!compressor && p_request_context->m_remaining_to_write != std::numeric_limits::max()) + { + if (bytes_read == 0 && p_request_context->m_remaining_to_write) + { + // The stream ended earlier than we detected it should + http_exception ex( + U("Unexpected end of request body stream encountered before expected length met.")); + p_request_context->report_exception(ex); + return; + } + p_request_context->m_remaining_to_write -= bytes_read; + } + + // Stop writing chunks if we reached the end of the stream. + // Note that we could detect end-of-stream based on !m_remaining_to_write, and insert + // the last (0) chunk if we have enough extra space... though we currently don't. + if (bytes_read == 0) + { + p_request_context->m_bodyType = no_body; + if (p_request_context->m_readBufferCopy) + { + // Move the saved buffer into the read buffer, which now supports seeking. + p_request_context->m_readStream = + concurrency::streams::container_stream>::open_istream( + std::move(p_request_context->m_readBufferCopy->collection())); + p_request_context->m_readBufferCopy.reset(); + } + } + + const auto length = bytes_read + (http::details::chunked_encoding::additional_encoding_space - offset); + + if (!WinHttpWriteData(p_request_context->m_request_handle, + &p_request_context->m_body_data.get()[offset], + static_cast(length), + nullptr)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); + } + }; + + if (compressor) + { + auto do_compress = + [p_request_context, chunk_size, &compressor](pplx::task op) -> pplx::task { + size_t bytes_read; + + try + { + bytes_read = op.get(); + } + catch (...) + { + return pplx::task_from_exception(std::current_exception()); + } + // _ASSERTE(bytes_read >= 0); + + uint8_t* buffer = p_request_context->m_compression_state.m_acquired; + if (buffer == nullptr) + { + buffer = p_request_context->m_compression_state.m_buffer.data(); + } + + web::http::compression::operation_hint hint = web::http::compression::operation_hint::has_more; + + if (bytes_read) + { + // An actual read always resets compression state for the next chunk + _ASSERTE(p_request_context->m_compression_state.m_bytes_processed == + p_request_context->m_compression_state.m_bytes_read); + _ASSERTE(!p_request_context->m_compression_state.m_needs_flush); + p_request_context->m_compression_state.m_bytes_read = bytes_read; + p_request_context->m_compression_state.m_bytes_processed = 0; + if (p_request_context->m_readBufferCopy) + { + // If we've been asked to keep a copy of the raw data for restarts, do so here, pre-compression + p_request_context->m_readBufferCopy->putn_nocopy(buffer, bytes_read).wait(); + } + if (p_request_context->m_remaining_to_write == bytes_read) + { + // We've read to the end of the stream; finalize here if possible. We'll + // decrement the remaining count as we actually process the read buffer. + hint = web::http::compression::operation_hint::is_last; + } + } + else if (p_request_context->m_compression_state.m_needs_flush) + { + // All input has been consumed, but we still need to collect additional compressed output; + // this is done (in theory it can be multiple times) as a finalizing operation + hint = web::http::compression::operation_hint::is_last; + } + else if (p_request_context->m_compression_state.m_bytes_processed == + p_request_context->m_compression_state.m_bytes_read) + { + if (p_request_context->m_remaining_to_write && + p_request_context->m_remaining_to_write != std::numeric_limits::max()) + { + // The stream ended earlier than we detected it should + return pplx::task_from_exception(http_exception( + U("Unexpected end of request body stream encountered before expected length met."))); + } + + // We think we're done; inform the compression library so it can finalize and/or give us any pending + // compressed bytes. Note that we may end up here multiple times if m_needs_flush is set, until all + // compressed bytes are drained. + hint = web::http::compression::operation_hint::is_last; + } + // else we're still compressing bytes from the previous read + + _ASSERTE(p_request_context->m_compression_state.m_bytes_processed <= + p_request_context->m_compression_state.m_bytes_read); + + uint8_t* in = buffer + p_request_context->m_compression_state.m_bytes_processed; + size_t inbytes = p_request_context->m_compression_state.m_bytes_read - + p_request_context->m_compression_state.m_bytes_processed; + return compressor + ->compress(in, + inbytes, + &p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset], + chunk_size, + hint) + .then([p_request_context, bytes_read, hint, chunk_size]( + pplx::task op) -> pplx::task { + http::compression::operation_result r; + + try + { + r = op.get(); + } + catch (...) + { + return pplx::task_from_exception(std::current_exception()); + } + + if (hint == web::http::compression::operation_hint::is_last) + { + // We're done reading all chunks, but the compressor may still have compressed bytes to + // drain from previous reads + _ASSERTE(r.done || r.output_bytes_produced == chunk_size); + p_request_context->m_compression_state.m_needs_flush = !r.done; + p_request_context->m_compression_state.m_done = r.done; + } + + // Update the number of bytes compressed in this read chunk; if it's been fully compressed, + // we'll reset m_bytes_processed and m_bytes_read after reading the next chunk + p_request_context->m_compression_state.m_bytes_processed += r.input_bytes_processed; + _ASSERTE(p_request_context->m_compression_state.m_bytes_processed <= + p_request_context->m_compression_state.m_bytes_read); + if (p_request_context->m_remaining_to_write != std::numeric_limits::max()) + { + _ASSERTE(p_request_context->m_remaining_to_write >= r.input_bytes_processed); + p_request_context->m_remaining_to_write -= r.input_bytes_processed; + } + + if (p_request_context->m_compression_state.m_acquired != nullptr && + p_request_context->m_compression_state.m_bytes_processed == + p_request_context->m_compression_state.m_bytes_read) + { + // Release the acquired buffer back to the streambuf at the earliest possible point + p_request_context->_get_readbuffer().release( + p_request_context->m_compression_state.m_acquired, + p_request_context->m_compression_state.m_bytes_processed); + p_request_context->m_compression_state.m_acquired = nullptr; + } + + return pplx::task_from_result(r.output_bytes_produced); + }); + }; + + if (p_request_context->m_compression_state.m_bytes_processed < + p_request_context->m_compression_state.m_bytes_read || + p_request_context->m_compression_state.m_needs_flush) + { + // We're still working on data from a previous read; continue compression without reading new data + do_compress(pplx::task_from_result(0)).then(after_read); + } + else if (p_request_context->m_compression_state.m_done) + { + // We just need to send the last (zero-length) chunk; there's no sense in going through the compression + // path + after_read(pplx::task_from_result(0)); + } + else + { + size_t length; + + // We need to read from the input stream, then compress before sending + if (p_request_context->_get_readbuffer().acquire(p_request_context->m_compression_state.m_acquired, + length)) + { + if (length == 0) + { + if (p_request_context->_get_readbuffer().exception()) + { + p_request_context->report_exception(p_request_context->_get_readbuffer().exception()); + return; + } + else if (p_request_context->m_remaining_to_write && + p_request_context->m_remaining_to_write != std::numeric_limits::max()) + { + // Unexpected end-of-stream. + p_request_context->report_error(GetLastError(), + _XPLATSTR("Outgoing HTTP body stream ended early.")); + return; + } + } + else if (length > p_request_context->m_remaining_to_write) + { + // The stream grew, but we won't + length = static_cast(p_request_context->m_remaining_to_write); + } + + do_compress(pplx::task_from_result(length)).then(after_read); + } + else + { + length = std::min(static_cast(p_request_context->m_remaining_to_write), + p_request_context->m_http_client->client_config().chunksize()); + if (p_request_context->m_compression_state.m_buffer.capacity() < length) + { + p_request_context->m_compression_state.m_buffer.reserve(length); + } + p_request_context->_get_readbuffer() + .getn(p_request_context->m_compression_state.m_buffer.data(), length) + .then(do_compress) + .then(after_read); + } + } + } + else + { + // We're not compressing; just read and chunk + p_request_context->_get_readbuffer() + .getn(&p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset], chunk_size) + .then(after_read); + } + } + + static void _multiple_segment_write_data(_In_ winhttp_request_context* p_request_context) + { + auto rbuf = p_request_context->_get_readbuffer(); + msl::safeint3::SafeInt safeCount = p_request_context->m_remaining_to_write; + safeCount = safeCount.Min(p_request_context->m_http_client->client_config().chunksize()); + + uint8_t* block = nullptr; + size_t length = 0; + if (rbuf.acquire(block, length)) + { + if (length == 0) + { + // Unexpected end-of-stream. + if (rbuf.exception() == nullptr) + { + p_request_context->report_error(GetLastError(), + _XPLATSTR("Error reading outgoing HTTP body from its stream.")); + } + else + { + p_request_context->report_exception(rbuf.exception()); + } + return; + } + + p_request_context->allocate_request_space(block, length); + + const size_t to_write = safeCount.Min(length); + + // Stop writing chunks after this one if no more data. + p_request_context->m_remaining_to_write -= to_write; + if (p_request_context->m_remaining_to_write == 0) + { + p_request_context->m_bodyType = no_body; + } + + if (!WinHttpWriteData(p_request_context->m_request_handle, + p_request_context->m_body_data.get(), + static_cast(to_write), + nullptr)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); + } + } + else + { + p_request_context->allocate_request_space(nullptr, safeCount); + + rbuf.getn(p_request_context->m_body_data.get(), safeCount) + .then([p_request_context, rbuf](pplx::task op) { + size_t read; + try + { + read = op.get(); + } + catch (...) + { + p_request_context->report_exception(std::current_exception()); + return; + } + _ASSERTE(read != static_cast(-1)); + + if (read == 0) + { + p_request_context->report_exception(http_exception( + U("Unexpected end of request body stream encountered before Content-Length met."))); + return; + } + + p_request_context->m_remaining_to_write -= read; + + // Stop writing chunks after this one if no more data. + if (p_request_context->m_remaining_to_write == 0) + { + p_request_context->m_bodyType = no_body; + } + + if (!WinHttpWriteData(p_request_context->m_request_handle, + p_request_context->m_body_data.get(), + static_cast(read), + nullptr)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); + } + }); + } + } + + static std::string get_request_url(HINTERNET hRequestHandle) + { + std::string url; + auto urlSize = static_cast(url.capacity()) * 2; // use initial small string optimization capacity + for (;;) + { + url.resize(urlSize / sizeof(TCHAR)); + if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize)) + { + url.resize(strlen(url.c_str())); + return url; + } + + const auto lastError = GetLastError(); + if (lastError != ERROR_INSUFFICIENT_BUFFER || urlSize == 0) + { + url.clear(); + url.shrink_to_fit(); + return url; + } + } + } + + // Returns true if we handle successfully and resending the request + // or false if we fail to handle. + static bool handle_authentication_failure(HINTERNET hRequestHandle, + const std::shared_ptr& p_request_context, + _In_ DWORD error = 0) + { + http_request& request = p_request_context->m_request; + + _ASSERTE(p_request_context->m_response.status_code() == status_codes::Unauthorized || + p_request_context->m_response.status_code() == status_codes::ProxyAuthRequired || + error == ERROR_WINHTTP_RESEND_REQUEST); + + // Check if the saved read position is valid + auto rdpos = p_request_context->m_startingPosition; + if (rdpos != static_cast::pos_type>(std::char_traits::eof())) + { + // Try to seek back to the saved read position + auto rbuf = p_request_context->_get_readbuffer(); + if (rbuf.seekpos(rdpos, std::ios::ios_base::in) != rdpos) + { + return false; + } + + // We successfully seeked back; now reset the compression state, if any, to match + if (p_request_context->m_request.compressor()) + { + try + { + p_request_context->m_request.compressor()->reset(); + } + catch (...) + { + return false; + } + } + } + p_request_context->m_compression_state = winhttp_request_context::compression_state(); + + // If we got ERROR_WINHTTP_RESEND_REQUEST, the response header is not available, + // we cannot call WinHttpQueryAuthSchemes and WinHttpSetCredentials. +#if 0 + if (error != ERROR_WINHTTP_RESEND_REQUEST) + { + // Obtain the supported and preferred schemes. + DWORD dwSupportedSchemes; + DWORD dwFirstScheme; + DWORD dwAuthTarget; + if (!WinHttpQueryAuthSchemes(hRequestHandle, &dwSupportedSchemes, &dwFirstScheme, &dwAuthTarget)) + { + // This will return the authentication failure to the user, without reporting fatal errors + return false; + } + + DWORD dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes); + if (dwSelectedScheme == 0) + { + // This will return the authentication failure to the user, without reporting fatal errors + return false; + } + + credentials cred; + if (dwAuthTarget == WINHTTP_AUTH_TARGET_SERVER && !p_request_context->m_server_authentication_tried) + { + cred = p_request_context->m_http_client->client_config().credentials(); + p_request_context->m_server_authentication_tried = true; + } + else if (dwAuthTarget == WINHTTP_AUTH_TARGET_PROXY) + { + bool is_redirect = false; + try + { + web::uri current_uri(get_request_url(hRequestHandle)); + is_redirect = p_request_context->m_request.absolute_uri().to_string() != current_uri.to_string(); + } + catch (const std::exception&) + { + } + + // If we have been redirected, then WinHttp needs the proxy credentials again to make the next request + // leg (which may be on a different server) + if (is_redirect || !p_request_context->m_proxy_authentication_tried) + { + cred = p_request_context->m_http_client->client_config().proxy().credentials(); + p_request_context->m_proxy_authentication_tried = true; + } + } + + // No credentials found so can't resend. + if (!cred.is_set()) + { + return false; + } + + // New scope to ensure plaintext password is cleared as soon as possible. + { + auto password = cred._internal_decrypt(); + if (!WinHttpSetCredentials(hRequestHandle, + dwAuthTarget, + dwSelectedScheme, + cred.username().c_str(), + password->c_str(), + nullptr)) + { + return false; + } + } + } +#endif + // Reset the request body type since it might have already started sending. + size_t content_length; + try + { + content_length = request._get_impl()->_get_content_length_and_set_compression(); + } + catch (...) + { + return false; + } + + if (content_length > 0) + { + // There is a request body that needs to be transferred. + if (content_length == std::numeric_limits::max()) + { + // The content length is unknown and the application set a stream. This is an + // indication that we will need to chunk the data. + p_request_context->m_bodyType = transfer_encoding_chunked; + p_request_context->m_remaining_to_write = request._get_impl()->_get_stream_length(); + } + else + { + // While we won't be transfer-encoding the data, we will write it in portions. + p_request_context->m_bodyType = content_length_chunked; + p_request_context->m_remaining_to_write = content_length; + } + } + else + { + p_request_context->m_bodyType = no_body; + } + + // We're good. + winhttp_client* winclnt = reinterpret_cast(p_request_context->m_http_client.get()); + winclnt->_start_request_send(p_request_context, content_length); + + // We will not complete the request. Instead wait for the response to the request that was resent + return true; + } + + // Callback used with WinHTTP to listen for async completions. + static void CALLBACK completion_callback( + HINTERNET hRequestHandle, DWORD_PTR context, DWORD statusCode, _In_ void* statusInfo, DWORD statusInfoLength) + { + CASABLANCA_UNREFERENCED_PARAMETER(statusInfoLength); + + std::weak_ptr* p_weak_request_context = + reinterpret_cast*>(context); + + if (p_weak_request_context == nullptr) + { + return; + } + + if (statusCode == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) + { + // This callback is responsible for freeing the type-erased context. + // This particular status code indicates that this is the final callback call, suitable for context + // destruction. + delete p_weak_request_context; + return; + } + + auto p_request_context = p_weak_request_context->lock(); + if (!p_request_context) + { + // The request context was already released, probably due to cancellation + return; + } + + switch (statusCode) + { + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + { + WINHTTP_ASYNC_RESULT* error_result = reinterpret_cast(statusInfo); + const DWORD errorCode = error_result->dwError; + + // Some authentication schemes require multiple transactions. + // When ERROR_WINHTTP_RESEND_REQUEST is encountered, + // we should continue to resend the request until a response is received that does not contain a 401 or + // 407 status code. + if (errorCode == ERROR_WINHTTP_RESEND_REQUEST) + { + bool resending = handle_authentication_failure(hRequestHandle, p_request_context, errorCode); + if (resending) + { + // The request is resending. Wait until we get a new response. + return; + } + } + + p_request_context->report_error(errorCode, build_error_msg(error_result)); + return; + } + case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: + { + if (!p_request_context->m_request.body()) + { + // Report progress finished uploading with no message body. + auto progress = p_request_context->m_request._get_impl()->_progress_handler(); + if (progress) + { + try + { + (*progress)(message_direction::upload, 0); + } + catch (...) + { + p_request_context->report_exception(std::current_exception()); + return; + } + } + } + + if (p_request_context->m_bodyType == transfer_encoding_chunked) + { + _transfer_encoding_chunked_write_data(p_request_context.get()); + } + else if (p_request_context->m_bodyType == content_length_chunked) + { + _multiple_segment_write_data(p_request_context.get()); + } + else + { + if (!WinHttpReceiveResponse(hRequestHandle, nullptr)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, + build_error_msg(errorCode, "WinHttpReceiveResponse")); + } + } + return; + } + case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: p_request_context->on_send_request_validate_cn(); return; + case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: + p_request_context->report_exception(web::http::http_exception( + generate_security_failure_message(*reinterpret_cast(statusInfo)))); + return; + case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: + { + DWORD bytesWritten = *((DWORD*)statusInfo); + _ASSERTE(statusInfoLength == sizeof(DWORD)); + + if (bytesWritten > 0) + { + auto progress = p_request_context->m_request._get_impl()->_progress_handler(); + if (progress) + { + p_request_context->m_uploaded += bytesWritten; + try + { + (*progress)(message_direction::upload, p_request_context->m_uploaded); + } + catch (...) + { + p_request_context->report_exception(std::current_exception()); + return; + } + } + } + + if (p_request_context->is_externally_allocated()) + { + p_request_context->_get_readbuffer().release(p_request_context->m_body_data.get(), bytesWritten); + } + + if (p_request_context->m_bodyType == transfer_encoding_chunked) + { + _transfer_encoding_chunked_write_data(p_request_context.get()); + } + else if (p_request_context->m_bodyType == content_length_chunked) + { + _multiple_segment_write_data(p_request_context.get()); + } + else + { + if (!WinHttpReceiveResponse(hRequestHandle, nullptr)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, + build_error_msg(errorCode, "WinHttpReceiveResponse")); + } + } + return; + } + case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: + { + // First need to query to see what the headers size is. + DWORD headerBufferLength = 0; + query_header_length(hRequestHandle, WINHTTP_QUERY_RAW_HEADERS_CRLF, headerBufferLength); + + // Now allocate buffer for headers and query for them. + std::vector header_raw_buffer; + header_raw_buffer.resize(headerBufferLength); + TCHAR* header_buffer = reinterpret_cast(&header_raw_buffer[0]); + if (!WinHttpQueryHeaders(hRequestHandle, + WINHTTP_QUERY_RAW_HEADERS_CRLF, + WINHTTP_HEADER_NAME_BY_INDEX, + header_buffer, + &headerBufferLength, + WINHTTP_NO_HEADER_INDEX)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryHeaders")); + ; + return; + } + + http_response& response = p_request_context->m_response; + parse_winhttp_headers(hRequestHandle, header_buffer, response); + + if (response.status_code() == status_codes::Unauthorized /*401*/ || + response.status_code() == status_codes::ProxyAuthRequired /*407*/) + { + bool resending = handle_authentication_failure(hRequestHandle, p_request_context); + if (resending) + { + // The request was not completed but resent with credentials. Wait until we get a new response + return; + } + } + + // Check whether the request is compressed, and if so, whether we're handling it. + if (!p_request_context->handle_compression()) + { + // false indicates report_exception was called + return; + } + if (p_request_context->m_decompressor && + !p_request_context->m_http_client->client_config().request_compressed_response()) + { + p_request_context->m_compression_state.m_chunk = + make_unique(); + p_request_context->m_compression_state.m_chunked = true; + } + + // Signal that the headers are available. + p_request_context->complete_headers(); + + // If the method was 'HEAD,' the body of the message is by definition empty. No need to + // read it. Any headers that suggest the presence of a body can safely be ignored. + if (p_request_context->m_request.method() == methods::HEAD) + { + p_request_context->allocate_request_space(nullptr, 0); + p_request_context->complete_request(0); + return; + } + + // HTTP Specification states: + // If a message is received with both a Transfer-Encoding header field + // and a Content-Length header field, the latter MUST be ignored. + // If none of them is specified, the message length should be determined by the server closing the + // connection. http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 + + read_next_response_chunk(p_request_context.get(), 0, true); + return; + } + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + { + // Status information contains pointer to DWORD containing number of bytes available. + const DWORD num_bytes = *(PDWORD)statusInfo; + uint8_t* buffer; + + if (num_bytes > 0) + { + if (p_request_context->m_decompressor) + { + // Allocate space for the compressed data; we'll decompress it into the caller stream once it's + // been filled in + if (p_request_context->m_compression_state.m_buffer.capacity() < num_bytes) + { + p_request_context->m_compression_state.m_buffer.reserve(num_bytes); + } + buffer = p_request_context->m_compression_state.m_buffer.data(); + } + else + { + auto writebuf = p_request_context->_get_writebuffer(); + p_request_context->allocate_reply_space(writebuf.alloc(num_bytes), num_bytes); + buffer = p_request_context->m_body_data.get(); + } + + // Read in available body data all at once. + if (!WinHttpReadData(hRequestHandle, buffer, num_bytes, nullptr)) + { + printf("%s:%d\n", __func__, __LINE__); + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData")); + } + } + else + { + if (p_request_context->m_decompressor) + { + if (p_request_context->m_compression_state.m_chunked) + { + // We haven't seen the 0-length chunk and/or trailing delimiter that indicate the end of + // chunked input + p_request_context->report_exception( + http_exception("Chunked response stream ended unexpectedly")); + return; + } + if (p_request_context->m_compression_state.m_started && + !p_request_context->m_compression_state.m_done) + { + p_request_context->report_exception( + http_exception("Received incomplete compressed stream")); + return; + } + } + + // No more data available, complete the request. + auto progress = p_request_context->m_request._get_impl()->_progress_handler(); + if (progress) + { + try + { + (*progress)(message_direction::download, p_request_context->m_downloaded); + } + catch (...) + { + p_request_context->report_exception(std::current_exception()); + return; + } + } + + p_request_context->complete_request(p_request_context->m_downloaded); + } + return; + } + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + { + // Status information length contains the number of bytes read. + DWORD bytesRead = statusInfoLength; + + // Report progress about downloaded bytes. + auto progress = p_request_context->m_request._get_impl()->_progress_handler(); + p_request_context->m_downloaded += statusInfoLength; + if (progress) + { + try + { + (*progress)(message_direction::download, p_request_context->m_downloaded); + } + catch (...) + { + p_request_context->report_exception(std::current_exception()); + return; + } + } + + // If no bytes have been read, then this is the end of the response. + if (bytesRead == 0) + { + if (p_request_context->m_decompressor) + { + if (p_request_context->m_compression_state.m_chunked) + { + // We haven't seen the 0-length chunk and/or trailing delimiter that indicate the end of + // chunked input + p_request_context->report_exception( + http_exception("Chunked response stream ended unexpectedly")); + return; + } + if (p_request_context->m_compression_state.m_started && + !p_request_context->m_compression_state.m_done) + { + p_request_context->report_exception( + http_exception("Received incomplete compressed stream")); + return; + } + } + p_request_context->complete_request(p_request_context->m_downloaded); + return; + } + + auto writebuf = p_request_context->_get_writebuffer(); + + if (p_request_context->m_decompressor) + { + size_t chunk_size = std::max(static_cast(bytesRead), + p_request_context->m_http_client->client_config().chunksize()); + p_request_context->m_compression_state.m_bytes_read = static_cast(bytesRead); + p_request_context->m_compression_state.m_chunk_bytes = 0; + + // Note, some servers seem to send a first chunk of body data that decompresses to nothing, but + // initializes the decompression state; this produces no decompressed output. Subsequent chunks + // will then begin emitting decompressed body data. + + // Oddly enough, WinHttp doesn't de-chunk for us if "chunked" isn't the only + // encoding, so we need to do so on the fly as we process the received data + auto process_buffer = + [chunk_size](winhttp_request_context* c, size_t bytes_produced, bool outer) -> bool { + if (!c->m_compression_state.m_chunk_bytes) + { + if (c->m_compression_state.m_chunked) + { + size_t offset; + bool done; + + // Process the next portion of this piece of the transfer-encoded message + done = c->m_compression_state.m_chunk->process_buffer( + c->m_compression_state.m_buffer.data() + c->m_compression_state.m_bytes_processed, + c->m_compression_state.m_bytes_read - c->m_compression_state.m_bytes_processed, + offset, + c->m_compression_state.m_chunk_bytes); + + // Skip chunk-related metadata; it isn't relevant to decompression + _ASSERTE(c->m_compression_state.m_bytes_processed + offset <= + c->m_compression_state.m_bytes_read); + c->m_compression_state.m_bytes_processed += offset; + + if (!c->m_compression_state.m_chunk_bytes) + { + if (done) + { + // We've processed/validated all bytes in this transfer-encoded message. + // Note that we currently ignore "extra" trailing bytes, i.e. + // c->m_compression_state.m_bytes_processed < + // c->m_compression_state.m_bytes_read + if (c->m_compression_state.m_done) + { + c->complete_request(c->m_downloaded); + return false; + } + else if (!outer && bytes_produced != chunk_size) + { + throw http_exception("Transfer ended before decompression completed"); + } + } + else if (!outer && bytes_produced != chunk_size) + { + // There should be more data to receive; look for it + c->m_compression_state.m_bytes_processed = 0; + read_next_response_chunk( + c, static_cast(c->m_compression_state.m_bytes_read)); + return false; + } + } + } + else + { + _ASSERTE(!c->m_compression_state.m_bytes_processed || + c->m_compression_state.m_bytes_processed == + c->m_compression_state.m_bytes_read); + if (c->m_compression_state.m_done) + { + // Decompression is done; complete the request + c->complete_request(c->m_downloaded); + return false; + } + else if (c->m_compression_state.m_bytes_processed != + c->m_compression_state.m_bytes_read) + { + // We still have more data to process in the current buffer + c->m_compression_state.m_chunk_bytes = + c->m_compression_state.m_bytes_read - c->m_compression_state.m_bytes_processed; + } + else if (!outer && bytes_produced != chunk_size) + { + // There should be more data to receive; look for it + c->m_compression_state.m_bytes_processed = 0; + read_next_response_chunk(c, + static_cast(c->m_compression_state.m_bytes_read)); + return false; + } + // Otherwise, we've processed all bytes in the input buffer, but there's a good chance + // that there are still decompressed bytes to emit; we'll do so before reading the next + // chunk + } + } + + // We're still processing the current message chunk + return true; + }; + + pplx::details::_do_while([p_request_context, chunk_size, process_buffer]() -> pplx::task { + uint8_t* buffer; + + try + { + if (!process_buffer(p_request_context.get(), 0, true)) + { + // The chunked request has been completely processed (or contains no data in the first + // place) + return pplx::task_from_result(false); + } + } + catch (...) + { + // The outer do-while requires an explicit task return to activate the then() clause + return pplx::task_from_exception(std::current_exception()); + } + + // If it's possible to know how much post-compression data we're expecting (for instance if we + // can discern how much total data the ostream can support, we could allocate (or at least + // attempt to acquire) based on that + p_request_context->m_compression_state.m_acquired = + p_request_context->_get_writebuffer().alloc(chunk_size); + if (p_request_context->m_compression_state.m_acquired) + { + buffer = p_request_context->m_compression_state.m_acquired; + } + else + { + // The streambuf couldn't accommodate our request; we'll use m_body_data's + // internal vector as temporary storage, then putn() to the caller's stream + p_request_context->allocate_reply_space(nullptr, chunk_size); + buffer = p_request_context->m_body_data.get(); + } + + uint8_t* in = p_request_context->m_compression_state.m_buffer.data() + + p_request_context->m_compression_state.m_bytes_processed; + size_t inbytes = p_request_context->m_compression_state.m_chunk_bytes; + if (inbytes) + { + p_request_context->m_compression_state.m_started = true; + } + return p_request_context->m_decompressor + ->decompress( + in, inbytes, buffer, chunk_size, web::http::compression::operation_hint::has_more) + .then([p_request_context, buffer, chunk_size, process_buffer]( + pplx::task op) { + auto r = op.get(); + auto keep_going = [&r, process_buffer](winhttp_request_context* c) -> pplx::task { + _ASSERTE(r.input_bytes_processed <= c->m_compression_state.m_chunk_bytes); + c->m_compression_state.m_chunk_bytes -= r.input_bytes_processed; + c->m_compression_state.m_bytes_processed += r.input_bytes_processed; + c->m_compression_state.m_done = r.done; + + try + { + // See if we still have more work to do for this section and/or for the response + // in general + return pplx::task_from_result( + process_buffer(c, r.output_bytes_produced, false)); + } + catch (...) + { + return pplx::task_from_exception(std::current_exception()); + } + }; + + _ASSERTE(p_request_context->m_compression_state.m_bytes_processed + + r.input_bytes_processed <= + p_request_context->m_compression_state.m_bytes_read); + + if (p_request_context->m_compression_state.m_acquired != nullptr) + { + // We decompressed directly into the output stream + p_request_context->m_compression_state.m_acquired = nullptr; + p_request_context->_get_writebuffer().commit(r.output_bytes_produced); + return keep_going(p_request_context.get()); + } + + // We decompressed into our own buffer; let the stream copy the data + return p_request_context->_get_writebuffer() + .putn_nocopy(buffer, r.output_bytes_produced) + .then([p_request_context, r, keep_going](pplx::task op) { + if (op.get() != r.output_bytes_produced) + { + return pplx::task_from_exception( + std::runtime_error("Response stream unexpectedly failed to write the " + "requested number of bytes")); + } + return keep_going(p_request_context.get()); + }); + }); + }) + .then([p_request_context](pplx::task op) { + try + { + op.get(); + } + catch (...) + { + // We're only here to pick up any exception that may have been thrown, and to clean up + // if needed + if (p_request_context->m_compression_state.m_acquired) + { + p_request_context->_get_writebuffer().commit(0); + p_request_context->m_compression_state.m_acquired = nullptr; + } + p_request_context->report_exception(std::current_exception()); + } + }); + } + else + { + // If the data was allocated directly from the buffer then commit, otherwise we still + // need to write to the response stream buffer. + if (p_request_context->is_externally_allocated()) + { + writebuf.commit(bytesRead); + read_next_response_chunk(p_request_context.get(), bytesRead); + } + else + { + writebuf.putn_nocopy(p_request_context->m_body_data.get(), bytesRead) + .then([hRequestHandle, p_request_context, bytesRead](pplx::task op) { + size_t written = 0; + try + { + written = op.get(); + } + catch (...) + { + p_request_context->report_exception(std::current_exception()); + return; + } + + // If we couldn't write everything, it's time to exit. + if (written != bytesRead) + { + p_request_context->report_exception(std::runtime_error( + "response stream unexpectedly failed to write the requested number of bytes")); + return; + } + + read_next_response_chunk(p_request_context.get(), bytesRead); + }); + } + } + return; + } + } + } + + std::atomic m_opened; + + // WinHTTP session and connection + HINTERNET m_hSession; + HINTERNET m_hConnection; + bool m_secure; + + // If auto config is true, dynamically find the proxy for each URL using + // the proxy configuration script at the given URL if it's not empty or + // using WPAD otherwise. + bool m_proxy_auto_config {false}; + utility::string_t m_proxy_auto_config_url; +}; + +std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri, + http_client_config&& client_config) +{ + return std::make_shared(std::move(base_uri), std::move(client_config)); +} + + +} // namespace details +} // namespace client +} // namespace http +} // namespace web diff --git a/Release/src/http/client/http_client_impl.h b/Release/src/http/client/http_client_impl.h index 384c9b2de6..923961a0ee 100644 --- a/Release/src/http/client/http_client_impl.h +++ b/Release/src/http/client/http_client_impl.h @@ -30,12 +30,10 @@ namespace details /// Serialize the http_headers into name:value pairs separated by a carriage return and line feed. /// utility::string_t flatten_http_headers(const http_headers& headers); -#if defined(_WIN32) /// /// Parses a string containing Http headers. /// void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers); -#endif } // namespace details } // namespace http diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp new file mode 100644 index 0000000000..b61463de80 --- /dev/null +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -0,0 +1,3804 @@ +// winhttp.cpp : This file contains the 'main' function. Program execution begins and ends there. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _WINHTTP_INTERNAL_ +#ifdef _MSC_VER +#include +#include "winhttp.h" +#else +#include +#include +#include "winhttppal.h" +#endif + +#include +extern "C" +{ +#include +} + +class WinHttpSessionImp; +class WinHttpRequestImp; + +#ifdef min +#define MIN min +#define MAX max +#else +#define MIN std::min +#define MAX std::max +#endif + +#ifdef UNICODE +#define WCTLEN wcslen +#define WCTCMP wcscmp +#define WCTCPY wcscpy +#define WCTNCPY wcsncpy +#define STNPRINTF swprintf +#define TSTRING std::wstring +#define STRING_LITERAL "%S" +#define TO_STRING std::to_wstring +#define TREGEX std::wregex +#define TREGEX_SEARCH std::regex_search +#define TREGEX_MATCH std::wsmatch +#else +#define WCTLEN strlen +#define WCTCMP strcmp +#define WCTCPY strcpy +#define WCTNCPY strncpy +#define STNPRINTF snprintf +#define TEXT(T) T +#define TSTRING std::string +#define STRING_LITERAL "%s" +#define TO_STRING std::to_string +#define TREGEX std::regex +#define TREGEX_SEARCH std::regex_search +#define TREGEX_MATCH std::smatch +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "Ws2_32.lib") +#endif + +std::map StatusCodeMap = { +{ 100, TEXT("Continue") }, +{ 101, TEXT("Switching Protocols") }, +{ 200, TEXT("OK") }, +{ 201, TEXT("Created") }, +{ 202, TEXT("Accepted") }, +{ 203, TEXT("Non-Authoritative Information") }, +{ 204, TEXT("No Content") }, +{ 205, TEXT("Reset Content") }, +{ 206, TEXT("Partial Content") }, +{ 300, TEXT("Multiple Choices") }, +{ 301, TEXT("Moved Permanently") }, +{ 302, TEXT("Found") }, +{ 303, TEXT("See Other") }, +{ 304, TEXT("Not Modified") }, +{ 305, TEXT("Use Proxy") }, +{ 306, TEXT("(Unused)") }, +{ 307, TEXT("Temporary Redirect") }, +{ 400, TEXT("Bad Request") }, +{ 401, TEXT("Unauthorized") }, +{ 402, TEXT("Payment Required") }, +{ 403, TEXT("Forbidden") }, +{ 404, TEXT("Not Found") }, +{ 405, TEXT("Method Not Allowed") }, +{ 406, TEXT("Not Acceptable") }, +{ 407, TEXT("Proxy Authentication Required") }, +{ 408, TEXT("Request Timeout") }, +{ 409, TEXT("Conflict") }, +{ 410, TEXT("Gone") }, +{ 411, TEXT("Length Required") }, +{ 412, TEXT("Precondition Failed") }, +{ 413, TEXT("Request Entity Too Large") }, +{ 414, TEXT("Request-URI Too Long") }, +{ 415, TEXT("Unsupported Media Type") }, +{ 416, TEXT("Requested Range Not Satisfiable") }, +{ 417, TEXT("Expectation Failed") }, +{ 500, TEXT("Internal Server Error") }, +{ 501, TEXT("Not Implemented") }, +{ 502, TEXT("Bad Gateway") }, +{ 503, TEXT("Service Unavailable") }, +{ 504, TEXT("Gateway Timeout") }, +{ 505, TEXT("HTTP Version Not Supported") }, +}; + +enum +{ + WINHTTP_CLASS_SESSION, + WINHTTP_CLASS_REQUEST, + WINHTTP_CLASS_IMP, +}; + +int winhttp_tracing = false; +int winhttp_tracing_verbose = false; + +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp); +#endif + +std::mutex trcmtx; + +#ifndef _MSC_VER +static void TRACE(const char *fmt, ...) __attribute__ ((format (gnu_printf, 1, 2))); +#endif + +static void TRACE(const char *fmt, ...) +{ + if (!winhttp_tracing) + return; + + std::lock_guard lck(trcmtx); + va_list args; + va_start(args, fmt); + + struct timeval tv; + time_t nowtime; + struct tm *nowtm; + char tmbuf[64], buf[64]; + + gettimeofday(&tv, NULL); + nowtime = tv.tv_sec; + nowtm = localtime(&nowtime); + strftime(tmbuf, sizeof tmbuf, "%H:%M:%S", nowtm); + snprintf(buf, sizeof buf, "%s.%06ld ", tmbuf, tv.tv_usec); + printf("%s", buf); + + char szBuffer[512]; + vsnprintf(szBuffer, sizeof szBuffer -1, fmt, args); + szBuffer[sizeof(szBuffer)/sizeof(szBuffer[0]) - 1] = '\0'; + printf("%s", szBuffer); + va_end(args); +} + +#ifndef _MSC_VER +static void TRACE_VERBOSE(const char *fmt, ...) __attribute__ ((format (gnu_printf, 1, 2))); +#endif +static void TRACE_VERBOSE(const char *fmt, ...) +{ + if (!winhttp_tracing_verbose) + return; + + std::lock_guard lck(trcmtx); + struct timeval tv; + time_t nowtime; + struct tm *nowtm; + char tmbuf[64], buf[64]; + + gettimeofday(&tv, NULL); + nowtime = tv.tv_sec; + nowtm = localtime(&nowtime); + strftime(tmbuf, sizeof tmbuf, "%H:%M:%S", nowtm); + snprintf(buf, sizeof buf, "%s.%06ld ", tmbuf, tv.tv_usec); + printf("%s", buf); + + va_list args; + va_start(args, fmt); + char szBuffer[512]; + vsnprintf(szBuffer, sizeof szBuffer -1, fmt, args); + szBuffer[sizeof(szBuffer)/sizeof(szBuffer[0]) - 1] = '\0'; + printf("%s", szBuffer); + va_end(args); +} + +typedef void (*CompletionCb)(std::shared_ptr, DWORD status); + +#define MUTEX_TYPE std::mutex +#define MUTEX_SETUP(x) +#define MUTEX_CLEANUP(x) +#define MUTEX_LOCK(x) x.lock() +#define MUTEX_UNLOCK(x) x.unlock() +#ifdef WIN32 +#define THREAD_ID GetCurrentThreadId() +#define THREAD_HANDLE HANDLE +#define THREADPARAM LPVOID +#define CREATETHREAD(func, param, id) \ + CreateThread( \ + NULL, \ + 0, \ + (LPTHREAD_START_ROUTINE)func, \ + param, \ + 0, \ + id) +#define THREADJOIN(h) WaitForSingleObject(h, INFINITE); +#define THREADRETURN DWORD +#else +#define THREADPARAM void* +#define THREADRETURN void* +typedef void* (*LPTHREAD_START_ROUTINE)(void *); +#define THREAD_ID pthread_self() +#define THREAD_HANDLE pthread_t +static inline THREAD_HANDLE CREATETHREAD(LPTHREAD_START_ROUTINE func, LPVOID param, pthread_t *id) +{ + pthread_t inc_x_thread; + pthread_create(&inc_x_thread, NULL, func, param); + return inc_x_thread; +} +#define THREADJOIN(x) pthread_join(x, NULL) +#endif + +void handle_error(const char *file, int lineno, const char *msg) +{ + fprintf(stderr, "** %s:%d %s\n", file, lineno, msg); + ERR_print_errors_fp(stderr); + /* exit(-1); */ +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +/* This array will store all of the mutexes available to OpenSSL. */ +static MUTEX_TYPE *mutex_buf = NULL; + +static void locking_function(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + MUTEX_LOCK(mutex_buf[n]); + else + MUTEX_UNLOCK(mutex_buf[n]); +} + +static unsigned long id_function(void) +{ + return ((unsigned long)(THREAD_ID)); +} +#endif + +int thread_setup(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + int i; + + mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; + if (!mutex_buf) + return 0; + for (i = 0; i < CRYPTO_num_locks(); i++) + MUTEX_SETUP(mutex_buf[i]); + CRYPTO_set_id_callback(id_function); + CRYPTO_set_locking_callback(locking_function); +#endif + return 1; +} + +int thread_cleanup(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + int i; + + if (!mutex_buf) + return 0; + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + for (i = 0; i < CRYPTO_num_locks(); i++) + MUTEX_CLEANUP(mutex_buf[i]); + delete [] mutex_buf; + mutex_buf = NULL; +#endif + return 1; +} + +class UserCallbackContext +{ + std::shared_ptr m_request; + DWORD m_dwInternetStatus; + DWORD m_dwStatusInformationLength; + DWORD m_dwNotificationFlags; + WINHTTP_STATUS_CALLBACK m_cb; + LPVOID m_userdata; + LPVOID m_StatusInformationVal = NULL; + BYTE* m_StatusInformation = NULL; + bool m_allocate = FALSE; + BOOL m_AsyncResultValid = false; + CompletionCb m_requestCompletionCb; + + BOOL SetAsyncResult(LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate) { + + if (allocate) + { + m_StatusInformation = new BYTE[statusInformationCopySize]; + if (m_StatusInformation) + { + memcpy(m_StatusInformation, statusInformation, statusInformationCopySize); + } + else + return FALSE; + } + else + { + m_StatusInformationVal = statusInformation; + } + m_AsyncResultValid = true; + m_allocate = allocate; + + return TRUE; + } + +public: + UserCallbackContext(std::shared_ptr &request, + DWORD dwInternetStatus, + DWORD dwStatusInformationLength, + DWORD dwNotificationFlags, + WINHTTP_STATUS_CALLBACK cb, + LPVOID userdata, + LPVOID statusInformation, + DWORD statusInformationCopySize, + bool allocate, + CompletionCb completion); + ~UserCallbackContext(); + std::shared_ptr GetRequestRef() { return m_request; } + WinHttpRequestImp *GetRequest() { return m_request.get(); } + DWORD GetInternetStatus() { return m_dwInternetStatus; } + DWORD GetStatusInformationLength() { return m_dwStatusInformationLength; } + DWORD GetNotificationFlags() { return m_dwNotificationFlags; } + WINHTTP_STATUS_CALLBACK &GetCb() { return m_cb; } + CompletionCb &GetRequestCompletionCb() { return m_requestCompletionCb; } + LPVOID GetUserdata() { return m_userdata; } + LPVOID GetStatusInformation() { + if (m_allocate) + return m_StatusInformation; + else + return m_StatusInformationVal; + } + BOOL GetStatusInformationValid() { return m_AsyncResultValid; } +}; + +static void ConvertCstrAssign(const TCHAR *lpstr, size_t cLen, std::string &target) +{ + size_t bLen = cLen; + +#ifdef UNICODE + char *nstring = new char[bLen + 1]; + if (!nstring) + return; + + cLen = MIN(WCTLEN(lpstr), cLen); + WideCharToMultiByte(CP_UTF8, 0, &lpstr[0], cLen, &nstring[0], bLen, NULL, NULL); + target.assign(nstring, bLen); + delete [] nstring; +#else + target.assign(lpstr, cLen); +#endif + target[bLen] = '\0'; +} + +static std::vector Split(std::string &str, char delimiter) { + std::vector internal; + std::stringstream ss(str); // Turn the string into a stream. + std::string tok; + + while (getline(ss, tok, delimiter)) { + internal.push_back(tok); + } + + return internal; +} + +static void ReplaceStr(std::string &str, const char *searchStr, const char *replaceStr) +{ + size_t index = 0; + while (true) { + /* Locate the substring to replace. */ + index = str.find(searchStr, index); + if (index == std::string::npos) break; + + /* Make the replacement. */ + str.replace(index, strlen(searchStr), replaceStr); + + /* Advance index forward so the next iteration doesn't pick it up as well. */ + index += strlen(searchStr); + } +} + +static BOOL SizeCheck(LPVOID lpBuffer, LPDWORD lpdwBufferLength, DWORD Required) +{ + if (!lpBuffer) + { + if (lpdwBufferLength) + *lpdwBufferLength = Required; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (lpdwBufferLength && (*lpdwBufferLength < Required)) { + *lpdwBufferLength = Required; + + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if ((Required == 0) || (lpdwBufferLength && (*lpdwBufferLength == 0))) + return FALSE; + + return TRUE; +} + +class WinHttpBase +{ +public: + virtual ~WinHttpBase() {} +}; + +class WinHttpConnectImp :public WinHttpBase +{ + WinHttpSessionImp *m_Handle = NULL; + void *m_UserBuffer = NULL; + +public: + void SetHandle(WinHttpSessionImp *session) { m_Handle = session; } + WinHttpSessionImp *GetHandle() { return m_Handle; } + + BOOL SetUserData(void **data) + { + m_UserBuffer = *data; + return TRUE; + } + void *GetUserData() { return m_UserBuffer; } +}; + +class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this +{ + CURL *m_curl = NULL; + std::vector m_ResponseString; + std::string m_HeaderString = ""; + DWORD m_TotalHeaderStringLength = 0; + std::string m_Header = ""; + std::string m_FullPath = ""; + std::string m_OptionalData = ""; + DWORD m_TotalSize = 0; + DWORD m_TotalReceiveSize = 0; + std::vector m_ReadData; + + std::mutex m_ReadDataEventMtx; + std::condition_variable m_ReadDataEvent; + bool m_ReadDataEventState = false; + THREAD_HANDLE m_UploadCallbackThread; + bool m_UploadThreadExitStatus = false; + + DWORD m_SecureProtocol = 0; + DWORD m_MaxConnections = 0; + + std::string m_Type = ""; + LPVOID m_UserBuffer = NULL; + bool m_HeaderReceiveComplete = false; + + struct curl_slist *m_HeaderList = NULL; + WinHttpConnectImp *m_Session = NULL; + std::mutex m_HeaderStringMutex; + std::mutex m_BodyStringMutex; + + std::condition_variable m_CompletionEvent; + std::mutex m_CompletionEventMtx; + bool m_CompletionEventState = false; + + std::condition_variable m_QueryDataEvent; + std::mutex m_QueryDataEventMtx; + bool m_QueryDataEventState = false; + std::atomic m_QueryDataPending; + + std::condition_variable m_ReceiveCompletionEvent; + std::mutex m_ReceiveCompletionEventMtx; + std::atomic m_ReceiveCompletionEventCounter; + + std::mutex m_ReceiveResponseMutex; + std::atomic m_ReceiveResponsePending; + std::atomic m_ReceiveResponseEventCounter; + int m_ReceiveResponseSendCounter; + + std::atomic m_RedirectPending; + + std::condition_variable m_DataAvailable; + std::mutex m_DataAvailableMtx; + std::atomic m_DataAvailableEventCounter; + bool m_closing = false; + bool m_closed = false; + bool m_Completion = false; + bool m_Async = false; + CURLcode m_CompletionCode = CURLE_OK; + long m_VerifyPeer = 1; + long m_VerifyHost = 2; + + WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; + DWORD m_NotificationFlags = 0; + +public: + long &VerifyPeer() { return m_VerifyPeer; } + long &VerifyHost() { return m_VerifyHost; } + + static size_t WriteHeaderFunction(void *ptr, size_t size, size_t nmemb, void* rqst); + static size_t WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst); + void SetCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags) { + m_InternetCallback = lpfnInternetCallback; + m_NotificationFlags = dwNotificationFlags; + } + WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) + { + *dwNotificationFlags = m_NotificationFlags; + return m_InternetCallback; + } + + void SetAsync() { m_Async = TRUE; } + BOOL GetAsync() { return m_Async; } + + WinHttpRequestImp(); + + bool &GetQueryDataEventState() { return m_QueryDataEventState; } + std::mutex &GetQueryDataEventMtx() { return m_QueryDataEventMtx; } + std::condition_variable &GetQueryDataEvent() { return m_QueryDataEvent; } + + bool HandleQueryDataNotifications(std::shared_ptr, DWORD available); + void WaitAsyncQueryDataCompletion(std::shared_ptr); + + void HandleReceiveNotifications(std::shared_ptr); + void WaitAsyncReceiveCompletion(std::shared_ptr srequest); + DWORD WaitDataAvailable(); + + CURLcode &GetCompletionCode() { return m_CompletionCode; } + bool &GetCompletionStatus() { return m_Completion; } + bool &GetClosing() { return m_closing; } + bool &GetClosed() { return m_closed; } + void CleanUp(); + ~WinHttpRequestImp(); + + bool &GetUploadThreadExitStatus() { return m_UploadThreadExitStatus; } + std::atomic &GetQueryDataPending() { return m_QueryDataPending; } + + THREAD_HANDLE &GetUploadThread() { + return m_UploadCallbackThread; + } + + static THREADRETURN UploadThreadFunction(THREADPARAM lpThreadParameter) + { + WinHttpRequestImp *request = static_cast(lpThreadParameter); + CURL *curl = request->GetCurl(); + CURLcode res; + + TRACE("%s:%d\n", __func__, __LINE__); + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + { + TRACE("curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + return FALSE; + } + TRACE("%s:%d res:%d\n", __func__, __LINE__, res); + request->GetUploadThreadExitStatus() = true; + + #if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); + #elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); + #endif + return 0; + } + + // used to wake up CURL ReadCallback triggered on a upload request + std::condition_variable &GetReadDataEvent() { return m_ReadDataEvent; } + std::mutex &GetReadDataEventMtx() { return m_ReadDataEventMtx; } + bool &GetReadDataEventState() { return m_ReadDataEventState; } + + // sent by WriteHeaderFunction and WriteBodyFunction during incoming data + // to send user notifications for the following events: + // + // WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE + // WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED + // WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE + // + // WinHttpReceiveResponse is blocked until one of the above events happen + // + std::condition_variable &GetReceiveCompletionEvent() { return m_ReceiveCompletionEvent; } + std::mutex &GetReceiveCompletionEventMtx() { return m_ReceiveCompletionEventMtx; } + std::atomic &GetReceiveCompletionEventCounter() { return m_ReceiveCompletionEventCounter; } + + // counters used to see which one of these events are observed: ResponseCallbackEventCounter + // counters used to see which one of these events are broadcasted: ResponseCallbackSendCounter + // WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE + // WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED + // WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE + // + // These counters need to be 3 and identical under normal circumstances + std::atomic &ResponseCallbackEventCounter() { return m_ReceiveResponseEventCounter; } + int &ResponseCallbackSendCounter() { return m_ReceiveResponseSendCounter; } + std::atomic &GetReceiveResponsePending() { return m_ReceiveResponsePending; } + std::mutex &GetReceiveResponseMutex() { return m_ReceiveResponseMutex; } + + std::atomic &GetRedirectPending() { return m_RedirectPending; } + + std::condition_variable &GetDataAvailableEvent() { return m_DataAvailable; } + std::mutex &GetDataAvailableMtx() { return m_DataAvailableMtx; } + std::atomic &GetDataAvailableEventCounter() { return m_DataAvailableEventCounter; } + + std::mutex &GetHeaderStringMutex() { return m_HeaderStringMutex; } + std::mutex &GetBodyStringMutex() { return m_HeaderStringMutex; } + + BOOL AsyncQueue(std::shared_ptr &, + DWORD dwInternetStatus, size_t statusInformationLength, + LPVOID async, DWORD asyncLength, bool allocate); + + void SetHeaderReceiveComplete() { m_HeaderReceiveComplete = TRUE; } + + BOOL SetSecureProtocol(DWORD *data) + { + m_SecureProtocol = *data; + return TRUE; + } + DWORD GetSecureProtocol() { return m_SecureProtocol; } + + BOOL SetMaxConnections(DWORD *data) + { + m_MaxConnections = *data; + return TRUE; + } + DWORD GetMaxConnections() { return m_MaxConnections; } + + BOOL SetUserData(void **data) + { + m_UserBuffer = *data; + return TRUE; + } + LPVOID GetUserData() { return m_UserBuffer; } + + void SetFullPath(const char *server, const char *relative) + { + m_FullPath.append(server, strlen(server)); + m_FullPath.append(relative, strlen(relative)); + } + std::string &GetFullPath() { return m_FullPath; } + std::string &GetType() { return m_Type; } + + void SetSession(WinHttpConnectImp *connect) { m_Session = connect; } + WinHttpConnectImp *GetSession() { return m_Session; } + + struct curl_slist *GetHeaderList() { return m_HeaderList; } + + void SetHeaderList(struct curl_slist *list) { m_HeaderList = list; } + std::string &GetOutgoingHeaderList() { return m_Header; } + void AddHeader(std::string &headers) { + std::stringstream check1(headers); + std::string str; + + while(getline(check1, str, '\n')) + { + str.erase(std::remove(str.begin(), str.end(), '\r'), str.end()); + SetHeaderList(curl_slist_append(GetHeaderList(), str.c_str())); + } + } + + std::vector &GetResponseString() { return m_ResponseString; } + DWORD &GetTotalHeaderStringLength() { return m_TotalHeaderStringLength; } + std::string &GetHeaderString() { return m_HeaderString; } + CURL *GetCurl() { return m_curl; } + + BOOL SetProxy(std::vector &proxies) + { + std::vector::iterator it; + + for (it = proxies.begin(); it != proxies.end(); it++) + { + std::string &urlstr = (*it); + CURLcode res; + + res = curl_easy_setopt(GetCurl(), CURLOPT_PROXY, urlstr.c_str()); + if (res != CURLE_OK) + return FALSE; + } + + return TRUE; + } + + BOOL SetServer(const char *ServerName, int nServerPort) + { + CURLcode res; + + res = curl_easy_setopt(GetCurl(), CURLOPT_URL, ServerName); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(GetCurl(), CURLOPT_PORT, nServerPort); + if (res != CURLE_OK) + return FALSE; + + return TRUE; + } + + DWORD &GetTotalLength() { return m_TotalSize; } + DWORD &GetReceivedLength() { return m_TotalReceiveSize; } + + std::string &GetOptionalData() { return m_OptionalData; } + void SetOptionalData(void *lpOptional, DWORD dwOptionalLength) + { + m_OptionalData = std::string(&(reinterpret_cast(lpOptional))[0], dwOptionalLength); + } + + std::vector &GetReadData() { return m_ReadData; } + + void AppendReadData(const void *data, DWORD len) + { + std::lock_guard lck(GetReadDataEventMtx()); + m_ReadData.insert(m_ReadData.end(), reinterpret_cast(data), reinterpret_cast(data) + len); + } + + static int SocketCallback(CURL *handle, curl_infotype type, + char *data, size_t size, + void *userp); + + static size_t ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp); +}; + +typedef std::vector UserCallbackQueue; + +class UserCallbackContainer +{ + UserCallbackQueue m_Queue; + + // used to protect GetCallbackMap() + std::mutex m_MapMutex; + + THREAD_HANDLE m_hThread; + + // used by UserCallbackThreadFunction as a wake-up signal + std::mutex m_hEventMtx; + std::condition_variable m_hEvent; + + // cleared by UserCallbackThreadFunction, set by multiple event providers + std::atomic m_EventCounter; + + bool m_closing = false; + UserCallbackQueue &GetCallbackQueue() { return m_Queue; } + + +public: + + bool GetClosing() { return m_closing; } + + UserCallbackContext* GetNext() + { + UserCallbackContext *ctx = NULL; + // show content: + if (!GetCallbackQueue().empty()) + { + ctx = GetCallbackQueue().front(); + GetCallbackQueue().erase(GetCallbackQueue().begin()); + } + + return ctx; + } + + static THREADRETURN UserCallbackThreadFunction(LPVOID lpThreadParameter); + + void Queue(UserCallbackContext *ctx) + { + { + std::lock_guard lck(m_MapMutex); + + TRACE_VERBOSE("%s:%d ctx = %p cb = %p userdata = %p dwInternetStatus = %p\n", + __func__, __LINE__, reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), + ctx->GetUserdata(), ctx->GetStatusInformation()); + GetCallbackQueue().push_back(ctx); + } + { + std::lock_guard lck(m_hEventMtx); + m_EventCounter++; + m_hEvent.notify_all(); + } + } + + void DrainQueue() + { + while (1) + { + m_MapMutex.lock(); + UserCallbackContext *ctx = GetNext(); + m_MapMutex.unlock(); + + if (!ctx) + break; + + delete ctx; + } + } + + UserCallbackContainer(): m_EventCounter(0) + { + DWORD thread_id; + m_hThread = CREATETHREAD( + UserCallbackThreadFunction, // thread function name + this, // argument to thread function + &thread_id + ); + } + ~UserCallbackContainer() + { + m_closing = true; + { + std::lock_guard lck(m_hEventMtx); + m_EventCounter++; + m_hEvent.notify_all(); + } + THREADJOIN(m_hThread); + DrainQueue(); + } + static UserCallbackContainer &GetInstance() + { + static UserCallbackContainer the_instance; + return the_instance; + } +}; + +class EnvInit +{ +public: + EnvInit() + { + if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG")) + winhttp_tracing = atoi(env_p); + + if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG_VERBOSE")) + winhttp_tracing_verbose = atoi(env_p); + } +}; + +static EnvInit envinit; + +class ComContainer +{ + THREAD_HANDLE m_hAsyncThread; + std::mutex m_hAsyncEventMtx; + + // used to wake up the Async Thread + // Set by external components, cleared by the Async thread + std::atomic m_hAsyncEventCounter; + std::condition_variable m_hAsyncEvent; + std::mutex m_MultiMutex; + CURLM *m_curlm = NULL; + std::vector< CURL *> m_ActiveCurl; + std::vector> m_ActiveRequests; + BOOL m_closing = FALSE; + + // used to protect CURLM data structures + BOOL GetThreadClosing() { return m_closing; } + +public: + CURL *AllocCURL() + { + CURL *ptr; + + m_MultiMutex.lock(); + ptr = curl_easy_init(); + m_MultiMutex.unlock(); + + return ptr; + } + + void FreeCURL(CURL *ptr) + { + m_MultiMutex.lock(); + curl_easy_cleanup(ptr); + m_MultiMutex.unlock(); + } + + static ComContainer &GetInstance() + { + static ComContainer the_instance; + return the_instance; + } + + void ResumeTransfer(CURL *handle, int bitmask) + { + int still_running; + + m_MultiMutex.lock(); + curl_easy_pause(handle, bitmask); + curl_multi_perform(m_curlm, &still_running); + m_MultiMutex.unlock(); + } + + BOOL AddHandle(std::shared_ptr srequest, CURL *handle) + { + CURLMcode mres = CURLM_OK; + + m_MultiMutex.lock(); + if (std::find(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle) != m_ActiveCurl.end()) { + mres = curl_multi_remove_handle(m_curlm, handle); + m_ActiveCurl.erase(std::remove(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle), m_ActiveCurl.end()); + } + if (std::find(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest) != m_ActiveRequests.end()) { + m_ActiveRequests.erase(std::remove(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest), m_ActiveRequests.end()); + } + m_MultiMutex.unlock(); + if (mres != CURLM_OK) + { + TRACE("curl_multi_remove_handle() failed: %s\n", curl_multi_strerror(mres)); + return FALSE; + } + + m_MultiMutex.lock(); + mres = curl_multi_add_handle(m_curlm, handle); + m_ActiveCurl.push_back(handle); + m_ActiveRequests.push_back(srequest); + m_MultiMutex.unlock(); + if (mres != CURLM_OK) + { + TRACE("curl_multi_add_handle() failed: %s\n", curl_multi_strerror(mres)); + return FALSE; + } + + return TRUE; + } + BOOL RemoveHandle(std::shared_ptr srequest, CURL *handle, bool clearPrivate) + { + CURLMcode mres; + + m_MultiMutex.lock(); + mres = curl_multi_remove_handle(m_curlm, handle); + + if (clearPrivate) + curl_easy_getinfo(handle, CURLINFO_PRIVATE, NULL); + + m_ActiveCurl.erase(std::remove(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle), m_ActiveCurl.end()); + m_ActiveRequests.erase(std::remove(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest), m_ActiveRequests.end()); + m_MultiMutex.unlock(); + if (mres != CURLM_OK) + { + TRACE("curl_multi_add_handle() failed: %s\n", curl_multi_strerror(mres)); + return FALSE; + } + + return TRUE; + } + + long GetTimeout() + { + long curl_timeo; + + m_MultiMutex.lock(); + curl_multi_timeout(m_curlm, &curl_timeo); + m_MultiMutex.unlock(); + + return curl_timeo; + } + + void KickStart() + { + std::lock_guard lck(m_hAsyncEventMtx); + m_hAsyncEventCounter++; + m_hAsyncEvent.notify_all(); + } + + ComContainer(): m_hAsyncEventCounter(0) + { + curl_global_init(CURL_GLOBAL_ALL); + thread_setup(); + + m_curlm = curl_multi_init(); + + DWORD thread_id; + m_hAsyncThread = CREATETHREAD( + AsyncThreadFunction, // thread function name + this, &thread_id); // argument to thread function + } + + ~ComContainer() + { + TRACE("==>%s:%d\n", __func__, __LINE__); + m_closing = true; + { + std::lock_guard lck(m_hAsyncEventMtx); + m_hAsyncEventCounter++; + m_hAsyncEvent.notify_all(); + } + THREADJOIN(m_hAsyncThread); + TRACE("<==%s:%d\n", __func__, __LINE__); + + curl_multi_cleanup(m_curlm); + curl_global_cleanup(); + thread_cleanup(); + } + + + static THREADRETURN AsyncThreadFunction(THREADPARAM lpThreadParameter); + + int QueryData(int *still_running) + { + int rc = 0; + struct timeval timeout; + + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int maxfd = -1; + + long curl_timeo = -1; + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + /* set a suitable timeout to play around with */ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + m_MultiMutex.lock(); + curl_multi_timeout(m_curlm, &curl_timeo); + m_MultiMutex.unlock(); + if (curl_timeo < 0) + /* no set timeout, use a default */ + curl_timeo = 10000; + + if (curl_timeo > 0) { + timeout.tv_sec = curl_timeo / 1000; + if (timeout.tv_sec > 1) + timeout.tv_sec = 1; + else + timeout.tv_usec = (curl_timeo % 1000) * 1000; + } + + /* get file descriptors from the transfers */ + m_MultiMutex.lock(); + rc = curl_multi_fdset(m_curlm, &fdread, &fdwrite, &fdexcep, &maxfd); + m_MultiMutex.unlock(); + + if ((maxfd == -1) || (rc != CURLM_OK)) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + else + rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); + + switch (rc) { + case -1: + /* select error */ + *still_running = 0; + TRACE("%s\n", "select() returns error, this is badness\n"); + break; + case 0: + default: + /* timeout or readable/writable sockets */ + m_MultiMutex.lock(); + curl_multi_perform(m_curlm, still_running); + m_MultiMutex.unlock(); + break; + } + + return rc; + } +}; + +template +class WinHttpHandleContainer +{ + std::mutex m_ActiveRequestMtx; + std::vector> m_ActiveRequests; + +public: + static WinHttpHandleContainer &Instance() + { + static WinHttpHandleContainer the_instance; + return the_instance; + } + + typedef std::shared_ptr t_shared; + void UnRegister(T *val) + { + typename std::vector::iterator findit; + bool found = false; + std::lock_guard lck(m_ActiveRequestMtx); + + TRACE("%s:%d\n", __func__, __LINE__); + + for (auto it = m_ActiveRequests.begin(); it != m_ActiveRequests.end(); ++it) + { + auto v = (*it); + if (v.get() == val) + { + findit = it; + found = true; + break; + } + } + if (found) + m_ActiveRequests.erase(findit); + } + + bool IsRegistered(T *val) + { + bool found = false; + std::lock_guard lck(m_ActiveRequestMtx); + + for (auto it = m_ActiveRequests.begin(); it != m_ActiveRequests.end(); ++it) + { + auto v = (*it); + if (v.get() == val) + { + found = true; + break; + } + } + TRACE("%s:%d found:%d\n", __func__, __LINE__, found); + + return found; + } + + void Register(std::shared_ptr rqst) + { + TRACE("%s:%d\n", __func__, __LINE__); + std::lock_guard lck(m_ActiveRequestMtx); + m_ActiveRequests.push_back(rqst); + } +}; + +class WinHttpSessionImp :public WinHttpBase +{ + std::string m_ServerName = ""; + std::string m_Referrer = ""; + std::vector m_Proxies; + std::string m_Proxy = ""; + WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; + DWORD m_NotificationFlags = 0; + + int m_ServerPort = 0; + long m_Timeout = 15000; + BOOL m_Async = false; + + bool m_closing = false; + DWORD m_MaxConnections = 0; + DWORD m_SecureProtocol = 0; + void *m_UserBuffer = NULL; + +public: + + BOOL SetUserData(void **data) + { + m_UserBuffer = *data; + return TRUE; + } + void *GetUserData() { return m_UserBuffer; } + + BOOL SetSecureProtocol(DWORD *data) + { + m_SecureProtocol = *data; + return TRUE; + } + DWORD GetSecureProtocol() { return m_SecureProtocol; } + + BOOL SetMaxConnections(DWORD *data) + { + m_MaxConnections = *data; + return TRUE; + } + DWORD GetMaxConnections() { return m_MaxConnections; } + + void SetAsync() { m_Async = TRUE; } + BOOL GetAsync() { return m_Async; } + + BOOL GetThreadClosing() { return m_closing; } + + std::vector &GetProxies() { return m_Proxies; } + void SetProxies(std::vector &proxies) { m_Proxies = proxies; } + + std::string &GetProxy() { return m_Proxy; } + + int GetServerPort() { return m_ServerPort; } + void SetServerPort(int port) { m_ServerPort = port; } + + long GetTimeout() { + if (m_Timeout) + return m_Timeout; + + long curl_timeo; + + curl_timeo = ComContainer::GetInstance().GetTimeout(); + + if (curl_timeo < 0) + curl_timeo = 10000; + + return curl_timeo; + } + void SetTimeout(long timeout) { m_Timeout = timeout; } + + std::string &GetServerName() { return m_ServerName; } + + std::string &GetReferrer() { return m_Referrer; } + + void SetCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags) { + m_InternetCallback = lpfnInternetCallback; + m_NotificationFlags = dwNotificationFlags; + } + WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) + { + *dwNotificationFlags = m_NotificationFlags; + return m_InternetCallback; + } + + WinHttpSessionImp() + { + } + + ~WinHttpSessionImp() + { + TRACE("==>%s:%d sesion\n", __func__, __LINE__); + SetCallback(NULL, 0); + TRACE("<==%s:%d\n", __func__, __LINE__); + } +}; + +THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) +{ + ComContainer *comContainer = static_cast(lpThreadParameter); + + while (1) + { + { + std::unique_lock getAsyncEventHndlMtx(comContainer->m_hAsyncEventMtx); + while (!comContainer->m_hAsyncEventCounter) + comContainer->m_hAsyncEvent.wait(getAsyncEventHndlMtx); + getAsyncEventHndlMtx.unlock(); + + comContainer->m_hAsyncEventCounter--; + } + + int still_running = 1; + + while (still_running && !comContainer->GetThreadClosing()) + { + WinHttpRequestImp *request = NULL; + + comContainer->QueryData(&still_running); + + //TRACE("%s:%d\n", __func__, __LINE__); + struct CURLMsg *m; + + /* call curl_multi_perform or curl_multi_socket_action first, then loop + through and check if there are any transfers that have completed */ + + do { + int msgq = 0; + request = NULL; + std::shared_ptr srequest; + + comContainer->m_MultiMutex.lock(); + m = curl_multi_info_read(comContainer->m_curlm, &msgq); + if (m) + curl_easy_getinfo(m->easy_handle, CURLINFO_PRIVATE, &request); + if (request) + { + srequest = request->shared_from_this(); + } + comContainer->m_MultiMutex.unlock(); + + if (m && (m->msg == CURLMSG_DONE) && request && srequest) { + WINHTTP_ASYNC_RESULT result = { 0, 0 }; + DWORD dwInternetStatus; + + TRACE("%s:%d type:%s result:%d\n", __func__, __LINE__, request->GetType().c_str(), m->data.result); + request->GetCompletionStatus() = true; + request->GetCompletionCode() = m->data.result; + + // unblock ReceiveResponse if waiting + if (m->data.result != CURLE_OK) + { + std::lock_guard lck(request->GetReceiveCompletionEventMtx()); + request->GetReceiveCompletionEventCounter()++; + request->GetReceiveCompletionEvent().notify_all(); + TRACE("%s:%d GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__); + } + + if (m->data.result == CURLE_OK) + { + if (request->HandleQueryDataNotifications(srequest, 0)) + { + TRACE("%s:%d GetQueryDataEvent().notify_all\n", __func__, __LINE__); + } + { + std::lock_guard lck(request->GetQueryDataEventMtx()); + request->GetQueryDataEventState() = true; + request->GetQueryDataEvent().notify_all(); + } + request->HandleQueryDataNotifications(srequest, 0); + } + + if (m->data.result == CURLE_OK) + { + TRACE("%s:%d m->data.result:%d\n", __func__, __LINE__, m->data.result); + std::lock_guard lck(request->GetDataAvailableMtx()); + request->GetDataAvailableEventCounter()++; + request->GetDataAvailableEvent().notify_all(); + } + + if (m->data.result == CURLE_OK) + { + } + else if (m->data.result == CURLE_OPERATION_TIMEDOUT) + { + result.dwError = ERROR_WINHTTP_TIMEOUT; + dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); + TRACE("request done type = %s CURLE_OPERATION_TIMEDOUT\n", request->GetType().c_str()); + } + else + { + result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; + dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + TRACE("unknown async request done m->data.result = %d\n", m->data.result); +#ifdef _DEBUG + assert(0); +#endif + request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); + } + } else if (m && (m->msg != CURLMSG_DONE)) { + TRACE("%s\n", "unknown async request done\n"); + DWORD dwInternetStatus; + WINHTTP_ASYNC_RESULT result = { 0, 0 }; + result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; + dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; +#ifdef _DEBUG + assert(0); +#endif + if (request) + request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); + } + + if (request) + { + comContainer->RemoveHandle(srequest, request->GetCurl(), true); + TRACE("%s:%d\n", __func__, __LINE__); + } + } while (m); + } + if (comContainer->GetThreadClosing()) + { + TRACE("%s:%d %s\n", __func__, __LINE__, "exiting\n"); + break; + } + } + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); +#elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); +#endif + return 0; +} + +THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadParameter) +{ + UserCallbackContainer *cbContainer = static_cast(lpThreadParameter); + + while (1) + { + { + std::unique_lock GetEventHndlMtx(cbContainer->m_hEventMtx); + while (!cbContainer->m_EventCounter) + cbContainer->m_hEvent.wait(GetEventHndlMtx); + GetEventHndlMtx.unlock(); + + cbContainer->m_EventCounter--; + } + + if (cbContainer->GetClosing()) + { + TRACE("%s", "exiting\n"); + break; + } + + while (1) + { + cbContainer->m_MapMutex.lock(); + UserCallbackContext *ctx = NULL; + ctx = cbContainer->GetNext(); + cbContainer->m_MapMutex.unlock(); + if (!ctx) + break; + + void *statusInformation = NULL; + + if (ctx->GetStatusInformationValid()) + statusInformation = reinterpret_cast(ctx->GetStatusInformation()); + + if (!ctx->GetRequestRef()->GetClosed() && ctx->GetCb()) { + TRACE_VERBOSE("%s:%d ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", + __func__, __LINE__, reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), + ctx->GetUserdata(), ctx->GetInternetStatus(), statusInformation, ctx->GetRequestRef().use_count()); + ctx->GetCb()(ctx->GetRequest(), + (DWORD_PTR)(ctx->GetUserdata()), + ctx->GetInternetStatus(), + statusInformation, + ctx->GetStatusInformationLength()); + + } + ctx->GetRequestCompletionCb()(ctx->GetRequestRef(), ctx->GetInternetStatus()); + delete ctx; + } + } + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); +#elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); +#endif + + return 0; +} + +UserCallbackContext::UserCallbackContext(std::shared_ptr &request, + DWORD dwInternetStatus, + DWORD dwStatusInformationLength, + DWORD dwNotificationFlags, + WINHTTP_STATUS_CALLBACK cb, + LPVOID userdata, + LPVOID statusInformation, + DWORD statusInformationCopySize, bool allocate, + CompletionCb completion) + : m_request(request), m_dwInternetStatus(dwInternetStatus), + m_dwStatusInformationLength(dwStatusInformationLength), + m_dwNotificationFlags(dwNotificationFlags), m_cb(cb), m_userdata(userdata), + m_requestCompletionCb(completion) +{ + if (statusInformation) + SetAsyncResult(statusInformation, statusInformationCopySize, allocate); +} + +WinHttpSessionImp *GetImp(WinHttpBase *base) +{ + WinHttpSessionImp *session; + + if (dynamic_cast(base)) + { + WinHttpConnectImp *connect = dynamic_cast(base); + + if (!connect) + return NULL; + + session = connect->GetHandle(); + } + else if (dynamic_cast(base)) + { + WinHttpRequestImp *request = dynamic_cast(base); + + if (!request) + return NULL; + + WinHttpConnectImp *connect = request->GetSession(); + session = connect->GetHandle(); + } + else if (dynamic_cast(base)) + { + session = dynamic_cast(base); + if (!session) + return NULL; + } + else + return NULL; + + return session; +} + + +UserCallbackContext::~UserCallbackContext() +{ + if (m_StatusInformation) + delete [] m_StatusInformation; +} + +void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr srequest) +{ + bool expected = true; + + bool result = std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, false); + if (result) + { + bool redirectPending; + { + expected = true; + redirectPending = std::atomic_compare_exchange_strong(&GetRedirectPending(), &expected, false); + TRACE("%s:%d redirectPending = %d ResponseCallbackSendCounter = %d\n", __func__, __LINE__, + redirectPending, (int)ResponseCallbackSendCounter()); + } + GetReceiveResponseMutex().lock(); + if (redirectPending) + { + if (ResponseCallbackSendCounter() == 0) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); + ResponseCallbackSendCounter()++; + } + if (ResponseCallbackSendCounter() == 1) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__); + AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, GetHeaderString().length(), NULL, 0, false); + ResponseCallbackSendCounter()++; + } + AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REDIRECT, 0, NULL, 0, false); + ResponseCallbackSendCounter()++; + } + if (ResponseCallbackSendCounter() == (0 + redirectPending * 3)) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); + ResponseCallbackSendCounter()++; + } + if (ResponseCallbackSendCounter() == (1 + redirectPending * 3)) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__); + AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, GetHeaderString().length(), NULL, 0, false); + ResponseCallbackSendCounter()++; + } + if (ResponseCallbackSendCounter() == (2 + redirectPending * 3)) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", __func__, __LINE__); + SetHeaderReceiveComplete(); + AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, 0, NULL, 0, false); + ResponseCallbackSendCounter()++; + } + GetReceiveResponseMutex().unlock(); + } +} + +size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { + WinHttpRequestImp *request = static_cast(rqst); + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return size * nmemb; + bool EofHeaders = false; + + { + std::lock_guard lck(request->GetHeaderStringMutex()); + + if (request->GetAsync() && request->GetHeaderString().length() == 0) { + request->ResponseCallbackEventCounter()++; + + if (request->GetReceiveResponsePending()) { + request->GetReceiveResponseMutex().lock(); + + if (request->ResponseCallbackSendCounter() == 0) { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); + request->ResponseCallbackSendCounter()++; + } + request->GetReceiveResponseMutex().unlock(); + } + } + + request->GetHeaderString().append(reinterpret_cast(ptr), size * nmemb); + request->GetTotalHeaderStringLength() += size * nmemb; + + if (request->GetHeaderString().find("\r\n\r\n") != std::string::npos) + EofHeaders = true; + if (request->GetHeaderString().find("\n\n") != std::string::npos) + EofHeaders = true; + + if (request->GetAsync() && EofHeaders) { + request->ResponseCallbackEventCounter()++; + + if (request->GetReceiveResponsePending()) + { + request->GetReceiveResponseMutex().lock(); + + if (request->ResponseCallbackSendCounter() == 0) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); + request->ResponseCallbackSendCounter()++; + } + if (request->ResponseCallbackSendCounter() == 1) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, request->GetHeaderString().length(), NULL, 0, false); + request->ResponseCallbackSendCounter()++; + } + request->GetReceiveResponseMutex().unlock(); + } + } + } + if (EofHeaders && request->GetAsync()) + { + DWORD retValue; + + curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &retValue); + if (retValue == 302) + { + std::lock_guard lck(request->GetHeaderStringMutex()); + request->GetHeaderString() = ""; + TRACE("%s:%d Redirect \n", __func__, __LINE__); + request->ResponseCallbackEventCounter()++; + request->GetRedirectPending() = true; + return size * nmemb; + } + + request->ResponseCallbackEventCounter()++; + TRACE("%s:%d HandleReceiveNotifications \n", __func__, __LINE__); + request->HandleReceiveNotifications(srequest); + { + std::lock_guard lck(request->GetReceiveCompletionEventMtx()); + request->GetReceiveCompletionEventCounter()++; + request->GetReceiveCompletionEvent().notify_all(); + TRACE("%s:%d GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__); + } + + } + return size * nmemb; +} + +size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { + WinHttpRequestImp *request = static_cast(rqst); + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return size * nmemb; + + { + std::lock_guard lck(request->GetBodyStringMutex()); + request->GetResponseString().insert(request->GetResponseString().end(), + reinterpret_cast(ptr), + reinterpret_cast(ptr) + size * nmemb); + } + + if (request->GetAsync()) { + bool expected = true; + + bool result = std::atomic_compare_exchange_strong(&request->GetReceiveResponsePending(), &expected, false); + + if (result) { + request->GetReceiveResponseMutex().lock(); + if (request->ResponseCallbackSendCounter() == 0) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); + request->ResponseCallbackSendCounter()++; + } + + if (request->ResponseCallbackSendCounter() == 1) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, request->GetHeaderString().length(), NULL, 0, false); + request->ResponseCallbackSendCounter()++; + } + + if (request->ResponseCallbackSendCounter() == 2) + { + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", __func__, __LINE__); + request->SetHeaderReceiveComplete(); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, 0, NULL, 0, false); + request->ResponseCallbackSendCounter()++; + } + { + std::lock_guard lck(request->GetReceiveCompletionEventMtx()); + request->GetReceiveCompletionEventCounter()++; + request->GetReceiveCompletionEvent().notify_all(); + TRACE("%s:%d GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__); + } + request->GetReceiveResponseMutex().unlock(); + } + + { + std::lock_guard lck(request->GetDataAvailableMtx()); + request->GetDataAvailableEventCounter()++; + request->GetDataAvailableEvent().notify_all(); + TRACE_VERBOSE("%s:%d data available:%lu\n", __func__, __LINE__, size * nmemb); + } + + if (request->HandleQueryDataNotifications(srequest, size * nmemb)) + TRACE("%s:%d GetQueryDataEvent().notify_all\n", __func__, __LINE__); + } + + return size * nmemb; +} + +void WinHttpRequestImp::CleanUp() +{ + m_CompletionCode = CURLE_OK; + m_ResponseString.clear(); + m_HeaderString.clear(); + m_TotalHeaderStringLength = 0; + m_TotalReceiveSize = 0; + m_ReadData.clear(); + m_ReadDataEventState = false; + m_UploadThreadExitStatus = false; + m_ReceiveCompletionEventCounter = 0; + m_CompletionEventState = false; + m_QueryDataPending = false; + m_ReceiveResponsePending = false; + m_RedirectPending = false; + m_ReceiveResponseEventCounter = 0; + m_ReceiveResponseSendCounter = 0; + m_DataAvailableEventCounter = 0; + m_Completion = false; +} + +WinHttpRequestImp::WinHttpRequestImp(): + m_QueryDataPending(false), + m_ReceiveCompletionEventCounter(0), + m_ReceiveResponsePending(false), + m_ReceiveResponseEventCounter(0), + m_ReceiveResponseSendCounter(0), + m_RedirectPending(false), + m_DataAvailableEventCounter(0) +{ + m_curl = ComContainer::GetInstance().AllocCURL(); + if (!m_curl) + return; + return; +} + +WinHttpRequestImp::~WinHttpRequestImp() +{ + TRACE("==>%s:%d\n", __func__, __LINE__); + + m_closed = true; + + if (!GetAsync() && (GetType() == "PUT")) + { + { + std::lock_guard lck(GetReadDataEventMtx()); + GetReadDataEventState() = true; + GetReadDataEvent().notify_all(); + } + THREADJOIN(m_UploadCallbackThread); + } + + if (GetAsync()) { + TRACE("%s:%d\n", __func__, __LINE__); + } + else + curl_easy_getinfo(GetCurl(), CURLINFO_PRIVATE, NULL); + + /* always cleanup */ + ComContainer::GetInstance().FreeCURL(m_curl); + + /* free the custom headers */ + if (m_HeaderList) + curl_slist_free_all(m_HeaderList); + + TRACE("<==%s:%d\n", __func__, __LINE__); +} + +static void RequestCompletionCb(std::shared_ptr requestRef, DWORD status) +{ + if (status == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) + { + TRACE_VERBOSE("%s:%d status:0x%lx\n", __func__, __LINE__, status); + requestRef->GetClosed() = true; + } +} + +BOOL WinHttpRequestImp::AsyncQueue(std::shared_ptr &requestRef, + DWORD dwInternetStatus, size_t statusInformationLength, + LPVOID statusInformation, DWORD statusInformationCopySize, + bool allocate) +{ + UserCallbackContext* ctx = NULL; + DWORD dwNotificationFlags; + WINHTTP_STATUS_CALLBACK cb = GetCallback(&dwNotificationFlags); + LPVOID userdata = NULL; + + if (this->GetUserData()) + userdata = (LPVOID)GetUserData(); + + ctx = new UserCallbackContext(requestRef, dwInternetStatus, (DWORD)statusInformationLength, + dwNotificationFlags, cb, userdata, (LPVOID)statusInformation, + statusInformationCopySize, allocate, RequestCompletionCb); + if (ctx) { + UserCallbackContainer::GetInstance().Queue(ctx); + } + else + return FALSE; + + + return TRUE; +} + + +size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp) +{ + WinHttpRequestImp *request = static_cast(userp); + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return size * nmemb; + + size_t len; + + TRACE("request->GetTotalLength(): %lu\n", request->GetTotalLength()); + if (request->GetOptionalData().length() > 0) + { + len = request->GetOptionalData().length(); + TRACE("%s:%d writing optional length of %lu\n", __func__, __LINE__, len); + memcpy(ptr, request->GetOptionalData().c_str(), len); + request->GetOptionalData().erase(0, len); + request->GetReceivedLength() += len; + return len; + } + + if (request->GetClosed()) + return -1; + + request->GetReadDataEventMtx().lock(); + len = MIN(request->GetReadData().size(), size * nmemb); + request->GetReadDataEventMtx().unlock(); + + TRACE("%s:%d request->GetTotalLength():%lu request->GetReceivedLength():%lu\n", __func__, __LINE__, request->GetTotalLength(), request->GetReceivedLength()); + if (((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && (request->GetType() == "PUT")) || + (request->GetTotalLength() != request->GetReceivedLength())) + { + std::unique_lock getReadDataEventHndlMtx(request->GetReadDataEventMtx()); + if (!request->GetReadDataEventState()) + { + TRACE("%s:%d transfer paused:%lu\n", __func__, __LINE__, len); + return CURL_READFUNC_PAUSE; + } + request->GetReadDataEventState() = false; + TRACE("%s:%d transfer resumed as already signalled:%lu\n", __func__, __LINE__, len); + } + + request->GetReadDataEventMtx().lock(); + len = MIN(request->GetReadData().size(), size * nmemb); + request->GetReadDataEventMtx().unlock(); + + TRACE("%s:%d writing additional length:%lu\n", __func__, __LINE__, len); + request->GetReadDataEventMtx().lock(); + memcpy(ptr, request->GetReadData().data(), len); + request->GetReceivedLength() += len; + request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); + request->GetReadData().shrink_to_fit(); + request->GetReadDataEventMtx().unlock(); + + if (request->GetAsync()) + { + DWORD dwInternetStatus; + DWORD result = len; + + dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; + request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); + } + + return len; +} + +int WinHttpRequestImp::SocketCallback(CURL *handle, curl_infotype type, + char *data, size_t size, + void *userp) +{ + const char *text = ""; + + switch (type) { + case CURLINFO_TEXT: + TRACE_VERBOSE("== Info: %s", data); + return 0; + default: /* in case a new one is introduced to shock us */ + TRACE_VERBOSE("%s:%d type:%d\n", __func__, __LINE__, type); + return 0; + + case CURLINFO_HEADER_IN: + text = "=> Receive header"; + break; + case CURLINFO_HEADER_OUT: + text = "=> Send header"; + break; + case CURLINFO_DATA_OUT: + text = "=> Send data"; + break; + case CURLINFO_SSL_DATA_OUT: + text = "=> Send SSL data"; + break; + case CURLINFO_DATA_IN: + text = "<= Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + text = "<= Recv SSL data"; + break; + } + TRACE_VERBOSE("%s:%d %s\n", __func__, __LINE__, text); + + return 0; +} + +WINHTTPAPI HINTERNET WINAPI WinHttpOpen +( + LPCTSTR pszAgentW, + DWORD dwAccessType, + LPCTSTR pszProxyW, + LPCTSTR pszProxyBypassW, + DWORD dwFlags +) +{ + std::shared_ptr session = std::make_shared (); + + TRACE("%s:%d\n", __func__, __LINE__); + if (dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) + { + if (!pszProxyW) + return NULL; + + ConvertCstrAssign(pszProxyW, WCTLEN(pszProxyW), session->GetProxy()); + + std::vector proxies = Split(session->GetProxy(), ';'); + session->SetProxies(proxies); + } + + if (dwFlags & WINHTTP_FLAG_ASYNC) { + session->SetAsync(); + } + + WinHttpHandleContainer::Instance().Register(session); + return session.get(); +} + +WINHTTPAPI HINTERNET WINAPI WinHttpConnect +( + HINTERNET hSession, + LPCTSTR pswzServerName, + INTERNET_PORT nServerPort, + DWORD dwReserved +) +{ + WinHttpSessionImp *session = static_cast(hSession); + + TRACE("%s:%d pswzServerName: " STRING_LITERAL " nServerPort:%d\n", __func__, __LINE__, pswzServerName, nServerPort); + ConvertCstrAssign(pswzServerName, WCTLEN(pswzServerName), session->GetServerName()); + + session->SetServerPort(nServerPort); + std::shared_ptr connect = std::make_shared (); + if (!connect) + return NULL; + + connect->SetHandle(session); + WinHttpHandleContainer::Instance().Register(connect); + return connect.get(); +} + + +BOOLAPI WinHttpCloseHandle( + HINTERNET hInternet +) +{ + WinHttpBase *base = static_cast(hInternet); + + TRACE("%s:%d\n", __func__, __LINE__); + if (!base) + return FALSE; + + if (WinHttpHandleContainer::Instance().IsRegistered(reinterpret_cast(hInternet))) + { + WinHttpRequestImp *request = dynamic_cast(base); + if (!request) + return FALSE; + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + request->GetClosing() = true; + WinHttpHandleContainer::Instance().UnRegister(request); + return TRUE; + } + + if (WinHttpHandleContainer::Instance().IsRegistered(reinterpret_cast(hInternet))) + { + WinHttpSessionImp *session = dynamic_cast(base); + if (session) + { + WinHttpHandleContainer::Instance().UnRegister(session); + return TRUE; + } + return TRUE; + } + + if (WinHttpHandleContainer::Instance().IsRegistered(reinterpret_cast(hInternet))) + { + WinHttpConnectImp *connect = dynamic_cast(base); + if (connect) + { + WinHttpHandleContainer::Instance().UnRegister(connect); + return TRUE; + } + } + + return TRUE; +} + +WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( + HINTERNET hConnect, + LPCTSTR pwszVerb, /* include "GET", "POST", and "HEAD" */ + LPCTSTR pwszObjectName, + LPCTSTR pwszVersion, + LPCTSTR pwszReferrer, + LPCTSTR * ppwszAcceptTypes, + DWORD dwFlags /* isSecurePort ? (WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE) : WINHTTP_FLAG_REFRESH */ +) +{ + WinHttpConnectImp *connect = static_cast(hConnect); + WinHttpSessionImp *session = connect->GetHandle(); + CURLcode res; + + if (!pwszVerb) + pwszVerb = TEXT("GET"); + + TRACE("%s:%d pwszVerb = " STRING_LITERAL " pwszObjectName: " STRING_LITERAL " pwszVersion = " STRING_LITERAL " pwszReferrer = " STRING_LITERAL "\n", + __func__, __LINE__, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer); + + std::shared_ptr srequest(new WinHttpRequestImp, [](WinHttpRequestImp *request) { + + std::shared_ptr close_request(request); + TRACE("%s::%d reference count reached to 0\n", __func__, __LINE__); + + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n", __func__, __LINE__); + request->AsyncQueue(close_request, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0, NULL, 0, false); + }); + + if (!srequest) + return NULL; + WinHttpRequestImp *request = srequest.get(); + if (!request) + return NULL; + + if (session->GetAsync()) + { + DWORD flag; + WINHTTP_STATUS_CALLBACK cb; + LPVOID userdata = NULL; + + cb = session->GetCallback(&flag); + request->SetAsync(); + request->SetCallback(cb, flag); + + if (connect->GetUserData()) + userdata = (LPVOID)connect->GetUserData(); + else if (session->GetUserData()) + userdata = (LPVOID)session->GetUserData(); + + if (userdata) + request->SetUserData(&userdata); + + } + + const char *prefix; + std::string server = session->GetServerName(); + if (dwFlags & WINHTTP_FLAG_SECURE) + prefix = "https://"; + else + prefix = "http://"; + + if (server.find("http://") == std::string::npos) + server = prefix + server; + if (pwszObjectName) + { + std::string objectname; + + ConvertCstrAssign(pwszObjectName, WCTLEN(pwszObjectName), objectname); + + size_t index = 0; + // convert # to %23 to support links to fragments + while (true) { + /* Locate the substring to replace. */ + index = objectname.find("#", index); + if (index == std::string::npos) break; + /* Make the replacement. */ + objectname.replace(index, 1, "%23"); + /* Advance index forward so the next iteration doesn't pick it up as well. */ + index += 3; + } + request->SetFullPath(server.c_str(), objectname.c_str()); + if (!request->SetServer(request->GetFullPath().c_str(), session->GetServerPort())) { + return NULL; + } + } + else + { + request->SetFullPath(server.c_str(), ""); + if (!request->SetServer(request->GetFullPath().c_str(), session->GetServerPort())) { + return NULL; + } + } + + if (session->GetTimeout() > 0) + { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, session->GetTimeout()); + if (res != CURLE_OK) { + return NULL; + } + } + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_TIME, 60L); + if (res != CURLE_OK) + return NULL; + res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_LIMIT, 1L); + if (res != CURLE_OK) + return NULL; + + request->SetProxy(session->GetProxies()); + + if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("PUT")) == 0) + { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_PUT, 1L); + if (res != CURLE_OK) { + return NULL; + } + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); + if (res != CURLE_OK) { + return NULL; + } + } + else if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("GET")) == 0) + { + } + else if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("POST")) == 0) + { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_POST, 1L); + if (res != CURLE_OK) { + return NULL; + } + } + else + { + std::string verb; + + ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), verb); + TRACE("%s:%d setting custom header %s\n", __func__, __LINE__, verb.c_str()); + res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, verb.c_str()); + if (res != CURLE_OK) { + return NULL; + } + } + + if (pwszVersion) + { + int ver; + + if (WCTCMP(pwszVerb, (const TCHAR*)TEXT("1.0")) == 0) + ver = CURL_HTTP_VERSION_1_0; + else if (WCTCMP(pwszVerb, (const TCHAR*)TEXT("1.1")) == 0) + ver = CURL_HTTP_VERSION_1_1; + else if (WCTCMP(pwszVerb, (const TCHAR*)TEXT("2.0")) == 0) + ver = CURL_HTTP_VERSION_2_0; + else + return NULL; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTP_VERSION, ver); + if (res != CURLE_OK) { + return NULL; + } + } + + if (pwszReferrer) + { + ConvertCstrAssign(pwszReferrer, WCTLEN(pwszReferrer), session->GetReferrer()); + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_REFERER, session->GetReferrer().c_str()); + if (res != CURLE_OK) { + return NULL; + } + } + + if (dwFlags & WINHTTP_FLAG_SECURE) + { + const char *pKeyName; + const char *pKeyType; + const char *pEngine; + static const char *pCertFile = "testcert.pem"; + static const char *pCACertFile = "cacert.pem"; + //static const char *pHeaderFile = "dumpit"; + const char *pPassphrase = NULL; + +#ifdef USE_ENGINE + pKeyName = "rsa_test"; + pKeyType = "ENG"; + pEngine = "chil"; /* for nChiper HSM... */ +#else + pKeyName = NULL; + pKeyType = "PEM"; + pCertFile = NULL; + pCACertFile = NULL; + pEngine = NULL; +#endif + if (const char* env_p = std::getenv("WINHTTP_PAL_KEYNAME")) + pKeyName = env_p; + + if (const char* env_p = std::getenv("WINHTTP_PAL_KEYTYPE")) + pKeyType = env_p; + + if (const char* env_p = std::getenv("WINHTTP_PAL_CERTFILE")) + pCertFile = env_p; + + if (const char* env_p = std::getenv("WINHTTP_PAL_CACERTFILE")) + pCACertFile = env_p; + + if (const char* env_p = std::getenv("WINHTTP_PAL_ENGINE")) + pEngine = env_p; + + if (pEngine) { + if (curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE, pEngine) != CURLE_OK) { + /* load the crypto engine */ + TRACE("%s", "can't set crypto engine\n"); + return NULL; + } + if (curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK) { + /* set the crypto engine as default */ + /* only needed for the first time you load + a engine a curl object... */ + TRACE("%s", "can't set crypto engine as default\n"); + return NULL; + } + } + /* cert is stored PEM coded in file... */ + /* since PEM is default, we needn't set it for PEM */ + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERTTYPE, "PEM"); + if (res != CURLE_OK) { + return NULL; + } + + /* set the cert for client authentication */ + if (pCertFile) { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERT, pCertFile); + if (res != CURLE_OK) { + return NULL; + } + } + + /* sorry, for engine we must set the passphrase + (if the key has one...) */ + if (pPassphrase) { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_KEYPASSWD, pPassphrase); + if (res != CURLE_OK) { + return NULL; + } + } + + /* if we use a key stored in a crypto engine, + we must set the key type to "ENG" */ + if (pKeyType) { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEYTYPE, pKeyType); + if (res != CURLE_OK) { + return NULL; + } + } + + /* set the private key (file or ID in engine) */ + if (pKeyName) { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEY, pKeyName); + if (res != CURLE_OK) { + return NULL; + } + } + + /* set the file with the certs vaildating the server */ + if (pCACertFile) { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_CAINFO, pCACertFile); + if (res != CURLE_OK) { + return NULL; + } + } + } + if (pwszVerb) + { + ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), request->GetType()); + } + request->SetSession(connect); + WinHttpHandleContainer::Instance().Register(srequest); + + return request; +} + +BOOLAPI +WinHttpAddRequestHeaders +( + HINTERNET hRequest, + LPCTSTR lpszHeaders, + DWORD dwHeadersLength, + DWORD dwModifiers +) +{ + CURLcode res; + WinHttpRequestImp *request = static_cast(hRequest); + if (!request) + return FALSE; + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + TRACE("%s:%d lpszHeaders = " STRING_LITERAL " dwModifiers: %lu\n", __func__, __LINE__, + lpszHeaders ? lpszHeaders: TEXT(""), dwModifiers); + if (lpszHeaders) + { + size_t nstringLen; + + if (dwHeadersLength == (DWORD)-1) + { + nstringLen = WCTLEN(lpszHeaders) + 1; + } + else + { + nstringLen = dwHeadersLength + 1; + } + + std::string &headers = request->GetOutgoingHeaderList(); + + ConvertCstrAssign(lpszHeaders, nstringLen, headers); + request->AddHeader(headers); + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTPHEADER, request->GetHeaderList()); + if (res != CURLE_OK) + { + return FALSE; + } + } + + return TRUE; +} + +BOOLAPI WinHttpSendRequest +( + HINTERNET hRequest, + LPCTSTR lpszHeaders, + DWORD dwHeadersLength, + LPVOID lpOptional, + DWORD dwOptionalLength, + DWORD dwTotalLength, + DWORD_PTR dwContext +) +{ + CURLcode res; + WinHttpRequestImp *request = static_cast(hRequest); + if (!request) + return FALSE; + + WinHttpConnectImp *connect = request->GetSession(); + WinHttpSessionImp *session = connect->GetHandle(); + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + TSTRING customHeader; + + if (lpszHeaders) + customHeader.assign(lpszHeaders, WCTLEN(lpszHeaders)); + + if ((dwTotalLength == 0) && (dwOptionalLength == 0) && (request->GetType() == "PUT")) + customHeader += TEXT("Transfer-Encoding: chunked\r\n"); + + TRACE("%s:%d lpszHeaders:%p dwHeadersLength:%lu lpOptional:%p dwOptionalLength:%lu dwTotalLength:%lu\n", + __func__, __LINE__, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength); + + if ((customHeader != TEXT("")) && !WinHttpAddRequestHeaders(hRequest, customHeader.c_str(), customHeader.length(), 0)) + return FALSE; + + if (lpOptional) + { + if (dwOptionalLength == 0) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + request->SetOptionalData(lpOptional, dwOptionalLength); + + if (request->GetType() == "POST") + { + /* Now specify the POST data */ + res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDS, request->GetOptionalData().c_str()); + if (res != CURLE_OK) + return FALSE; + } + } + + if ((request->GetType() == "PUT") || (request->GetType() == "POST")) + { + if (!request->GetAsync() && (dwTotalLength == 0)) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + } + + if (dwOptionalLength == (DWORD)-1) + dwOptionalLength = strlen(request->GetOptionalData().c_str()); + + DWORD totalsize = MAX(dwOptionalLength, dwTotalLength); + /* provide the size of the upload, we specicially typecast the value + to curl_off_t since we must be sure to use the correct data size */ + if (request->GetType() == "POST") + { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDSIZE, (long)totalsize); + if (res != CURLE_OK) + return FALSE; + } + else if (totalsize) + { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_INFILESIZE_LARGE, (curl_off_t)totalsize); + if (res != CURLE_OK) + return FALSE; + } + + request->GetTotalLength() = dwTotalLength; + res = curl_easy_setopt(request->GetCurl(), CURLOPT_READDATA, request); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_READFUNCTION, request->ReadCallback); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGFUNCTION, request->SocketCallback); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGDATA, request); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEFUNCTION, request->WriteBodyFunction); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEDATA, request); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERFUNCTION, request->WriteHeaderFunction); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERDATA, request); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_PRIVATE, request); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_FOLLOWLOCATION, 1L); + if (res != CURLE_OK) + return FALSE; + + if (winhttp_tracing_verbose) + { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_VERBOSE, 1); + if (res != CURLE_OK) + return FALSE; + } + + /* enable TCP keep-alive for this transfer */ + res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPALIVE, 1L); + if (res != CURLE_OK) + return FALSE; + + /* keep-alive idle time to 120 seconds */ + res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPIDLE, 120L); + if (res != CURLE_OK) + return FALSE; + + /* interval time between keep-alive probes: 60 seconds */ + res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPINTVL, 60L); + if (res != CURLE_OK) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYPEER, request->VerifyPeer()); + if (res != CURLE_OK) { + return FALSE; + } + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYHOST, request->VerifyHost()); + if (res != CURLE_OK) { + return FALSE; + } + + DWORD maxConnections = 0; + + if (request->GetMaxConnections()) + maxConnections = request->GetMaxConnections(); + else if (session->GetMaxConnections()) + maxConnections = session->GetMaxConnections(); + + if (maxConnections) { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_MAXCONNECTS, maxConnections); + if (res != CURLE_OK) + return FALSE; + } + + if (dwContext) + request->SetUserData(reinterpret_cast(&dwContext)); + + DWORD securityProtocols = 0; + + if (request->GetSecureProtocol()) + securityProtocols = request->GetSecureProtocol(); + else if (session->GetSecureProtocol()) + securityProtocols = session->GetSecureProtocol(); + + if (securityProtocols) { + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLVERSION, securityProtocols); + if (res != CURLE_OK) + return FALSE; + } + + if (request->GetAsync()) + { + if (request->GetClosing()) + { + TRACE("%s:%d \n", __func__, __LINE__); + return FALSE; + } + request->CleanUp(); + + TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0, NULL, 0, false); + + TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + + if (!ComContainer::GetInstance().AddHandle(srequest, request->GetCurl())) + return FALSE; + + TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + ComContainer::GetInstance().KickStart(); + + TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0, NULL, 0, false); + + TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, 0, NULL, 0, false); + + TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + } + else + { + if (dwTotalLength && (request->GetType() != "POST")) + { + DWORD thread_id; + request->GetUploadThread() = CREATETHREAD( + request->UploadThreadFunction, // thread function name + request, &thread_id); // argument to thread function + } + else + { + /* Perform the request, res will get the return code */ + res = curl_easy_perform(request->GetCurl()); + /* Check for errors */ + if (res != CURLE_OK) + { + TRACE("curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + return FALSE; + } + } + } + + return TRUE; +} + +void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptr srequest) +{ + bool expected = false; + bool result = std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, true); + + TRACE("%s:%d GetReceiveResponsePending() = %d\n", __func__, __LINE__, (int) GetReceiveResponsePending()); + std::unique_lock lck(GetReceiveCompletionEventMtx()); + while (!GetReceiveCompletionEventCounter()) + GetReceiveCompletionEvent().wait(lck); + lck.unlock(); + + if (result && (GetCompletionCode() == CURLE_OK)) + { + TRACE("%s:%d HandleReceiveNotifications \n", __func__, __LINE__); + HandleReceiveNotifications(srequest); + } +} + +WINHTTPAPI +BOOL +WINAPI +WinHttpReceiveResponse +( + HINTERNET hRequest, + LPVOID lpReserved +) +{ + WinHttpRequestImp *request = static_cast(hRequest); + if (!request) + return FALSE; + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + TRACE("%s:%d\n", __func__, __LINE__); + + if ((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && (request->GetType() == "PUT")) + { + if (request->GetAsync()) + { + { + std::lock_guard lck(request->GetReadDataEventMtx()); + request->GetReadDataEventState() = true; + request->GetReadDataEvent().notify_all(); + TRACE("%s:%d\n", __func__, __LINE__); + } + + ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); + } + } + + if (request->GetAsync()) + { + TRACE("%s:%d\n", __func__, __LINE__); + request->WaitAsyncReceiveCompletion(srequest); + } + else + { + if (request->GetType() == "PUT") + { + while (request->GetTotalLength() != request->GetReceivedLength()) { + if (request->GetUploadThreadExitStatus()) { + if (request->GetTotalLength() != request->GetReceivedLength()) { + SetLastError(ERROR_WINHTTP_OPERATION_CANCELLED); + return FALSE; + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + + return TRUE; + } + DWORD headerLength; + { + std::lock_guard lck(request->GetHeaderStringMutex()); + headerLength = request->GetHeaderString().length(); + } + + return headerLength > 0; + } + return TRUE; +} + +bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr srequest, DWORD available) +{ + bool expected = true; + bool result = std::atomic_compare_exchange_strong(&GetQueryDataPending(), &expected, false); + if (result) + { + if (!available) + { + size_t length; + + GetBodyStringMutex().lock(); + length = GetResponseString().size(); + available = (DWORD)(length); + GetBodyStringMutex().unlock(); + } + + DWORD lpvStatusInformation = available; + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, lpvStatusInformation); + AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), + (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); + } + return result; +} + +void WinHttpRequestImp::WaitAsyncQueryDataCompletion(std::shared_ptr srequest) +{ + bool completed; + GetQueryDataPending() = true; + TRACE("%s:%d GetQueryDataPending() = %d\n", __func__, __LINE__, (int)GetQueryDataPending()); + { + std::lock_guard lck(GetQueryDataEventMtx()); + completed = GetQueryDataEventState(); + } + + if (completed) { + TRACE("%s:%d transfer already finished\n", __func__, __LINE__); + HandleQueryDataNotifications(srequest, 0); + } +} + +BOOLAPI +WinHttpQueryDataAvailable +( + HINTERNET hRequest, + LPDWORD lpdwNumberOfBytesAvailable +) +{ + WinHttpRequestImp *request = static_cast(hRequest); + if (!request) + return FALSE; + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + if (request->GetClosing()) + { + TRACE("%s:%d \n", __func__, __LINE__); + return FALSE; + } + + size_t length; + + request->GetBodyStringMutex().lock(); + length = request->GetResponseString().size(); + DWORD available = (DWORD)(length); + request->GetBodyStringMutex().unlock(); + + if (request->GetAsync()) + { + if (available == 0) + { + TRACE("%s:%d !!!!!!!\n", __func__, __LINE__); + request->WaitAsyncQueryDataCompletion(srequest); + request->GetBodyStringMutex().lock(); + length = request->GetResponseString().size(); + available = (DWORD)(length); + TRACE("%s:%d available = %lu\n", __func__, __LINE__, available); + request->GetBodyStringMutex().unlock(); + } + else + { + TRACE("%s:%d available = %lu\n", __func__, __LINE__, available); + DWORD lpvStatusInformation = available; + TRACE("%s:%d WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, lpvStatusInformation); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), + (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); + + } + + } + + + if (lpdwNumberOfBytesAvailable) + *lpdwNumberOfBytesAvailable = available; + + return TRUE; +} + +DWORD WinHttpRequestImp::WaitDataAvailable() +{ + DWORD length, remainingLength, readLength; + + GetBodyStringMutex().lock(); + length = GetResponseString().size(); + readLength = remainingLength = length; + if (GetAsync() == TRUE) + { +/* check the size */ + while (((length == 0) || (remainingLength == 0)) && + (!GetCompletionStatus())) + { + GetBodyStringMutex().unlock(); + + TRACE("%s:%d\n", __func__, __LINE__); + ComContainer::GetInstance().KickStart(); + + TRACE("%s:%d\n", __func__, __LINE__); + { + std::unique_lock lck(GetDataAvailableMtx()); + while (!GetDataAvailableEventCounter() && !GetCompletionStatus()) + GetDataAvailableEvent().wait(lck); + lck.unlock(); + + TRACE("%s:%d\n", __func__, __LINE__); + GetDataAvailableEventCounter()--; + } + + GetBodyStringMutex().lock(); + + length = GetResponseString().size(); + readLength = remainingLength = length; + TRACE("%s:%d length:%lu readLength:%lu completion: %d\n", __func__, __LINE__, + length, readLength, GetCompletionStatus()); + } + } + GetBodyStringMutex().unlock(); + return readLength; +} + +BOOLAPI +WinHttpReadData +( + HINTERNET hRequest, + LPVOID lpBuffer, + DWORD dwNumberOfBytesToRead, + LPDWORD lpdwNumberOfBytesRead +) +{ + WinHttpRequestImp *request = static_cast(hRequest); + if (!request) + return FALSE; + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + if (request->GetClosing()) + { + TRACE("%s:%d \n", __func__, __LINE__); + return FALSE; + } + + size_t readLength; + + TRACE("%s:%d\n", __func__, __LINE__); + if (dwNumberOfBytesToRead == 0) + { + if (lpdwNumberOfBytesRead) + *lpdwNumberOfBytesRead = 0; + + if (request->GetAsync()) + { + LPVOID StatusInformation = (LPVOID)lpBuffer; + + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, 0, (LPVOID)StatusInformation, sizeof(StatusInformation), false); + } + return TRUE; + } + + readLength = request->WaitDataAvailable(); + + request->GetBodyStringMutex().lock(); + if (readLength > dwNumberOfBytesToRead) + { + readLength = dwNumberOfBytesToRead; + } + if (readLength) + { + memcpy(reinterpret_cast(lpBuffer), request->GetResponseString().data(), readLength); + request->GetResponseString().erase(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength); + request->GetResponseString().shrink_to_fit(); + } + request->GetBodyStringMutex().unlock(); + + if (readLength == 0) + { + TRACE("%s", "!!!!!!!!!!done!!!!!!!!!!!!!!!\n"); + } + TRACE("%s:%d lpBuffer: %p length:%lu\n", __func__, __LINE__, lpBuffer, readLength); + if (request->GetAsync()) + { + LPVOID StatusInformation = (LPVOID)lpBuffer; + + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, readLength, (LPVOID)StatusInformation, sizeof(StatusInformation), false); + } + + if (lpdwNumberOfBytesRead) + *lpdwNumberOfBytesRead = (DWORD)readLength; + TRACE("%s:%d\n", __func__, __LINE__); + return TRUE; +} + +BOOLAPI +WinHttpSetTimeouts +( + HINTERNET hInternet, // Session/Request handle. + int nResolveTimeout, + int nConnectTimeout, + int nSendTimeout, + int nReceiveTimeout +) +{ + WinHttpBase *base = static_cast(hInternet); + CURLcode res; + + TRACE("%s:%d\n", __func__, __LINE__); + if (dynamic_cast(base)) + { + WinHttpSessionImp *session; + + session = dynamic_cast(base); + if (!session) + return FALSE; + + session->SetTimeout(nReceiveTimeout); + } + else if (dynamic_cast(base)) + { + WinHttpRequestImp *request = dynamic_cast(base); + + if (!request) + return FALSE; + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, nReceiveTimeout); + if (res != CURLE_OK) + return FALSE; + } + else + return FALSE; + + return TRUE; +} + +static int NumDigits(DWORD value) +{ + int count = 0; + + while (value != 0) + { + // n = n/10 + value /= 10; + ++count; + } + + return count; +} + +static TSTRING FindRegex(const TSTRING &subject,const TSTRING ®str) +{ + TSTRING result; + try { + TREGEX re(regstr, std::regex_constants::icase); + TREGEX_MATCH match; + if (TREGEX_SEARCH(subject, match, re)) { + result = match.str(0); + } else { + result = TSTRING(TEXT("")); + return TEXT(""); + } + } catch (std::regex_error&) { + return TEXT(""); + } + return result; +} + +BOOLAPI WinHttpQueryHeaders( + HINTERNET hRequest, + DWORD dwInfoLevel, + LPCTSTR pwszName, + LPVOID lpBuffer, + LPDWORD lpdwBufferLength, + LPDWORD lpdwIndex +) +{ + WinHttpRequestImp *request = static_cast(hRequest); + if (!request) + return FALSE; + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + bool returnDWORD = false; + + if (pwszName != WINHTTP_HEADER_NAME_BY_INDEX) + return FALSE; + + if (lpdwIndex != WINHTTP_NO_HEADER_INDEX) + return FALSE; + + if (request->GetHeaderString().length() == 0) + return FALSE; + + if (dwInfoLevel & WINHTTP_QUERY_FLAG_NUMBER) + { + dwInfoLevel &= ~WINHTTP_QUERY_FLAG_NUMBER; + returnDWORD = true; + } + TRACE("%s:%d dwInfoLevel = 0x%lx\n", __func__, __LINE__, dwInfoLevel); + + if (returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) + return FALSE; + + if (dwInfoLevel == WINHTTP_QUERY_VERSION) + { + CURLcode res; + DWORD retValue; + + res = curl_easy_getinfo(request->GetCurl(), CURLINFO_HTTP_VERSION, &retValue); + if (res != CURLE_OK) + return FALSE; + + if (!returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, (NumDigits(retValue) + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + + if (!lpBuffer) + return FALSE; + + if (returnDWORD) + { + memcpy(lpBuffer, &retValue, sizeof(retValue)); + } + else + { + TCHAR *outbuf = reinterpret_cast(lpBuffer); + STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%ld"), retValue); + outbuf[*lpdwBufferLength/sizeof(outbuf[0]) - 1] = TEXT('\0'); + } + return TRUE; + } + + if (dwInfoLevel == WINHTTP_QUERY_STATUS_CODE) + { + CURLcode res; + DWORD retValue; + + res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &retValue); + if (res != CURLE_OK) + return FALSE; + + if (!returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, (NumDigits(retValue) + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + + if (!lpBuffer) + return FALSE; + + if (returnDWORD) + { + memcpy(lpBuffer, &retValue, sizeof(retValue)); + } + else + { + TCHAR *outbuf = reinterpret_cast(lpBuffer); + STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%ld"), retValue); + outbuf[*lpdwBufferLength/sizeof(outbuf[0]) - 1] = TEXT('\0'); + } + TRACE("%s:%d status code :%lu\n", __func__, __LINE__, retValue); + return TRUE; + } + + if (dwInfoLevel == WINHTTP_QUERY_STATUS_TEXT) + { + CURLcode res; + DWORD retValue; + TCHAR responseCodeStr[30]; + DWORD length; + + res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &retValue); + if (res != CURLE_OK) + return FALSE; + + STNPRINTF(responseCodeStr, sizeof(responseCodeStr) - 1, TEXT("%lu"), retValue); + responseCodeStr[sizeof(responseCodeStr)/sizeof(responseCodeStr[0]) - 1] = TEXT('\0'); + + std::lock_guard lck(request->GetHeaderStringMutex()); + length = request->GetHeaderString().length(); + + if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + + if (!lpBuffer) + return FALSE; + +#ifdef UNICODE + TSTRING subject; + + length = request->GetHeaderString().length(); + subject.resize(length + 1); + + MultiByteToWideChar(CP_UTF8, 0, request->GetHeaderString().c_str(), -1, (LPWSTR)subject.c_str(), length); + subject[length] = TEXT('\0'); +#else + TSTRING &subject = request->GetHeaderString(); +#endif + + TSTRING regstr; + + regstr.append(TEXT("HTTP.*")); + regstr.append(responseCodeStr); + + TSTRING result = FindRegex(subject, regstr); + if (result != TEXT("")) + { + size_t offset = subject.find(result); + if (offset == std::string::npos) + return FALSE; + + size_t offsetendofline = subject.find(TEXT("\r\n"), offset); + if (offsetendofline == std::string::npos) + return FALSE; + + size_t linelength = offsetendofline - offset; + if (linelength == std::string::npos) + return FALSE; + + WCTNCPY((TCHAR*)lpBuffer, + subject.c_str() + offset + result.length() + 1, + linelength - result.length() - 1); + + ((TCHAR*)lpBuffer)[linelength - result.length() - 1] = TEXT('\0'); + return TRUE; + } + else + { + TSTRING retStr = StatusCodeMap[retValue]; + if (retStr == TEXT("")) + return FALSE; + + const TCHAR *cstr = retStr.c_str(); + WCTCPY((TCHAR*)lpBuffer, cstr); + } + return TRUE; + } + + + if (dwInfoLevel == WINHTTP_QUERY_RAW_HEADERS) + { + size_t length; + std::string header; + TCHAR *wbuffer = reinterpret_cast(lpBuffer); + + request->GetHeaderStringMutex().lock(); + length = request->GetHeaderString().length(); + header.append(request->GetHeaderString().c_str()); + request->GetHeaderStringMutex().unlock(); + + if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + + if (!wbuffer) + return FALSE; + + ReplaceStr(header, "\r\n", "\n"); + ReplaceStr(header, "\n\r", "\n"); + ReplaceStr(header, "\r", "\n"); + ReplaceStr(header, "\n\n", "\n"); + std::replace(header.begin(), header.end(), '\n', '\0'); + header.resize(header.size() + 1); + length = header.size(); + +#ifdef UNICODE + MultiByteToWideChar(CP_UTF8, 0, header.c_str(), length + 1, wbuffer, length + 1); +#else + strncpy(wbuffer, header.c_str(), length); +#endif + if (wbuffer[length] != TEXT('\0')) + { + if (lpdwBufferLength) + { + if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) + { + wbuffer[length] = 0; + } + else + { + wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; + } + } + else + { + wbuffer[length] = 0; + } + } + if (lpdwBufferLength) + *lpdwBufferLength = (DWORD)length; + return TRUE; + } + + if (dwInfoLevel == WINHTTP_QUERY_RAW_HEADERS_CRLF) + { + std::lock_guard lck(request->GetHeaderStringMutex()); + size_t length; + TCHAR *wbuffer = reinterpret_cast(lpBuffer); + + length = request->GetHeaderString().length(); + + if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + + if (!wbuffer) + return FALSE; + +#ifdef UNICODE + MultiByteToWideChar(CP_UTF8, 0, request->GetHeaderString().c_str(), -1, wbuffer, length); +#else + strncpy(wbuffer, request->GetHeaderString().c_str(), length); +#endif + if (lpdwBufferLength) + { + if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) + { + wbuffer[length] = 0; + } + else + { + wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; + } + } + else + { + wbuffer[length] = 0; + } + if (lpdwBufferLength) + *lpdwBufferLength = (DWORD)(length * sizeof(TCHAR)); + return TRUE; + } + + + return FALSE; +} + +DWORD ConvertSecurityProtocol(DWORD offered) +{ + DWORD curlOffered = CURL_SSLVERSION_DEFAULT; + + if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) + curlOffered = CURL_SSLVERSION_SSLv2; + + if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) + curlOffered = CURL_SSLVERSION_SSLv3; + + if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { + curlOffered = CURL_SSLVERSION_TLSv1; + curlOffered |= CURL_SSLVERSION_MAX_TLSv1_0; + } + + if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { + curlOffered = CURL_SSLVERSION_TLSv1; + curlOffered &= ~0xFFFF; + curlOffered |= CURL_SSLVERSION_MAX_TLSv1_1; + } + + if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { + curlOffered = CURL_SSLVERSION_TLSv1; + curlOffered &= ~0xFFFF; + curlOffered |= CURL_SSLVERSION_MAX_TLSv1_2; + } + return curlOffered; +} + +BOOLAPI WinHttpSetOption( + HINTERNET hInternet, + DWORD dwOption, + LPVOID lpBuffer, + DWORD dwBufferLength +) +{ + WinHttpBase *base = static_cast(hInternet); + + TRACE("%s:%d dwOption:%lu\n", __func__, __LINE__, dwOption); + + if (dwOption == WINHTTP_OPTION_MAX_CONNS_PER_SERVER) + { + if (dwBufferLength != sizeof(DWORD)) + return FALSE; + + if (dynamic_cast(base)) + { + WinHttpSessionImp *session = dynamic_cast(base); + + if (!session || !lpBuffer) + return FALSE; + + if (!session->SetMaxConnections(reinterpret_cast(lpBuffer))) + return FALSE; + return TRUE; + } + else if (dynamic_cast(base)) + { + WinHttpRequestImp *request = dynamic_cast(base); + + if (!request || !lpBuffer) + return FALSE; + + if (!request->SetMaxConnections(reinterpret_cast(lpBuffer))) + return FALSE; + return TRUE; + } + else + return FALSE; + + } + else if (dwOption == WINHTTP_OPTION_CONTEXT_VALUE) + { + if (dwBufferLength != sizeof(void*)) + return FALSE; + + if (dynamic_cast(base)) + { + WinHttpConnectImp *connect = dynamic_cast(base); + + if (!connect || !lpBuffer) + return FALSE; + + if (!connect->SetUserData(reinterpret_cast(lpBuffer))) + return FALSE; + return TRUE; + } + else if (dynamic_cast(base)) + { + WinHttpSessionImp *session = dynamic_cast(base); + + if (!session || !lpBuffer) + return FALSE; + + if (!session->SetUserData(reinterpret_cast(lpBuffer))) + return FALSE; + return TRUE; + } + else if (dynamic_cast(base)) + { + WinHttpRequestImp *request = dynamic_cast(base); + + if (!request || !lpBuffer) + return FALSE; + + if (!request->SetUserData(reinterpret_cast(lpBuffer))) + return FALSE; + return TRUE; + } + else + return FALSE; + } + else if (dwOption == WINHTTP_OPTION_SECURE_PROTOCOLS) + { + if (dwBufferLength != sizeof(DWORD)) + return FALSE; + + if (dynamic_cast(base)) + { + WinHttpSessionImp *session = dynamic_cast(base); + + if (!session || !lpBuffer) + return FALSE; + + DWORD curlOffered = ConvertSecurityProtocol(*reinterpret_cast(lpBuffer)); + if (curlOffered && !session->SetSecureProtocol(&curlOffered)) + return FALSE; + return TRUE; + } + else if (dynamic_cast(base)) + { + WinHttpRequestImp *request = dynamic_cast(base); + + if (!request || !lpBuffer) + return FALSE; + + DWORD curlOffered = ConvertSecurityProtocol(*reinterpret_cast(lpBuffer)); + if (curlOffered && !request->SetSecureProtocol(&curlOffered)) + return FALSE; + return TRUE; + } + else + return FALSE; + } + else if (dwOption == WINHTTP_OPTION_SECURITY_FLAGS) + { + if (dwBufferLength != sizeof(DWORD)) + return FALSE; + + if (dynamic_cast(base)) + { + WinHttpRequestImp *request = dynamic_cast(base); + + if (!request || !lpBuffer) + return FALSE; + + DWORD value = *reinterpret_cast(lpBuffer); + if (value == (SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | + SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE)) + { + request->VerifyPeer() = 0L; + request->VerifyHost() = 0L; + } + else if (!value) + { + request->VerifyPeer() = 1L; + request->VerifyHost() = 2L; + } + else + return FALSE; + + return TRUE; + } + else + return FALSE; + } + + return FALSE; +} + +WINHTTPAPI +WINHTTP_STATUS_CALLBACK +WINAPI +WinHttpSetStatusCallback +( + HINTERNET hInternet, + WINHTTP_STATUS_CALLBACK lpfnInternetCallback, + DWORD dwNotificationFlags, + DWORD_PTR dwReserved +) +{ + WinHttpSessionImp *session = static_cast(hInternet); + WINHTTP_STATUS_CALLBACK oldcb; + DWORD olddwNotificationFlags; + + TRACE("%s:%d\n", __func__, __LINE__); + + if (hInternet == NULL) + return WINHTTP_INVALID_STATUS_CALLBACK; + + oldcb = session->GetCallback(&olddwNotificationFlags); + session->SetCallback(lpfnInternetCallback, dwNotificationFlags); + return oldcb; +} + +BOOLAPI +WinHttpQueryOption +( + HINTERNET hInternet, + DWORD dwOption, + LPVOID lpBuffer, + LPDWORD lpdwBufferLength +) +{ + WinHttpBase *base = static_cast(hInternet); + WinHttpSessionImp *session; + + TRACE("%s:%d\n", __func__, __LINE__); + + if (WINHTTP_OPTION_CONNECT_TIMEOUT == dwOption) + { + if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) + return FALSE; + + if (!lpBuffer) + return FALSE; + + session = GetImp(base); + if (!session) + return FALSE; + + *reinterpret_cast(lpBuffer) = session->GetTimeout(); + } + if (WINHTTP_OPTION_CALLBACK == dwOption) + { + if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(LPVOID)) == FALSE) + return FALSE; + + if (!lpBuffer) + return FALSE; + + session = GetImp(base); + if (!session) + return FALSE; + + DWORD dwNotificationFlags; + WINHTTP_STATUS_CALLBACK cb = session->GetCallback(&dwNotificationFlags); + *reinterpret_cast(lpBuffer) = cb; + } + else if (WINHTTP_OPTION_URL == dwOption) + { + WinHttpRequestImp *request; + + if (dynamic_cast(base)) + { + request = dynamic_cast(base); + } + else + return FALSE; + + char *url = NULL; + curl_easy_getinfo(request->GetCurl(), CURLINFO_EFFECTIVE_URL, &url); + if (!url) + return FALSE; + + if (SizeCheck(lpBuffer, lpdwBufferLength, (strlen(url) + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + + TCHAR *wbuffer = reinterpret_cast(lpBuffer); + size_t length = strlen(url); +#ifdef UNICODE + MultiByteToWideChar(CP_UTF8, 0, url, length + 1, wbuffer, length + 1); +#else + strncpy(wbuffer, url, length); +#endif + if (wbuffer[length] != TEXT('\0')) + { + if (lpdwBufferLength) + { + if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) + { + wbuffer[length] = 0; + } + else + { + wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; + } + } + else + { + wbuffer[length] = 0; + } + } + if (lpdwBufferLength) + *lpdwBufferLength = (DWORD)length; + } + else if (WINHTTP_OPTION_HTTP_VERSION == dwOption) + { + WinHttpRequestImp *request; + + if (dynamic_cast(base)) + { + request = dynamic_cast(base); + } + else + return FALSE; + + if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(HTTP_VERSION_INFO)) == FALSE) + return FALSE; + + if (!lpBuffer) + return FALSE; + + long curlversion; + CURLcode rc; + + rc = curl_easy_getinfo(request->GetCurl(), CURLINFO_HTTP_VERSION, &curlversion); + if (rc != CURLE_OK) + return FALSE; + + HTTP_VERSION_INFO version; + + if (curlversion == CURL_HTTP_VERSION_1_0) + { + version.dwMinorVersion = 0; + version.dwMajorVersion = 1; + } + else if (curlversion == CURL_HTTP_VERSION_1_1) + { + version.dwMinorVersion = 1; + version.dwMajorVersion = 1; + } + else if (curlversion == CURL_HTTP_VERSION_2_0) + { + version.dwMinorVersion = 1; + version.dwMajorVersion = 1; + } + else + return FALSE; + + memcpy(lpBuffer, &version, sizeof(version)); + if (lpdwBufferLength) + *lpdwBufferLength = (DWORD)sizeof(version); + } + + return TRUE; +} + +#ifdef CURL_SUPPORTS_URL_API +BOOLAPI WinHttpCrackUrl( + LPCTSTR pwszUrl, + DWORD dwUrlLength, + DWORD dwFlags, + LPURL_COMPONENTS lpUrlComponents +) +{ + DWORD urlLen; + + if (!pwszUrl || !lpUrlComponents) + return FALSE; + + if (!lpUrlComponents->dwStructSize) + return FALSE; + + if (lpUrlComponents->dwStructSize != sizeof(*lpUrlComponents)) + return FALSE; + + if (dwUrlLength == 0) + urlLen = WCTLEN(pwszUrl); + else + urlLen = dwUrlLength; + + std::string urlstr; + ConvertCstrAssign(pwszUrl, urlLen, urlstr); + + CURLUcode rc; + CURLU *url = curl_url(); + rc = curl_url_set(url, CURLUPART_URL, urlstr.c_str(), 0); + if (rc) + return FALSE; + + char *host; + rc = curl_url_get(url, CURLUPART_HOST, &host, 0); + if (!rc) { + size_t pos = urlstr.find(host); + if (pos != std::string::npos) + { + if (lpUrlComponents->dwHostNameLength != (DWORD)-1) + { + if (lpUrlComponents->dwHostNameLength >= (strlen(host) + 1)) { + WCTNCPY(lpUrlComponents->lpszHostName, (TCHAR*)pwszUrl + pos, strlen(host)); + lpUrlComponents->lpszHostName[strlen(host)] = TEXT('\0'); + lpUrlComponents->dwHostNameLength = strlen(host); + } + } + else + { + lpUrlComponents->lpszHostName = const_cast(pwszUrl) + pos; + lpUrlComponents->dwHostNameLength = strlen(host); + } + } + curl_free(host); + } + + char *scheme; + rc = curl_url_get(url, CURLUPART_SCHEME, &scheme, 0); + if (!rc) { + size_t pos = urlstr.find(scheme); + if (pos != std::string::npos) + { + if (lpUrlComponents->dwSchemeLength != (DWORD)-1) + { + if (lpUrlComponents->dwSchemeLength >= (strlen(scheme) + 1)) { + WCTNCPY(lpUrlComponents->lpszScheme, (TCHAR*)pwszUrl + pos, strlen(scheme)); + lpUrlComponents->lpszScheme[strlen(scheme)] = TEXT('\0'); + lpUrlComponents->dwSchemeLength = strlen(scheme); + } + } + else + { + lpUrlComponents->lpszScheme = const_cast(pwszUrl) + pos; + lpUrlComponents->dwSchemeLength = strlen(scheme); + } + + if (strcmp(scheme, "http") == 0) { + lpUrlComponents->nPort = 80; + lpUrlComponents->nScheme = INTERNET_SCHEME_HTTP; + } + else if (strcmp(scheme, "https") == 0) + { + lpUrlComponents->nPort = 443; + lpUrlComponents->nScheme = INTERNET_SCHEME_HTTPS; + } + } + curl_free(scheme); + } + char *path; + rc = curl_url_get(url, CURLUPART_PATH, &path, 0); + if (!rc) { + size_t pos = urlstr.find(path); + if (pos != std::string::npos) + { + if (lpUrlComponents->dwUrlPathLength != (DWORD)-1) + { + if (lpUrlComponents->dwUrlPathLength >= (strlen(path) + 1)) { + WCTNCPY(lpUrlComponents->lpszUrlPath, (TCHAR*)pwszUrl + pos, strlen(path)); + lpUrlComponents->lpszUrlPath[strlen(path)] = TEXT('\0'); + lpUrlComponents->dwUrlPathLength = strlen(path); + } + } + else + { + if (strcmp(path, "/") == 0) + { + lpUrlComponents->lpszUrlPath = (LPWSTR)TEXT(""); + lpUrlComponents->dwUrlPathLength = 0; + } + else + { + lpUrlComponents->lpszUrlPath = const_cast(pwszUrl) + pos; + lpUrlComponents->dwUrlPathLength = strlen(path); + } + } + } + curl_free(path); + } + char *query; + rc = curl_url_get(url, CURLUPART_QUERY, &query, 0); + if (!rc) { + size_t pos = urlstr.find(query); + if (pos != std::string::npos) + { + if (lpUrlComponents->dwExtraInfoLength != (DWORD)-1) + { + if (lpUrlComponents->dwExtraInfoLength >= (strlen(query) + 1)) { + WCTNCPY(lpUrlComponents->lpszExtraInfo, (TCHAR*)pwszUrl + pos - 1, strlen(query)); + lpUrlComponents->lpszExtraInfo[strlen(query)] = TEXT('\0'); + lpUrlComponents->dwExtraInfoLength = strlen(query); + } + } + else + { + lpUrlComponents->lpszExtraInfo = const_cast(pwszUrl) + pos - 1; + lpUrlComponents->dwExtraInfoLength = strlen(query) + 1; + } + } + curl_free(query); + } + char *user; + rc = curl_url_get(url, CURLUPART_USER, &user, 0); + if (!rc) { + size_t pos = urlstr.find(user); + if (pos != std::string::npos) + { + if (lpUrlComponents->dwUserNameLength != (DWORD)-1) + { + if (lpUrlComponents->dwUserNameLength >= (strlen(user) + 1)) { + WCTNCPY(lpUrlComponents->lpszUserName, (TCHAR*)pwszUrl + pos - 1, strlen(user)); + lpUrlComponents->lpszUserName[strlen(user)] = TEXT('\0'); + lpUrlComponents->dwUserNameLength = strlen(user); + } + } + else + { + lpUrlComponents->lpszUserName = const_cast(pwszUrl) + pos - 1; + lpUrlComponents->dwUserNameLength = strlen(user); + } + } + curl_free(user); + } + char *pw; + rc = curl_url_get(url, CURLUPART_PASSWORD, &pw, 0); + if (!rc) { + size_t pos = urlstr.find(pw); + if (pos != std::string::npos) + { + if (lpUrlComponents->dwPasswordLength != (DWORD)-1) + { + if (lpUrlComponents->dwPasswordLength >= (strlen(pw) + 1)) { + WCTNCPY(lpUrlComponents->lpszPassword, (TCHAR*)pwszUrl + pos - 1, strlen(pw)); + lpUrlComponents->lpszPassword[strlen(pw)] = TEXT('\0'); + lpUrlComponents->dwPasswordLength = strlen(pw); + } + } + else + { + lpUrlComponents->lpszPassword = const_cast(pwszUrl) + pos - 1; + lpUrlComponents->dwPasswordLength = strlen(pw); + } + } + curl_free(pw); + } + curl_url_cleanup(url); + + return TRUE; +} + +#endif + + +BOOLAPI WinHttpWriteData +( + HINTERNET hRequest, + LPCVOID lpBuffer, + DWORD dwNumberOfBytesToWrite, + LPDWORD lpdwNumberOfBytesWritten +) +{ + WinHttpRequestImp *request = static_cast(hRequest); + if (!request) + return FALSE; + + std::shared_ptr srequest = request->shared_from_this(); + if (!srequest) + return FALSE; + + if (request->GetClosing()) + { + TRACE("%s:%d \n", __func__, __LINE__); + return FALSE; + } + + TRACE("%s:%d dwNumberOfBytesToWrite:%lu\n", __func__, __LINE__, dwNumberOfBytesToWrite); + request->AppendReadData(lpBuffer, dwNumberOfBytesToWrite); + + { + std::lock_guard lck(request->GetReadDataEventMtx()); + request->GetReadDataEventState() = true; + request->GetReadDataEvent().notify_all(); + } + + if (request->GetAsync()) + ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); + + if (lpdwNumberOfBytesWritten) + *lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite; + + return TRUE; +} + diff --git a/Release/src/http/client/winhttppal/winhttppal.h b/Release/src/http/client/winhttppal/winhttppal.h new file mode 100644 index 0000000000..57fc71236f --- /dev/null +++ b/Release/src/http/client/winhttppal/winhttppal.h @@ -0,0 +1,426 @@ +typedef void VOID; +typedef void *LPVOID; +typedef unsigned long DWORD; +typedef const void* LPCVOID; +typedef long LONG; +typedef unsigned char BYTE; +#define __int3264 long int +typedef unsigned __int3264 ULONG_PTR; +typedef ULONG_PTR DWORD_PTR; +typedef DWORD *PDWORD; +typedef DWORD *LPDWORD; +typedef char *LPSTR; +typedef const char *LPCSTR; + +#ifdef UNICODE +typedef wchar_t TCHAR; +typedef const wchar_t* LPCTSTR; +typedef wchar_t* LPTSTR, *PTSTR; +typedef const wchar_t* LPCTSTR, *PCTSTR; +#else +typedef char TCHAR; +typedef const char* LPCTSTR; +typedef char* LPTSTR, *PTSTR; +typedef const char* LPCTSTR, *PCTSTR; +#endif + +typedef LPVOID HINTERNET; +typedef bool BOOL; + +typedef VOID (* WINHTTP_STATUS_CALLBACK)( + HINTERNET hInternet, + DWORD_PTR dwContext, + DWORD dwInternetStatus, + LPVOID lpvStatusInformation, + DWORD dwStatusInformationLength +); + +enum +{ + API_RECEIVE_RESPONSE = 1, + API_QUERY_DATA_AVAILABLE, + API_READ_DATA, + API_WRITE_DATA, + API_SEND_REQUEST, +}; + +typedef struct +{ + DWORD_PTR dwResult; + DWORD dwError; +} +WINHTTP_ASYNC_RESULT, * LPWINHTTP_ASYNC_RESULT; + +typedef struct +{ + DWORD dwMajorVersion; + DWORD dwMinorVersion; +} +HTTP_VERSION_INFO; + +#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0 + +enum +{ + WINHTTP_QUERY_RAW_HEADERS, + WINHTTP_QUERY_STATUS_CODE, + WINHTTP_QUERY_VERSION, + WINHTTP_QUERY_RAW_HEADERS_CRLF, + WINHTTP_QUERY_STATUS_TEXT, + WINHTTP_QUERY_FLAG_NUMBER = 0x80000000, +}; + +enum +{ + WINHTTP_AUTH_SCHEME_NEGOTIATE, + WINHTTP_AUTH_SCHEME_NTLM, + WINHTTP_AUTH_SCHEME_PASSPORT, + WINHTTP_AUTH_SCHEME_DIGEST, + WINHTTP_AUTH_SCHEME_BASIC, + WINHTTP_AUTH_TARGET_PROXY, + WINHTTP_AUTH_TARGET_SERVER, +}; + +#define WINHTTP_INVALID_STATUS_CALLBACK (WINHTTP_STATUS_CALLBACK)-1 + +#define WINHTTP_NO_REFERER NULL +#define WINHTTP_DEFAULT_ACCEPT_TYPES NULL +#define WINHTTP_NO_ADDITIONAL_HEADERS NULL +#define WINHTTP_NO_REQUEST_DATA NULL +#define WINHTTP_NO_PROXY_NAME NULL +#define WINHTTP_NO_PROXY_BYPASS NULL +#define WINHTTP_NO_HEADER_INDEX NULL +#define WINHTTP_HEADER_NAME_BY_INDEX NULL +#define WINHTTP_NO_OUTPUT_BUFFER NULL + +#define WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH 0 +#define WINHTTP_ADDREQ_FLAG_ADD 0 +#define WINHTTP_ENABLE_SSL_REVOCATION 0 + +enum +{ + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = 0x00080000, + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = 0x00100000, + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = 0x00200000, + WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = 0x00400000, + WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 = 0x00040000, + WINHTTP_FLAG_SECURE = 0x00800000, + WINHTTP_FLAG_ASYNC = 0x10000000, + WINHTTP_FLAG_REFRESH = 0x00000100, + WINHTTP_FLAG_ESCAPE_DISABLE = 0x20000000, +}; + +enum +{ + WINHTTP_CALLBACK_STATUS_READ_COMPLETE = (1 << 0), + WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE = (1 << 1), + WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE = (1 << 2), + WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE = (1 << 3), + WINHTTP_CALLBACK_STATUS_REQUEST_ERROR = (1 << 4), + WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING = (1 << 5), + WINHTTP_CALLBACK_STATUS_SECURE_FAILURE = (1 << 6), + WINHTTP_CALLBACK_STATUS_SENDING_REQUEST = (1 << 7), + WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE = (1 << 8), + WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED = (1 << 9), + WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT = (1 << 10), + WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED = (1 << 11), + WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA = (1 << 12), + WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID = (1 << 13), + WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID = (1 << 14), + WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR = (1 << 15), + WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE = (1 << 16), + WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED = (1 << 17), + WINHTTP_CALLBACK_STATUS_REQUEST_SENT = (1 << 18), + WINHTTP_CALLBACK_STATUS_REDIRECT = (1 << 19), + WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS = (1 << 24), + WINHTTP_CALLBACK_FLAG_HANDLES = (1 << 25), + WINHTTP_CALLBACK_FLAG_SECURE_FAILURE = (1 << 26), + WINHTTP_CALLBACK_FLAG_SEND_REQUEST = (1 << 27), +}; + +enum +{ + WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS = (1 << 0), +}; + +enum +{ + WINHTTP_OPTION_CONTEXT_VALUE = 1, + WINHTTP_OPTION_CONNECT_TIMEOUT, + WINHTTP_OPTION_CALLBACK, + WINHTTP_OPTION_URL, + WINHTTP_OPTION_HTTP_VERSION, + WINHTTP_OPTION_MAX_CONNS_PER_SERVER, + WINHTTP_OPTION_SECURE_PROTOCOLS, + WINHTTP_OPTION_PROXY, + WINHTTP_OPTION_AUTOLOGON_POLICY, + WINHTTP_OPTION_ENABLE_FEATURE, + WINHTTP_OPTION_SECURITY_FLAGS, +}; + +enum +{ + SECURITY_FLAG_IGNORE_UNKNOWN_CA, + SECURITY_FLAG_IGNORE_CERT_DATE_INVALID, + SECURITY_FLAG_IGNORE_CERT_CN_INVALID, + SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE, +}; +enum +{ + ERROR_WINHTTP_OPERATION_CANCELLED = 12017, +}; + +enum +{ + WINHTTP_ACCESS_TYPE_NAMED_PROXY = 1, + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY =2, + WINHTTP_ACCESS_TYPE_NO_PROXY = 3, + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY = 4, +}; + +typedef unsigned int INTERNET_PORT; + +#define INTERNET_DEFAULT_HTTP_PORT 80 + + +BOOL WinHttpCloseHandle( + HINTERNET hInternet +); + + +BOOL +WinHttpReceiveResponse +( + HINTERNET hRequest, + LPVOID lpReserved +); + +BOOL +WinHttpSetTimeouts +( + HINTERNET hInternet, // Session/Request handle. + int nResolveTimeout, + int nConnectTimeout, + int nSendTimeout, + int nReceiveTimeout +); + + +WINHTTP_STATUS_CALLBACK +WinHttpSetStatusCallback +( + HINTERNET hInternet, + WINHTTP_STATUS_CALLBACK lpfnInternetCallback, + DWORD dwNotificationFlags, + DWORD_PTR dwReserved +); + +BOOL WinHttpSetOption( + HINTERNET hInternet, + DWORD dwOption, + LPVOID lpBuffer, + DWORD dwBufferLength +); + +BOOL WinHttpSendRequest +( + HINTERNET hRequest, + LPCTSTR lpszHeaders, + DWORD dwHeadersLength, + LPVOID lpOptional, + DWORD dwOptionalLength, + DWORD dwTotalLength, + DWORD_PTR dwContext +); + +BOOL +WinHttpReadData +( + HINTERNET hRequest, + LPVOID lpBuffer, + DWORD dwNumberOfBytesToRead, + LPDWORD lpdwNumberOfBytesRead +); + +BOOL WinHttpQueryHeaders( + HINTERNET hRequest, + DWORD dwInfoLevel, + LPCTSTR pwszName, + LPVOID lpBuffer, + LPDWORD lpdwBufferLength, + LPDWORD lpdwIndex +); + + +BOOL +WinHttpQueryDataAvailable +( + HINTERNET hRequest, + LPDWORD lpdwNumberOfBytesAvailable +); + + +HINTERNET WinHttpOpenRequest +( + HINTERNET hConnect, + LPCTSTR pwszVerb, + LPCTSTR pwszObjectName, + LPCTSTR pwszVersion, + LPCTSTR pwszReferrer, + LPCTSTR * ppwszAcceptTypes, + DWORD dwFlags +); + +HINTERNET WinHttpOpen +( + LPCTSTR pszAgentW, + DWORD dwAccessType, + LPCTSTR pszProxyW, + LPCTSTR pszProxyBypassW, + DWORD dwFlags +); + +HINTERNET WinHttpConnect +( + HINTERNET hSession, + LPCTSTR pswzServerName, + INTERNET_PORT nServerPort, + DWORD dwReserved +); + +BOOL +WinHttpAddRequestHeaders +( + HINTERNET hRequest, + LPCTSTR lpszHeaders, + DWORD dwHeadersLength, + DWORD dwModifiers +); + +BOOL +WinHttpQueryOption +( + HINTERNET hInternet, + DWORD dwOption, + LPVOID lpBuffer, + LPDWORD lpdwBufferLength +); + +BOOL WinHttpWriteData( + HINTERNET hRequest, + LPCVOID lpBuffer, + DWORD dwNumberOfBytesToWrite, + LPDWORD lpdwNumberOfBytesWritten +); + +#define ARRAYSIZE(n) sizeof(n)/sizeof(n[0]) + +#define GetLastError() errno +#define CALLBACK + + +typedef struct { + DWORD dwAccessType; + LPTSTR lpszProxy; + LPTSTR lpszProxyBypass; +} WINHTTP_PROXY_INFO, *LPWINHTTP_PROXY_INFO; + +typedef struct { + BOOL fAutoDetect; + LPTSTR lpszAutoConfigUrl; + LPTSTR lpszProxy; + LPTSTR lpszProxyBypass; +} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; + +typedef struct { + DWORD dwFlags; + DWORD dwAutoDetectFlags; + LPCTSTR lpszAutoConfigUrl; + LPVOID lpvReserved; + DWORD dwReserved; + BOOL fAutoLogonIfChallenged; +} WINHTTP_AUTOPROXY_OPTIONS; + +BOOL WinHttpGetProxyForUrl( + HINTERNET hSession, + LPCTSTR lpcwszUrl, + WINHTTP_AUTOPROXY_OPTIONS *pAutoProxyOptions, + WINHTTP_PROXY_INFO *pProxyInfo +); + + +BOOL WinHttpQueryAuthSchemes( + HINTERNET hRequest, + LPDWORD lpdwSupportedSchemes, + LPDWORD lpdwFirstScheme, + LPDWORD pdwAuthTarget +); + +BOOL WinHttpSetCredentials( + HINTERNET hRequest, + DWORD AuthTargets, + DWORD AuthScheme, + LPCTSTR pwszUserName, + LPCTSTR pwszPassword, + LPVOID pAuthParams +); + +enum +{ + WINHTTP_AUTOPROXY_AUTO_DETECT, + WINHTTP_AUTO_DETECT_TYPE_DHCP, + WINHTTP_AUTO_DETECT_TYPE_DNS_A, + WINHTTP_AUTOPROXY_CONFIG_URL, +}; + + +#define GlobalFree free + +#define FALSE 0 +#define TRUE 1 + +#define INTERNET_DEFAULT_HTTPS_PORT 443 +#define S_OK 0 + +#define ERROR_INSUFFICIENT_BUFFER ENOMEM +#define ERROR_WINHTTP_RESEND_REQUEST EBUSY +#define ERROR_SUCCESS 0 +#define ERROR_OPERATION_ABORTED EINVAL +#define ERROR_NOT_ENOUGH_MEMORY ENOMEM +#define ERROR_WINHTTP_TIMEOUT ETIMEDOUT +#define ERROR_INVALID_PARAMETER EINVAL + +#include + +template +std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +#define BOOLAPI BOOL +#define SetLastError(val) errno = val +#define WINHTTPAPI +#define WINAPI + +typedef enum { + INTERNET_SCHEME_HTTP = 1, + INTERNET_SCHEME_HTTPS, +} INTERNET_SCHEME; + +typedef struct { + DWORD dwStructSize; + LPTSTR lpszScheme; + DWORD dwSchemeLength; + INTERNET_SCHEME nScheme; + LPTSTR lpszHostName; + DWORD dwHostNameLength; + INTERNET_PORT nPort; + LPTSTR lpszUserName; + DWORD dwUserNameLength; + LPTSTR lpszPassword; + DWORD dwPasswordLength; + LPTSTR lpszUrlPath; + DWORD dwUrlPathLength; + LPTSTR lpszExtraInfo; + DWORD dwExtraInfoLength; +} URL_COMPONENTS, *LPURL_COMPONENTS; diff --git a/Release/tests/functional/http/client/client_construction.cpp b/Release/tests/functional/http/client/client_construction.cpp index 1229b2cfd7..f5d20b8379 100644 --- a/Release/tests/functional/http/client/client_construction.cpp +++ b/Release/tests/functional/http/client/client_construction.cpp @@ -173,7 +173,7 @@ SUITE(client_construction) VERIFY_ARE_EQUAL(baseclient2.base_uri(), m_uri); } -#if !defined(_WIN32) && !defined(__cplusplus_winrt) +#if defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) // Verify that the callback of sslcontext is called for HTTPS TEST_FIXTURE(uri_address, ssl_context_callback_https) diff --git a/Release/tests/functional/http/client/request_uri_tests.cpp b/Release/tests/functional/http/client/request_uri_tests.cpp index c856b600e3..18a5555c3f 100644 --- a/Release/tests/functional/http/client/request_uri_tests.cpp +++ b/Release/tests/functional/http/client/request_uri_tests.cpp @@ -88,7 +88,7 @@ SUITE(request_uri_tests) // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/heheh?key1=value2#fragment"); -#ifdef __cplusplus_winrt +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_CURL) expected_value = percent_encode_pound(expected_value); #endif @@ -137,7 +137,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1?key1=value1&key2=value2#frag"); -#ifdef __cplusplus_winrt +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_CURL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); @@ -157,7 +157,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1/path2?key2=value2#fragmentfg2"); -#ifdef __cplusplus_winrt +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_CURL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); From 606565f142c98c7697d9db8a365ec6183c39baed Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Wed, 10 Jul 2019 18:08:45 -0400 Subject: [PATCH 02/41] Include buffer request for CURL based client --- Release/include/cpprest/http_client.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index 0bedfbd42a..3227f0199b 100644 --- a/Release/include/cpprest/http_client.h +++ b/Release/include/cpprest/http_client.h @@ -104,7 +104,7 @@ class http_client_config #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) , m_tlsext_sni_enabled(true) #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) , m_buffer_request(false) #endif { @@ -262,7 +262,7 @@ class http_client_config void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; } #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) /// /// Checks if request data buffering is turned on, the default is off. /// @@ -389,7 +389,7 @@ class http_client_config std::function m_ssl_context_callback; bool m_tlsext_sni_enabled; #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) bool m_buffer_request; #endif }; From ea3a590ead315d9b29fea8f78bf914239451d187 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Thu, 11 Jul 2019 23:45:40 -0400 Subject: [PATCH 03/41] Rename to curl_xyz functions and remove if 0s --- Release/src/http/client/http_client_curl.cpp | 240 ++++++------------- 1 file changed, 67 insertions(+), 173 deletions(-) diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp index c0a4321121..b091a53daa 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_curl.cpp @@ -150,7 +150,7 @@ static void parse_headers_string(_Inout_z_ TCHAR* headersStr, web::http::http_he /// /// Parses a string containing HTTP headers. /// -static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ TCHAR* headersStr, http_response& response) +static void parse_curl_headers(HINTERNET request_handle, _In_z_ TCHAR* headersStr, http_response& response) { // Clear the header map for each new response; otherwise, the header values will be combined. response.headers().clear(); @@ -227,19 +227,19 @@ enum msg_body_type }; // Additional information necessary to track a WinHTTP request. -class winhttp_request_context final : public request_context +class curl_request_context final : public request_context { public: // Factory function to create requests on the heap. static std::shared_ptr create_request_context( const std::shared_ptr<_http_client_communicator>& client, const http_request& request) { - std::shared_ptr ret(new winhttp_request_context(client, request)); + std::shared_ptr ret(new curl_request_context(client, request)); ret->m_self_reference = ret; return std::move(ret); } - ~winhttp_request_context() { cleanup(); } + ~curl_request_context() { cleanup(); } void allocate_request_space(_In_opt_ uint8_t* block, size_t length) { @@ -260,7 +260,7 @@ class winhttp_request_context final : public request_context bool is_externally_allocated() const { return !m_body_data.is_internally_allocated(); } HINTERNET m_request_handle; - std::weak_ptr* + std::weak_ptr* m_request_handle_context; // owned by m_request_handle to be delete'd by WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING bool m_proxy_authentication_tried; @@ -280,7 +280,7 @@ class winhttp_request_context final : public request_context virtual concurrency::streams::streambuf _get_readbuffer() { return m_readStream.streambuf(); } // This self reference will keep us alive until finish() is called. - std::shared_ptr m_self_reference; + std::shared_ptr m_self_reference; memory_holder m_body_data; // Compress/decompress-related processing state lives here @@ -577,7 +577,7 @@ class winhttp_request_context final : public request_context std::vector m_cachedEncodedCert; // Can only create on the heap using factory function. - winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) + curl_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) : request_context(client, request) , m_request_handle(nullptr) , m_proxy_authentication_tried(false) @@ -591,32 +591,6 @@ class winhttp_request_context final : public request_context } }; - -#if 0 -static DWORD ChooseAuthScheme(DWORD dwSupportedSchemes) -{ - // It is the server's responsibility only to accept - // authentication schemes that provide a sufficient - // level of security to protect the servers resources. - // - // The client is also obligated only to use an authentication - // scheme that adequately protects its username and password. - // - if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE) - return WINHTTP_AUTH_SCHEME_NEGOTIATE; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM) - return WINHTTP_AUTH_SCHEME_NTLM; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT) - return WINHTTP_AUTH_SCHEME_PASSPORT; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST) - return WINHTTP_AUTH_SCHEME_DIGEST; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_BASIC) - return WINHTTP_AUTH_SCHEME_BASIC; - else - return 0; -} -#endif - // Small RAII helper to ensure that the fields of this struct are always // properly freed. struct proxy_info : WINHTTP_PROXY_INFO @@ -643,11 +617,11 @@ struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG }; -// WinHTTP client. -class winhttp_client final : public _http_client_communicator +// CURL client. +class curl_client final : public _http_client_communicator { public: - winhttp_client(http::uri address, http_client_config client_config) + curl_client(http::uri address, http_client_config client_config) : _http_client_communicator(std::move(address), std::move(client_config)) , m_opened(false) , m_hSession(nullptr) @@ -656,11 +630,11 @@ class winhttp_client final : public _http_client_communicator { } - winhttp_client(const winhttp_client&) = delete; - winhttp_client& operator=(const winhttp_client&) = delete; + curl_client(const curl_client&) = delete; + curl_client& operator=(const curl_client&) = delete; // Closes session. - ~winhttp_client() + ~curl_client() { if (m_hConnection != nullptr) { @@ -679,7 +653,7 @@ class winhttp_client final : public _http_client_communicator virtual pplx::task propagate(http_request request) override { auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this()); - auto context = details::winhttp_request_context::create_request_context(self, request); + auto context = details::curl_request_context::create_request_context(self, request); // Use a task to externally signal the final result and completion of the task. auto result_task = pplx::create_task(context->m_request_completion); @@ -802,7 +776,7 @@ class winhttp_client final : public _http_client_communicator // Register asynchronous callback. if (WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback(m_hSession, - &winhttp_client::completion_callback, + &curl_client::completion_callback, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_HANDLES | WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, 0)) @@ -843,9 +817,9 @@ class winhttp_client final : public _http_client_communicator http_request& msg = request->m_request; http_headers& headers = msg.headers(); - std::shared_ptr winhttp_context = - std::static_pointer_cast(request); - std::weak_ptr weak_winhttp_context = winhttp_context; + std::shared_ptr curl_context = + std::static_pointer_cast(request); + std::weak_ptr weak_curl_context = curl_context; proxy_info info; bool proxy_info_required = false; @@ -878,18 +852,6 @@ class winhttp_client final : public _http_client_communicator } autoproxy_options.fAutoLogonIfChallenged = TRUE; - -#if 0 - auto result = WinHttpGetProxyForUrl(m_hSession, m_uri.to_string().c_str(), &autoproxy_options, &info); - if (result) - { - proxy_info_required = true; - } - else - { - // Failure to download the auto-configuration script is not fatal. Fall back to the default proxy. - } -#endif } // Need to form uri path, query, and fragment for this request. @@ -898,9 +860,9 @@ class winhttp_client final : public _http_client_communicator http::uri_builder(m_uri).append(msg.relative_uri()).to_uri().resource().to_string(); // Open the request. - winhttp_context->m_request_handle_context = new std::weak_ptr(winhttp_context); + curl_context->m_request_handle_context = new std::weak_ptr(curl_context); - winhttp_context->m_request_handle = + curl_context->m_request_handle = WinHttpOpenRequest(m_hConnection, msg.method().c_str(), encoded_resource.c_str(), @@ -908,25 +870,25 @@ class winhttp_client final : public _http_client_communicator WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_ESCAPE_DISABLE | (m_secure ? WINHTTP_FLAG_SECURE : 0)); - if (winhttp_context->m_request_handle == nullptr) + if (curl_context->m_request_handle == nullptr) { printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); - delete winhttp_context->m_request_handle_context; - winhttp_context->m_request_handle_context = 0; + delete curl_context->m_request_handle_context; + curl_context->m_request_handle_context = 0; request->report_error(errorCode, build_error_msg(errorCode, "WinHttpOpenRequest")); return; } - if (!WinHttpSetOption(winhttp_context->m_request_handle, + if (!WinHttpSetOption(curl_context->m_request_handle, WINHTTP_OPTION_CONTEXT_VALUE, - &winhttp_context->m_request_handle_context, + &curl_context->m_request_handle_context, sizeof(void*))) { printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); - delete winhttp_context->m_request_handle_context; - winhttp_context->m_request_handle_context = 0; + delete curl_context->m_request_handle_context; + curl_context->m_request_handle_context = 0; request->report_error(errorCode, build_error_msg(errorCode, "WinHttpSetOption request context")); return; } @@ -934,7 +896,7 @@ class winhttp_client final : public _http_client_communicator if (proxy_info_required) { auto result = WinHttpSetOption( - winhttp_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO)); + curl_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO)); if (!result) { printf("%s:%d\n", __func__, __LINE__); @@ -952,7 +914,7 @@ class winhttp_client final : public _http_client_communicator DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; auto result = WinHttpSetOption( - winhttp_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data)); + curl_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data)); if (!result) { printf("%s:%d\n", __func__, __LINE__); @@ -976,7 +938,7 @@ class winhttp_client final : public _http_client_communicator const auto& requestHost = hostHeader->second; if (!utility::details::str_iequal(requestHost, m_uri.host())) { - winhttp_context->install_custom_cn_check(requestHost); + curl_context->install_custom_cn_check(requestHost); ignoredCertificateValidationSteps = SECURITY_FLAG_IGNORE_CERT_CN_INVALID; } } @@ -988,7 +950,7 @@ class winhttp_client final : public _http_client_communicator SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; } - if (ignoredCertificateValidationSteps && !WinHttpSetOption(winhttp_context->m_request_handle, + if (ignoredCertificateValidationSteps && !WinHttpSetOption(curl_context->m_request_handle, WINHTTP_OPTION_SECURITY_FLAGS, &ignoredCertificateValidationSteps, sizeof(ignoredCertificateValidationSteps))) @@ -1024,28 +986,28 @@ class winhttp_client final : public _http_client_communicator // The content length is not set and the application set a stream. This is an // indication that we will use transfer encoding chunked. We still want to // know that stream's effective length if possible for memory efficiency. - winhttp_context->m_bodyType = transfer_encoding_chunked; - winhttp_context->m_remaining_to_write = msg._get_impl()->_get_stream_length(); + curl_context->m_bodyType = transfer_encoding_chunked; + curl_context->m_remaining_to_write = msg._get_impl()->_get_stream_length(); } else { // While we won't be transfer-encoding the data, we will write it in portions. - winhttp_context->m_bodyType = content_length_chunked; - winhttp_context->m_remaining_to_write = content_length; + curl_context->m_bodyType = content_length_chunked; + curl_context->m_remaining_to_write = content_length; } } utility::string_t flattened_headers = web::http::details::flatten_http_headers(headers); - if (winhttp_context->m_request.method() == http::methods::GET) + if (curl_context->m_request.method() == http::methods::GET) { // Prepare to request a compressed response from the server if necessary. - flattened_headers += winhttp_context->get_compression_header(); + flattened_headers += curl_context->get_compression_header(); } // Add headers. if (!flattened_headers.empty()) { - if (!WinHttpAddRequestHeaders(winhttp_context->m_request_handle, + if (!WinHttpAddRequestHeaders(curl_context->m_request_handle, flattened_headers.c_str(), static_cast(flattened_headers.length()), WINHTTP_ADDREQ_FLAG_ADD)) @@ -1061,12 +1023,12 @@ class winhttp_client final : public _http_client_communicator if (msg._cancellation_token() != pplx::cancellation_token::none()) { // cancellation callback is unregistered when request is completed. - winhttp_context->m_cancellationRegistration = - msg._cancellation_token().register_callback([weak_winhttp_context]() { + curl_context->m_cancellationRegistration = + msg._cancellation_token().register_callback([weak_curl_context]() { // Call the WinHttpSendRequest API after WinHttpCloseHandle will give invalid handle error and we // throw this exception. Call the cleanup to make the m_request_handle as nullptr, otherwise, // Application Verifier will give AV exception on m_request_handle. - auto lock = weak_winhttp_context.lock(); + auto lock = weak_curl_context.lock(); if (!lock) return; lock->cleanup(); }); @@ -1075,7 +1037,7 @@ class winhttp_client final : public _http_client_communicator // Call the callback function of user customized options. try { - client_config().invoke_nativehandle_options(winhttp_context->m_request_handle); + client_config().invoke_nativehandle_options(curl_context->m_request_handle); } catch (...) { @@ -1084,57 +1046,57 @@ class winhttp_client final : public _http_client_communicator } // Only need to cache the request body if user specified and the request stream doesn't support seeking. - if (winhttp_context->m_bodyType != no_body && client_config().buffer_request() && - !winhttp_context->_get_readbuffer().can_seek()) + if (curl_context->m_bodyType != no_body && client_config().buffer_request() && + !curl_context->_get_readbuffer().can_seek()) { - winhttp_context->m_readBufferCopy = + curl_context->m_readBufferCopy = ::utility::details::make_unique<::concurrency::streams::container_buffer>>(); } - _start_request_send(winhttp_context, content_length); + _start_request_send(curl_context, content_length); return; } private: - void _start_request_send(const std::shared_ptr& winhttp_context, size_t content_length) + void _start_request_send(const std::shared_ptr& curl_context, size_t content_length) { DWORD totalLength; - if (winhttp_context->m_bodyType == no_body) + if (curl_context->m_bodyType == no_body) { totalLength = 0; } else { // Capture the current read position of the stream. - auto rbuf = winhttp_context->_get_readbuffer(); + auto rbuf = curl_context->_get_readbuffer(); // Record starting position in case request is challenged for authorization // and needs to seek back to where reading is started from. - winhttp_context->m_startingPosition = rbuf.getpos(std::ios_base::in); + curl_context->m_startingPosition = rbuf.getpos(std::ios_base::in); // If we find ourselves here, we either don't know how large the message - totalLength = winhttp_context->m_bodyType == content_length_chunked ? (DWORD)content_length + totalLength = curl_context->m_bodyType == content_length_chunked ? (DWORD)content_length : WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH; } - const auto requestSuccess = WinHttpSendRequest(winhttp_context->m_request_handle, + const auto requestSuccess = WinHttpSendRequest(curl_context->m_request_handle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, nullptr, 0, totalLength, - (DWORD_PTR)winhttp_context->m_request_handle_context); + (DWORD_PTR)curl_context->m_request_handle_context); if (!requestSuccess) { printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); - winhttp_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest")); + curl_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest")); } } // Helper function to query/read next part of response data from winhttp. - static void read_next_response_chunk(winhttp_request_context* pContext, DWORD bytesRead, bool firstRead = false) + static void read_next_response_chunk(curl_request_context* pContext, DWORD bytesRead, bool firstRead = false) { const bool defaultChunkSize = pContext->m_http_client->client_config().is_default_chunksize(); @@ -1187,7 +1149,7 @@ class winhttp_client final : public _http_client_communicator } } - static void _transfer_encoding_chunked_write_data(_In_ winhttp_request_context* p_request_context) + static void _transfer_encoding_chunked_write_data(_In_ curl_request_context* p_request_context) { size_t chunk_size; std::unique_ptr& compressor = p_request_context->m_request.compressor(); @@ -1497,7 +1459,7 @@ class winhttp_client final : public _http_client_communicator } } - static void _multiple_segment_write_data(_In_ winhttp_request_context* p_request_context) + static void _multiple_segment_write_data(_In_ curl_request_context* p_request_context) { auto rbuf = p_request_context->_get_readbuffer(); msl::safeint3::SafeInt safeCount = p_request_context->m_remaining_to_write; @@ -1615,7 +1577,7 @@ class winhttp_client final : public _http_client_communicator // Returns true if we handle successfully and resending the request // or false if we fail to handle. static bool handle_authentication_failure(HINTERNET hRequestHandle, - const std::shared_ptr& p_request_context, + const std::shared_ptr& p_request_context, _In_ DWORD error = 0) { http_request& request = p_request_context->m_request; @@ -1648,78 +1610,10 @@ class winhttp_client final : public _http_client_communicator } } } - p_request_context->m_compression_state = winhttp_request_context::compression_state(); + p_request_context->m_compression_state = curl_request_context::compression_state(); // If we got ERROR_WINHTTP_RESEND_REQUEST, the response header is not available, // we cannot call WinHttpQueryAuthSchemes and WinHttpSetCredentials. -#if 0 - if (error != ERROR_WINHTTP_RESEND_REQUEST) - { - // Obtain the supported and preferred schemes. - DWORD dwSupportedSchemes; - DWORD dwFirstScheme; - DWORD dwAuthTarget; - if (!WinHttpQueryAuthSchemes(hRequestHandle, &dwSupportedSchemes, &dwFirstScheme, &dwAuthTarget)) - { - // This will return the authentication failure to the user, without reporting fatal errors - return false; - } - - DWORD dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes); - if (dwSelectedScheme == 0) - { - // This will return the authentication failure to the user, without reporting fatal errors - return false; - } - - credentials cred; - if (dwAuthTarget == WINHTTP_AUTH_TARGET_SERVER && !p_request_context->m_server_authentication_tried) - { - cred = p_request_context->m_http_client->client_config().credentials(); - p_request_context->m_server_authentication_tried = true; - } - else if (dwAuthTarget == WINHTTP_AUTH_TARGET_PROXY) - { - bool is_redirect = false; - try - { - web::uri current_uri(get_request_url(hRequestHandle)); - is_redirect = p_request_context->m_request.absolute_uri().to_string() != current_uri.to_string(); - } - catch (const std::exception&) - { - } - - // If we have been redirected, then WinHttp needs the proxy credentials again to make the next request - // leg (which may be on a different server) - if (is_redirect || !p_request_context->m_proxy_authentication_tried) - { - cred = p_request_context->m_http_client->client_config().proxy().credentials(); - p_request_context->m_proxy_authentication_tried = true; - } - } - - // No credentials found so can't resend. - if (!cred.is_set()) - { - return false; - } - - // New scope to ensure plaintext password is cleared as soon as possible. - { - auto password = cred._internal_decrypt(); - if (!WinHttpSetCredentials(hRequestHandle, - dwAuthTarget, - dwSelectedScheme, - cred.username().c_str(), - password->c_str(), - nullptr)) - { - return false; - } - } - } -#endif // Reset the request body type since it might have already started sending. size_t content_length; try @@ -1754,7 +1648,7 @@ class winhttp_client final : public _http_client_communicator } // We're good. - winhttp_client* winclnt = reinterpret_cast(p_request_context->m_http_client.get()); + curl_client* winclnt = reinterpret_cast(p_request_context->m_http_client.get()); winclnt->_start_request_send(p_request_context, content_length); // We will not complete the request. Instead wait for the response to the request that was resent @@ -1767,8 +1661,8 @@ class winhttp_client final : public _http_client_communicator { CASABLANCA_UNREFERENCED_PARAMETER(statusInfoLength); - std::weak_ptr* p_weak_request_context = - reinterpret_cast*>(context); + std::weak_ptr* p_weak_request_context = + reinterpret_cast*>(context); if (p_weak_request_context == nullptr) { @@ -1933,7 +1827,7 @@ class winhttp_client final : public _http_client_communicator } http_response& response = p_request_context->m_response; - parse_winhttp_headers(hRequestHandle, header_buffer, response); + parse_curl_headers(hRequestHandle, header_buffer, response); if (response.status_code() == status_codes::Unauthorized /*401*/ || response.status_code() == status_codes::ProxyAuthRequired /*407*/) @@ -1956,7 +1850,7 @@ class winhttp_client final : public _http_client_communicator !p_request_context->m_http_client->client_config().request_compressed_response()) { p_request_context->m_compression_state.m_chunk = - make_unique(); + make_unique(); p_request_context->m_compression_state.m_chunked = true; } @@ -2116,7 +2010,7 @@ class winhttp_client final : public _http_client_communicator // Oddly enough, WinHttp doesn't de-chunk for us if "chunked" isn't the only // encoding, so we need to do so on the fly as we process the received data auto process_buffer = - [chunk_size](winhttp_request_context* c, size_t bytes_produced, bool outer) -> bool { + [chunk_size](curl_request_context* c, size_t bytes_produced, bool outer) -> bool { if (!c->m_compression_state.m_chunk_bytes) { if (c->m_compression_state.m_chunked) @@ -2248,7 +2142,7 @@ class winhttp_client final : public _http_client_communicator .then([p_request_context, buffer, chunk_size, process_buffer]( pplx::task op) { auto r = op.get(); - auto keep_going = [&r, process_buffer](winhttp_request_context* c) -> pplx::task { + auto keep_going = [&r, process_buffer](curl_request_context* c) -> pplx::task { _ASSERTE(r.input_bytes_processed <= c->m_compression_state.m_chunk_bytes); c->m_compression_state.m_chunk_bytes -= r.input_bytes_processed; c->m_compression_state.m_bytes_processed += r.input_bytes_processed; @@ -2369,7 +2263,7 @@ class winhttp_client final : public _http_client_communicator std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri, http_client_config&& client_config) { - return std::make_shared(std::move(base_uri), std::move(client_config)); + return std::make_shared(std::move(base_uri), std::move(client_config)); } From 281402946c7a53450d37ae84e21dfedb355d6f00 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Thu, 11 Jul 2019 23:56:20 -0400 Subject: [PATCH 04/41] Restore ifdef --- Release/src/http/client/http_client_impl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Release/src/http/client/http_client_impl.h b/Release/src/http/client/http_client_impl.h index 923961a0ee..b3f9329e3a 100644 --- a/Release/src/http/client/http_client_impl.h +++ b/Release/src/http/client/http_client_impl.h @@ -30,11 +30,12 @@ namespace details /// Serialize the http_headers into name:value pairs separated by a carriage return and line feed. /// utility::string_t flatten_http_headers(const http_headers& headers); +#if defined(_WIN32) /// /// Parses a string containing Http headers. /// void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers); - +#endif } // namespace details } // namespace http } // namespace web From ca838bebddd916ee5d0fc9914f32f7df9d99abb5 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Thu, 11 Jul 2019 23:59:17 -0400 Subject: [PATCH 05/41] Remove conflicting definitions --- Release/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 8d68817bd9..3df8be3c9e 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -165,7 +165,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS) elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") message("-- Setting gcc options") - set(WARNINGS -Wall -Wextra -Wunused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wno-unused-parameter -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code) + set(WARNINGS -Wall -Wextra -Wno-unused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code) set(LD_FLAGS "${LD_FLAGS} -Wl,-z,defs") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") From 2ceebcb10b566004879d3fd7c4f1c55e35279763 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Fri, 12 Jul 2019 00:11:06 -0400 Subject: [PATCH 06/41] Prefer utility::char_t to TCHAR --- Release/src/http/client/http_client_curl.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp index b091a53daa..1cf34ba3cb 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_curl.cpp @@ -128,9 +128,9 @@ static utility::string_t parse_reason_phrase(HINTERNET request_handle) } -static void parse_headers_string(_Inout_z_ TCHAR* headersStr, web::http::http_headers& headers) +static void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers) { - TCHAR* line = strtok (headersStr, "\r\n"); + utility::char_t* line = strtok (headersStr, "\r\n"); while (line != nullptr) { const utility::string_t header_line(line); @@ -150,7 +150,7 @@ static void parse_headers_string(_Inout_z_ TCHAR* headersStr, web::http::http_he /// /// Parses a string containing HTTP headers. /// -static void parse_curl_headers(HINTERNET request_handle, _In_z_ TCHAR* headersStr, http_response& response) +static void parse_curl_headers(HINTERNET request_handle, _In_z_ utility::char_t* headersStr, http_response& response) { // Clear the header map for each new response; otherwise, the header values will be combined. response.headers().clear(); @@ -1557,7 +1557,7 @@ class curl_client final : public _http_client_communicator auto urlSize = static_cast(url.capacity()) * 2; // use initial small string optimization capacity for (;;) { - url.resize(urlSize / sizeof(TCHAR)); + url.resize(urlSize / sizeof(utility::char_t)); if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize)) { url.resize(strlen(url.c_str())); @@ -1811,7 +1811,7 @@ class curl_client final : public _http_client_communicator // Now allocate buffer for headers and query for them. std::vector header_raw_buffer; header_raw_buffer.resize(headerBufferLength); - TCHAR* header_buffer = reinterpret_cast(&header_raw_buffer[0]); + utility::char_t* header_buffer = reinterpret_cast(&header_raw_buffer[0]); if (!WinHttpQueryHeaders(hRequestHandle, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, From bdc6efcbf58cb7ea59cb014cb12aa6fc02d840e7 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Fri, 12 Jul 2019 10:36:42 -0400 Subject: [PATCH 07/41] Bring writedata optimization by removing the intermediate buffering --- .../src/http/client/winhttppal/winhttppal.cpp | 148 +++++++++++------- 1 file changed, 93 insertions(+), 55 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index b61463de80..81c8f15114 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -456,6 +456,12 @@ class WinHttpConnectImp :public WinHttpBase void *GetUserData() { return m_UserBuffer; } }; +struct BufferRequest +{ + LPCVOID m_Buffer = NULL; + DWORD m_Length = 0; +}; + class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this { CURL *m_curl = NULL; @@ -470,8 +476,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::vector m_ReadData; std::mutex m_ReadDataEventMtx; - std::condition_variable m_ReadDataEvent; - bool m_ReadDataEventState = false; + DWORD m_ReadDataEventCounter = 0; THREAD_HANDLE m_UploadCallbackThread; bool m_UploadThreadExitStatus = false; @@ -520,8 +525,26 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; DWORD m_NotificationFlags = 0; + std::vector m_OutstandingWrites; public: + void QueueWriteRequest(LPCVOID buffer, DWORD length) + { + BufferRequest shr; + shr.m_Buffer = buffer; + shr.m_Length = length; + m_OutstandingWrites.push_back(shr); + } + + BufferRequest GetWriteRequest() + { + if (m_OutstandingWrites.empty()) + return BufferRequest(); + BufferRequest shr = m_OutstandingWrites.front(); + m_OutstandingWrites.pop_back(); + return shr; + } + long &VerifyPeer() { return m_VerifyPeer; } long &VerifyHost() { return m_VerifyHost; } @@ -595,9 +618,8 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this } // used to wake up CURL ReadCallback triggered on a upload request - std::condition_variable &GetReadDataEvent() { return m_ReadDataEvent; } std::mutex &GetReadDataEventMtx() { return m_ReadDataEventMtx; } - bool &GetReadDataEventState() { return m_ReadDataEventState; } + DWORD &GetReadDataEventCounter() { return m_ReadDataEventCounter; } // sent by WriteHeaderFunction and WriteBodyFunction during incoming data // to send user notifications for the following events: @@ -724,12 +746,12 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this } DWORD &GetTotalLength() { return m_TotalSize; } - DWORD &GetReceivedLength() { return m_TotalReceiveSize; } + DWORD &GetReadLength() { return m_TotalReceiveSize; } std::string &GetOptionalData() { return m_OptionalData; } void SetOptionalData(void *lpOptional, DWORD dwOptionalLength) { - m_OptionalData = std::string(&(reinterpret_cast(lpOptional))[0], dwOptionalLength); + m_OptionalData.assign(&(reinterpret_cast(lpOptional))[0], dwOptionalLength); } std::vector &GetReadData() { return m_ReadData; } @@ -1713,7 +1735,7 @@ void WinHttpRequestImp::CleanUp() m_TotalHeaderStringLength = 0; m_TotalReceiveSize = 0; m_ReadData.clear(); - m_ReadDataEventState = false; + m_ReadDataEventCounter = 0; m_UploadThreadExitStatus = false; m_ReceiveCompletionEventCounter = 0; m_CompletionEventState = false; @@ -1723,6 +1745,7 @@ void WinHttpRequestImp::CleanUp() m_ReceiveResponseEventCounter = 0; m_ReceiveResponseSendCounter = 0; m_DataAvailableEventCounter = 0; + m_OutstandingWrites.clear(); m_Completion = false; } @@ -1751,8 +1774,7 @@ WinHttpRequestImp::~WinHttpRequestImp() { { std::lock_guard lck(GetReadDataEventMtx()); - GetReadDataEventState() = true; - GetReadDataEvent().notify_all(); + GetReadDataEventCounter()++; } THREADJOIN(m_UploadCallbackThread); } @@ -1825,50 +1847,63 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi TRACE("%s:%d writing optional length of %lu\n", __func__, __LINE__, len); memcpy(ptr, request->GetOptionalData().c_str(), len); request->GetOptionalData().erase(0, len); - request->GetReceivedLength() += len; + request->GetReadLength() += len; return len; } if (request->GetClosed()) return -1; - request->GetReadDataEventMtx().lock(); - len = MIN(request->GetReadData().size(), size * nmemb); - request->GetReadDataEventMtx().unlock(); - - TRACE("%s:%d request->GetTotalLength():%lu request->GetReceivedLength():%lu\n", __func__, __LINE__, request->GetTotalLength(), request->GetReceivedLength()); + TRACE("%s:%d request->GetTotalLength():%lu request->GetReadLength():%lu\n", __func__, __LINE__, request->GetTotalLength(), request->GetReadLength()); if (((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && (request->GetType() == "PUT")) || - (request->GetTotalLength() != request->GetReceivedLength())) + (request->GetTotalLength() != request->GetReadLength())) { std::unique_lock getReadDataEventHndlMtx(request->GetReadDataEventMtx()); - if (!request->GetReadDataEventState()) + if (!request->GetReadDataEventCounter()) { - TRACE("%s:%d transfer paused:%lu\n", __func__, __LINE__, len); + TRACE("%s:%d transfer paused:%lu\n", __func__, __LINE__, size * nmemb); return CURL_READFUNC_PAUSE; } - request->GetReadDataEventState() = false; - TRACE("%s:%d transfer resumed as already signalled:%lu\n", __func__, __LINE__, len); + request->GetReadDataEventCounter()--; + TRACE("%s:%d transfer resumed as already signalled:%lu\n", __func__, __LINE__, size * nmemb); } - request->GetReadDataEventMtx().lock(); - len = MIN(request->GetReadData().size(), size * nmemb); - request->GetReadDataEventMtx().unlock(); - - TRACE("%s:%d writing additional length:%lu\n", __func__, __LINE__, len); - request->GetReadDataEventMtx().lock(); - memcpy(ptr, request->GetReadData().data(), len); - request->GetReceivedLength() += len; - request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); - request->GetReadData().shrink_to_fit(); - request->GetReadDataEventMtx().unlock(); - if (request->GetAsync()) { DWORD dwInternetStatus; - DWORD result = len; + DWORD result; + DWORD written = 0; + + { + request->GetReadDataEventMtx().lock(); + BufferRequest buf = request->GetWriteRequest(); + len = MIN(buf.m_Length, size * nmemb); + request->GetReadLength() += len; + request->GetReadDataEventMtx().unlock(); + + TRACE("%s:%d writing additional length:%lu written:%lu\n", __func__, __LINE__, len, written); + if (len) + memcpy(&((char*)ptr)[written], buf.m_Buffer, len); + + result = len; + dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; + request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); + + written += len; + } - dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; - request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); + len = written; + } + else + { + request->GetReadDataEventMtx().lock(); + len = MIN(request->GetReadData().size(), size * nmemb); + TRACE("%s:%d writing additional length:%lu\n", __func__, __LINE__, len); + memcpy(ptr, request->GetReadData().data(), len); + request->GetReadLength() += len; + request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); + request->GetReadData().shrink_to_fit(); + request->GetReadDataEventMtx().unlock(); } return len; @@ -2628,8 +2663,7 @@ WinHttpReceiveResponse { { std::lock_guard lck(request->GetReadDataEventMtx()); - request->GetReadDataEventState() = true; - request->GetReadDataEvent().notify_all(); + request->GetReadDataEventCounter()++; TRACE("%s:%d\n", __func__, __LINE__); } @@ -2646,9 +2680,9 @@ WinHttpReceiveResponse { if (request->GetType() == "PUT") { - while (request->GetTotalLength() != request->GetReceivedLength()) { + while (request->GetTotalLength() != request->GetReadLength()) { if (request->GetUploadThreadExitStatus()) { - if (request->GetTotalLength() != request->GetReceivedLength()) { + if (request->GetTotalLength() != request->GetReadLength()) { SetLastError(ERROR_WINHTTP_OPERATION_CANCELLED); return FALSE; } @@ -3054,17 +3088,13 @@ BOOLAPI WinHttpQueryHeaders( if (dwInfoLevel == WINHTTP_QUERY_STATUS_TEXT) { CURLcode res; - DWORD retValue; - TCHAR responseCodeStr[30]; + DWORD responseCode; DWORD length; - res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &retValue); + res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &responseCode); if (res != CURLE_OK) return FALSE; - STNPRINTF(responseCodeStr, sizeof(responseCodeStr) - 1, TEXT("%lu"), retValue); - responseCodeStr[sizeof(responseCodeStr)/sizeof(responseCodeStr[0]) - 1] = TEXT('\0'); - std::lock_guard lck(request->GetHeaderStringMutex()); length = request->GetHeaderString().length(); @@ -3080,7 +3110,7 @@ BOOLAPI WinHttpQueryHeaders( length = request->GetHeaderString().length(); subject.resize(length + 1); - MultiByteToWideChar(CP_UTF8, 0, request->GetHeaderString().c_str(), -1, (LPWSTR)subject.c_str(), length); + MultiByteToWideChar(CP_UTF8, 0, request->GetHeaderString().c_str(), -1, &subject[0], length); subject[length] = TEXT('\0'); #else TSTRING &subject = request->GetHeaderString(); @@ -3089,7 +3119,7 @@ BOOLAPI WinHttpQueryHeaders( TSTRING regstr; regstr.append(TEXT("HTTP.*")); - regstr.append(responseCodeStr); + regstr.append(TO_STRING(responseCode)); TSTRING result = FindRegex(subject, regstr); if (result != TEXT("")) @@ -3115,7 +3145,7 @@ BOOLAPI WinHttpQueryHeaders( } else { - TSTRING retStr = StatusCodeMap[retValue]; + TSTRING retStr = StatusCodeMap[responseCode]; if (retStr == TEXT("")) return FALSE; @@ -3785,16 +3815,24 @@ BOOLAPI WinHttpWriteData } TRACE("%s:%d dwNumberOfBytesToWrite:%lu\n", __func__, __LINE__, dwNumberOfBytesToWrite); - request->AppendReadData(lpBuffer, dwNumberOfBytesToWrite); - + if (request->GetAsync()) { - std::lock_guard lck(request->GetReadDataEventMtx()); - request->GetReadDataEventState() = true; - request->GetReadDataEvent().notify_all(); - } + { + std::lock_guard lck(request->GetReadDataEventMtx()); + request->QueueWriteRequest(lpBuffer, dwNumberOfBytesToWrite); + request->GetReadDataEventCounter()++; + } - if (request->GetAsync()) ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); + } + else + { + request->AppendReadData(lpBuffer, dwNumberOfBytesToWrite); + { + std::lock_guard lck(request->GetReadDataEventMtx()); + request->GetReadDataEventCounter()++; + } + } if (lpdwNumberOfBytesWritten) *lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite; From e7858d7840e502d83d85d71d67c2860a59c73944 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Fri, 12 Jul 2019 10:55:32 -0400 Subject: [PATCH 08/41] Use std::string where possible --- .../src/http/client/winhttppal/winhttppal.cpp | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 81c8f15114..7e87fd157d 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -682,10 +682,10 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this } LPVOID GetUserData() { return m_UserBuffer; } - void SetFullPath(const char *server, const char *relative) + void SetFullPath(std::string &server, std::string &relative) { - m_FullPath.append(server, strlen(server)); - m_FullPath.append(relative, strlen(relative)); + m_FullPath.append(server); + m_FullPath.append(relative); } std::string &GetFullPath() { return m_FullPath; } std::string &GetType() { return m_Type; } @@ -730,11 +730,11 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this return TRUE; } - BOOL SetServer(const char *ServerName, int nServerPort) + BOOL SetServer(std::string &ServerName, int nServerPort) { CURLcode res; - res = curl_easy_setopt(GetCurl(), CURLOPT_URL, ServerName); + res = curl_easy_setopt(GetCurl(), CURLOPT_URL, ServerName.c_str()); if (res != CURLE_OK) return FALSE; @@ -2132,15 +2132,16 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( /* Advance index forward so the next iteration doesn't pick it up as well. */ index += 3; } - request->SetFullPath(server.c_str(), objectname.c_str()); - if (!request->SetServer(request->GetFullPath().c_str(), session->GetServerPort())) { + request->SetFullPath(server, objectname); + if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { return NULL; } } else { - request->SetFullPath(server.c_str(), ""); - if (!request->SetServer(request->GetFullPath().c_str(), session->GetServerPort())) { + std::string nullstr(""); + request->SetFullPath(server, nullstr); + if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { return NULL; } } @@ -2446,7 +2447,7 @@ BOOLAPI WinHttpSendRequest } if (dwOptionalLength == (DWORD)-1) - dwOptionalLength = strlen(request->GetOptionalData().c_str()); + dwOptionalLength = request->GetOptionalData().length(); DWORD totalsize = MAX(dwOptionalLength, dwTotalLength); /* provide the size of the upload, we specicially typecast the value @@ -3164,7 +3165,7 @@ BOOLAPI WinHttpQueryHeaders( request->GetHeaderStringMutex().lock(); length = request->GetHeaderString().length(); - header.append(request->GetHeaderString().c_str()); + header.append(request->GetHeaderString()); request->GetHeaderStringMutex().unlock(); if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) From 2b9e77850936ab33f3fb16154679ad4d317abc1e Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Fri, 12 Jul 2019 17:27:46 -0400 Subject: [PATCH 09/41] Unify trace functions and reduce dynamic_cast calls --- .../src/http/client/winhttppal/winhttppal.cpp | 202 +++++++----------- 1 file changed, 72 insertions(+), 130 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 7e87fd157d..46d9d26440 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -143,14 +143,11 @@ int gettimeofday(struct timeval * tp, struct timezone * tzp); std::mutex trcmtx; #ifndef _MSC_VER -static void TRACE(const char *fmt, ...) __attribute__ ((format (gnu_printf, 1, 2))); +static void TRACE_INTERNAL(const char *fmt, ...) __attribute__ ((format (gnu_printf, 1, 2))); #endif -static void TRACE(const char *fmt, ...) +static void TRACE_INTERNAL(const char *fmt, ...) { - if (!winhttp_tracing) - return; - std::lock_guard lck(trcmtx); va_list args; va_start(args, fmt); @@ -174,70 +171,48 @@ static void TRACE(const char *fmt, ...) va_end(args); } -#ifndef _MSC_VER -static void TRACE_VERBOSE(const char *fmt, ...) __attribute__ ((format (gnu_printf, 1, 2))); -#endif -static void TRACE_VERBOSE(const char *fmt, ...) -{ - if (!winhttp_tracing_verbose) - return; - - std::lock_guard lck(trcmtx); - struct timeval tv; - time_t nowtime; - struct tm *nowtm; - char tmbuf[64], buf[64]; +#define TRACE(fmt, ...) \ + do { if (winhttp_tracing) TRACE_INTERNAL(fmt, __VA_ARGS__); } while (0) - gettimeofday(&tv, NULL); - nowtime = tv.tv_sec; - nowtm = localtime(&nowtime); - strftime(tmbuf, sizeof tmbuf, "%H:%M:%S", nowtm); - snprintf(buf, sizeof buf, "%s.%06ld ", tmbuf, tv.tv_usec); - printf("%s", buf); - - va_list args; - va_start(args, fmt); - char szBuffer[512]; - vsnprintf(szBuffer, sizeof szBuffer -1, fmt, args); - szBuffer[sizeof(szBuffer)/sizeof(szBuffer[0]) - 1] = '\0'; - printf("%s", szBuffer); - va_end(args); -} +#define TRACE_VERBOSE(fmt, ...) \ + do { if (winhttp_tracing_verbose) TRACE_INTERNAL(fmt, __VA_ARGS__); } while (0) typedef void (*CompletionCb)(std::shared_ptr, DWORD status); -#define MUTEX_TYPE std::mutex +#define MUTEX_TYPE std::mutex #define MUTEX_SETUP(x) #define MUTEX_CLEANUP(x) -#define MUTEX_LOCK(x) x.lock() -#define MUTEX_UNLOCK(x) x.unlock() +#define MUTEX_LOCK(x) x.lock() +#define MUTEX_UNLOCK(x) x.unlock() #ifdef WIN32 -#define THREAD_ID GetCurrentThreadId() -#define THREAD_HANDLE HANDLE -#define THREADPARAM LPVOID +#define THREAD_ID GetCurrentThreadId() +#define THREAD_HANDLE HANDLE +#define THREADPARAM LPVOID #define CREATETHREAD(func, param, id) \ - CreateThread( \ - NULL, \ - 0, \ - (LPTHREAD_START_ROUTINE)func, \ - param, \ - 0, \ - id) -#define THREADJOIN(h) WaitForSingleObject(h, INFINITE); -#define THREADRETURN DWORD + CreateThread( \ + NULL, \ + 0, \ + (LPTHREAD_START_ROUTINE)func, \ + param, \ + 0, \ + id) + +#define THREADJOIN(h) WaitForSingleObject(h, INFINITE); +#define THREADRETURN DWORD #else -#define THREADPARAM void* -#define THREADRETURN void* +#define THREADPARAM void* +#define THREADRETURN void* +#define THREAD_ID pthread_self() +#define THREAD_HANDLE pthread_t +#define THREADJOIN(x) pthread_join(x, NULL) + typedef void* (*LPTHREAD_START_ROUTINE)(void *); -#define THREAD_ID pthread_self() -#define THREAD_HANDLE pthread_t static inline THREAD_HANDLE CREATETHREAD(LPTHREAD_START_ROUTINE func, LPVOID param, pthread_t *id) { pthread_t inc_x_thread; pthread_create(&inc_x_thread, NULL, func, param); return inc_x_thread; } -#define THREADJOIN(x) pthread_join(x, NULL) #endif void handle_error(const char *file, int lineno, const char *msg) @@ -1483,35 +1458,23 @@ UserCallbackContext::UserCallbackContext(std::shared_ptr &req WinHttpSessionImp *GetImp(WinHttpBase *base) { + WinHttpConnectImp *connect; WinHttpSessionImp *session; + WinHttpRequestImp *request; - if (dynamic_cast(base)) + if ((connect = dynamic_cast(base))) { - WinHttpConnectImp *connect = dynamic_cast(base); - - if (!connect) - return NULL; - session = connect->GetHandle(); } - else if (dynamic_cast(base)) + else if ((request = dynamic_cast(base))) { - WinHttpRequestImp *request = dynamic_cast(base); - - if (!request) - return NULL; - WinHttpConnectImp *connect = request->GetSession(); session = connect->GetHandle(); } - else if (dynamic_cast(base)) + else { session = dynamic_cast(base); - if (!session) - return NULL; } - else - return NULL; return session; } @@ -1845,7 +1808,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi { len = request->GetOptionalData().length(); TRACE("%s:%d writing optional length of %lu\n", __func__, __LINE__, len); - memcpy(ptr, request->GetOptionalData().c_str(), len); + std::copy(request->GetOptionalData().begin(), request->GetOptionalData().end(), (char*)ptr); request->GetOptionalData().erase(0, len); request->GetReadLength() += len; return len; @@ -1883,7 +1846,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi TRACE("%s:%d writing additional length:%lu written:%lu\n", __func__, __LINE__, len, written); if (len) - memcpy(&((char*)ptr)[written], buf.m_Buffer, len); + memcpy((char*)ptr, buf.m_Buffer, len); result = len; dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; @@ -1899,7 +1862,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi request->GetReadDataEventMtx().lock(); len = MIN(request->GetReadData().size(), size * nmemb); TRACE("%s:%d writing additional length:%lu\n", __func__, __LINE__, len); - memcpy(ptr, request->GetReadData().data(), len); + std::copy(request->GetReadData().begin(), request->GetReadData().begin() + len, (char*)ptr); request->GetReadLength() += len; request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); request->GetReadData().shrink_to_fit(); @@ -2139,7 +2102,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( } else { - std::string nullstr(""); + std::string nullstr(""); request->SetFullPath(server, nullstr); if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { return NULL; @@ -2894,7 +2857,7 @@ WinHttpReadData } if (readLength) { - memcpy(reinterpret_cast(lpBuffer), request->GetResponseString().data(), readLength); + std::copy(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength, reinterpret_cast(lpBuffer)); request->GetResponseString().erase(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength); request->GetResponseString().shrink_to_fit(); } @@ -2929,26 +2892,17 @@ WinHttpSetTimeouts ) { WinHttpBase *base = static_cast(hInternet); + WinHttpSessionImp *session; + WinHttpRequestImp *request; CURLcode res; TRACE("%s:%d\n", __func__, __LINE__); - if (dynamic_cast(base)) + if ((session = dynamic_cast(base))) { - WinHttpSessionImp *session; - - session = dynamic_cast(base); - if (!session) - return FALSE; - session->SetTimeout(nReceiveTimeout); } - else if (dynamic_cast(base)) + else if ((request = dynamic_cast(base))) { - WinHttpRequestImp *request = dynamic_cast(base); - - if (!request) - return FALSE; - res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, nReceiveTimeout); if (res != CURLE_OK) return FALSE; @@ -3295,25 +3249,24 @@ BOOLAPI WinHttpSetOption( if (dwOption == WINHTTP_OPTION_MAX_CONNS_PER_SERVER) { + WinHttpSessionImp *session; + WinHttpRequestImp *request; + if (dwBufferLength != sizeof(DWORD)) return FALSE; - if (dynamic_cast(base)) + if ((session = dynamic_cast(base))) { - WinHttpSessionImp *session = dynamic_cast(base); - - if (!session || !lpBuffer) + if (!lpBuffer) return FALSE; if (!session->SetMaxConnections(reinterpret_cast(lpBuffer))) return FALSE; return TRUE; } - else if (dynamic_cast(base)) + else if ((request = dynamic_cast(base))) { - WinHttpRequestImp *request = dynamic_cast(base); - - if (!request || !lpBuffer) + if (!lpBuffer) return FALSE; if (!request->SetMaxConnections(reinterpret_cast(lpBuffer))) @@ -3326,36 +3279,34 @@ BOOLAPI WinHttpSetOption( } else if (dwOption == WINHTTP_OPTION_CONTEXT_VALUE) { + WinHttpConnectImp *connect; + WinHttpSessionImp *session; + WinHttpRequestImp *request; + if (dwBufferLength != sizeof(void*)) return FALSE; - if (dynamic_cast(base)) + if ((connect = dynamic_cast(base))) { - WinHttpConnectImp *connect = dynamic_cast(base); - - if (!connect || !lpBuffer) + if (!lpBuffer) return FALSE; if (!connect->SetUserData(reinterpret_cast(lpBuffer))) return FALSE; return TRUE; } - else if (dynamic_cast(base)) + else if ((session = dynamic_cast(base))) { - WinHttpSessionImp *session = dynamic_cast(base); - - if (!session || !lpBuffer) + if (!lpBuffer) return FALSE; if (!session->SetUserData(reinterpret_cast(lpBuffer))) return FALSE; return TRUE; } - else if (dynamic_cast(base)) + else if ((request = dynamic_cast(base))) { - WinHttpRequestImp *request = dynamic_cast(base); - - if (!request || !lpBuffer) + if (!lpBuffer) return FALSE; if (!request->SetUserData(reinterpret_cast(lpBuffer))) @@ -3367,14 +3318,15 @@ BOOLAPI WinHttpSetOption( } else if (dwOption == WINHTTP_OPTION_SECURE_PROTOCOLS) { + WinHttpSessionImp *session; + WinHttpRequestImp *request; + if (dwBufferLength != sizeof(DWORD)) return FALSE; - if (dynamic_cast(base)) + if ((session = dynamic_cast(base))) { - WinHttpSessionImp *session = dynamic_cast(base); - - if (!session || !lpBuffer) + if (!lpBuffer) return FALSE; DWORD curlOffered = ConvertSecurityProtocol(*reinterpret_cast(lpBuffer)); @@ -3382,11 +3334,9 @@ BOOLAPI WinHttpSetOption( return FALSE; return TRUE; } - else if (dynamic_cast(base)) + else if ((request = dynamic_cast(base))) { - WinHttpRequestImp *request = dynamic_cast(base); - - if (!request || !lpBuffer) + if (!lpBuffer) return FALSE; DWORD curlOffered = ConvertSecurityProtocol(*reinterpret_cast(lpBuffer)); @@ -3399,14 +3349,14 @@ BOOLAPI WinHttpSetOption( } else if (dwOption == WINHTTP_OPTION_SECURITY_FLAGS) { + WinHttpRequestImp *request; + if (dwBufferLength != sizeof(DWORD)) return FALSE; - if (dynamic_cast(base)) + if ((request = dynamic_cast(base))) { - WinHttpRequestImp *request = dynamic_cast(base); - - if (!request || !lpBuffer) + if (!lpBuffer) return FALSE; DWORD value = *reinterpret_cast(lpBuffer); @@ -3506,11 +3456,7 @@ WinHttpQueryOption { WinHttpRequestImp *request; - if (dynamic_cast(base)) - { - request = dynamic_cast(base); - } - else + if (!(request = dynamic_cast(base))) return FALSE; char *url = NULL; @@ -3553,11 +3499,7 @@ WinHttpQueryOption { WinHttpRequestImp *request; - if (dynamic_cast(base)) - { - request = dynamic_cast(base); - } - else + if (!(request = dynamic_cast(base))) return FALSE; if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(HTTP_VERSION_INFO)) == FALSE) From 17c50d13915e03fcb2e0766158b534907f8b823e Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Fri, 12 Jul 2019 22:45:41 -0400 Subject: [PATCH 10/41] Add ubuntu 18.04 azure pipeline with no tests for the moment --- azure-pipelines.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8c878f0901..51d4db84db 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -312,3 +312,28 @@ jobs: # cd Build_iOS # ./configure.sh # displayName: 'Build for iOS' + - job: Ubuntu_1804_Apt + pool: + vmImage: 'Ubuntu 18.04' + steps: + - script: | + sudo apt -y remove php* + sudo apt install -y ppa-purge + sudo ppa-purge -y ppa:ondrej/php + unset BOOST_ROOT + sudo apt install -y libboost-atomic-dev libboost-thread-dev libboost-system-dev libboost-date-time-dev libboost-regex-dev libboost-filesystem-dev libboost-random-dev libboost-chrono-dev libboost-serialization-dev libwebsocketpp-dev openssl libssl-dev ninja-build + mkdir build.debug + cd build.debug + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCPPREST_HTTP_CLIENT_IMPL=curl .. + cd .. + mkdir build.release + cd build.release + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCPPREST_HTTP_CLIENT_IMPL=curl .. + cd .. + ninja -C build.debug + ninja -C build.release + cd build.debug/Release/Binaries + #./test_runner *test.so + cd ../../../build.release/Release/Binaries + #./test_runner *test.so + displayName: Run build From fb85f5a6df28c529a3ad5ff0a16a7b313c0fdb72 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sat, 13 Jul 2019 00:27:10 -0400 Subject: [PATCH 11/41] Use ubuntu 16.04 but update the curl in it --- azure-pipelines.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 51d4db84db..bb3756ba24 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -312,16 +312,26 @@ jobs: # cd Build_iOS # ./configure.sh # displayName: 'Build for iOS' - - job: Ubuntu_1804_Apt + - job: Ubuntu_1604_Apt_Curl pool: - vmImage: 'Ubuntu 18.04' + vmImage: 'Ubuntu 16.04' steps: - script: | sudo apt -y remove php* sudo apt install -y ppa-purge sudo ppa-purge -y ppa:ondrej/php unset BOOST_ROOT - sudo apt install -y libboost-atomic-dev libboost-thread-dev libboost-system-dev libboost-date-time-dev libboost-regex-dev libboost-filesystem-dev libboost-random-dev libboost-chrono-dev libboost-serialization-dev libwebsocketpp-dev openssl libssl-dev ninja-build + sudo apt install -y libboost-atomic-dev libboost-thread-dev libboost-system-dev libboost-date-time-dev libboost-regex-dev libboost-filesystem-dev libboost-random-dev libboost-chrono-dev libboost-serialization-dev libwebsocketpp-dev openssl libssl-dev ninja-build wget + wget http://curl.haxx.se/download/curl-7.57.0.tar.gz + sudo apt install -y libtool + sudo apt install -y make + tar -xvf curl-7.57.0.tar.gz + cd curl-7.57.0 + ./buildconf + ./configure --with-ssl --prefix=/usr + make + make install + cd .. mkdir build.debug cd build.debug /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCPPREST_HTTP_CLIENT_IMPL=curl .. From 50ac9d480a073c3d53dc836fa82e8e65b4df15b6 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sat, 13 Jul 2019 00:32:02 -0400 Subject: [PATCH 12/41] pipelines: needs sudo for install --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bb3756ba24..66a0442198 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -330,7 +330,7 @@ jobs: ./buildconf ./configure --with-ssl --prefix=/usr make - make install + sudo make install cd .. mkdir build.debug cd build.debug From 55bc08b2ef53cd6392af10ae5fd173731c1e17f1 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sat, 13 Jul 2019 01:08:13 -0400 Subject: [PATCH 13/41] Remove reference to undefined type --- Release/src/http/client/http_client_curl.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp index 1cf34ba3cb..5212c70fe4 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_curl.cpp @@ -1659,8 +1659,6 @@ class curl_client final : public _http_client_communicator static void CALLBACK completion_callback( HINTERNET hRequestHandle, DWORD_PTR context, DWORD statusCode, _In_ void* statusInfo, DWORD statusInfoLength) { - CASABLANCA_UNREFERENCED_PARAMETER(statusInfoLength); - std::weak_ptr* p_weak_request_context = reinterpret_cast*>(context); From ee4c0ccf63bdc9bf8afdcc9b7fe63967fe2352cf Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 15 Jul 2019 12:28:36 -0400 Subject: [PATCH 14/41] Eliminate size conversations --- .../src/http/client/winhttppal/winhttppal.cpp | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 46d9d26440..2e9622457c 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -434,7 +434,7 @@ class WinHttpConnectImp :public WinHttpBase struct BufferRequest { LPCVOID m_Buffer = NULL; - DWORD m_Length = 0; + size_t m_Length = 0; }; class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this @@ -442,12 +442,12 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this CURL *m_curl = NULL; std::vector m_ResponseString; std::string m_HeaderString = ""; - DWORD m_TotalHeaderStringLength = 0; + size_t m_TotalHeaderStringLength = 0; std::string m_Header = ""; std::string m_FullPath = ""; std::string m_OptionalData = ""; - DWORD m_TotalSize = 0; - DWORD m_TotalReceiveSize = 0; + size_t m_TotalSize = 0; + size_t m_TotalReceiveSize = 0; std::vector m_ReadData; std::mutex m_ReadDataEventMtx; @@ -503,7 +503,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::vector m_OutstandingWrites; public: - void QueueWriteRequest(LPCVOID buffer, DWORD length) + void QueueWriteRequest(LPCVOID buffer, size_t length) { BufferRequest shr; shr.m_Buffer = buffer; @@ -544,12 +544,12 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::mutex &GetQueryDataEventMtx() { return m_QueryDataEventMtx; } std::condition_variable &GetQueryDataEvent() { return m_QueryDataEvent; } - bool HandleQueryDataNotifications(std::shared_ptr, DWORD available); + bool HandleQueryDataNotifications(std::shared_ptr, size_t available); void WaitAsyncQueryDataCompletion(std::shared_ptr); void HandleReceiveNotifications(std::shared_ptr); void WaitAsyncReceiveCompletion(std::shared_ptr srequest); - DWORD WaitDataAvailable(); + size_t WaitDataAvailable(); CURLcode &GetCompletionCode() { return m_CompletionCode; } bool &GetCompletionStatus() { return m_Completion; } @@ -684,7 +684,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this } std::vector &GetResponseString() { return m_ResponseString; } - DWORD &GetTotalHeaderStringLength() { return m_TotalHeaderStringLength; } + size_t &GetTotalHeaderStringLength() { return m_TotalHeaderStringLength; } std::string &GetHeaderString() { return m_HeaderString; } CURL *GetCurl() { return m_curl; } @@ -720,18 +720,18 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this return TRUE; } - DWORD &GetTotalLength() { return m_TotalSize; } - DWORD &GetReadLength() { return m_TotalReceiveSize; } + size_t &GetTotalLength() { return m_TotalSize; } + size_t &GetReadLength() { return m_TotalReceiveSize; } std::string &GetOptionalData() { return m_OptionalData; } - void SetOptionalData(void *lpOptional, DWORD dwOptionalLength) + void SetOptionalData(void *lpOptional, size_t dwOptionalLength) { m_OptionalData.assign(&(reinterpret_cast(lpOptional))[0], dwOptionalLength); } std::vector &GetReadData() { return m_ReadData; } - void AppendReadData(const void *data, DWORD len) + void AppendReadData(const void *data, size_t len) { std::lock_guard lck(GetReadDataEventMtx()); m_ReadData.insert(m_ReadData.end(), reinterpret_cast(data), reinterpret_cast(data) + len); @@ -1835,7 +1835,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi { DWORD dwInternetStatus; DWORD result; - DWORD written = 0; + size_t written = 0; { request->GetReadDataEventMtx().lock(); @@ -2657,7 +2657,7 @@ WinHttpReceiveResponse return TRUE; } - DWORD headerLength; + size_t headerLength; { std::lock_guard lck(request->GetHeaderStringMutex()); headerLength = request->GetHeaderString().length(); @@ -2668,7 +2668,7 @@ WinHttpReceiveResponse return TRUE; } -bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr srequest, DWORD available) +bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr srequest, size_t available) { bool expected = true; bool result = std::atomic_compare_exchange_strong(&GetQueryDataPending(), &expected, false); @@ -2680,11 +2680,11 @@ bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr(available); TRACE("%s:%d WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, lpvStatusInformation); AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); @@ -2733,7 +2733,7 @@ WinHttpQueryDataAvailable request->GetBodyStringMutex().lock(); length = request->GetResponseString().size(); - DWORD available = (DWORD)(length); + size_t available = length; request->GetBodyStringMutex().unlock(); if (request->GetAsync()) @@ -2744,7 +2744,7 @@ WinHttpQueryDataAvailable request->WaitAsyncQueryDataCompletion(srequest); request->GetBodyStringMutex().lock(); length = request->GetResponseString().size(); - available = (DWORD)(length); + available = length; TRACE("%s:%d available = %lu\n", __func__, __LINE__, available); request->GetBodyStringMutex().unlock(); } @@ -2767,9 +2767,9 @@ WinHttpQueryDataAvailable return TRUE; } -DWORD WinHttpRequestImp::WaitDataAvailable() +size_t WinHttpRequestImp::WaitDataAvailable() { - DWORD length, remainingLength, readLength; + size_t length, remainingLength, readLength; GetBodyStringMutex().lock(); length = GetResponseString().size(); @@ -3044,7 +3044,7 @@ BOOLAPI WinHttpQueryHeaders( { CURLcode res; DWORD responseCode; - DWORD length; + size_t length; res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &responseCode); if (res != CURLE_OK) From 06b5d1355cc25363433d777ba567b7a05b9e532e Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 15 Jul 2019 19:27:06 -0400 Subject: [PATCH 15/41] Formatting changes and convert MultiByte functions to C++ native functions --- .../src/http/client/winhttppal/winhttppal.cpp | 77 +++----- .../src/http/client/winhttppal/winhttppal.h | 172 ++++++++++-------- 2 files changed, 123 insertions(+), 126 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 2e9622457c..9939a4f4ae 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -43,41 +44,6 @@ extern "C" class WinHttpSessionImp; class WinHttpRequestImp; -#ifdef min -#define MIN min -#define MAX max -#else -#define MIN std::min -#define MAX std::max -#endif - -#ifdef UNICODE -#define WCTLEN wcslen -#define WCTCMP wcscmp -#define WCTCPY wcscpy -#define WCTNCPY wcsncpy -#define STNPRINTF swprintf -#define TSTRING std::wstring -#define STRING_LITERAL "%S" -#define TO_STRING std::to_wstring -#define TREGEX std::wregex -#define TREGEX_SEARCH std::regex_search -#define TREGEX_MATCH std::wsmatch -#else -#define WCTLEN strlen -#define WCTCMP strcmp -#define WCTCPY strcpy -#define WCTNCPY strncpy -#define STNPRINTF snprintf -#define TEXT(T) T -#define TSTRING std::string -#define STRING_LITERAL "%s" -#define TO_STRING std::to_string -#define TREGEX std::regex -#define TREGEX_SEARCH std::regex_search -#define TREGEX_MATCH std::smatch -#endif - #ifdef _MSC_VER #pragma comment(lib, "Ws2_32.lib") #endif @@ -180,8 +146,8 @@ static void TRACE_INTERNAL(const char *fmt, ...) typedef void (*CompletionCb)(std::shared_ptr, DWORD status); #define MUTEX_TYPE std::mutex -#define MUTEX_SETUP(x) -#define MUTEX_CLEANUP(x) +#define MUTEX_SETUP(x) +#define MUTEX_CLEANUP(x) #define MUTEX_LOCK(x) x.lock() #define MUTEX_UNLOCK(x) x.unlock() #ifdef WIN32 @@ -209,7 +175,7 @@ typedef void (*CompletionCb)(std::shared_ptr, DWORD status); typedef void* (*LPTHREAD_START_ROUTINE)(void *); static inline THREAD_HANDLE CREATETHREAD(LPTHREAD_START_ROUTINE func, LPVOID param, pthread_t *id) { - pthread_t inc_x_thread; + pthread_t inc_x_thread; pthread_create(&inc_x_thread, NULL, func, param); return inc_x_thread; } @@ -343,14 +309,8 @@ static void ConvertCstrAssign(const TCHAR *lpstr, size_t cLen, std::string &targ size_t bLen = cLen; #ifdef UNICODE - char *nstring = new char[bLen + 1]; - if (!nstring) - return; - - cLen = MIN(WCTLEN(lpstr), cLen); - WideCharToMultiByte(CP_UTF8, 0, &lpstr[0], cLen, &nstring[0], bLen, NULL, NULL); - target.assign(nstring, bLen); - delete [] nstring; + std::wstring_convert> conv; + target = conv.to_bytes(std::wstring(lpstr, cLen)); #else target.assign(lpstr, cLen); #endif @@ -990,7 +950,7 @@ class ComContainer thread_setup(); m_curlm = curl_multi_init(); - + DWORD thread_id; m_hAsyncThread = CREATETHREAD( AsyncThreadFunction, // thread function name @@ -3062,11 +3022,10 @@ BOOLAPI WinHttpQueryHeaders( #ifdef UNICODE TSTRING subject; - length = request->GetHeaderString().length(); - subject.resize(length + 1); + std::wstring_convert> conv; + subject = conv.from_bytes(request->GetHeaderString()); + subject[request->GetHeaderString().length()] = TEXT('\0'); - MultiByteToWideChar(CP_UTF8, 0, request->GetHeaderString().c_str(), -1, &subject[0], length); - subject[length] = TEXT('\0'); #else TSTRING &subject = request->GetHeaderString(); #endif @@ -3137,7 +3096,10 @@ BOOLAPI WinHttpQueryHeaders( length = header.size(); #ifdef UNICODE - MultiByteToWideChar(CP_UTF8, 0, header.c_str(), length + 1, wbuffer, length + 1); + std::wstring_convert> conv; + std::wstring wc = conv.from_bytes(header); + std::copy(wc.begin(), wc.end(), wbuffer); + wbuffer[header.length()] = TEXT('\0'); #else strncpy(wbuffer, header.c_str(), length); #endif @@ -3179,7 +3141,11 @@ BOOLAPI WinHttpQueryHeaders( return FALSE; #ifdef UNICODE - MultiByteToWideChar(CP_UTF8, 0, request->GetHeaderString().c_str(), -1, wbuffer, length); + std::wstring_convert> conv; + std::wstring wc = conv.from_bytes(request->GetHeaderString()); + std::copy(wc.begin(), wc.end(), wbuffer); + wbuffer[request->GetHeaderString().length()] = TEXT('\0'); + #else strncpy(wbuffer, request->GetHeaderString().c_str(), length); #endif @@ -3470,7 +3436,10 @@ WinHttpQueryOption TCHAR *wbuffer = reinterpret_cast(lpBuffer); size_t length = strlen(url); #ifdef UNICODE - MultiByteToWideChar(CP_UTF8, 0, url, length + 1, wbuffer, length + 1); + std::wstring_convert> conv; + std::wstring wc = conv.from_bytes(std::string(url)); + std::copy(wc.begin(), wc.end(), wbuffer); + wbuffer[strlen(url)] = TEXT('\0'); #else strncpy(wbuffer, url, length); #endif diff --git a/Release/src/http/client/winhttppal/winhttppal.h b/Release/src/http/client/winhttppal/winhttppal.h index 57fc71236f..5089e7f985 100644 --- a/Release/src/http/client/winhttppal/winhttppal.h +++ b/Release/src/http/client/winhttppal/winhttppal.h @@ -1,29 +1,35 @@ -typedef void VOID; -typedef void *LPVOID; +typedef void VOID; +typedef void* LPVOID; typedef unsigned long DWORD; -typedef const void* LPCVOID; -typedef long LONG; -typedef unsigned char BYTE; +typedef const void* LPCVOID; +typedef long LONG; +typedef unsigned char BYTE; #define __int3264 long int -typedef unsigned __int3264 ULONG_PTR; -typedef ULONG_PTR DWORD_PTR; -typedef DWORD *PDWORD; -typedef DWORD *LPDWORD; -typedef char *LPSTR; -typedef const char *LPCSTR; +typedef unsigned __int3264 ULONG_PTR; +typedef ULONG_PTR DWORD_PTR; +typedef DWORD* PDWORD; +typedef DWORD* LPDWORD; +typedef char* LPSTR; +typedef const char* LPCSTR; #ifdef UNICODE -typedef wchar_t TCHAR; -typedef const wchar_t* LPCTSTR; -typedef wchar_t* LPTSTR, *PTSTR; -typedef const wchar_t* LPCTSTR, *PCTSTR; +typedef wchar_t TCHAR; +typedef const wchar_t* LPCTSTR; +typedef wchar_t* LPTSTR; +typedef wchar_t* PTSTR; +typedef const wchar_t* LPCTSTR; +typedef const wchar_t* PCTSTR; +#define TEXT(a) L##a #else -typedef char TCHAR; -typedef const char* LPCTSTR; -typedef char* LPTSTR, *PTSTR; -typedef const char* LPCTSTR, *PCTSTR; +typedef char TCHAR; +typedef const char* LPCTSTR; +typedef char* LPTSTR; +typedef char* PTSTR; +typedef const char* LPCTSTR; +typedef const char* PCTSTR; +#define TEXT(a) a #endif - + typedef LPVOID HINTERNET; typedef bool BOOL; @@ -58,8 +64,6 @@ typedef struct } HTTP_VERSION_INFO; -#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0 - enum { WINHTTP_QUERY_RAW_HEADERS, @@ -81,21 +85,81 @@ enum WINHTTP_AUTH_TARGET_SERVER, }; -#define WINHTTP_INVALID_STATUS_CALLBACK (WINHTTP_STATUS_CALLBACK)-1 +#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0 +#define WINHTTP_INVALID_STATUS_CALLBACK (WINHTTP_STATUS_CALLBACK)-1 + +#define WINHTTP_NO_REFERER NULL +#define WINHTTP_DEFAULT_ACCEPT_TYPES NULL +#define WINHTTP_NO_ADDITIONAL_HEADERS NULL +#define WINHTTP_NO_REQUEST_DATA NULL +#define WINHTTP_NO_PROXY_NAME NULL +#define WINHTTP_NO_PROXY_BYPASS NULL +#define WINHTTP_NO_HEADER_INDEX NULL +#define WINHTTP_HEADER_NAME_BY_INDEX NULL +#define WINHTTP_NO_OUTPUT_BUFFER NULL + +#define WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH 0 +#define WINHTTP_ADDREQ_FLAG_ADD 0 +#define WINHTTP_ENABLE_SSL_REVOCATION 0 + +#define INTERNET_DEFAULT_HTTP_PORT 80 +#define ARRAYSIZE(n) sizeof(n)/sizeof(n[0]) + +#define GetLastError() errno +#define CALLBACK + +#define FALSE 0 +#define TRUE 1 + +#define INTERNET_DEFAULT_HTTPS_PORT 443 +#define S_OK 0 -#define WINHTTP_NO_REFERER NULL -#define WINHTTP_DEFAULT_ACCEPT_TYPES NULL -#define WINHTTP_NO_ADDITIONAL_HEADERS NULL -#define WINHTTP_NO_REQUEST_DATA NULL -#define WINHTTP_NO_PROXY_NAME NULL -#define WINHTTP_NO_PROXY_BYPASS NULL -#define WINHTTP_NO_HEADER_INDEX NULL -#define WINHTTP_HEADER_NAME_BY_INDEX NULL -#define WINHTTP_NO_OUTPUT_BUFFER NULL +#define ERROR_INSUFFICIENT_BUFFER ENOMEM +#define ERROR_WINHTTP_RESEND_REQUEST EBUSY +#define ERROR_SUCCESS 0 +#define ERROR_OPERATION_ABORTED EINVAL +#define ERROR_NOT_ENOUGH_MEMORY ENOMEM +#define ERROR_WINHTTP_TIMEOUT ETIMEDOUT +#define ERROR_INVALID_PARAMETER EINVAL -#define WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH 0 -#define WINHTTP_ADDREQ_FLAG_ADD 0 -#define WINHTTP_ENABLE_SSL_REVOCATION 0 +#define BOOLAPI BOOL +#define SetLastError(val) errno = val +#define WINHTTPAPI +#define WINAPI + +#ifdef min +#define MIN min +#define MAX max +#else +#define MIN std::min +#define MAX std::max +#endif + +#ifdef UNICODE +#define WCTLEN wcslen +#define WCTCMP wcscmp +#define WCTCPY wcscpy +#define WCTNCPY wcsncpy +#define STNPRINTF swprintf +#define TSTRING std::wstring +#define STRING_LITERAL "%S" +#define TO_STRING std::to_wstring +#define TREGEX std::wregex +#define TREGEX_SEARCH std::regex_search +#define TREGEX_MATCH std::wsmatch +#else +#define WCTLEN strlen +#define WCTCMP strcmp +#define WCTCPY strcpy +#define WCTNCPY strncpy +#define STNPRINTF snprintf +#define TSTRING std::string +#define STRING_LITERAL "%s" +#define TO_STRING std::to_string +#define TREGEX std::regex +#define TREGEX_SEARCH std::regex_search +#define TREGEX_MATCH std::smatch +#endif enum { @@ -132,6 +196,7 @@ enum WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED = (1 << 17), WINHTTP_CALLBACK_STATUS_REQUEST_SENT = (1 << 18), WINHTTP_CALLBACK_STATUS_REDIRECT = (1 << 19), + WINHTTP_CALLBACK_STATUS_RESOLVING_NAME = (1 << 20), WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS = (1 << 24), WINHTTP_CALLBACK_FLAG_HANDLES = (1 << 25), WINHTTP_CALLBACK_FLAG_SECURE_FAILURE = (1 << 26), @@ -180,9 +245,6 @@ enum typedef unsigned int INTERNET_PORT; -#define INTERNET_DEFAULT_HTTP_PORT 80 - - BOOL WinHttpCloseHandle( HINTERNET hInternet ); @@ -313,11 +375,6 @@ BOOL WinHttpWriteData( LPDWORD lpdwNumberOfBytesWritten ); -#define ARRAYSIZE(n) sizeof(n)/sizeof(n[0]) - -#define GetLastError() errno -#define CALLBACK - typedef struct { DWORD dwAccessType; @@ -373,35 +430,6 @@ enum WINHTTP_AUTOPROXY_CONFIG_URL, }; - -#define GlobalFree free - -#define FALSE 0 -#define TRUE 1 - -#define INTERNET_DEFAULT_HTTPS_PORT 443 -#define S_OK 0 - -#define ERROR_INSUFFICIENT_BUFFER ENOMEM -#define ERROR_WINHTTP_RESEND_REQUEST EBUSY -#define ERROR_SUCCESS 0 -#define ERROR_OPERATION_ABORTED EINVAL -#define ERROR_NOT_ENOUGH_MEMORY ENOMEM -#define ERROR_WINHTTP_TIMEOUT ETIMEDOUT -#define ERROR_INVALID_PARAMETER EINVAL - -#include - -template -std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - -#define BOOLAPI BOOL -#define SetLastError(val) errno = val -#define WINHTTPAPI -#define WINAPI - typedef enum { INTERNET_SCHEME_HTTP = 1, INTERNET_SCHEME_HTTPS, From cd4d172a22b2fbe6c51b3d89b3c5dac24ce80c0c Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 16 Jul 2019 13:18:48 -0400 Subject: [PATCH 16/41] Remove printfs --- Release/src/http/client/http_client_curl.cpp | 21 -------------------- 1 file changed, 21 deletions(-) diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp index 5212c70fe4..874abbd6a6 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_curl.cpp @@ -733,7 +733,6 @@ class curl_client final : public _http_client_communicator m_hSession = WinHttpOpen(NULL, access_type, proxy_name, proxy_bypass, WINHTTP_FLAG_ASYNC); if (!m_hSession) { - printf("%s:%d\n", __func__, __LINE__); return GetLastError(); } @@ -742,7 +741,6 @@ class curl_client final : public _http_client_communicator milliseconds = std::max(milliseconds, 1); if (!WinHttpSetTimeouts(m_hSession, milliseconds, milliseconds, milliseconds, milliseconds)) { - printf("%s:%d\n", __func__, __LINE__); return GetLastError(); } @@ -753,7 +751,6 @@ class curl_client final : public _http_client_communicator if (!WinHttpSetOption( m_hSession, WINHTTP_OPTION_MAX_CONNS_PER_SERVER, &maxConnections, sizeof(maxConnections))) { - printf("%s:%d\n", __func__, __LINE__); return GetLastError(); } } @@ -767,7 +764,6 @@ class curl_client final : public _http_client_communicator m_hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols)); if (FALSE == win32_result) { - printf("%s:%d\n", __func__, __LINE__); return GetLastError(); } @@ -781,7 +777,6 @@ class curl_client final : public _http_client_communicator WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, 0)) { - printf("%s:%d\n", __func__, __LINE__); return GetLastError(); } @@ -793,7 +788,6 @@ class curl_client final : public _http_client_communicator if (m_hConnection == nullptr) { - printf("%s:%d\n", __func__, __LINE__); return GetLastError(); } @@ -872,7 +866,6 @@ class curl_client final : public _http_client_communicator WINHTTP_FLAG_ESCAPE_DISABLE | (m_secure ? WINHTTP_FLAG_SECURE : 0)); if (curl_context->m_request_handle == nullptr) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); delete curl_context->m_request_handle_context; curl_context->m_request_handle_context = 0; @@ -885,7 +878,6 @@ class curl_client final : public _http_client_communicator &curl_context->m_request_handle_context, sizeof(void*))) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); delete curl_context->m_request_handle_context; curl_context->m_request_handle_context = 0; @@ -899,7 +891,6 @@ class curl_client final : public _http_client_communicator curl_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO)); if (!result) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); request->report_error(errorCode, build_error_msg(errorCode, "Setting proxy options")); return; @@ -917,7 +908,6 @@ class curl_client final : public _http_client_communicator curl_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data)); if (!result) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); request->report_error( errorCode, @@ -955,7 +945,6 @@ class curl_client final : public _http_client_communicator &ignoredCertificateValidationSteps, sizeof(ignoredCertificateValidationSteps))) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); request->report_error(errorCode, build_error_msg(errorCode, "Setting ignore server certificate verification")); @@ -1012,7 +1001,6 @@ class curl_client final : public _http_client_communicator static_cast(flattened_headers.length()), WINHTTP_ADDREQ_FLAG_ADD)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); request->report_error(errorCode, build_error_msg(errorCode, "WinHttpAddRequestHeaders")); return; @@ -1089,7 +1077,6 @@ class curl_client final : public _http_client_communicator (DWORD_PTR)curl_context->m_request_handle_context); if (!requestSuccess) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); curl_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest")); } @@ -1141,7 +1128,6 @@ class curl_client final : public _http_client_communicator if (!WinHttpReadData(pContext->m_request_handle, buffer, static_cast(chunkSize), nullptr)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); pContext->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData")); } @@ -1256,7 +1242,6 @@ class curl_client final : public _http_client_communicator static_cast(length), nullptr)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); } @@ -1500,7 +1485,6 @@ class curl_client final : public _http_client_communicator static_cast(to_write), nullptr)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); } @@ -1543,7 +1527,6 @@ class curl_client final : public _http_client_communicator static_cast(read), nullptr)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); } @@ -1739,7 +1722,6 @@ class curl_client final : public _http_client_communicator { if (!WinHttpReceiveResponse(hRequestHandle, nullptr)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpReceiveResponse")); @@ -1792,7 +1774,6 @@ class curl_client final : public _http_client_communicator { if (!WinHttpReceiveResponse(hRequestHandle, nullptr)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpReceiveResponse")); @@ -1817,7 +1798,6 @@ class curl_client final : public _http_client_communicator &headerBufferLength, WINHTTP_NO_HEADER_INDEX)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryHeaders")); ; @@ -1901,7 +1881,6 @@ class curl_client final : public _http_client_communicator // Read in available body data all at once. if (!WinHttpReadData(hRequestHandle, buffer, num_bytes, nullptr)) { - printf("%s:%d\n", __func__, __LINE__); auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData")); } From b27cb39379a65e563559b3d9a3e642feec5b5025 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Thu, 18 Jul 2019 12:57:17 -0400 Subject: [PATCH 17/41] Move GlobalFree and make_unique to http_client_curl.cpp --- Release/src/http/client/http_client_curl.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp index 874abbd6a6..682dc85428 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_curl.cpp @@ -20,6 +20,14 @@ #include "http_client_impl.h" #include "winhttppal/winhttppal.h" #include +#include + +#define GlobalFree free + +template +std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} namespace { From d4208844de3f53ecb39ca900d3b1e65c7095952c Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Thu, 18 Jul 2019 12:59:35 -0400 Subject: [PATCH 18/41] Exit on build error --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 66a0442198..6073063e21 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -317,6 +317,7 @@ jobs: vmImage: 'Ubuntu 16.04' steps: - script: | + set -e sudo apt -y remove php* sudo apt install -y ppa-purge sudo ppa-purge -y ppa:ondrej/php From 8e0bf83bddcbd4eb51b33ba85c0f36bd1a2170d4 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 21 Jul 2019 18:27:03 -0400 Subject: [PATCH 19/41] Use unique from utility --- Release/src/http/client/http_client_curl.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp index 682dc85428..63da82d06b 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_curl.cpp @@ -24,11 +24,6 @@ #define GlobalFree free -template -std::unique_ptr make_unique(Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} - namespace { struct security_failure_message @@ -1836,7 +1831,7 @@ class curl_client final : public _http_client_communicator !p_request_context->m_http_client->client_config().request_compressed_response()) { p_request_context->m_compression_state.m_chunk = - make_unique(); + ::utility::details::make_unique(); p_request_context->m_compression_state.m_chunked = true; } From 817dd1638eab0e867d1b57785e3d051ce974a7cf Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 21 Jul 2019 19:47:36 -0400 Subject: [PATCH 20/41] Use const where possible, convert reinterpret cast to static cast --- .../src/http/client/winhttppal/winhttppal.cpp | 70 ++++++++++--------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 9939a4f4ae..884842a96e 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -202,7 +202,7 @@ static void locking_function(int mode, int n, const char *file, int line) static unsigned long id_function(void) { - return ((unsigned long)(THREAD_ID)); + return static_cast(THREAD_ID); } #endif @@ -289,9 +289,9 @@ class UserCallbackContext ~UserCallbackContext(); std::shared_ptr GetRequestRef() { return m_request; } WinHttpRequestImp *GetRequest() { return m_request.get(); } - DWORD GetInternetStatus() { return m_dwInternetStatus; } - DWORD GetStatusInformationLength() { return m_dwStatusInformationLength; } - DWORD GetNotificationFlags() { return m_dwNotificationFlags; } + DWORD GetInternetStatus() const { return m_dwInternetStatus; } + DWORD GetStatusInformationLength() const { return m_dwStatusInformationLength; } + DWORD GetNotificationFlags() const { return m_dwNotificationFlags; } WINHTTP_STATUS_CALLBACK &GetCb() { return m_cb; } CompletionCb &GetRequestCompletionCb() { return m_requestCompletionCb; } LPVOID GetUserdata() { return m_userdata; } @@ -301,7 +301,7 @@ class UserCallbackContext else return m_StatusInformationVal; } - BOOL GetStatusInformationValid() { return m_AsyncResultValid; } + BOOL GetStatusInformationValid() const { return m_AsyncResultValid; } }; static void ConvertCstrAssign(const TCHAR *lpstr, size_t cLen, std::string &target) @@ -686,7 +686,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::string &GetOptionalData() { return m_OptionalData; } void SetOptionalData(void *lpOptional, size_t dwOptionalLength) { - m_OptionalData.assign(&(reinterpret_cast(lpOptional))[0], dwOptionalLength); + m_OptionalData.assign(&(static_cast(lpOptional))[0], dwOptionalLength); } std::vector &GetReadData() { return m_ReadData; } @@ -694,7 +694,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this void AppendReadData(const void *data, size_t len) { std::lock_guard lck(GetReadDataEventMtx()); - m_ReadData.insert(m_ReadData.end(), reinterpret_cast(data), reinterpret_cast(data) + len); + m_ReadData.insert(m_ReadData.end(), static_cast(data), static_cast(data) + len); } static int SocketCallback(CURL *handle, curl_infotype type, @@ -728,7 +728,7 @@ class UserCallbackContainer public: - bool GetClosing() { return m_closing; } + bool GetClosing() const { return m_closing; } UserCallbackContext* GetNext() { @@ -802,6 +802,9 @@ class UserCallbackContainer static UserCallbackContainer the_instance; return the_instance; } +private: + UserCallbackContainer(const UserCallbackContainer&); + UserCallbackContainer& operator=(const UserCallbackContainer&); }; class EnvInit @@ -835,7 +838,7 @@ class ComContainer BOOL m_closing = FALSE; // used to protect CURLM data structures - BOOL GetThreadClosing() { return m_closing; } + BOOL GetThreadClosing() const { return m_closing; } public: CURL *AllocCURL() @@ -1040,6 +1043,9 @@ class ComContainer return rc; } +private: + ComContainer(const ComContainer&); + ComContainer& operator=(const ComContainer&); }; template @@ -1137,29 +1143,29 @@ class WinHttpSessionImp :public WinHttpBase m_SecureProtocol = *data; return TRUE; } - DWORD GetSecureProtocol() { return m_SecureProtocol; } + DWORD GetSecureProtocol() const { return m_SecureProtocol; } BOOL SetMaxConnections(DWORD *data) { m_MaxConnections = *data; return TRUE; } - DWORD GetMaxConnections() { return m_MaxConnections; } + DWORD GetMaxConnections() const { return m_MaxConnections; } void SetAsync() { m_Async = TRUE; } - BOOL GetAsync() { return m_Async; } + BOOL GetAsync() const { return m_Async; } - BOOL GetThreadClosing() { return m_closing; } + BOOL GetThreadClosing() const { return m_closing; } std::vector &GetProxies() { return m_Proxies; } void SetProxies(std::vector &proxies) { m_Proxies = proxies; } std::string &GetProxy() { return m_Proxy; } - int GetServerPort() { return m_ServerPort; } + int GetServerPort() const { return m_ServerPort; } void SetServerPort(int port) { m_ServerPort = port; } - long GetTimeout() { + long GetTimeout() const { if (m_Timeout) return m_Timeout; @@ -2964,8 +2970,8 @@ BOOLAPI WinHttpQueryHeaders( } else { - TCHAR *outbuf = reinterpret_cast(lpBuffer); - STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%ld"), retValue); + TCHAR *outbuf = static_cast(lpBuffer); + STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%lu"), retValue); outbuf[*lpdwBufferLength/sizeof(outbuf[0]) - 1] = TEXT('\0'); } return TRUE; @@ -2992,8 +2998,8 @@ BOOLAPI WinHttpQueryHeaders( } else { - TCHAR *outbuf = reinterpret_cast(lpBuffer); - STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%ld"), retValue); + TCHAR *outbuf = static_cast(lpBuffer); + STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%lu"), retValue); outbuf[*lpdwBufferLength/sizeof(outbuf[0]) - 1] = TEXT('\0'); } TRACE("%s:%d status code :%lu\n", __func__, __LINE__, retValue); @@ -3074,7 +3080,7 @@ BOOLAPI WinHttpQueryHeaders( { size_t length; std::string header; - TCHAR *wbuffer = reinterpret_cast(lpBuffer); + TCHAR *wbuffer = static_cast(lpBuffer); request->GetHeaderStringMutex().lock(); length = request->GetHeaderString().length(); @@ -3130,7 +3136,7 @@ BOOLAPI WinHttpQueryHeaders( { std::lock_guard lck(request->GetHeaderStringMutex()); size_t length; - TCHAR *wbuffer = reinterpret_cast(lpBuffer); + TCHAR *wbuffer = static_cast(lpBuffer); length = request->GetHeaderString().length(); @@ -3226,7 +3232,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - if (!session->SetMaxConnections(reinterpret_cast(lpBuffer))) + if (!session->SetMaxConnections(static_cast(lpBuffer))) return FALSE; return TRUE; } @@ -3235,7 +3241,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - if (!request->SetMaxConnections(reinterpret_cast(lpBuffer))) + if (!request->SetMaxConnections(static_cast(lpBuffer))) return FALSE; return TRUE; } @@ -3257,7 +3263,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - if (!connect->SetUserData(reinterpret_cast(lpBuffer))) + if (!connect->SetUserData(static_cast(lpBuffer))) return FALSE; return TRUE; } @@ -3266,7 +3272,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - if (!session->SetUserData(reinterpret_cast(lpBuffer))) + if (!session->SetUserData(static_cast(lpBuffer))) return FALSE; return TRUE; } @@ -3275,7 +3281,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - if (!request->SetUserData(reinterpret_cast(lpBuffer))) + if (!request->SetUserData(static_cast(lpBuffer))) return FALSE; return TRUE; } @@ -3295,7 +3301,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - DWORD curlOffered = ConvertSecurityProtocol(*reinterpret_cast(lpBuffer)); + DWORD curlOffered = ConvertSecurityProtocol(*static_cast(lpBuffer)); if (curlOffered && !session->SetSecureProtocol(&curlOffered)) return FALSE; return TRUE; @@ -3305,7 +3311,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - DWORD curlOffered = ConvertSecurityProtocol(*reinterpret_cast(lpBuffer)); + DWORD curlOffered = ConvertSecurityProtocol(*static_cast(lpBuffer)); if (curlOffered && !request->SetSecureProtocol(&curlOffered)) return FALSE; return TRUE; @@ -3325,7 +3331,7 @@ BOOLAPI WinHttpSetOption( if (!lpBuffer) return FALSE; - DWORD value = *reinterpret_cast(lpBuffer); + DWORD value = *static_cast(lpBuffer); if (value == (SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE)) { @@ -3400,7 +3406,7 @@ WinHttpQueryOption if (!session) return FALSE; - *reinterpret_cast(lpBuffer) = session->GetTimeout(); + *static_cast(lpBuffer) = session->GetTimeout(); } if (WINHTTP_OPTION_CALLBACK == dwOption) { @@ -3416,7 +3422,7 @@ WinHttpQueryOption DWORD dwNotificationFlags; WINHTTP_STATUS_CALLBACK cb = session->GetCallback(&dwNotificationFlags); - *reinterpret_cast(lpBuffer) = cb; + *static_cast(lpBuffer) = cb; } else if (WINHTTP_OPTION_URL == dwOption) { @@ -3433,7 +3439,7 @@ WinHttpQueryOption if (SizeCheck(lpBuffer, lpdwBufferLength, (strlen(url) + 1) * sizeof(TCHAR)) == FALSE) return FALSE; - TCHAR *wbuffer = reinterpret_cast(lpBuffer); + TCHAR *wbuffer = static_cast(lpBuffer); size_t length = strlen(url); #ifdef UNICODE std::wstring_convert> conv; From 44ca34a2973fab76da4b62534e68f5b54578ee25 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 21 Jul 2019 19:50:20 -0400 Subject: [PATCH 21/41] Use nullize_newlines --- .../src/http/client/winhttppal/winhttppal.cpp | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 884842a96e..bc824ea126 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -329,22 +329,6 @@ static std::vector Split(std::string &str, char delimiter) { return internal; } -static void ReplaceStr(std::string &str, const char *searchStr, const char *replaceStr) -{ - size_t index = 0; - while (true) { - /* Locate the substring to replace. */ - index = str.find(searchStr, index); - if (index == std::string::npos) break; - - /* Make the replacement. */ - str.replace(index, strlen(searchStr), replaceStr); - - /* Advance index forward so the next iteration doesn't pick it up as well. */ - index += strlen(searchStr); - } -} - static BOOL SizeCheck(LPVOID lpBuffer, LPDWORD lpdwBufferLength, DWORD Required) { if (!lpBuffer) @@ -2911,6 +2895,31 @@ static TSTRING FindRegex(const TSTRING &subject,const TSTRING ®str) return result; } +bool is_newline(char i) +{ + return (i == '\n') || (i == '\r'); +} + +template +std::basic_string nullize_newlines(const std::basic_string& str) { + std::basic_string result; + result.reserve(str.size()); + + auto cursor = str.begin(); + const auto end = str.end(); + for (;;) { + cursor = std::find_if_not(cursor, end, is_newline); + if (cursor == end) { + return result; + } + + const auto nextNewline = std::find_if(cursor, end, is_newline); + result.append(cursor, nextNewline); + result.push_back(CharT{}); + cursor = nextNewline; + } +} + BOOLAPI WinHttpQueryHeaders( HINTERNET hRequest, DWORD dwInfoLevel, @@ -3093,11 +3102,7 @@ BOOLAPI WinHttpQueryHeaders( if (!wbuffer) return FALSE; - ReplaceStr(header, "\r\n", "\n"); - ReplaceStr(header, "\n\r", "\n"); - ReplaceStr(header, "\r", "\n"); - ReplaceStr(header, "\n\n", "\n"); - std::replace(header.begin(), header.end(), '\n', '\0'); + header = nullize_newlines(header); header.resize(header.size() + 1); length = header.size(); From e2ced5a25d17e40b0444cf922d56d34f2c7a02d0 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 21 Jul 2019 19:53:29 -0400 Subject: [PATCH 22/41] Structure TRACE output for each request --- .../src/http/client/winhttppal/winhttppal.cpp | 190 +++++++++--------- 1 file changed, 99 insertions(+), 91 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index bc824ea126..1a72f7165d 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -515,7 +515,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this CURL *curl = request->GetCurl(); CURLcode res; - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ @@ -525,7 +525,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this curl_easy_strerror(res)); return FALSE; } - TRACE("%s:%d res:%d\n", __func__, __LINE__, res); + TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)request, res); request->GetUploadThreadExitStatus() = true; #if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L @@ -734,8 +734,8 @@ class UserCallbackContainer { std::lock_guard lck(m_MapMutex); - TRACE_VERBOSE("%s:%d ctx = %p cb = %p userdata = %p dwInternetStatus = %p\n", - __func__, __LINE__, reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), + TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p userdata = %p dwInternetStatus = %p\n", + __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), ctx->GetUserdata(), ctx->GetStatusInformation()); GetCallbackQueue().push_back(ctx); } @@ -946,7 +946,7 @@ class ComContainer ~ComContainer() { - TRACE("==>%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); m_closing = true; { std::lock_guard lck(m_hAsyncEventMtx); @@ -954,7 +954,7 @@ class ComContainer m_hAsyncEvent.notify_all(); } THREADJOIN(m_hAsyncThread); - TRACE("<==%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); curl_multi_cleanup(m_curlm); curl_global_cleanup(); @@ -1052,7 +1052,7 @@ class WinHttpHandleContainer bool found = false; std::lock_guard lck(m_ActiveRequestMtx); - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)val); for (auto it = m_ActiveRequests.begin(); it != m_ActiveRequests.end(); ++it) { @@ -1082,14 +1082,14 @@ class WinHttpHandleContainer break; } } - TRACE("%s:%d found:%d\n", __func__, __LINE__, found); + TRACE("%-35s:%-8d:%-16p found:%d\n", __func__, __LINE__, (void*)val, found); return found; } void Register(std::shared_ptr rqst) { - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)(rqst.get())); std::lock_guard lck(m_ActiveRequestMtx); m_ActiveRequests.push_back(rqst); } @@ -1184,9 +1184,9 @@ class WinHttpSessionImp :public WinHttpBase ~WinHttpSessionImp() { - TRACE("==>%s:%d sesion\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p sesion\n", __func__, __LINE__, (void*)this); SetCallback(NULL, 0); - TRACE("<==%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); } }; @@ -1213,7 +1213,7 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) comContainer->QueryData(&still_running); - //TRACE("%s:%d\n", __func__, __LINE__); + //TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); struct CURLMsg *m; /* call curl_multi_perform or curl_multi_socket_action first, then loop @@ -1238,7 +1238,7 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) WINHTTP_ASYNC_RESULT result = { 0, 0 }; DWORD dwInternetStatus; - TRACE("%s:%d type:%s result:%d\n", __func__, __LINE__, request->GetType().c_str(), m->data.result); + TRACE("%-35s:%-8d:%-16p type:%s result:%d\n", __func__, __LINE__, (void*)request, request->GetType().c_str(), m->data.result); request->GetCompletionStatus() = true; request->GetCompletionCode() = m->data.result; @@ -1248,14 +1248,14 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) std::lock_guard lck(request->GetReceiveCompletionEventMtx()); request->GetReceiveCompletionEventCounter()++; request->GetReceiveCompletionEvent().notify_all(); - TRACE("%s:%d GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); } if (m->data.result == CURLE_OK) { if (request->HandleQueryDataNotifications(srequest, 0)) { - TRACE("%s:%d GetQueryDataEvent().notify_all\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); } { std::lock_guard lck(request->GetQueryDataEventMtx()); @@ -1281,20 +1281,22 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) result.dwError = ERROR_WINHTTP_TIMEOUT; dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); - TRACE("request done type = %s CURLE_OPERATION_TIMEDOUT\n", request->GetType().c_str()); + TRACE("%-35s:%-8d:%-16p request done type = %s CURLE_OPERATION_TIMEDOUT\n", + __func__, __LINE__, (void*)request, request->GetType().c_str()); } else { result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; - TRACE("unknown async request done m->data.result = %d\n", m->data.result); + TRACE("%-35s:%-8d:%-16p unknown async request done m->data.result = %d\n", + __func__, __LINE__, (void*)request, m->data.result); #ifdef _DEBUG assert(0); #endif request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); } } else if (m && (m->msg != CURLMSG_DONE)) { - TRACE("%s\n", "unknown async request done\n"); + TRACE("%-35s:%-8d:%-16p unknown async request done\n", __func__, __LINE__, (void*)request); DWORD dwInternetStatus; WINHTTP_ASYNC_RESULT result = { 0, 0 }; result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; @@ -1309,13 +1311,13 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) if (request) { comContainer->RemoveHandle(srequest, request->GetCurl(), true); - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); } } while (m); } if (comContainer->GetThreadClosing()) { - TRACE("%s:%d %s\n", __func__, __LINE__, "exiting\n"); + TRACE("%s:%d exiting\n", __func__, __LINE__); break; } } @@ -1364,8 +1366,8 @@ THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadPa statusInformation = reinterpret_cast(ctx->GetStatusInformation()); if (!ctx->GetRequestRef()->GetClosed() && ctx->GetCb()) { - TRACE_VERBOSE("%s:%d ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", - __func__, __LINE__, reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), + TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", + __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), ctx->GetUserdata(), ctx->GetInternetStatus(), statusInformation, ctx->GetRequestRef().use_count()); ctx->GetCb()(ctx->GetRequest(), (DWORD_PTR)(ctx->GetUserdata()), @@ -1373,6 +1375,9 @@ THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadPa statusInformation, ctx->GetStatusInformationLength()); + TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", + __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), + ctx->GetUserdata(), ctx->GetInternetStatus(), statusInformation, ctx->GetRequestRef().use_count()); } ctx->GetRequestCompletionCb()(ctx->GetRequestRef(), ctx->GetInternetStatus()); delete ctx; @@ -1447,7 +1452,7 @@ void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr lck(request->GetHeaderStringMutex()); @@ -1508,7 +1514,7 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme request->GetReceiveResponseMutex().lock(); if (request->ResponseCallbackSendCounter() == 0) { - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)request); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); request->ResponseCallbackSendCounter()++; } @@ -1533,13 +1539,13 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme if (request->ResponseCallbackSendCounter() == 0) { - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)request); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); request->ResponseCallbackSendCounter()++; } if (request->ResponseCallbackSendCounter() == 1) { - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)request); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, request->GetHeaderString().length(), NULL, 0, false); request->ResponseCallbackSendCounter()++; } @@ -1556,20 +1562,20 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme { std::lock_guard lck(request->GetHeaderStringMutex()); request->GetHeaderString() = ""; - TRACE("%s:%d Redirect \n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p Redirect \n", __func__, __LINE__, (void*)request); request->ResponseCallbackEventCounter()++; request->GetRedirectPending() = true; return size * nmemb; } request->ResponseCallbackEventCounter()++; - TRACE("%s:%d HandleReceiveNotifications \n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p HandleReceiveNotifications \n", __func__, __LINE__, (void*)request); request->HandleReceiveNotifications(srequest); { std::lock_guard lck(request->GetReceiveCompletionEventMtx()); request->GetReceiveCompletionEventCounter()++; request->GetReceiveCompletionEvent().notify_all(); - TRACE("%s:%d GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); } } @@ -1598,21 +1604,21 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb request->GetReceiveResponseMutex().lock(); if (request->ResponseCallbackSendCounter() == 0) { - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)request); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); request->ResponseCallbackSendCounter()++; } if (request->ResponseCallbackSendCounter() == 1) { - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)request); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, request->GetHeaderString().length(), NULL, 0, false); request->ResponseCallbackSendCounter()++; } if (request->ResponseCallbackSendCounter() == 2) { - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", __func__, __LINE__, (void*)request); request->SetHeaderReceiveComplete(); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, 0, NULL, 0, false); request->ResponseCallbackSendCounter()++; @@ -1621,7 +1627,7 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb std::lock_guard lck(request->GetReceiveCompletionEventMtx()); request->GetReceiveCompletionEventCounter()++; request->GetReceiveCompletionEvent().notify_all(); - TRACE("%s:%d GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); } request->GetReceiveResponseMutex().unlock(); } @@ -1634,7 +1640,7 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb } if (request->HandleQueryDataNotifications(srequest, size * nmemb)) - TRACE("%s:%d GetQueryDataEvent().notify_all\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); } return size * nmemb; @@ -1679,7 +1685,7 @@ WinHttpRequestImp::WinHttpRequestImp(): WinHttpRequestImp::~WinHttpRequestImp() { - TRACE("==>%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); m_closed = true; @@ -1693,7 +1699,7 @@ WinHttpRequestImp::~WinHttpRequestImp() } if (GetAsync()) { - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); } else curl_easy_getinfo(GetCurl(), CURLINFO_PRIVATE, NULL); @@ -1705,14 +1711,14 @@ WinHttpRequestImp::~WinHttpRequestImp() if (m_HeaderList) curl_slist_free_all(m_HeaderList); - TRACE("<==%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); } static void RequestCompletionCb(std::shared_ptr requestRef, DWORD status) { if (status == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) { - TRACE_VERBOSE("%s:%d status:0x%lx\n", __func__, __LINE__, status); + TRACE_VERBOSE("%-35s:%-8d:%-16p status:0x%lx\n", __func__, __LINE__, (void*)(requestRef.get()), status); requestRef->GetClosed() = true; } } @@ -1767,7 +1773,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi if (request->GetClosed()) return -1; - TRACE("%s:%d request->GetTotalLength():%lu request->GetReadLength():%lu\n", __func__, __LINE__, request->GetTotalLength(), request->GetReadLength()); + TRACE("%-35s:%-8d:%-16p request->GetTotalLength():%lu request->GetReadLength():%lu\n", __func__, __LINE__, (void*)request, request->GetTotalLength(), request->GetReadLength()); if (((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && (request->GetType() == "PUT")) || (request->GetTotalLength() != request->GetReadLength())) { @@ -1778,7 +1784,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi return CURL_READFUNC_PAUSE; } request->GetReadDataEventCounter()--; - TRACE("%s:%d transfer resumed as already signalled:%lu\n", __func__, __LINE__, size * nmemb); + TRACE("%-35s:%-8d:%-16p transfer resumed as already signalled:%lu\n", __func__, __LINE__, (void*)request, size * nmemb); } if (request->GetAsync()) @@ -1811,7 +1817,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi { request->GetReadDataEventMtx().lock(); len = MIN(request->GetReadData().size(), size * nmemb); - TRACE("%s:%d writing additional length:%lu\n", __func__, __LINE__, len); + TRACE("%-35s:%-8d:%-16p writing additional length:%lu\n", __func__, __LINE__, (void*)request, len); std::copy(request->GetReadData().begin(), request->GetReadData().begin() + len, (char*)ptr); request->GetReadLength() += len; request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); @@ -1830,10 +1836,10 @@ int WinHttpRequestImp::SocketCallback(CURL *handle, curl_infotype type, switch (type) { case CURLINFO_TEXT: - TRACE_VERBOSE("== Info: %s", data); + TRACE_VERBOSE("%-35s:%-8d:%-16p == Info: %s", __func__, __LINE__, (void*)userp, data); return 0; default: /* in case a new one is introduced to shock us */ - TRACE_VERBOSE("%s:%d type:%d\n", __func__, __LINE__, type); + TRACE_VERBOSE("%-35s:%-8d:%-16p type:%d\n", __func__, __LINE__, (void*)userp, type); return 0; case CURLINFO_HEADER_IN: @@ -1855,7 +1861,7 @@ int WinHttpRequestImp::SocketCallback(CURL *handle, curl_infotype type, text = "<= Recv SSL data"; break; } - TRACE_VERBOSE("%s:%d %s\n", __func__, __LINE__, text); + TRACE_VERBOSE("%-35s:%-8d:%-16p %s\n", __func__, __LINE__, (void*)userp, text); return 0; } @@ -1871,7 +1877,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpen { std::shared_ptr session = std::make_shared (); - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*) (session.get())); if (dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { if (!pszProxyW) @@ -1901,7 +1907,8 @@ WINHTTPAPI HINTERNET WINAPI WinHttpConnect { WinHttpSessionImp *session = static_cast(hSession); - TRACE("%s:%d pswzServerName: " STRING_LITERAL " nServerPort:%d\n", __func__, __LINE__, pswzServerName, nServerPort); + TRACE("%-35s:%-8d:%-16p pswzServerName: " STRING_LITERAL " nServerPort:%d\n", + __func__, __LINE__, (void*)session, pswzServerName, nServerPort); ConvertCstrAssign(pswzServerName, WCTLEN(pswzServerName), session->GetServerName()); session->SetServerPort(nServerPort); @@ -1921,7 +1928,7 @@ BOOLAPI WinHttpCloseHandle( { WinHttpBase *base = static_cast(hInternet); - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); if (!base) return FALSE; @@ -1981,15 +1988,15 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( if (!pwszVerb) pwszVerb = TEXT("GET"); - TRACE("%s:%d pwszVerb = " STRING_LITERAL " pwszObjectName: " STRING_LITERAL " pwszVersion = " STRING_LITERAL " pwszReferrer = " STRING_LITERAL "\n", - __func__, __LINE__, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer); + TRACE("%-35s:%-8d:%-16p pwszVerb = " STRING_LITERAL " pwszObjectName: " STRING_LITERAL " pwszVersion = " STRING_LITERAL " pwszReferrer = " STRING_LITERAL "\n", + __func__, __LINE__, (void*)session, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer); std::shared_ptr srequest(new WinHttpRequestImp, [](WinHttpRequestImp *request) { std::shared_ptr close_request(request); - TRACE("%s::%d reference count reached to 0\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p reference count reached to 0\n", __func__, __LINE__, (void*)request); - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n", __func__, __LINE__, (void*)request); request->AsyncQueue(close_request, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0, NULL, 0, false); }); @@ -2103,7 +2110,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( std::string verb; ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), verb); - TRACE("%s:%d setting custom header %s\n", __func__, __LINE__, verb.c_str()); + TRACE("%-35s:%-8d:%-16p setting custom header %s\n", __func__, __LINE__, (void*)request, verb.c_str()); res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, verb.c_str()); if (res != CURLE_OK) { return NULL; @@ -2266,7 +2273,7 @@ WinHttpAddRequestHeaders if (!srequest) return FALSE; - TRACE("%s:%d lpszHeaders = " STRING_LITERAL " dwModifiers: %lu\n", __func__, __LINE__, + TRACE("%-35s:%-8d:%-16p lpszHeaders = " STRING_LITERAL " dwModifiers: %lu\n", __func__, __LINE__, (void*)request, lpszHeaders ? lpszHeaders: TEXT(""), dwModifiers); if (lpszHeaders) { @@ -2327,8 +2334,8 @@ BOOLAPI WinHttpSendRequest if ((dwTotalLength == 0) && (dwOptionalLength == 0) && (request->GetType() == "PUT")) customHeader += TEXT("Transfer-Encoding: chunked\r\n"); - TRACE("%s:%d lpszHeaders:%p dwHeadersLength:%lu lpOptional:%p dwOptionalLength:%lu dwTotalLength:%lu\n", - __func__, __LINE__, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength); + TRACE("%-35s:%-8d:%-16p lpszHeaders:%p dwHeadersLength:%lu lpOptional:%p dwOptionalLength:%lu dwTotalLength:%lu\n", + __func__, __LINE__, (void*)request, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength); if ((customHeader != TEXT("")) && !WinHttpAddRequestHeaders(hRequest, customHeader.c_str(), customHeader.length(), 0)) return FALSE; @@ -2484,29 +2491,29 @@ BOOLAPI WinHttpSendRequest { if (request->GetClosing()) { - TRACE("%s:%d \n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; } request->CleanUp(); - TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0, NULL, 0, false); - TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); if (!ComContainer::GetInstance().AddHandle(srequest, request->GetCurl())) return FALSE; - TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); ComContainer::GetInstance().KickStart(); - TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0, NULL, 0, false); - TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, 0, NULL, 0, false); - TRACE("%s:%d use_count = %lu\n", __func__, __LINE__, srequest.use_count()); + TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); } else { @@ -2539,7 +2546,7 @@ void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptr lck(GetReceiveCompletionEventMtx()); while (!GetReceiveCompletionEventCounter()) GetReceiveCompletionEvent().wait(lck); @@ -2547,7 +2554,7 @@ void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptrGetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && (request->GetType() == "PUT")) { @@ -2578,7 +2585,7 @@ WinHttpReceiveResponse { std::lock_guard lck(request->GetReadDataEventMtx()); request->GetReadDataEventCounter()++; - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); } ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); @@ -2587,7 +2594,7 @@ WinHttpReceiveResponse if (request->GetAsync()) { - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); request->WaitAsyncReceiveCompletion(srequest); } else @@ -2635,7 +2642,7 @@ bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr(available); - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, lpvStatusInformation); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, (void*)this, lpvStatusInformation); AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); } @@ -2646,14 +2653,14 @@ void WinHttpRequestImp::WaitAsyncQueryDataCompletion(std::shared_ptr lck(GetQueryDataEventMtx()); completed = GetQueryDataEventState(); } if (completed) { - TRACE("%s:%d transfer already finished\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p transfer already finished\n", __func__, __LINE__, (void*)this); HandleQueryDataNotifications(srequest, 0); } } @@ -2675,7 +2682,7 @@ WinHttpQueryDataAvailable if (request->GetClosing()) { - TRACE("%s:%d \n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; } @@ -2690,19 +2697,19 @@ WinHttpQueryDataAvailable { if (available == 0) { - TRACE("%s:%d !!!!!!!\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p !!!!!!!\n", __func__, __LINE__, (void*)request); request->WaitAsyncQueryDataCompletion(srequest); request->GetBodyStringMutex().lock(); length = request->GetResponseString().size(); available = length; - TRACE("%s:%d available = %lu\n", __func__, __LINE__, available); + TRACE("%-35s:%-8d:%-16p available = %lu\n", __func__, __LINE__, (void*)request, available); request->GetBodyStringMutex().unlock(); } else { - TRACE("%s:%d available = %lu\n", __func__, __LINE__, available); + TRACE("%-35s:%-8d:%-16p available = %lu\n", __func__, __LINE__, (void*)request, available); DWORD lpvStatusInformation = available; - TRACE("%s:%d WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, lpvStatusInformation); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, (void*)request, lpvStatusInformation); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); @@ -2777,13 +2784,13 @@ WinHttpReadData if (request->GetClosing()) { - TRACE("%s:%d \n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; } size_t readLength; - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); if (dwNumberOfBytesToRead == 0) { if (lpdwNumberOfBytesRead) @@ -2827,7 +2834,7 @@ WinHttpReadData if (lpdwNumberOfBytesRead) *lpdwNumberOfBytesRead = (DWORD)readLength; - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); return TRUE; } @@ -2846,7 +2853,7 @@ WinHttpSetTimeouts WinHttpRequestImp *request; CURLcode res; - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); if ((session = dynamic_cast(base))) { session->SetTimeout(nReceiveTimeout); @@ -2953,7 +2960,7 @@ BOOLAPI WinHttpQueryHeaders( dwInfoLevel &= ~WINHTTP_QUERY_FLAG_NUMBER; returnDWORD = true; } - TRACE("%s:%d dwInfoLevel = 0x%lx\n", __func__, __LINE__, dwInfoLevel); + TRACE("%-35s:%-8d:%-16p dwInfoLevel = 0x%lx\n", __func__, __LINE__, (void*)request, dwInfoLevel); if (returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) return FALSE; @@ -3011,7 +3018,7 @@ BOOLAPI WinHttpQueryHeaders( STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%lu"), retValue); outbuf[*lpdwBufferLength/sizeof(outbuf[0]) - 1] = TEXT('\0'); } - TRACE("%s:%d status code :%lu\n", __func__, __LINE__, retValue); + TRACE("%-35s:%-8d:%-16p status code :%lu\n", __func__, __LINE__, (void*)request, retValue); return TRUE; } @@ -3222,7 +3229,7 @@ BOOLAPI WinHttpSetOption( { WinHttpBase *base = static_cast(hInternet); - TRACE("%s:%d dwOption:%lu\n", __func__, __LINE__, dwOption); + TRACE("%-35s:%-8d:%-16p dwOption:%lu\n", __func__, __LINE__, (void*)base, dwOption); if (dwOption == WINHTTP_OPTION_MAX_CONNS_PER_SERVER) { @@ -3375,7 +3382,7 @@ WinHttpSetStatusCallback WINHTTP_STATUS_CALLBACK oldcb; DWORD olddwNotificationFlags; - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)session); if (hInternet == NULL) return WINHTTP_INVALID_STATUS_CALLBACK; @@ -3397,7 +3404,8 @@ WinHttpQueryOption WinHttpBase *base = static_cast(hInternet); WinHttpSessionImp *session; - TRACE("%s:%d\n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); + if (WINHTTP_OPTION_CONNECT_TIMEOUT == dwOption) { @@ -3733,11 +3741,11 @@ BOOLAPI WinHttpWriteData if (request->GetClosing()) { - TRACE("%s:%d \n", __func__, __LINE__); + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; } - TRACE("%s:%d dwNumberOfBytesToWrite:%lu\n", __func__, __LINE__, dwNumberOfBytesToWrite); + TRACE("%-35s:%-8d:%-16p dwNumberOfBytesToWrite:%lu\n", __func__, __LINE__, (void*)request, dwNumberOfBytesToWrite); if (request->GetAsync()) { { From 7055a349e1cbf3288bfd147c5540c96eb376529a Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 21 Jul 2019 19:55:33 -0400 Subject: [PATCH 23/41] Use queue instead of locks --- .../src/http/client/winhttppal/winhttppal.cpp | 304 ++++++++++-------- 1 file changed, 163 insertions(+), 141 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 1a72f7165d..de1dc9e338 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -377,10 +377,36 @@ class WinHttpConnectImp :public WinHttpBase struct BufferRequest { - LPCVOID m_Buffer = NULL; + LPVOID m_Buffer = NULL; size_t m_Length = 0; + size_t m_Used = 0; }; +static void QueueBufferRequest(std::vector &store, LPVOID buffer, size_t length) +{ + BufferRequest shr; + shr.m_Buffer = buffer; + shr.m_Length = length; + store.push_back(shr); +} + +static BufferRequest GetBufferRequest(std::vector &store) +{ + if (store.empty()) + return BufferRequest(); + BufferRequest shr = store.front(); + store.pop_back(); + return shr; +} + +static BufferRequest PeekBufferRequest(std::vector &store) +{ + if (store.empty()) + return BufferRequest(); + BufferRequest shr = store.front(); + return shr; +} + class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this { CURL *m_curl = NULL; @@ -411,10 +437,6 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::mutex m_HeaderStringMutex; std::mutex m_BodyStringMutex; - std::condition_variable m_CompletionEvent; - std::mutex m_CompletionEventMtx; - bool m_CompletionEventState = false; - std::condition_variable m_QueryDataEvent; std::mutex m_QueryDataEventMtx; bool m_QueryDataEventState = false; @@ -431,9 +453,6 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::atomic m_RedirectPending; - std::condition_variable m_DataAvailable; - std::mutex m_DataAvailableMtx; - std::atomic m_DataAvailableEventCounter; bool m_closing = false; bool m_closed = false; bool m_Completion = false; @@ -445,24 +464,11 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; DWORD m_NotificationFlags = 0; std::vector m_OutstandingWrites; + std::vector m_OutstandingReads; public: - void QueueWriteRequest(LPCVOID buffer, size_t length) - { - BufferRequest shr; - shr.m_Buffer = buffer; - shr.m_Length = length; - m_OutstandingWrites.push_back(shr); - } - - BufferRequest GetWriteRequest() - { - if (m_OutstandingWrites.empty()) - return BufferRequest(); - BufferRequest shr = m_OutstandingWrites.front(); - m_OutstandingWrites.pop_back(); - return shr; - } + std::vector &GetOutstandingWrites() { return m_OutstandingWrites; } + std::vector &GetOutstandingReads() { return m_OutstandingReads; } long &VerifyPeer() { return m_VerifyPeer; } long &VerifyHost() { return m_VerifyHost; } @@ -493,7 +499,6 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this void HandleReceiveNotifications(std::shared_ptr); void WaitAsyncReceiveCompletion(std::shared_ptr srequest); - size_t WaitDataAvailable(); CURLcode &GetCompletionCode() { return m_CompletionCode; } bool &GetCompletionStatus() { return m_Completion; } @@ -567,16 +572,12 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::atomic &GetRedirectPending() { return m_RedirectPending; } - std::condition_variable &GetDataAvailableEvent() { return m_DataAvailable; } - std::mutex &GetDataAvailableMtx() { return m_DataAvailableMtx; } - std::atomic &GetDataAvailableEventCounter() { return m_DataAvailableEventCounter; } - std::mutex &GetHeaderStringMutex() { return m_HeaderStringMutex; } std::mutex &GetBodyStringMutex() { return m_HeaderStringMutex; } BOOL AsyncQueue(std::shared_ptr &, DWORD dwInternetStatus, size_t statusInformationLength, - LPVOID async, DWORD asyncLength, bool allocate); + LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate); void SetHeaderReceiveComplete() { m_HeaderReceiveComplete = TRUE; } @@ -1239,7 +1240,6 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) DWORD dwInternetStatus; TRACE("%-35s:%-8d:%-16p type:%s result:%d\n", __func__, __LINE__, (void*)request, request->GetType().c_str(), m->data.result); - request->GetCompletionStatus() = true; request->GetCompletionCode() = m->data.result; // unblock ReceiveResponse if waiting @@ -1265,16 +1265,35 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) request->HandleQueryDataNotifications(srequest, 0); } - if (m->data.result == CURLE_OK) - { - TRACE("%s:%d m->data.result:%d\n", __func__, __LINE__, m->data.result); - std::lock_guard lck(request->GetDataAvailableMtx()); - request->GetDataAvailableEventCounter()++; - request->GetDataAvailableEvent().notify_all(); - } + request->GetBodyStringMutex().lock(); + + request->GetCompletionStatus() = true; if (m->data.result == CURLE_OK) { + DWORD read = 0; + BYTE *ptr = request->GetResponseString().data(); + size_t available = request->GetResponseString().size(); + while (1) + { + BufferRequest buf = GetBufferRequest(request->GetOutstandingReads()); + if (!buf.m_Length) + break; + + size_t len = MIN(buf.m_Length, available); + if (len) + { + TRACE("%-35s:%-8d:%-16p reading length:%lu written:%lu\n", __func__, __LINE__, (void*)request, len, read); + memcpy(static_cast(buf.m_Buffer), ptr, len); + } + + ptr = ptr + len; + available -= len; + + LPVOID StatusInformation = buf.m_Buffer; + + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, len, StatusInformation, sizeof(StatusInformation), false); + } } else if (m->data.result == CURLE_OPERATION_TIMEDOUT) { @@ -1295,6 +1314,9 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) #endif request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); } + + request->GetBodyStringMutex().unlock(); + } else if (m && (m->msg != CURLMSG_DONE)) { TRACE("%-35s:%-8d:%-16p unknown async request done\n", __func__, __LINE__, (void*)request); DWORD dwInternetStatus; @@ -1363,7 +1385,7 @@ THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadPa void *statusInformation = NULL; if (ctx->GetStatusInformationValid()) - statusInformation = reinterpret_cast(ctx->GetStatusInformation()); + statusInformation = ctx->GetStatusInformation(); if (!ctx->GetRequestRef()->GetClosed() && ctx->GetCb()) { TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", @@ -1423,7 +1445,7 @@ WinHttpSessionImp *GetImp(WinHttpBase *base) } else if ((request = dynamic_cast(base))) { - WinHttpConnectImp *connect = request->GetSession(); + connect = request->GetSession(); session = connect->GetHandle(); } else @@ -1522,7 +1544,7 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme } } - request->GetHeaderString().append(reinterpret_cast(ptr), size * nmemb); + request->GetHeaderString().append(static_cast(ptr), size * nmemb); request->GetTotalHeaderStringLength() += size * nmemb; if (request->GetHeaderString().find("\r\n\r\n") != std::string::npos) @@ -1583,16 +1605,51 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme } size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { + DWORD available = size * nmemb; + DWORD read = 0; WinHttpRequestImp *request = static_cast(rqst); std::shared_ptr srequest = request->shared_from_this(); if (!srequest) - return size * nmemb; + return available; { std::lock_guard lck(request->GetBodyStringMutex()); - request->GetResponseString().insert(request->GetResponseString().end(), - reinterpret_cast(ptr), - reinterpret_cast(ptr) + size * nmemb); + while (available) + { + BufferRequest buf = GetBufferRequest(request->GetOutstandingReads()); + if (!buf.m_Length) + break; + + size_t len = MIN(buf.m_Length, available); + + if (len) + { + TRACE("%-35s:%-8d:%-16p reading length:%lu written:%lu %p %ld\n", + __func__, __LINE__, (void*)request, len, read, buf.m_Buffer, buf.m_Length); + memcpy(static_cast(buf.m_Buffer), ptr, len); + } + + ptr = static_cast(ptr) + len; + available -= len; + + if (len) + { + LPVOID StatusInformation = buf.m_Buffer; + + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, len, StatusInformation, sizeof(StatusInformation), false); + } + + read += len; + } + + if (available) + { + request->GetResponseString().insert(request->GetResponseString().end(), + reinterpret_cast(ptr), + reinterpret_cast(ptr) + available); + + read += available; + } } if (request->GetAsync()) { @@ -1632,18 +1689,11 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb request->GetReceiveResponseMutex().unlock(); } - { - std::lock_guard lck(request->GetDataAvailableMtx()); - request->GetDataAvailableEventCounter()++; - request->GetDataAvailableEvent().notify_all(); - TRACE_VERBOSE("%s:%d data available:%lu\n", __func__, __LINE__, size * nmemb); - } - - if (request->HandleQueryDataNotifications(srequest, size * nmemb)) + if (request->HandleQueryDataNotifications(srequest, available)) TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); } - return size * nmemb; + return read; } void WinHttpRequestImp::CleanUp() @@ -1657,25 +1707,24 @@ void WinHttpRequestImp::CleanUp() m_ReadDataEventCounter = 0; m_UploadThreadExitStatus = false; m_ReceiveCompletionEventCounter = 0; - m_CompletionEventState = false; m_QueryDataPending = false; m_ReceiveResponsePending = false; m_RedirectPending = false; m_ReceiveResponseEventCounter = 0; m_ReceiveResponseSendCounter = 0; - m_DataAvailableEventCounter = 0; m_OutstandingWrites.clear(); + m_OutstandingReads.clear(); m_Completion = false; } WinHttpRequestImp::WinHttpRequestImp(): + m_UploadCallbackThread(), m_QueryDataPending(false), m_ReceiveCompletionEventCounter(0), m_ReceiveResponsePending(false), m_ReceiveResponseEventCounter(0), m_ReceiveResponseSendCounter(0), - m_RedirectPending(false), - m_DataAvailableEventCounter(0) + m_RedirectPending(false) { m_curl = ComContainer::GetInstance().AllocCURL(); if (!m_curl) @@ -1733,11 +1782,10 @@ BOOL WinHttpRequestImp::AsyncQueue(std::shared_ptr &requestRe WINHTTP_STATUS_CALLBACK cb = GetCallback(&dwNotificationFlags); LPVOID userdata = NULL; - if (this->GetUserData()) - userdata = (LPVOID)GetUserData(); + userdata = GetUserData(); ctx = new UserCallbackContext(requestRef, dwInternetStatus, (DWORD)statusInformationLength, - dwNotificationFlags, cb, userdata, (LPVOID)statusInformation, + dwNotificationFlags, cb, userdata, statusInformation, statusInformationCopySize, allocate, RequestCompletionCb); if (ctx) { UserCallbackContainer::GetInstance().Queue(ctx); @@ -1763,8 +1811,8 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi if (request->GetOptionalData().length() > 0) { len = request->GetOptionalData().length(); - TRACE("%s:%d writing optional length of %lu\n", __func__, __LINE__, len); - std::copy(request->GetOptionalData().begin(), request->GetOptionalData().end(), (char*)ptr); + TRACE("%-35s:%-8d:%-16p writing optional length of %lu\n", __func__, __LINE__, (void*)request, len); + std::copy(request->GetOptionalData().begin(), request->GetOptionalData().end(), static_cast(ptr)); request->GetOptionalData().erase(0, len); request->GetReadLength() += len; return len; @@ -1780,34 +1828,45 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi std::unique_lock getReadDataEventHndlMtx(request->GetReadDataEventMtx()); if (!request->GetReadDataEventCounter()) { - TRACE("%s:%d transfer paused:%lu\n", __func__, __LINE__, size * nmemb); + TRACE("%-35s:%-8d:%-16p transfer paused:%lu\n", __func__, __LINE__, (void*)request, size * nmemb); return CURL_READFUNC_PAUSE; } - request->GetReadDataEventCounter()--; TRACE("%-35s:%-8d:%-16p transfer resumed as already signalled:%lu\n", __func__, __LINE__, (void*)request, size * nmemb); } if (request->GetAsync()) { - DWORD dwInternetStatus; DWORD result; size_t written = 0; { request->GetReadDataEventMtx().lock(); - BufferRequest buf = request->GetWriteRequest(); - len = MIN(buf.m_Length, size * nmemb); + BufferRequest buf = PeekBufferRequest(request->GetOutstandingWrites()); + len = MIN(buf.m_Length - buf.m_Used, size * nmemb); request->GetReadLength() += len; - request->GetReadDataEventMtx().unlock(); - TRACE("%s:%d writing additional length:%lu written:%lu\n", __func__, __LINE__, len, written); + TRACE("%-35s:%-8d:%-16p writing additional length:%lu written:%lu buf.m_Length:%lu buf.m_Used:%lu\n", + __func__, __LINE__, (void*)request, len, written, buf.m_Length, buf.m_Used); + if (len) - memcpy((char*)ptr, buf.m_Buffer, len); + memcpy(static_cast(ptr), (char*)buf.m_Buffer + buf.m_Used, len); + + buf.m_Used += len; result = len; - dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; - request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); + if (buf.m_Used == buf.m_Length) + { + DWORD dwInternetStatus; + + dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; + request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); + GetBufferRequest(request->GetOutstandingWrites()); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:%lu written:%lu\n", __func__, __LINE__, (void*)request, len, written); + request->GetReadDataEventCounter()--; + } + + request->GetReadDataEventMtx().unlock(); written += len; } @@ -1816,9 +1875,10 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi else { request->GetReadDataEventMtx().lock(); + request->GetReadDataEventCounter()--; len = MIN(request->GetReadData().size(), size * nmemb); TRACE("%-35s:%-8d:%-16p writing additional length:%lu\n", __func__, __LINE__, (void*)request, len); - std::copy(request->GetReadData().begin(), request->GetReadData().begin() + len, (char*)ptr); + std::copy(request->GetReadData().begin(), request->GetReadData().begin() + len, static_cast(ptr)); request->GetReadLength() += len; request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); request->GetReadData().shrink_to_fit(); @@ -1932,7 +1992,7 @@ BOOLAPI WinHttpCloseHandle( if (!base) return FALSE; - if (WinHttpHandleContainer::Instance().IsRegistered(reinterpret_cast(hInternet))) + if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) { WinHttpRequestImp *request = dynamic_cast(base); if (!request) @@ -1947,7 +2007,7 @@ BOOLAPI WinHttpCloseHandle( return TRUE; } - if (WinHttpHandleContainer::Instance().IsRegistered(reinterpret_cast(hInternet))) + if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) { WinHttpSessionImp *session = dynamic_cast(base); if (session) @@ -1958,7 +2018,7 @@ BOOLAPI WinHttpCloseHandle( return TRUE; } - if (WinHttpHandleContainer::Instance().IsRegistered(reinterpret_cast(hInternet))) + if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) { WinHttpConnectImp *connect = dynamic_cast(base); if (connect) @@ -2156,17 +2216,12 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( //static const char *pHeaderFile = "dumpit"; const char *pPassphrase = NULL; -#ifdef USE_ENGINE - pKeyName = "rsa_test"; - pKeyType = "ENG"; - pEngine = "chil"; /* for nChiper HSM... */ -#else pKeyName = NULL; pKeyType = "PEM"; pCertFile = NULL; pCACertFile = NULL; pEngine = NULL; -#endif + if (const char* env_p = std::getenv("WINHTTP_PAL_KEYNAME")) pKeyName = env_p; @@ -2182,6 +2237,9 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( if (const char* env_p = std::getenv("WINHTTP_PAL_ENGINE")) pEngine = env_p; + if (const char* env_p = std::getenv("WINHTTP_PAL_KEY_PASSPHRASE")) + pPassphrase = env_p; + if (pEngine) { if (curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE, pEngine) != CURLE_OK) { /* load the crypto engine */ @@ -2285,7 +2343,7 @@ WinHttpAddRequestHeaders } else { - nstringLen = dwHeadersLength + 1; + nstringLen = (size_t)dwHeadersLength + 1; } std::string &headers = request->GetOutgoingHeaderList(); @@ -2724,47 +2782,6 @@ WinHttpQueryDataAvailable return TRUE; } -size_t WinHttpRequestImp::WaitDataAvailable() -{ - size_t length, remainingLength, readLength; - - GetBodyStringMutex().lock(); - length = GetResponseString().size(); - readLength = remainingLength = length; - if (GetAsync() == TRUE) - { -/* check the size */ - while (((length == 0) || (remainingLength == 0)) && - (!GetCompletionStatus())) - { - GetBodyStringMutex().unlock(); - - TRACE("%s:%d\n", __func__, __LINE__); - ComContainer::GetInstance().KickStart(); - - TRACE("%s:%d\n", __func__, __LINE__); - { - std::unique_lock lck(GetDataAvailableMtx()); - while (!GetDataAvailableEventCounter() && !GetCompletionStatus()) - GetDataAvailableEvent().wait(lck); - lck.unlock(); - - TRACE("%s:%d\n", __func__, __LINE__); - GetDataAvailableEventCounter()--; - } - - GetBodyStringMutex().lock(); - - length = GetResponseString().size(); - readLength = remainingLength = length; - TRACE("%s:%d length:%lu readLength:%lu completion: %d\n", __func__, __LINE__, - length, readLength, GetCompletionStatus()); - } - } - GetBodyStringMutex().unlock(); - return readLength; -} - BOOLAPI WinHttpReadData ( @@ -2805,32 +2822,35 @@ WinHttpReadData return TRUE; } - readLength = request->WaitDataAvailable(); - request->GetBodyStringMutex().lock(); - if (readLength > dwNumberOfBytesToRead) - { - readLength = dwNumberOfBytesToRead; - } + readLength = request->GetResponseString().size(); if (readLength) { - std::copy(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength, reinterpret_cast(lpBuffer)); + if (readLength > dwNumberOfBytesToRead) + { + readLength = dwNumberOfBytesToRead; + } + std::copy(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength, static_cast(lpBuffer)); request->GetResponseString().erase(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength); request->GetResponseString().shrink_to_fit(); } - request->GetBodyStringMutex().unlock(); - if (readLength == 0) - { - TRACE("%s", "!!!!!!!!!!done!!!!!!!!!!!!!!!\n"); - } - TRACE("%s:%d lpBuffer: %p length:%lu\n", __func__, __LINE__, lpBuffer, readLength); if (request->GetAsync()) { - LPVOID StatusInformation = (LPVOID)lpBuffer; + if ((readLength == 0) && (!request->GetCompletionStatus())) + { + TRACE("%-35s:%-8d:%-16p Queueing pending reads %p %ld\n", __func__, __LINE__, (void*)request, lpBuffer, dwNumberOfBytesToRead); + QueueBufferRequest(request->GetOutstandingReads(), lpBuffer, dwNumberOfBytesToRead); + } + else + { + TRACE("%-35s:%-8d:%-16p lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)request, lpBuffer, readLength); + LPVOID StatusInformation = (LPVOID)lpBuffer; - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, readLength, (LPVOID)StatusInformation, sizeof(StatusInformation), false); + request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, readLength, (LPVOID)StatusInformation, sizeof(StatusInformation), false); + } } + request->GetBodyStringMutex().unlock(); if (lpdwNumberOfBytesRead) *lpdwNumberOfBytesRead = (DWORD)readLength; @@ -3406,6 +3426,8 @@ WinHttpQueryOption TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); + if (!base) + return FALSE; if (WINHTTP_OPTION_CONNECT_TIMEOUT == dwOption) { @@ -3750,7 +3772,7 @@ BOOLAPI WinHttpWriteData { { std::lock_guard lck(request->GetReadDataEventMtx()); - request->QueueWriteRequest(lpBuffer, dwNumberOfBytesToWrite); + QueueBufferRequest(request->GetOutstandingWrites(), const_cast(lpBuffer), dwNumberOfBytesToWrite); request->GetReadDataEventCounter()++; } From df774d5ae5de8f7c20443ddd3a7b655fba0791a1 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 23 Jul 2019 12:07:38 -0400 Subject: [PATCH 24/41] Simplify reads, add breadcrumbs on failure --- .../src/http/client/winhttppal/winhttppal.cpp | 334 ++++++++---------- 1 file changed, 152 insertions(+), 182 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index de1dc9e338..54af7fc5a2 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -39,6 +39,7 @@ extern "C" { #include +#include } class WinHttpSessionImp; @@ -387,6 +388,7 @@ static void QueueBufferRequest(std::vector &store, LPVOID buffer, BufferRequest shr; shr.m_Buffer = buffer; shr.m_Length = length; + shr.m_Used = 0; store.push_back(shr); } @@ -395,16 +397,13 @@ static BufferRequest GetBufferRequest(std::vector &store) if (store.empty()) return BufferRequest(); BufferRequest shr = store.front(); - store.pop_back(); + store.erase(store.begin()); return shr; } -static BufferRequest PeekBufferRequest(std::vector &store) +static BufferRequest &PeekBufferRequest(std::vector &store) { - if (store.empty()) - return BufferRequest(); - BufferRequest shr = store.front(); - return shr; + return store.front(); } class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this @@ -437,14 +436,11 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::mutex m_HeaderStringMutex; std::mutex m_BodyStringMutex; - std::condition_variable m_QueryDataEvent; std::mutex m_QueryDataEventMtx; bool m_QueryDataEventState = false; std::atomic m_QueryDataPending; - std::condition_variable m_ReceiveCompletionEvent; std::mutex m_ReceiveCompletionEventMtx; - std::atomic m_ReceiveCompletionEventCounter; std::mutex m_ReceiveResponseMutex; std::atomic m_ReceiveResponsePending; @@ -492,7 +488,6 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this bool &GetQueryDataEventState() { return m_QueryDataEventState; } std::mutex &GetQueryDataEventMtx() { return m_QueryDataEventMtx; } - std::condition_variable &GetQueryDataEvent() { return m_QueryDataEvent; } bool HandleQueryDataNotifications(std::shared_ptr, size_t available); void WaitAsyncQueryDataCompletion(std::shared_ptr); @@ -545,18 +540,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::mutex &GetReadDataEventMtx() { return m_ReadDataEventMtx; } DWORD &GetReadDataEventCounter() { return m_ReadDataEventCounter; } - // sent by WriteHeaderFunction and WriteBodyFunction during incoming data - // to send user notifications for the following events: - // - // WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE - // WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED - // WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE - // - // WinHttpReceiveResponse is blocked until one of the above events happen - // - std::condition_variable &GetReceiveCompletionEvent() { return m_ReceiveCompletionEvent; } std::mutex &GetReceiveCompletionEventMtx() { return m_ReceiveCompletionEventMtx; } - std::atomic &GetReceiveCompletionEventCounter() { return m_ReceiveCompletionEventCounter; } // counters used to see which one of these events are observed: ResponseCallbackEventCounter // counters used to see which one of these events are broadcasted: ResponseCallbackSendCounter @@ -577,7 +561,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this BOOL AsyncQueue(std::shared_ptr &, DWORD dwInternetStatus, size_t statusInformationLength, - LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate); + LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate); void SetHeaderReceiveComplete() { m_HeaderReceiveComplete = TRUE; } @@ -788,8 +772,8 @@ class UserCallbackContainer return the_instance; } private: - UserCallbackContainer(const UserCallbackContainer&); - UserCallbackContainer& operator=(const UserCallbackContainer&); + UserCallbackContainer(const UserCallbackContainer&); + UserCallbackContainer& operator=(const UserCallbackContainer&); }; class EnvInit @@ -1029,8 +1013,8 @@ class ComContainer return rc; } private: - ComContainer(const ComContainer&); - ComContainer& operator=(const ComContainer&); + ComContainer(const ComContainer&); + ComContainer& operator=(const ComContainer&); }; template @@ -1242,15 +1226,6 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) TRACE("%-35s:%-8d:%-16p type:%s result:%d\n", __func__, __LINE__, (void*)request, request->GetType().c_str(), m->data.result); request->GetCompletionCode() = m->data.result; - // unblock ReceiveResponse if waiting - if (m->data.result != CURLE_OK) - { - std::lock_guard lck(request->GetReceiveCompletionEventMtx()); - request->GetReceiveCompletionEventCounter()++; - request->GetReceiveCompletionEvent().notify_all(); - TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); - } - if (m->data.result == CURLE_OK) { if (request->HandleQueryDataNotifications(srequest, 0)) @@ -1260,7 +1235,6 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) { std::lock_guard lck(request->GetQueryDataEventMtx()); request->GetQueryDataEventState() = true; - request->GetQueryDataEvent().notify_all(); } request->HandleQueryDataNotifications(srequest, 0); } @@ -1473,7 +1447,7 @@ void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr lck(request->GetHeaderStringMutex()); - if (request->GetAsync() && request->GetHeaderString().length() == 0) { - request->ResponseCallbackEventCounter()++; - - if (request->GetReceiveResponsePending()) { - request->GetReceiveResponseMutex().lock(); - - if (request->ResponseCallbackSendCounter() == 0) { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)request); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); - request->ResponseCallbackSendCounter()++; - } - request->GetReceiveResponseMutex().unlock(); - } - } - request->GetHeaderString().append(static_cast(ptr), size * nmemb); request->GetTotalHeaderStringLength() += size * nmemb; @@ -1551,29 +1510,6 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme EofHeaders = true; if (request->GetHeaderString().find("\n\n") != std::string::npos) EofHeaders = true; - - if (request->GetAsync() && EofHeaders) { - request->ResponseCallbackEventCounter()++; - - if (request->GetReceiveResponsePending()) - { - request->GetReceiveResponseMutex().lock(); - - if (request->ResponseCallbackSendCounter() == 0) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)request); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); - request->ResponseCallbackSendCounter()++; - } - if (request->ResponseCallbackSendCounter() == 1) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)request); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, request->GetHeaderString().length(), NULL, 0, false); - request->ResponseCallbackSendCounter()++; - } - request->GetReceiveResponseMutex().unlock(); - } - } } if (EofHeaders && request->GetAsync()) { @@ -1585,19 +1521,17 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme std::lock_guard lck(request->GetHeaderStringMutex()); request->GetHeaderString() = ""; TRACE("%-35s:%-8d:%-16p Redirect \n", __func__, __LINE__, (void*)request); - request->ResponseCallbackEventCounter()++; request->GetRedirectPending() = true; return size * nmemb; } - request->ResponseCallbackEventCounter()++; - TRACE("%-35s:%-8d:%-16p HandleReceiveNotifications \n", __func__, __LINE__, (void*)request); - request->HandleReceiveNotifications(srequest); { std::lock_guard lck(request->GetReceiveCompletionEventMtx()); - request->GetReceiveCompletionEventCounter()++; - request->GetReceiveCompletionEvent().notify_all(); - TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); + { + request->ResponseCallbackEventCounter()++; + request->HandleReceiveNotifications(srequest); + TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); + } } } @@ -1653,42 +1587,6 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb } if (request->GetAsync()) { - bool expected = true; - - bool result = std::atomic_compare_exchange_strong(&request->GetReceiveResponsePending(), &expected, false); - - if (result) { - request->GetReceiveResponseMutex().lock(); - if (request->ResponseCallbackSendCounter() == 0) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)request); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); - request->ResponseCallbackSendCounter()++; - } - - if (request->ResponseCallbackSendCounter() == 1) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)request); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, request->GetHeaderString().length(), NULL, 0, false); - request->ResponseCallbackSendCounter()++; - } - - if (request->ResponseCallbackSendCounter() == 2) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", __func__, __LINE__, (void*)request); - request->SetHeaderReceiveComplete(); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, 0, NULL, 0, false); - request->ResponseCallbackSendCounter()++; - } - { - std::lock_guard lck(request->GetReceiveCompletionEventMtx()); - request->GetReceiveCompletionEventCounter()++; - request->GetReceiveCompletionEvent().notify_all(); - TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); - } - request->GetReceiveResponseMutex().unlock(); - } - if (request->HandleQueryDataNotifications(srequest, available)) TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); } @@ -1706,7 +1604,6 @@ void WinHttpRequestImp::CleanUp() m_ReadData.clear(); m_ReadDataEventCounter = 0; m_UploadThreadExitStatus = false; - m_ReceiveCompletionEventCounter = 0; m_QueryDataPending = false; m_ReceiveResponsePending = false; m_RedirectPending = false; @@ -1720,7 +1617,6 @@ void WinHttpRequestImp::CleanUp() WinHttpRequestImp::WinHttpRequestImp(): m_UploadCallbackThread(), m_QueryDataPending(false), - m_ReceiveCompletionEventCounter(0), m_ReceiveResponsePending(false), m_ReceiveResponseEventCounter(0), m_ReceiveResponseSendCounter(0), @@ -1805,7 +1701,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi if (!srequest) return size * nmemb; - size_t len; + size_t len = 0; TRACE("request->GetTotalLength(): %lu\n", request->GetTotalLength()); if (request->GetOptionalData().length() > 0) @@ -1836,41 +1732,45 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi if (request->GetAsync()) { - DWORD result; - size_t written = 0; + std::lock_guard lck(request->GetReadDataEventMtx()); + if (!request->GetOutstandingWrites().empty()) { - request->GetReadDataEventMtx().lock(); - BufferRequest buf = PeekBufferRequest(request->GetOutstandingWrites()); + BufferRequest &buf = PeekBufferRequest(request->GetOutstandingWrites()); len = MIN(buf.m_Length - buf.m_Used, size * nmemb); - request->GetReadLength() += len; - TRACE("%-35s:%-8d:%-16p writing additional length:%lu written:%lu buf.m_Length:%lu buf.m_Used:%lu\n", - __func__, __LINE__, (void*)request, len, written, buf.m_Length, buf.m_Used); + if (request->GetTotalLength()) + { + size_t remaining = request->GetTotalLength() - request->GetReadLength(); + len = MIN(len, remaining); + } + + request->GetReadLength() += len; + TRACE("%-35s:%-8d:%-16p writing additional length:%lu buf.m_Length:%lu buf.m_Buffer:%p buf.m_Used:%lu\n", + __func__, __LINE__, (void*)request, len, buf.m_Length, buf.m_Buffer, buf.m_Used); if (len) + { memcpy(static_cast(ptr), (char*)buf.m_Buffer + buf.m_Used, len); + } buf.m_Used += len; - result = len; - if (buf.m_Used == buf.m_Length) { DWORD dwInternetStatus; + DWORD result; + result = buf.m_Length; dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); GetBufferRequest(request->GetOutstandingWrites()); - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:%lu written:%lu\n", __func__, __LINE__, (void*)request, len, written); + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:%p buf.m_Length:%lu\n", __func__, __LINE__, (void*)request, buf.m_Buffer, buf.m_Length); request->GetReadDataEventCounter()--; } - request->GetReadDataEventMtx().unlock(); - written += len; + TRACE("%-35s:%-8d:%-16p chunk written:%lu\n", __func__, __LINE__, (void*)request, len); } - - len = written; } else { @@ -2446,73 +2346,119 @@ BOOLAPI WinHttpSendRequest request->GetTotalLength() = dwTotalLength; res = curl_easy_setopt(request->GetCurl(), CURLOPT_READDATA, request); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_READFUNCTION, request->ReadCallback); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGFUNCTION, request->SocketCallback); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGDATA, request); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEFUNCTION, request->WriteBodyFunction); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEDATA, request); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERFUNCTION, request->WriteHeaderFunction); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERDATA, request); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_PRIVATE, request); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_FOLLOWLOCATION, 1L); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } if (winhttp_tracing_verbose) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_VERBOSE, 1); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } } /* enable TCP keep-alive for this transfer */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPALIVE, 1L); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } /* keep-alive idle time to 120 seconds */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPIDLE, 120L); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } /* interval time between keep-alive probes: 60 seconds */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPINTVL, 60L); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYPEER, request->VerifyPeer()); - if (res != CURLE_OK) { + if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; } res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYHOST, request->VerifyHost()); - if (res != CURLE_OK) { + if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; } @@ -2526,7 +2472,10 @@ BOOLAPI WinHttpSendRequest if (maxConnections) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_MAXCONNECTS, maxConnections); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); return FALSE; + } } if (dwContext) @@ -2542,7 +2491,10 @@ BOOLAPI WinHttpSendRequest if (securityProtocols) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLVERSION, securityProtocols); if (res != CURLE_OK) + { + TRACE("%-35s:%-8d:%-16p securityProtocols:0x%lx res:%d\n", __func__, __LINE__, (void*)request, securityProtocols, res); return FALSE; + } } if (request->GetAsync()) @@ -2602,18 +2554,16 @@ BOOLAPI WinHttpSendRequest void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptr srequest) { bool expected = false; - bool result = std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, true); + std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, true); TRACE("%-35s:%-8d:%-16p GetReceiveResponsePending() = %d\n", __func__, __LINE__, (void*)this, (int) GetReceiveResponsePending()); - std::unique_lock lck(GetReceiveCompletionEventMtx()); - while (!GetReceiveCompletionEventCounter()) - GetReceiveCompletionEvent().wait(lck); - lck.unlock(); - - if (result && (GetCompletionCode() == CURLE_OK)) { - TRACE("%-35s:%-8d:%-16p HandleReceiveNotifications \n", __func__, __LINE__, (void*)this); - HandleReceiveNotifications(srequest); + std::lock_guard lck(GetReceiveCompletionEventMtx()); + if (ResponseCallbackEventCounter()) + { + TRACE("%-35s:%-8d:%-16p HandleReceiveNotifications \n", __func__, __LINE__, (void*)this); + HandleReceiveNotifications(srequest); + } } } @@ -2873,7 +2823,8 @@ WinHttpSetTimeouts WinHttpRequestImp *request; CURLcode res; - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); + TRACE("%-35s:%-8d:%-16p nResolveTimeout:%d nConnectTimeout:%d nSendTimeout:%d nReceiveTimeout:%d\n", + __func__, __LINE__, (void*)base, nResolveTimeout, nConnectTimeout, nSendTimeout, nReceiveTimeout); if ((session = dynamic_cast(base))) { session->SetTimeout(nReceiveTimeout); @@ -2924,27 +2875,27 @@ static TSTRING FindRegex(const TSTRING &subject,const TSTRING ®str) bool is_newline(char i) { - return (i == '\n') || (i == '\r'); + return (i == '\n') || (i == '\r'); } template std::basic_string nullize_newlines(const std::basic_string& str) { - std::basic_string result; - result.reserve(str.size()); - - auto cursor = str.begin(); - const auto end = str.end(); - for (;;) { - cursor = std::find_if_not(cursor, end, is_newline); - if (cursor == end) { - return result; - } - - const auto nextNewline = std::find_if(cursor, end, is_newline); - result.append(cursor, nextNewline); - result.push_back(CharT{}); - cursor = nextNewline; - } + std::basic_string result; + result.reserve(str.size()); + + auto cursor = str.begin(); + const auto end = str.end(); + for (;;) { + cursor = std::find_if_not(cursor, end, is_newline); + if (cursor == end) { + return result; + } + + const auto nextNewline = std::find_if(cursor, end, is_newline); + result.append(cursor, nextNewline); + result.push_back(CharT{}); + cursor = nextNewline; + } } BOOLAPI WinHttpQueryHeaders( @@ -3213,31 +3164,50 @@ BOOLAPI WinHttpQueryHeaders( DWORD ConvertSecurityProtocol(DWORD offered) { - DWORD curlOffered = CURL_SSLVERSION_DEFAULT; + DWORD min = 0; + DWORD max = 0; - if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) - curlOffered = CURL_SSLVERSION_SSLv2; - - if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) - curlOffered = CURL_SSLVERSION_SSLv3; + if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) { + min = CURL_SSLVERSION_SSLv2; + } + else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) { + min = CURL_SSLVERSION_SSLv3; + } if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { - curlOffered = CURL_SSLVERSION_TLSv1; - curlOffered |= CURL_SSLVERSION_MAX_TLSv1_0; - } + min = CURL_SSLVERSION_TLSv1_0; - if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { - curlOffered = CURL_SSLVERSION_TLSv1; - curlOffered &= ~0xFFFF; - curlOffered |= CURL_SSLVERSION_MAX_TLSv1_1; } + else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { + min = CURL_SSLVERSION_TLSv1_1; + } + else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { + min = CURL_SSLVERSION_TLSv1_2; + } + else + min = CURL_SSLVERSION_DEFAULT; +#if (LIBCURL_VERSION_MAJOR==7) && (LIBCURL_VERSION_MINOR >= 61) if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { - curlOffered = CURL_SSLVERSION_TLSv1; - curlOffered &= ~0xFFFF; - curlOffered |= CURL_SSLVERSION_MAX_TLSv1_2; + max = CURL_SSLVERSION_MAX_TLSv1_2; + } + else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { + max = CURL_SSLVERSION_MAX_TLSv1_1; + } + else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { + max = CURL_SSLVERSION_MAX_TLSv1_0; + } + else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) { + max = CURL_SSLVERSION_SSLv3; } - return curlOffered; + else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) { + max = CURL_SSLVERSION_SSLv2; + } + else + max = CURL_SSLVERSION_MAX_DEFAULT; +#endif + + return min | max; } BOOLAPI WinHttpSetOption( From 0b8609a81f720c9e31316ce17b81367f429b935c Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 23 Jul 2019 16:46:19 -0400 Subject: [PATCH 25/41] Ignore http 100 continue --- Release/src/http/client/winhttppal/winhttppal.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 54af7fc5a2..723bb7a01e 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -1525,13 +1525,16 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme return size * nmemb; } + if (retValue != 100) { std::lock_guard lck(request->GetReceiveCompletionEventMtx()); - { - request->ResponseCallbackEventCounter()++; - request->HandleReceiveNotifications(srequest); - TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); - } + request->ResponseCallbackEventCounter()++; + request->HandleReceiveNotifications(srequest); + TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); + } + else + { + TRACE("%-35s:%-8d:%-16p retValue = %lu \n", __func__, __LINE__, (void*)request, retValue); } } From 95bfd6b414bc7b554cf8197cde89ca9c45baa912 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Fri, 26 Jul 2019 12:23:26 -0400 Subject: [PATCH 26/41] Remove buffering support --- Release/include/cpprest/http_client.h | 6 +++--- Release/src/http/client/http_client_curl.cpp | 8 -------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index 3227f0199b..2b6d011db2 100644 --- a/Release/include/cpprest/http_client.h +++ b/Release/include/cpprest/http_client.h @@ -104,7 +104,7 @@ class http_client_config #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) , m_tlsext_sni_enabled(true) #endif -#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) , m_buffer_request(false) #endif { @@ -262,7 +262,7 @@ class http_client_config void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; } #endif -#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) /// /// Checks if request data buffering is turned on, the default is off. /// @@ -389,7 +389,7 @@ class http_client_config std::function m_ssl_context_callback; bool m_tlsext_sni_enabled; #endif -#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) bool m_buffer_request; #endif }; diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_curl.cpp index 63da82d06b..62cd9a10eb 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_curl.cpp @@ -1036,14 +1036,6 @@ class curl_client final : public _http_client_communicator return; } - // Only need to cache the request body if user specified and the request stream doesn't support seeking. - if (curl_context->m_bodyType != no_body && client_config().buffer_request() && - !curl_context->_get_readbuffer().can_seek()) - { - curl_context->m_readBufferCopy = - ::utility::details::make_unique<::concurrency::streams::container_buffer>>(); - } - _start_request_send(curl_context, content_length); return; From 593954c248e741a00b918919a33073bb78fd8aae Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Fri, 26 Jul 2019 16:38:28 -0400 Subject: [PATCH 27/41] Cleanup repeating patterns and add null checks --- .../src/http/client/winhttppal/winhttppal.cpp | 548 +++++++----------- 1 file changed, 211 insertions(+), 337 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 723bb7a01e..582e6ac4e5 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -21,10 +21,16 @@ #include #include #include +#include +#include #include #include #include +#ifdef WIN32 +#define localtime_r(_Time, _Tm) localtime_s(_Tm, _Time) +#endif + #define _WINHTTP_INTERNAL_ #ifdef _MSC_VER #include @@ -49,7 +55,7 @@ class WinHttpRequestImp; #pragma comment(lib, "Ws2_32.lib") #endif -std::map StatusCodeMap = { +static const std::map StatusCodeMap = { { 100, TEXT("Continue") }, { 101, TEXT("Switching Protocols") }, { 200, TEXT("OK") }, @@ -100,14 +106,14 @@ enum WINHTTP_CLASS_IMP, }; -int winhttp_tracing = false; -int winhttp_tracing_verbose = false; +static int winhttp_tracing = false; +static int winhttp_tracing_verbose = false; #ifdef _MSC_VER int gettimeofday(struct timeval * tp, struct timezone * tzp); #endif -std::mutex trcmtx; +static std::mutex trcmtx; #ifndef _MSC_VER static void TRACE_INTERNAL(const char *fmt, ...) __attribute__ ((format (gnu_printf, 1, 2))); @@ -119,17 +125,21 @@ static void TRACE_INTERNAL(const char *fmt, ...) va_list args; va_start(args, fmt); - struct timeval tv; - time_t nowtime; - struct tm *nowtm; - char tmbuf[64], buf[64]; + tm localTime; + std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); + time_t now = std::chrono::system_clock::to_time_t(t); + localtime_r(&now, &localTime); + + const std::chrono::duration tse = t.time_since_epoch(); + std::chrono::seconds::rep milliseconds = std::chrono::duration_cast(tse).count() % 1000; - gettimeofday(&tv, NULL); - nowtime = tv.tv_sec; - nowtm = localtime(&nowtime); - strftime(tmbuf, sizeof tmbuf, "%H:%M:%S", nowtm); - snprintf(buf, sizeof buf, "%s.%06ld ", tmbuf, tv.tv_usec); - printf("%s", buf); + std::cout << (1900 + localTime.tm_year) << '-' + << std::setfill('0') << std::setw(2) << (localTime.tm_mon + 1) << '-' + << std::setfill('0') << std::setw(2) << localTime.tm_mday << ' ' + << std::setfill('0') << std::setw(2) << localTime.tm_hour << ':' + << std::setfill('0') << std::setw(2) << localTime.tm_min << ':' + << std::setfill('0') << std::setw(2) << localTime.tm_sec << '.' + << std::setfill('0') << std::setw(3) << milliseconds << " "; char szBuffer[512]; vsnprintf(szBuffer, sizeof szBuffer -1, fmt, args); @@ -144,6 +154,13 @@ static void TRACE_INTERNAL(const char *fmt, ...) #define TRACE_VERBOSE(fmt, ...) \ do { if (winhttp_tracing_verbose) TRACE_INTERNAL(fmt, __VA_ARGS__); } while (0) +#define CURL_BAILOUT_ONERROR(res, request, retval) \ + if ((res) != CURLE_OK) \ + { \ + TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)request, res); \ + return retval; \ + } + typedef void (*CompletionCb)(std::shared_ptr, DWORD status); #define MUTEX_TYPE std::mutex @@ -182,13 +199,6 @@ static inline THREAD_HANDLE CREATETHREAD(LPTHREAD_START_ROUTINE func, LPVOID par } #endif -void handle_error(const char *file, int lineno, const char *msg) -{ - fprintf(stderr, "** %s:%d %s\n", file, lineno, msg); - ERR_print_errors_fp(stderr); - /* exit(-1); */ -} - #if OPENSSL_VERSION_NUMBER < 0x10100000L /* This array will store all of the mutexes available to OpenSSL. */ static MUTEX_TYPE *mutex_buf = NULL; @@ -207,7 +217,7 @@ static unsigned long id_function(void) } #endif -int thread_setup(void) +static int thread_setup(void) { #if OPENSSL_VERSION_NUMBER < 0x10100000L int i; @@ -223,7 +233,7 @@ int thread_setup(void) return 1; } -int thread_cleanup(void) +static int thread_cleanup(void) { #if OPENSSL_VERSION_NUMBER < 0x10100000L int i; @@ -370,6 +380,9 @@ class WinHttpConnectImp :public WinHttpBase BOOL SetUserData(void **data) { + if (!data) + return FALSE; + m_UserBuffer = *data; return TRUE; } @@ -411,7 +424,6 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this CURL *m_curl = NULL; std::vector m_ResponseString; std::string m_HeaderString = ""; - size_t m_TotalHeaderStringLength = 0; std::string m_Header = ""; std::string m_FullPath = ""; std::string m_OptionalData = ""; @@ -477,7 +489,8 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this } WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) { - *dwNotificationFlags = m_NotificationFlags; + if (dwNotificationFlags) + *dwNotificationFlags = m_NotificationFlags; return m_InternetCallback; } @@ -492,7 +505,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this bool HandleQueryDataNotifications(std::shared_ptr, size_t available); void WaitAsyncQueryDataCompletion(std::shared_ptr); - void HandleReceiveNotifications(std::shared_ptr); + void HandleReceiveNotifications(std::shared_ptr srequest); void WaitAsyncReceiveCompletion(std::shared_ptr srequest); CURLcode &GetCompletionCode() { return m_CompletionCode; } @@ -512,6 +525,9 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this static THREADRETURN UploadThreadFunction(THREADPARAM lpThreadParameter) { WinHttpRequestImp *request = static_cast(lpThreadParameter); + if (!request) + return NULL; + CURL *curl = request->GetCurl(); CURLcode res; @@ -519,12 +535,8 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ - if (res != CURLE_OK) - { - TRACE("curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, NULL); + TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)request, res); request->GetUploadThreadExitStatus() = true; @@ -567,6 +579,9 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this BOOL SetSecureProtocol(DWORD *data) { + if (!data) + return FALSE; + m_SecureProtocol = *data; return TRUE; } @@ -574,6 +589,9 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this BOOL SetMaxConnections(DWORD *data) { + if (!data) + return FALSE; + m_MaxConnections = *data; return TRUE; } @@ -581,6 +599,9 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this BOOL SetUserData(void **data) { + if (!data) + return FALSE; + m_UserBuffer = *data; return TRUE; } @@ -613,7 +634,6 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this } std::vector &GetResponseString() { return m_ResponseString; } - size_t &GetTotalHeaderStringLength() { return m_TotalHeaderStringLength; } std::string &GetHeaderString() { return m_HeaderString; } CURL *GetCurl() { return m_curl; } @@ -627,8 +647,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this CURLcode res; res = curl_easy_setopt(GetCurl(), CURLOPT_PROXY, urlstr.c_str()); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, this, FALSE); } return TRUE; @@ -639,12 +658,10 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this CURLcode res; res = curl_easy_setopt(GetCurl(), CURLOPT_URL, ServerName.c_str()); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, this, FALSE); res = curl_easy_setopt(GetCurl(), CURLOPT_PORT, nServerPort); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, this, FALSE); return TRUE; } @@ -653,9 +670,13 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this size_t &GetReadLength() { return m_TotalReceiveSize; } std::string &GetOptionalData() { return m_OptionalData; } - void SetOptionalData(void *lpOptional, size_t dwOptionalLength) + BOOL SetOptionalData(void *lpOptional, size_t dwOptionalLength) { + if (!lpOptional || !dwOptionalLength) + return FALSE; + m_OptionalData.assign(&(static_cast(lpOptional))[0], dwOptionalLength); + return TRUE; } std::vector &GetReadData() { return m_ReadData; } @@ -714,8 +735,11 @@ class UserCallbackContainer static THREADRETURN UserCallbackThreadFunction(LPVOID lpThreadParameter); - void Queue(UserCallbackContext *ctx) + BOOL Queue(UserCallbackContext *ctx) { + if (!ctx) + return FALSE; + { std::lock_guard lck(m_MapMutex); @@ -729,6 +753,7 @@ class UserCallbackContainer m_EventCounter++; m_hEvent.notify_all(); } + return TRUE; } void DrainQueue() @@ -951,6 +976,9 @@ class ComContainer int QueryData(int *still_running) { + if (!still_running) + return 0; + int rc = 0; struct timeval timeout; @@ -1102,6 +1130,9 @@ class WinHttpSessionImp :public WinHttpBase BOOL SetUserData(void **data) { + if (!data) + return FALSE; + m_UserBuffer = *data; return TRUE; } @@ -1109,6 +1140,9 @@ class WinHttpSessionImp :public WinHttpBase BOOL SetSecureProtocol(DWORD *data) { + if (!data) + return FALSE; + m_SecureProtocol = *data; return TRUE; } @@ -1116,6 +1150,9 @@ class WinHttpSessionImp :public WinHttpBase BOOL SetMaxConnections(DWORD *data) { + if (!data) + return FALSE; + m_MaxConnections = *data; return TRUE; } @@ -1159,14 +1196,11 @@ class WinHttpSessionImp :public WinHttpBase } WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) { - *dwNotificationFlags = m_NotificationFlags; + if (dwNotificationFlags) + *dwNotificationFlags = m_NotificationFlags; return m_InternetCallback; } - WinHttpSessionImp() - { - } - ~WinHttpSessionImp() { TRACE("%-35s:%-8d:%-16p sesion\n", __func__, __LINE__, (void*)this); @@ -1245,7 +1279,6 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) if (m->data.result == CURLE_OK) { - DWORD read = 0; BYTE *ptr = request->GetResponseString().data(); size_t available = request->GetResponseString().size(); while (1) @@ -1257,7 +1290,7 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) size_t len = MIN(buf.m_Length, available); if (len) { - TRACE("%-35s:%-8d:%-16p reading length:%lu written:%lu\n", __func__, __LINE__, (void*)request, len, read); + TRACE("%-35s:%-8d:%-16p reading length:%lu\n", __func__, __LINE__, (void*)request, len); memcpy(static_cast(buf.m_Buffer), ptr, len); } @@ -1420,6 +1453,8 @@ WinHttpSessionImp *GetImp(WinHttpBase *base) else if ((request = dynamic_cast(base))) { connect = request->GetSession(); + if (!connect) + return NULL; session = connect->GetHandle(); } else @@ -1494,6 +1529,9 @@ void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr(rqst); + if (!request) + return 0; + std::shared_ptr srequest = request->shared_from_this(); if (!srequest) return size * nmemb; @@ -1504,7 +1542,6 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme std::lock_guard lck(request->GetHeaderStringMutex()); request->GetHeaderString().append(static_cast(ptr), size * nmemb); - request->GetTotalHeaderStringLength() += size * nmemb; if (request->GetHeaderString().find("\r\n\r\n") != std::string::npos) EofHeaders = true; @@ -1545,6 +1582,9 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb DWORD available = size * nmemb; DWORD read = 0; WinHttpRequestImp *request = static_cast(rqst); + if (!request) + return 0; + std::shared_ptr srequest = request->shared_from_this(); if (!srequest) return available; @@ -1602,7 +1642,6 @@ void WinHttpRequestImp::CleanUp() m_CompletionCode = CURLE_OK; m_ResponseString.clear(); m_HeaderString.clear(); - m_TotalHeaderStringLength = 0; m_TotalReceiveSize = 0; m_ReadData.clear(); m_ReadDataEventCounter = 0; @@ -1683,7 +1722,7 @@ BOOL WinHttpRequestImp::AsyncQueue(std::shared_ptr &requestRe userdata = GetUserData(); - ctx = new UserCallbackContext(requestRef, dwInternetStatus, (DWORD)statusInformationLength, + ctx = new UserCallbackContext(requestRef, dwInternetStatus, static_cast(statusInformationLength), dwNotificationFlags, cb, userdata, statusInformation, statusInformationCopySize, allocate, RequestCompletionCb); if (ctx) { @@ -1754,7 +1793,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi if (len) { - memcpy(static_cast(ptr), (char*)buf.m_Buffer + buf.m_Used, len); + memcpy(static_cast(ptr), static_cast(buf.m_Buffer) + buf.m_Used, len); } buf.m_Used += len; @@ -1839,6 +1878,8 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpen ) { std::shared_ptr session = std::make_shared (); + if (!session) + return NULL; TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*) (session.get())); if (dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) @@ -1869,6 +1910,8 @@ WINHTTPAPI HINTERNET WINAPI WinHttpConnect ) { WinHttpSessionImp *session = static_cast(hSession); + if (!session) + return NULL; TRACE("%-35s:%-8d:%-16p pswzServerName: " STRING_LITERAL " nServerPort:%d\n", __func__, __LINE__, (void*)session, pswzServerName, nServerPort); @@ -1945,7 +1988,13 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( ) { WinHttpConnectImp *connect = static_cast(hConnect); + if (!connect) + return NULL; + WinHttpSessionImp *session = connect->GetHandle(); + if (!session) + return NULL; + CURLcode res; if (!pwszVerb) @@ -2032,31 +2081,24 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( if (session->GetTimeout() > 0) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, session->GetTimeout()); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_TIME, 60L); - if (res != CURLE_OK) - return NULL; + CURL_BAILOUT_ONERROR(res, request, NULL); + res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_LIMIT, 1L); - if (res != CURLE_OK) - return NULL; + CURL_BAILOUT_ONERROR(res, request, NULL); request->SetProxy(session->GetProxies()); if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("PUT")) == 0) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_PUT, 1L); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } else if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("GET")) == 0) { @@ -2064,9 +2106,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( else if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("POST")) == 0) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_POST, 1L); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } else { @@ -2075,9 +2115,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), verb); TRACE("%-35s:%-8d:%-16p setting custom header %s\n", __func__, __LINE__, (void*)request, verb.c_str()); res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, verb.c_str()); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } if (pwszVersion) @@ -2094,9 +2132,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( return NULL; res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTP_VERSION, ver); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } if (pwszReferrer) @@ -2104,9 +2140,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( ConvertCstrAssign(pwszReferrer, WCTLEN(pwszReferrer), session->GetReferrer()); res = curl_easy_setopt(request->GetCurl(), CURLOPT_REFERER, session->GetReferrer().c_str()); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } if (dwFlags & WINHTTP_FLAG_SECURE) @@ -2144,66 +2178,47 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( pPassphrase = env_p; if (pEngine) { - if (curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE, pEngine) != CURLE_OK) { - /* load the crypto engine */ - TRACE("%s", "can't set crypto engine\n"); - return NULL; - } - if (curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK) { - /* set the crypto engine as default */ - /* only needed for the first time you load - a engine a curl object... */ - TRACE("%s", "can't set crypto engine as default\n"); - return NULL; - } + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE, pEngine); + CURL_BAILOUT_ONERROR(res, request, NULL); + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE_DEFAULT, 1L); + CURL_BAILOUT_ONERROR(res, request, NULL); } /* cert is stored PEM coded in file... */ /* since PEM is default, we needn't set it for PEM */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERTTYPE, "PEM"); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); /* set the cert for client authentication */ if (pCertFile) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERT, pCertFile); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } /* sorry, for engine we must set the passphrase (if the key has one...) */ if (pPassphrase) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_KEYPASSWD, pPassphrase); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } /* if we use a key stored in a crypto engine, we must set the key type to "ENG" */ if (pKeyType) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEYTYPE, pKeyType); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } /* set the private key (file or ID in engine) */ if (pKeyName) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEY, pKeyName); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } /* set the file with the certs vaildating the server */ if (pCACertFile) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_CAINFO, pCACertFile); - if (res != CURLE_OK) { - return NULL; - } + CURL_BAILOUT_ONERROR(res, request, NULL); } } if (pwszVerb) @@ -2255,10 +2270,7 @@ WinHttpAddRequestHeaders request->AddHeader(headers); res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTPHEADER, request->GetHeaderList()); - if (res != CURLE_OK) - { - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); } return TRUE; @@ -2281,6 +2293,9 @@ BOOLAPI WinHttpSendRequest return FALSE; WinHttpConnectImp *connect = request->GetSession(); + if (!connect) + return FALSE; + WinHttpSessionImp *session = connect->GetHandle(); std::shared_ptr srequest = request->shared_from_this(); @@ -2314,8 +2329,7 @@ BOOLAPI WinHttpSendRequest { /* Now specify the POST data */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDS, request->GetOptionalData().c_str()); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, request, FALSE); } } @@ -2336,134 +2350,68 @@ BOOLAPI WinHttpSendRequest if (request->GetType() == "POST") { res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDSIZE, (long)totalsize); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, request, FALSE); } else if (totalsize) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_INFILESIZE_LARGE, (curl_off_t)totalsize); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, request, FALSE); } request->GetTotalLength() = dwTotalLength; res = curl_easy_setopt(request->GetCurl(), CURLOPT_READDATA, request); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_READFUNCTION, request->ReadCallback); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGFUNCTION, request->SocketCallback); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGDATA, request); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEFUNCTION, request->WriteBodyFunction); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEDATA, request); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERFUNCTION, request->WriteHeaderFunction); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERDATA, request); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_PRIVATE, request); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_FOLLOWLOCATION, 1L); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); if (winhttp_tracing_verbose) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_VERBOSE, 1); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); } /* enable TCP keep-alive for this transfer */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPALIVE, 1L); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); /* keep-alive idle time to 120 seconds */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPIDLE, 120L); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); /* interval time between keep-alive probes: 60 seconds */ res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPINTVL, 60L); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYPEER, request->VerifyPeer()); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYHOST, request->VerifyHost()); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); DWORD maxConnections = 0; @@ -2474,11 +2422,7 @@ BOOLAPI WinHttpSendRequest if (maxConnections) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_MAXCONNECTS, maxConnections); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); } if (dwContext) @@ -2493,11 +2437,7 @@ BOOLAPI WinHttpSendRequest if (securityProtocols) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLVERSION, securityProtocols); - if (res != CURLE_OK) - { - TRACE("%-35s:%-8d:%-16p securityProtocols:0x%lx res:%d\n", __func__, __LINE__, (void*)request, securityProtocols, res); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); } if (request->GetAsync()) @@ -2508,22 +2448,14 @@ BOOLAPI WinHttpSendRequest return FALSE; } request->CleanUp(); - - TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0, NULL, 0, false); - TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); - if (!ComContainer::GetInstance().AddHandle(srequest, request->GetCurl())) return FALSE; - TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); ComContainer::GetInstance().KickStart(); - TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0, NULL, 0, false); - - TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, 0, NULL, 0, false); TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); @@ -2542,12 +2474,7 @@ BOOLAPI WinHttpSendRequest /* Perform the request, res will get the return code */ res = curl_easy_perform(request->GetCurl()); /* Check for errors */ - if (res != CURLE_OK) - { - TRACE("curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - return FALSE; - } + CURL_BAILOUT_ONERROR(res, request, FALSE); } } @@ -2835,8 +2762,7 @@ WinHttpSetTimeouts else if ((request = dynamic_cast(base))) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, nReceiveTimeout); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, request, FALSE); } else return FALSE; @@ -2901,6 +2827,28 @@ std::basic_string nullize_newlines(const std::basic_string& str) { } } +static void TerminateString(TCHAR *wbuffer, size_t length, LPDWORD lpdwBufferLength) +{ + if (wbuffer[length] != TEXT('\0')) + { + if (lpdwBufferLength) + { + if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) + { + wbuffer[length] = 0; + } + else + { + wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; + } + } + else + { + wbuffer[length] = 0; + } + } +} + BOOLAPI WinHttpQueryHeaders( HINTERNET hRequest, DWORD dwInfoLevel, @@ -2945,8 +2893,7 @@ BOOLAPI WinHttpQueryHeaders( DWORD retValue; res = curl_easy_getinfo(request->GetCurl(), CURLINFO_HTTP_VERSION, &retValue); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, request, FALSE); if (!returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, (NumDigits(retValue) + 1) * sizeof(TCHAR)) == FALSE) return FALSE; @@ -2973,8 +2920,7 @@ BOOLAPI WinHttpQueryHeaders( DWORD retValue; res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &retValue); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, request, FALSE); if (!returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, (NumDigits(retValue) + 1) * sizeof(TCHAR)) == FALSE) return FALSE; @@ -3003,8 +2949,7 @@ BOOLAPI WinHttpQueryHeaders( size_t length; res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &responseCode); - if (res != CURLE_OK) - return FALSE; + CURL_BAILOUT_ONERROR(res, request, FALSE); std::lock_guard lck(request->GetHeaderStringMutex()); length = request->GetHeaderString().length(); @@ -3055,7 +3000,7 @@ BOOLAPI WinHttpQueryHeaders( } else { - TSTRING retStr = StatusCodeMap[responseCode]; + TSTRING retStr = StatusCodeMap.at(responseCode); if (retStr == TEXT("")) return FALSE; @@ -3093,26 +3038,9 @@ BOOLAPI WinHttpQueryHeaders( std::copy(wc.begin(), wc.end(), wbuffer); wbuffer[header.length()] = TEXT('\0'); #else - strncpy(wbuffer, header.c_str(), length); + memcpy(wbuffer, header.c_str(), length); #endif - if (wbuffer[length] != TEXT('\0')) - { - if (lpdwBufferLength) - { - if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) - { - wbuffer[length] = 0; - } - else - { - wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; - } - } - else - { - wbuffer[length] = 0; - } - } + TerminateString(wbuffer, length, lpdwBufferLength); if (lpdwBufferLength) *lpdwBufferLength = (DWORD)length; return TRUE; @@ -3139,23 +3067,9 @@ BOOLAPI WinHttpQueryHeaders( wbuffer[request->GetHeaderString().length()] = TEXT('\0'); #else - strncpy(wbuffer, request->GetHeaderString().c_str(), length); + memcpy(wbuffer, request->GetHeaderString().c_str(), length); #endif - if (lpdwBufferLength) - { - if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) - { - wbuffer[length] = 0; - } - else - { - wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; - } - } - else - { - wbuffer[length] = 0; - } + TerminateString(wbuffer, length, lpdwBufferLength); if (lpdwBufferLength) *lpdwBufferLength = (DWORD)(length * sizeof(TCHAR)); return TRUE; @@ -3165,7 +3079,7 @@ BOOLAPI WinHttpQueryHeaders( return FALSE; } -DWORD ConvertSecurityProtocol(DWORD offered) +static DWORD ConvertSecurityProtocol(DWORD offered) { DWORD min = 0; DWORD max = 0; @@ -3213,6 +3127,23 @@ DWORD ConvertSecurityProtocol(DWORD offered) return min | max; } + +template +static BOOL CallMemberFunction(WinHttpBase *base, std::function fn, LPVOID lpBuffer) +{ + T *obj; + + if (!lpBuffer) + return FALSE; + + if ((obj = dynamic_cast(base))) + { + if (fn(obj, static_cast(lpBuffer))) + return TRUE; + } + return FALSE; +} + BOOLAPI WinHttpSetOption( HINTERNET hInternet, DWORD dwOption, @@ -3226,103 +3157,46 @@ BOOLAPI WinHttpSetOption( if (dwOption == WINHTTP_OPTION_MAX_CONNS_PER_SERVER) { - WinHttpSessionImp *session; - WinHttpRequestImp *request; - if (dwBufferLength != sizeof(DWORD)) return FALSE; - if ((session = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - - if (!session->SetMaxConnections(static_cast(lpBuffer))) - return FALSE; + if (CallMemberFunction(base, &WinHttpSessionImp::SetMaxConnections, lpBuffer)) return TRUE; - } - else if ((request = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - if (!request->SetMaxConnections(static_cast(lpBuffer))) - return FALSE; + if (CallMemberFunction(base, &WinHttpRequestImp::SetMaxConnections, lpBuffer)) return TRUE; - } - else - return FALSE; + return FALSE; } else if (dwOption == WINHTTP_OPTION_CONTEXT_VALUE) { - WinHttpConnectImp *connect; - WinHttpSessionImp *session; - WinHttpRequestImp *request; - if (dwBufferLength != sizeof(void*)) return FALSE; - if ((connect = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - - if (!connect->SetUserData(static_cast(lpBuffer))) - return FALSE; + if (CallMemberFunction(base, &WinHttpConnectImp::SetUserData, lpBuffer)) return TRUE; - } - else if ((session = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - if (!session->SetUserData(static_cast(lpBuffer))) - return FALSE; + if (CallMemberFunction(base, &WinHttpSessionImp::SetUserData, lpBuffer)) return TRUE; - } - else if ((request = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - if (!request->SetUserData(static_cast(lpBuffer))) - return FALSE; + if (CallMemberFunction(base, &WinHttpRequestImp::SetUserData, lpBuffer)) return TRUE; - } - else - return FALSE; + + return FALSE; } else if (dwOption == WINHTTP_OPTION_SECURE_PROTOCOLS) { - WinHttpSessionImp *session; - WinHttpRequestImp *request; - if (dwBufferLength != sizeof(DWORD)) return FALSE; - if ((session = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - - DWORD curlOffered = ConvertSecurityProtocol(*static_cast(lpBuffer)); - if (curlOffered && !session->SetSecureProtocol(&curlOffered)) - return FALSE; + DWORD curlOffered = ConvertSecurityProtocol(*static_cast(lpBuffer)); + if (CallMemberFunction(base, &WinHttpSessionImp::SetSecureProtocol, &curlOffered)) return TRUE; - } - else if ((request = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - DWORD curlOffered = ConvertSecurityProtocol(*static_cast(lpBuffer)); - if (curlOffered && !request->SetSecureProtocol(&curlOffered)) - return FALSE; + if (CallMemberFunction(base, &WinHttpRequestImp::SetSecureProtocol, &curlOffered)) return TRUE; - } - else - return FALSE; + + return FALSE; } else if (dwOption == WINHTTP_OPTION_SECURITY_FLAGS) { @@ -3455,7 +3329,7 @@ WinHttpQueryOption std::copy(wc.begin(), wc.end(), wbuffer); wbuffer[strlen(url)] = TEXT('\0'); #else - strncpy(wbuffer, url, length); + memcpy(wbuffer, url, length); #endif if (wbuffer[length] != TEXT('\0')) { From 3ab541f98461ce0f87b24efe6c88f3b71483e2d6 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sat, 3 Aug 2019 16:42:15 -0400 Subject: [PATCH 28/41] More cleanup - static analysis - hide copy constructors - Remove terminatestring function and size the buffers appropriately - No need for empty string initialization - Use references instead of copies where possible - replace atoi with stoi - No need to check null pointer on delete - Fix copy paste mistake - Support older curl versions - More read function unifications --- .../src/http/client/winhttppal/winhttppal.cpp | 462 ++++++++---------- 1 file changed, 217 insertions(+), 245 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 582e6ac4e5..403ffea745 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -20,13 +20,16 @@ #include #include #include -#include #include #include #include #include #include +#ifdef UNICODE +#include +#endif + #ifdef WIN32 #define localtime_r(_Time, _Tm) localtime_s(_Tm, _Time) #endif @@ -116,7 +119,7 @@ int gettimeofday(struct timeval * tp, struct timezone * tzp); static std::mutex trcmtx; #ifndef _MSC_VER -static void TRACE_INTERNAL(const char *fmt, ...) __attribute__ ((format (gnu_printf, 1, 2))); +static void TRACE_INTERNAL(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); #endif static void TRACE_INTERNAL(const char *fmt, ...) @@ -133,18 +136,18 @@ static void TRACE_INTERNAL(const char *fmt, ...) const std::chrono::duration tse = t.time_since_epoch(); std::chrono::seconds::rep milliseconds = std::chrono::duration_cast(tse).count() % 1000; + char szBuffer[512]; + vsnprintf(szBuffer, sizeof szBuffer - 1, fmt, args); + szBuffer[sizeof(szBuffer) / sizeof(szBuffer[0]) - 1] = '\0'; + std::cout << (1900 + localTime.tm_year) << '-' << std::setfill('0') << std::setw(2) << (localTime.tm_mon + 1) << '-' << std::setfill('0') << std::setw(2) << localTime.tm_mday << ' ' << std::setfill('0') << std::setw(2) << localTime.tm_hour << ':' << std::setfill('0') << std::setw(2) << localTime.tm_min << ':' << std::setfill('0') << std::setw(2) << localTime.tm_sec << '.' - << std::setfill('0') << std::setw(3) << milliseconds << " "; + << std::setfill('0') << std::setw(3) << milliseconds << " " << szBuffer; - char szBuffer[512]; - vsnprintf(szBuffer, sizeof szBuffer -1, fmt, args); - szBuffer[sizeof(szBuffer)/sizeof(szBuffer[0]) - 1] = '\0'; - printf("%s", szBuffer); va_end(args); } @@ -157,11 +160,11 @@ static void TRACE_INTERNAL(const char *fmt, ...) #define CURL_BAILOUT_ONERROR(res, request, retval) \ if ((res) != CURLE_OK) \ { \ - TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)request, res); \ - return retval; \ + TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)(request), (res)); \ + return (retval); \ } -typedef void (*CompletionCb)(std::shared_ptr, DWORD status); +typedef void (*CompletionCb)(std::shared_ptr &, DWORD status); #define MUTEX_TYPE std::mutex #define MUTEX_SETUP(x) @@ -211,13 +214,13 @@ static void locking_function(int mode, int n, const char *file, int line) MUTEX_UNLOCK(mutex_buf[n]); } -static unsigned long id_function(void) +static unsigned long id_function() { return static_cast(THREAD_ID); } #endif -static int thread_setup(void) +static int thread_setup() { #if OPENSSL_VERSION_NUMBER < 0x10100000L int i; @@ -233,7 +236,7 @@ static int thread_setup(void) return 1; } -static int thread_cleanup(void) +static int thread_cleanup() { #if OPENSSL_VERSION_NUMBER < 0x10100000L int i; @@ -298,7 +301,7 @@ class UserCallbackContext bool allocate, CompletionCb completion); ~UserCallbackContext(); - std::shared_ptr GetRequestRef() { return m_request; } + std::shared_ptr &GetRequestRef() { return m_request; } WinHttpRequestImp *GetRequest() { return m_request.get(); } DWORD GetInternetStatus() const { return m_dwInternetStatus; } DWORD GetStatusInformationLength() const { return m_dwStatusInformationLength; } @@ -313,19 +316,20 @@ class UserCallbackContext return m_StatusInformationVal; } BOOL GetStatusInformationValid() const { return m_AsyncResultValid; } + +private: + UserCallbackContext(const UserCallbackContext&); + UserCallbackContext& operator=(const UserCallbackContext&); }; static void ConvertCstrAssign(const TCHAR *lpstr, size_t cLen, std::string &target) { - size_t bLen = cLen; - #ifdef UNICODE std::wstring_convert> conv; target = conv.to_bytes(std::wstring(lpstr, cLen)); #else target.assign(lpstr, cLen); #endif - target[bLen] = '\0'; } static std::vector Split(std::string &str, char delimiter) { @@ -360,6 +364,12 @@ static BOOL SizeCheck(LPVOID lpBuffer, LPDWORD lpdwBufferLength, DWORD Required) if ((Required == 0) || (lpdwBufferLength && (*lpdwBufferLength == 0))) return FALSE; + if (!lpBuffer) + return FALSE; + + if (lpdwBufferLength && (*lpdwBufferLength < Required)) + return FALSE; + return TRUE; } @@ -423,10 +433,10 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this { CURL *m_curl = NULL; std::vector m_ResponseString; - std::string m_HeaderString = ""; - std::string m_Header = ""; - std::string m_FullPath = ""; - std::string m_OptionalData = ""; + std::string m_HeaderString; + std::string m_Header; + std::string m_FullPath; + std::string m_OptionalData; size_t m_TotalSize = 0; size_t m_TotalReceiveSize = 0; std::vector m_ReadData; @@ -439,7 +449,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this DWORD m_SecureProtocol = 0; DWORD m_MaxConnections = 0; - std::string m_Type = ""; + std::string m_Type; LPVOID m_UserBuffer = NULL; bool m_HeaderReceiveComplete = false; @@ -457,7 +467,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this std::mutex m_ReceiveResponseMutex; std::atomic m_ReceiveResponsePending; std::atomic m_ReceiveResponseEventCounter; - int m_ReceiveResponseSendCounter; + int m_ReceiveResponseSendCounter = 0; std::atomic m_RedirectPending; @@ -468,6 +478,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this CURLcode m_CompletionCode = CURLE_OK; long m_VerifyPeer = 1; long m_VerifyHost = 2; + bool m_Uploading = false; WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; DWORD m_NotificationFlags = 0; @@ -502,11 +513,11 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this bool &GetQueryDataEventState() { return m_QueryDataEventState; } std::mutex &GetQueryDataEventMtx() { return m_QueryDataEventMtx; } - bool HandleQueryDataNotifications(std::shared_ptr, size_t available); - void WaitAsyncQueryDataCompletion(std::shared_ptr); + bool HandleQueryDataNotifications(std::shared_ptr &, size_t available); + void WaitAsyncQueryDataCompletion(std::shared_ptr &); - void HandleReceiveNotifications(std::shared_ptr srequest); - void WaitAsyncReceiveCompletion(std::shared_ptr srequest); + void HandleReceiveNotifications(std::shared_ptr &srequest); + void WaitAsyncReceiveCompletion(std::shared_ptr &srequest); CURLcode &GetCompletionCode() { return m_CompletionCode; } bool &GetCompletionStatus() { return m_Completion; } @@ -614,6 +625,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this } std::string &GetFullPath() { return m_FullPath; } std::string &GetType() { return m_Type; } + bool &Uploading() { return m_Uploading; } void SetSession(WinHttpConnectImp *connect) { m_Session = connect; } WinHttpConnectImp *GetSession() { return m_Session; } @@ -758,7 +770,7 @@ class UserCallbackContainer void DrainQueue() { - while (1) + while (true) { m_MapMutex.lock(); UserCallbackContext *ctx = GetNext(); @@ -807,10 +819,10 @@ class EnvInit EnvInit() { if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG")) - winhttp_tracing = atoi(env_p); + winhttp_tracing = std::stoi(std::string(env_p)); if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG_VERBOSE")) - winhttp_tracing_verbose = atoi(env_p); + winhttp_tracing_verbose = std::stoi(std::string(env_p)); } }; @@ -869,7 +881,7 @@ class ComContainer m_MultiMutex.unlock(); } - BOOL AddHandle(std::shared_ptr srequest, CURL *handle) + BOOL AddHandle(std::shared_ptr &srequest, CURL *handle) { CURLMcode mres = CURLM_OK; @@ -901,7 +913,7 @@ class ComContainer return TRUE; } - BOOL RemoveHandle(std::shared_ptr srequest, CURL *handle, bool clearPrivate) + BOOL RemoveHandle(std::shared_ptr &srequest, CURL *handle, bool clearPrivate) { CURLMcode mres; @@ -1110,10 +1122,10 @@ class WinHttpHandleContainer class WinHttpSessionImp :public WinHttpBase { - std::string m_ServerName = ""; - std::string m_Referrer = ""; + std::string m_ServerName; + std::string m_Referrer; std::vector m_Proxies; - std::string m_Proxy = ""; + std::string m_Proxy; WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; DWORD m_NotificationFlags = 0; @@ -1213,7 +1225,7 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) { ComContainer *comContainer = static_cast(lpThreadParameter); - while (1) + while (true) { { std::unique_lock getAsyncEventHndlMtx(comContainer->m_hAsyncEventMtx); @@ -1281,7 +1293,7 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) { BYTE *ptr = request->GetResponseString().data(); size_t available = request->GetResponseString().size(); - while (1) + while (true) { BufferRequest buf = GetBufferRequest(request->GetOutstandingReads()); if (!buf.m_Length) @@ -1363,7 +1375,7 @@ THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadPa { UserCallbackContainer *cbContainer = static_cast(lpThreadParameter); - while (1) + while (true) { { std::unique_lock GetEventHndlMtx(cbContainer->m_hEventMtx); @@ -1380,7 +1392,7 @@ THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadPa break; } - while (1) + while (true) { cbContainer->m_MapMutex.lock(); UserCallbackContext *ctx = NULL; @@ -1468,11 +1480,10 @@ WinHttpSessionImp *GetImp(WinHttpBase *base) UserCallbackContext::~UserCallbackContext() { - if (m_StatusInformation) - delete [] m_StatusInformation; + delete [] m_StatusInformation; } -void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr srequest) +void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr &srequest) { bool expected = true; @@ -1661,13 +1672,11 @@ WinHttpRequestImp::WinHttpRequestImp(): m_QueryDataPending(false), m_ReceiveResponsePending(false), m_ReceiveResponseEventCounter(0), - m_ReceiveResponseSendCounter(0), m_RedirectPending(false) { m_curl = ComContainer::GetInstance().AllocCURL(); if (!m_curl) return; - return; } WinHttpRequestImp::~WinHttpRequestImp() @@ -1676,7 +1685,7 @@ WinHttpRequestImp::~WinHttpRequestImp() m_closed = true; - if (!GetAsync() && (GetType() == "PUT")) + if (!GetAsync() && Uploading()) { { std::lock_guard lck(GetReadDataEventMtx()); @@ -1701,7 +1710,7 @@ WinHttpRequestImp::~WinHttpRequestImp() TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); } -static void RequestCompletionCb(std::shared_ptr requestRef, DWORD status) +static void RequestCompletionCb(std::shared_ptr &requestRef, DWORD status) { if (status == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) { @@ -1748,7 +1757,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi TRACE("request->GetTotalLength(): %lu\n", request->GetTotalLength()); if (request->GetOptionalData().length() > 0) { - len = request->GetOptionalData().length(); + len = MIN(request->GetOptionalData().length(), size * nmemb); TRACE("%-35s:%-8d:%-16p writing optional length of %lu\n", __func__, __LINE__, (void*)request, len); std::copy(request->GetOptionalData().begin(), request->GetOptionalData().end(), static_cast(ptr)); request->GetOptionalData().erase(0, len); @@ -1760,7 +1769,7 @@ size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, voi return -1; TRACE("%-35s:%-8d:%-16p request->GetTotalLength():%lu request->GetReadLength():%lu\n", __func__, __LINE__, (void*)request, request->GetTotalLength(), request->GetReadLength()); - if (((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && (request->GetType() == "PUT")) || + if (((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && request->Uploading()) || (request->GetTotalLength() != request->GetReadLength())) { std::unique_lock getReadDataEventHndlMtx(request->GetReadDataEventMtx()); @@ -2057,7 +2066,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( // convert # to %23 to support links to fragments while (true) { /* Locate the substring to replace. */ - index = objectname.find("#", index); + index = objectname.find('#', index); if (index == std::string::npos) break; /* Make the replacement. */ objectname.replace(index, 1, "%23"); @@ -2071,7 +2080,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( } else { - std::string nullstr(""); + std::string nullstr; request->SetFullPath(server, nullstr); if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { return NULL; @@ -2091,42 +2100,53 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( CURL_BAILOUT_ONERROR(res, request, NULL); request->SetProxy(session->GetProxies()); - - if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("PUT")) == 0) + TSTRING verb; + verb.assign(pwszVerb); + if (verb == TEXT("PUT")) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_PUT, 1L); CURL_BAILOUT_ONERROR(res, request, NULL); res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); CURL_BAILOUT_ONERROR(res, request, NULL); + + request->Uploading() = true; } - else if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("GET")) == 0) + else if (verb == TEXT("GET")) { } - else if (WCTCMP(pwszVerb, (const TCHAR *)TEXT("POST")) == 0) + else if (verb == TEXT("POST")) { res = curl_easy_setopt(request->GetCurl(), CURLOPT_POST, 1L); CURL_BAILOUT_ONERROR(res, request, NULL); } else { - std::string verb; + std::string verbcst; - ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), verb); - TRACE("%-35s:%-8d:%-16p setting custom header %s\n", __func__, __LINE__, (void*)request, verb.c_str()); - res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, verb.c_str()); + ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), verbcst); + TRACE("%-35s:%-8d:%-16p setting custom header %s\n", __func__, __LINE__, (void*)request, verbcst.c_str()); + res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, verbcst.c_str()); CURL_BAILOUT_ONERROR(res, request, NULL); + + res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); + CURL_BAILOUT_ONERROR(res, request, NULL); + + request->Uploading() = true; } +#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) if (pwszVersion) { int ver; + TSTRING version; + version.assign(pwszVersion); - if (WCTCMP(pwszVerb, (const TCHAR*)TEXT("1.0")) == 0) + if (version == TEXT("1.0")) ver = CURL_HTTP_VERSION_1_0; - else if (WCTCMP(pwszVerb, (const TCHAR*)TEXT("1.1")) == 0) + else if (version == TEXT("1.1")) ver = CURL_HTTP_VERSION_1_1; - else if (WCTCMP(pwszVerb, (const TCHAR*)TEXT("2.0")) == 0) + else if (version == TEXT("2.0")) ver = CURL_HTTP_VERSION_2_0; else return NULL; @@ -2134,6 +2154,7 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTP_VERSION, ver); CURL_BAILOUT_ONERROR(res, request, NULL); } +#endif if (pwszReferrer) { @@ -2251,22 +2272,17 @@ WinHttpAddRequestHeaders TRACE("%-35s:%-8d:%-16p lpszHeaders = " STRING_LITERAL " dwModifiers: %lu\n", __func__, __LINE__, (void*)request, lpszHeaders ? lpszHeaders: TEXT(""), dwModifiers); - if (lpszHeaders) - { - size_t nstringLen; - if (dwHeadersLength == (DWORD)-1) - { - nstringLen = WCTLEN(lpszHeaders) + 1; - } - else - { - nstringLen = (size_t)dwHeadersLength + 1; - } + if (dwHeadersLength == (DWORD)-1) + { + dwHeadersLength = lpszHeaders ? WCTLEN(lpszHeaders): 0; + } + if (lpszHeaders) + { std::string &headers = request->GetOutgoingHeaderList(); - ConvertCstrAssign(lpszHeaders, nstringLen, headers); + ConvertCstrAssign(lpszHeaders, dwHeadersLength, headers); request->AddHeader(headers); res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTPHEADER, request->GetHeaderList()); @@ -2304,16 +2320,21 @@ BOOLAPI WinHttpSendRequest TSTRING customHeader; + if (dwHeadersLength == (DWORD)-1) + { + dwHeadersLength = lpszHeaders ? WCTLEN(lpszHeaders): 0; + } + if (lpszHeaders) - customHeader.assign(lpszHeaders, WCTLEN(lpszHeaders)); + customHeader.assign(lpszHeaders, dwHeadersLength); - if ((dwTotalLength == 0) && (dwOptionalLength == 0) && (request->GetType() == "PUT")) + if ((dwTotalLength == 0) && (dwOptionalLength == 0) && request->Uploading()) customHeader += TEXT("Transfer-Encoding: chunked\r\n"); TRACE("%-35s:%-8d:%-16p lpszHeaders:%p dwHeadersLength:%lu lpOptional:%p dwOptionalLength:%lu dwTotalLength:%lu\n", - __func__, __LINE__, (void*)request, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength); + __func__, __LINE__, (void*)request, (const void*)lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength); - if ((customHeader != TEXT("")) && !WinHttpAddRequestHeaders(hRequest, customHeader.c_str(), customHeader.length(), 0)) + if (!customHeader.empty() && !WinHttpAddRequestHeaders(hRequest, customHeader.c_str(), customHeader.length(), 0)) return FALSE; if (lpOptional) @@ -2333,7 +2354,7 @@ BOOLAPI WinHttpSendRequest } } - if ((request->GetType() == "PUT") || (request->GetType() == "POST")) + if (request->Uploading() || (request->GetType() == "POST")) { if (!request->GetAsync() && (dwTotalLength == 0)) { SetLastError(ERROR_INVALID_PARAMETER); @@ -2466,7 +2487,7 @@ BOOLAPI WinHttpSendRequest { DWORD thread_id; request->GetUploadThread() = CREATETHREAD( - request->UploadThreadFunction, // thread function name + WinHttpRequestImp::UploadThreadFunction, // thread function name request, &thread_id); // argument to thread function } else @@ -2481,7 +2502,7 @@ BOOLAPI WinHttpSendRequest return TRUE; } -void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptr srequest) +void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptr &srequest) { bool expected = false; std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, true); @@ -2516,7 +2537,7 @@ WinHttpReceiveResponse TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - if ((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && (request->GetType() == "PUT")) + if ((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && request->Uploading()) { if (request->GetAsync()) { @@ -2537,7 +2558,7 @@ WinHttpReceiveResponse } else { - if (request->GetType() == "PUT") + if (request->Uploading()) { while (request->GetTotalLength() != request->GetReadLength()) { if (request->GetUploadThreadExitStatus()) { @@ -2563,7 +2584,7 @@ WinHttpReceiveResponse return TRUE; } -bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr srequest, size_t available) +bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr &srequest, size_t available) { bool expected = true; bool result = std::atomic_compare_exchange_strong(&GetQueryDataPending(), &expected, false); @@ -2587,7 +2608,7 @@ bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr srequest) +void WinHttpRequestImp::WaitAsyncQueryDataCompletion(std::shared_ptr &srequest) { bool completed; GetQueryDataPending() = true; @@ -2770,20 +2791,6 @@ WinHttpSetTimeouts return TRUE; } -static int NumDigits(DWORD value) -{ - int count = 0; - - while (value != 0) - { - // n = n/10 - value /= 10; - ++count; - } - - return count; -} - static TSTRING FindRegex(const TSTRING &subject,const TSTRING ®str) { TSTRING result; @@ -2793,7 +2800,7 @@ static TSTRING FindRegex(const TSTRING &subject,const TSTRING ®str) if (TREGEX_SEARCH(subject, match, re)) { result = match.str(0); } else { - result = TSTRING(TEXT("")); + result = TEXT(""); return TEXT(""); } } catch (std::regex_error&) { @@ -2827,26 +2834,43 @@ std::basic_string nullize_newlines(const std::basic_string& str) { } } -static void TerminateString(TCHAR *wbuffer, size_t length, LPDWORD lpdwBufferLength) +static BOOL ReadCurlValue(WinHttpRequestImp *request, LPVOID lpBuffer, LPDWORD lpdwBufferLength, + CURLINFO curlparam, bool returnDWORD) { - if (wbuffer[length] != TEXT('\0')) + CURLcode res; + DWORD retValue; + + res = curl_easy_getinfo(request->GetCurl(), curlparam, &retValue); + CURL_BAILOUT_ONERROR(res, request, FALSE); + + if (!returnDWORD) + { + TSTRING str = TO_STRING(retValue); + if (SizeCheck(lpBuffer, lpdwBufferLength, (str.size() + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + } + else + { + if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) + return FALSE; + } + + if (returnDWORD) + { + memcpy(lpBuffer, &retValue, sizeof(retValue)); + } + else { + TCHAR *outbuf = static_cast(lpBuffer); + TSTRING str = TO_STRING(retValue); + std::copy(str.begin(), str.end(), outbuf); + outbuf[str.size()] = TEXT('\0'); + if (lpdwBufferLength) - { - if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) - { - wbuffer[length] = 0; - } - else - { - wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; - } - } - else - { - wbuffer[length] = 0; - } + *lpdwBufferLength = (str.size() + 1) * sizeof(TCHAR); } + TRACE("%-35s:%-8d:%-16p curlparam:%d code :%lu\n", __func__, __LINE__, (void*)request, curlparam, retValue); + return TRUE; } BOOLAPI WinHttpQueryHeaders( @@ -2887,86 +2911,32 @@ BOOLAPI WinHttpQueryHeaders( if (returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) return FALSE; +#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) if (dwInfoLevel == WINHTTP_QUERY_VERSION) { - CURLcode res; - DWORD retValue; - - res = curl_easy_getinfo(request->GetCurl(), CURLINFO_HTTP_VERSION, &retValue); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - if (!returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, (NumDigits(retValue) + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - - if (!lpBuffer) - return FALSE; - - if (returnDWORD) - { - memcpy(lpBuffer, &retValue, sizeof(retValue)); - } - else - { - TCHAR *outbuf = static_cast(lpBuffer); - STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%lu"), retValue); - outbuf[*lpdwBufferLength/sizeof(outbuf[0]) - 1] = TEXT('\0'); - } - return TRUE; + return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_HTTP_VERSION, returnDWORD); } - +#endif if (dwInfoLevel == WINHTTP_QUERY_STATUS_CODE) { - CURLcode res; - DWORD retValue; - - res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &retValue); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - if (!returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, (NumDigits(retValue) + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - - if (!lpBuffer) - return FALSE; - - if (returnDWORD) - { - memcpy(lpBuffer, &retValue, sizeof(retValue)); - } - else - { - TCHAR *outbuf = static_cast(lpBuffer); - STNPRINTF(outbuf, *lpdwBufferLength, TEXT("%lu"), retValue); - outbuf[*lpdwBufferLength/sizeof(outbuf[0]) - 1] = TEXT('\0'); - } - TRACE("%-35s:%-8d:%-16p status code :%lu\n", __func__, __LINE__, (void*)request, retValue); - return TRUE; + return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_RESPONSE_CODE, returnDWORD); } if (dwInfoLevel == WINHTTP_QUERY_STATUS_TEXT) { CURLcode res; DWORD responseCode; - size_t length; res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &responseCode); CURL_BAILOUT_ONERROR(res, request, FALSE); std::lock_guard lck(request->GetHeaderStringMutex()); - length = request->GetHeaderString().length(); - - if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - - if (!lpBuffer) - return FALSE; #ifdef UNICODE TSTRING subject; std::wstring_convert> conv; subject = conv.from_bytes(request->GetHeaderString()); - subject[request->GetHeaderString().length()] = TEXT('\0'); - #else TSTRING &subject = request->GetHeaderString(); #endif @@ -2977,7 +2947,7 @@ BOOLAPI WinHttpQueryHeaders( regstr.append(TO_STRING(responseCode)); TSTRING result = FindRegex(subject, regstr); - if (result != TEXT("")) + if (!result.empty()) { size_t offset = subject.find(result); if (offset == std::string::npos) @@ -2987,25 +2957,30 @@ BOOLAPI WinHttpQueryHeaders( if (offsetendofline == std::string::npos) return FALSE; - size_t linelength = offsetendofline - offset; - if (linelength == std::string::npos) + size_t startpos = offset + result.length() + 1; + if (offsetendofline <= startpos) return FALSE; - WCTNCPY((TCHAR*)lpBuffer, - subject.c_str() + offset + result.length() + 1, - linelength - result.length() - 1); + size_t linelength = offsetendofline - startpos; + if (SizeCheck(lpBuffer, lpdwBufferLength, (linelength + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; - ((TCHAR*)lpBuffer)[linelength - result.length() - 1] = TEXT('\0'); + std::copy(subject.begin() + startpos, subject.begin() + offsetendofline, + (TCHAR*)lpBuffer); + ((TCHAR*)lpBuffer)[linelength] = TEXT('\0'); return TRUE; } else { TSTRING retStr = StatusCodeMap.at(responseCode); - if (retStr == TEXT("")) + if (retStr.empty()) return FALSE; - const TCHAR *cstr = retStr.c_str(); - WCTCPY((TCHAR*)lpBuffer, cstr); + if (SizeCheck(lpBuffer, lpdwBufferLength, (retStr.size() + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; + + std::copy(retStr.begin(), retStr.end(), (TCHAR*)lpBuffer); + ((TCHAR*)lpBuffer)[retStr.size()] = TEXT('\0'); } return TRUE; } @@ -3013,36 +2988,32 @@ BOOLAPI WinHttpQueryHeaders( if (dwInfoLevel == WINHTTP_QUERY_RAW_HEADERS) { - size_t length; std::string header; TCHAR *wbuffer = static_cast(lpBuffer); - request->GetHeaderStringMutex().lock(); - length = request->GetHeaderString().length(); + std::lock_guard lck(request->GetHeaderStringMutex()); header.append(request->GetHeaderString()); - request->GetHeaderStringMutex().unlock(); - - if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - - if (!wbuffer) - return FALSE; header = nullize_newlines(header); header.resize(header.size() + 1); - length = header.size(); + + if (SizeCheck(lpBuffer, lpdwBufferLength, (header.length() + 1) * sizeof(TCHAR)) == FALSE) + return FALSE; #ifdef UNICODE std::wstring_convert> conv; std::wstring wc = conv.from_bytes(header); + + if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) + return FALSE; + std::copy(wc.begin(), wc.end(), wbuffer); - wbuffer[header.length()] = TEXT('\0'); #else - memcpy(wbuffer, header.c_str(), length); + std::copy(header.begin(), header.end(), wbuffer); #endif - TerminateString(wbuffer, length, lpdwBufferLength); + wbuffer[header.length()] = TEXT('\0'); if (lpdwBufferLength) - *lpdwBufferLength = (DWORD)length; + *lpdwBufferLength = (DWORD)header.length(); return TRUE; } @@ -3057,19 +3028,17 @@ BOOLAPI WinHttpQueryHeaders( if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) return FALSE; - if (!wbuffer) - return FALSE; - #ifdef UNICODE std::wstring_convert> conv; std::wstring wc = conv.from_bytes(request->GetHeaderString()); - std::copy(wc.begin(), wc.end(), wbuffer); - wbuffer[request->GetHeaderString().length()] = TEXT('\0'); + if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) + return FALSE; + std::copy(wc.begin(), wc.end(), wbuffer); #else - memcpy(wbuffer, request->GetHeaderString().c_str(), length); + std::copy(request->GetHeaderString().begin(), request->GetHeaderString().end(), wbuffer); #endif - TerminateString(wbuffer, length, lpdwBufferLength); + wbuffer[length] = TEXT('\0'); if (lpdwBufferLength) *lpdwBufferLength = (DWORD)(length * sizeof(TCHAR)); return TRUE; @@ -3090,6 +3059,8 @@ static DWORD ConvertSecurityProtocol(DWORD offered) else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) { min = CURL_SSLVERSION_SSLv3; } + else + min = CURL_SSLVERSION_DEFAULT; if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { min = CURL_SSLVERSION_TLSv1_0; @@ -3101,10 +3072,8 @@ static DWORD ConvertSecurityProtocol(DWORD offered) else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { min = CURL_SSLVERSION_TLSv1_2; } - else - min = CURL_SSLVERSION_DEFAULT; -#if (LIBCURL_VERSION_MAJOR==7) && (LIBCURL_VERSION_MINOR >= 61) +#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 61) if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { max = CURL_SSLVERSION_MAX_TLSv1_2; } @@ -3281,9 +3250,6 @@ WinHttpQueryOption if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) return FALSE; - if (!lpBuffer) - return FALSE; - session = GetImp(base); if (!session) return FALSE; @@ -3295,9 +3261,6 @@ WinHttpQueryOption if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(LPVOID)) == FALSE) return FALSE; - if (!lpBuffer) - return FALSE; - session = GetImp(base); if (!session) return FALSE; @@ -3326,29 +3289,16 @@ WinHttpQueryOption #ifdef UNICODE std::wstring_convert> conv; std::wstring wc = conv.from_bytes(std::string(url)); + if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) + return FALSE; + std::copy(wc.begin(), wc.end(), wbuffer); - wbuffer[strlen(url)] = TEXT('\0'); #else - memcpy(wbuffer, url, length); + std::string urlstr; + urlstr.assign(url); + std::copy(urlstr.begin(), urlstr.end(), wbuffer); #endif - if (wbuffer[length] != TEXT('\0')) - { - if (lpdwBufferLength) - { - if ((length + 1) < (*lpdwBufferLength / sizeof(TCHAR))) - { - wbuffer[length] = 0; - } - else - { - wbuffer[(*lpdwBufferLength / sizeof(TCHAR)) - 1] = 0; - } - } - else - { - wbuffer[length] = 0; - } - } + wbuffer[strlen(url)] = TEXT('\0'); if (lpdwBufferLength) *lpdwBufferLength = (DWORD)length; } @@ -3362,9 +3312,9 @@ WinHttpQueryOption if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(HTTP_VERSION_INFO)) == FALSE) return FALSE; - if (!lpBuffer) - return FALSE; + HTTP_VERSION_INFO version; +#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) long curlversion; CURLcode rc; @@ -3372,7 +3322,6 @@ WinHttpQueryOption if (rc != CURLE_OK) return FALSE; - HTTP_VERSION_INFO version; if (curlversion == CURL_HTTP_VERSION_1_0) { @@ -3386,11 +3335,15 @@ WinHttpQueryOption } else if (curlversion == CURL_HTTP_VERSION_2_0) { - version.dwMinorVersion = 1; - version.dwMajorVersion = 1; + version.dwMinorVersion = 0; + version.dwMajorVersion = 2; } else return FALSE; +#else + version.dwMinorVersion = 1; + version.dwMajorVersion = 1; +#endif memcpy(lpBuffer, &version, sizeof(version)); if (lpdwBufferLength) @@ -3442,7 +3395,10 @@ BOOLAPI WinHttpCrackUrl( if (lpUrlComponents->dwHostNameLength != (DWORD)-1) { if (lpUrlComponents->dwHostNameLength >= (strlen(host) + 1)) { - WCTNCPY(lpUrlComponents->lpszHostName, (TCHAR*)pwszUrl + pos, strlen(host)); + TSTRING hoststr; + hoststr.assign((TCHAR*)pwszUrl + pos, strlen(host)); + + std::copy(hoststr.begin(), hoststr.end(), lpUrlComponents->lpszHostName); lpUrlComponents->lpszHostName[strlen(host)] = TEXT('\0'); lpUrlComponents->dwHostNameLength = strlen(host); } @@ -3465,7 +3421,10 @@ BOOLAPI WinHttpCrackUrl( if (lpUrlComponents->dwSchemeLength != (DWORD)-1) { if (lpUrlComponents->dwSchemeLength >= (strlen(scheme) + 1)) { - WCTNCPY(lpUrlComponents->lpszScheme, (TCHAR*)pwszUrl + pos, strlen(scheme)); + TSTRING schemestr; + schemestr.assign((TCHAR*)pwszUrl + pos, strlen(scheme)); + + std::copy(schemestr.begin(), schemestr.end(), lpUrlComponents->lpszScheme); lpUrlComponents->lpszScheme[strlen(scheme)] = TEXT('\0'); lpUrlComponents->dwSchemeLength = strlen(scheme); } @@ -3497,7 +3456,10 @@ BOOLAPI WinHttpCrackUrl( if (lpUrlComponents->dwUrlPathLength != (DWORD)-1) { if (lpUrlComponents->dwUrlPathLength >= (strlen(path) + 1)) { - WCTNCPY(lpUrlComponents->lpszUrlPath, (TCHAR*)pwszUrl + pos, strlen(path)); + TSTRING urlstr; + urlstr.assign((TCHAR*)pwszUrl + pos, strlen(path)); + + std::copy(urlstr.begin(), urlstr.end(), lpUrlComponents->lpszUrlPath); lpUrlComponents->lpszUrlPath[strlen(path)] = TEXT('\0'); lpUrlComponents->dwUrlPathLength = strlen(path); } @@ -3527,7 +3489,10 @@ BOOLAPI WinHttpCrackUrl( if (lpUrlComponents->dwExtraInfoLength != (DWORD)-1) { if (lpUrlComponents->dwExtraInfoLength >= (strlen(query) + 1)) { - WCTNCPY(lpUrlComponents->lpszExtraInfo, (TCHAR*)pwszUrl + pos - 1, strlen(query)); + TSTRING extrainfo; + extrainfo.assign((TCHAR*)pwszUrl + pos - 1, strlen(query)); + + std::copy(extrainfo.begin(), extrainfo.end(), lpUrlComponents->lpszExtraInfo); lpUrlComponents->lpszExtraInfo[strlen(query)] = TEXT('\0'); lpUrlComponents->dwExtraInfoLength = strlen(query); } @@ -3549,7 +3514,10 @@ BOOLAPI WinHttpCrackUrl( if (lpUrlComponents->dwUserNameLength != (DWORD)-1) { if (lpUrlComponents->dwUserNameLength >= (strlen(user) + 1)) { - WCTNCPY(lpUrlComponents->lpszUserName, (TCHAR*)pwszUrl + pos - 1, strlen(user)); + TSTRING userstr; + userstr.assign((TCHAR*)pwszUrl + pos - 1, strlen(user)); + + std::copy(userstr.begin(), userstr.end(), lpUrlComponents->lpszUserName); lpUrlComponents->lpszUserName[strlen(user)] = TEXT('\0'); lpUrlComponents->dwUserNameLength = strlen(user); } @@ -3571,7 +3539,11 @@ BOOLAPI WinHttpCrackUrl( if (lpUrlComponents->dwPasswordLength != (DWORD)-1) { if (lpUrlComponents->dwPasswordLength >= (strlen(pw) + 1)) { - WCTNCPY(lpUrlComponents->lpszPassword, (TCHAR*)pwszUrl + pos - 1, strlen(pw)); + TSTRING pwstr; + pwstr.assign((TCHAR*)pwszUrl + pos - 1, strlen(pw)); + + std::copy(pwstr.begin(), pwstr.end(), lpUrlComponents->lpszPassword); + lpUrlComponents->lpszPassword[strlen(pw)] = TEXT('\0'); lpUrlComponents->dwPasswordLength = strlen(pw); } From 03456524773f42e3392d235371c03e9f4f04b1d1 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 5 Aug 2019 00:54:21 -0400 Subject: [PATCH 29/41] tests: CURL doesn't support hostname override --- Release/tests/functional/http/client/outside_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Release/tests/functional/http/client/outside_tests.cpp b/Release/tests/functional/http/client/outside_tests.cpp index 3ff1a809ec..8897af7eac 100644 --- a/Release/tests/functional/http/client/outside_tests.cpp +++ b/Release/tests/functional/http/client/outside_tests.cpp @@ -228,7 +228,7 @@ SUITE(outside_tests) TEST(server_hostname_mismatch) { test_failed_ssl_cert(U("https://wrong.host.badssl.com/")); } -#if !defined(__cplusplus_winrt) +#if !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_LISTENER_CURL) TEST(server_hostname_host_override) { handle_timeout([] { @@ -253,7 +253,7 @@ SUITE(outside_tests) const auto statusCode = response.status_code(); CHECK(statusCode == status_codes::OK || statusCode == status_codes::MovedPermanently); } -#endif // !defined(__cplusplus_winrt) +#endif // !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_LISTENER_CURL) TEST(server_cert_expired) { test_failed_ssl_cert(U("https://expired.badssl.com/")); } From 422655cc62dd077df93e9053fd03edbfedf77230 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 5 Aug 2019 01:02:30 -0400 Subject: [PATCH 30/41] More function unification and prevent premature read completion --- .../src/http/client/winhttppal/winhttppal.cpp | 113 ++++++++++-------- 1 file changed, 62 insertions(+), 51 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 403ffea745..0050b303b1 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -494,6 +494,7 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this static size_t WriteHeaderFunction(void *ptr, size_t size, size_t nmemb, void* rqst); static size_t WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst); + void ConsumeIncoming(std::shared_ptr &srequest, void* &ptr, size_t &available, size_t &read); void SetCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags) { m_InternetCallback = lpfnInternetCallback; m_NotificationFlags = dwNotificationFlags; @@ -1291,27 +1292,13 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) if (m->data.result == CURLE_OK) { - BYTE *ptr = request->GetResponseString().data(); + void *ptr = request->GetResponseString().data(); size_t available = request->GetResponseString().size(); - while (true) + size_t totalread = 0; + request->ConsumeIncoming(srequest, ptr, available, totalread); + if (totalread) { - BufferRequest buf = GetBufferRequest(request->GetOutstandingReads()); - if (!buf.m_Length) - break; - - size_t len = MIN(buf.m_Length, available); - if (len) - { - TRACE("%-35s:%-8d:%-16p reading length:%lu\n", __func__, __LINE__, (void*)request, len); - memcpy(static_cast(buf.m_Buffer), ptr, len); - } - - ptr = ptr + len; - available -= len; - - LPVOID StatusInformation = buf.m_Buffer; - - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, len, StatusInformation, sizeof(StatusInformation), false); + TRACE("%-35s:%-8d:%-16p consumed length:%lu\n", __func__, __LINE__, (void*)srequest.get(), totalread); } } else if (m->data.result == CURLE_OPERATION_TIMEDOUT) @@ -1589,59 +1576,83 @@ size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nme return size * nmemb; } +void WinHttpRequestImp::ConsumeIncoming(std::shared_ptr &srequest, void* &ptr, size_t &available, size_t &read) +{ + while (available) + { + BufferRequest buf = GetBufferRequest(srequest->GetOutstandingReads()); + if (!buf.m_Length) + break; + + size_t len = MIN(buf.m_Length, available); + + if (len) + { + TRACE("%-35s:%-8d:%-16p reading length:%lu written:%lu %p %ld\n", + __func__, __LINE__, (void*)srequest.get(), len, read, buf.m_Buffer, buf.m_Length); + memcpy(static_cast(buf.m_Buffer), ptr, len); + } + + ptr = static_cast(ptr) + len; + available -= len; + + if (len) + { + LPVOID StatusInformation = buf.m_Buffer; + + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)srequest.get(), buf.m_Buffer, len); + srequest->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, len, StatusInformation, sizeof(StatusInformation), false); + } + + read += len; + } +} + size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { - DWORD available = size * nmemb; - DWORD read = 0; + size_t read = 0; WinHttpRequestImp *request = static_cast(rqst); if (!request) return 0; std::shared_ptr srequest = request->shared_from_this(); if (!srequest) - return available; + return 0; { std::lock_guard lck(request->GetBodyStringMutex()); - while (available) - { - BufferRequest buf = GetBufferRequest(request->GetOutstandingReads()); - if (!buf.m_Length) - break; - - size_t len = MIN(buf.m_Length, available); - - if (len) - { - TRACE("%-35s:%-8d:%-16p reading length:%lu written:%lu %p %ld\n", - __func__, __LINE__, (void*)request, len, read, buf.m_Buffer, buf.m_Length); - memcpy(static_cast(buf.m_Buffer), ptr, len); - } + void *buf = request->GetResponseString().data(); + size_t available = request->GetResponseString().size(); + size_t totalread = 0; - ptr = static_cast(ptr) + len; - available -= len; - - if (len) - { - LPVOID StatusInformation = buf.m_Buffer; + request->ConsumeIncoming(srequest, buf, available, totalread); + if (totalread) + { + TRACE("%-35s:%-8d:%-16p consumed length:%lu\n", __func__, __LINE__, (void*)srequest.get(), totalread); + request->GetReadLength() += totalread; + request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + totalread); + request->GetReadData().shrink_to_fit(); + } + } - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, len, StatusInformation, sizeof(StatusInformation), false); - } + size_t available = size * nmemb; + { + std::lock_guard lck(request->GetBodyStringMutex()); + void *buf = ptr; - read += len; - } + request->ConsumeIncoming(srequest, buf, available, read); if (available) { request->GetResponseString().insert(request->GetResponseString().end(), - reinterpret_cast(ptr), - reinterpret_cast(ptr) + available); + reinterpret_cast(buf), + reinterpret_cast(buf) + available); read += available; } } if (request->GetAsync()) { - if (request->HandleQueryDataNotifications(srequest, available)) + if (available && request->HandleQueryDataNotifications(srequest, available)) TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); } @@ -2666,7 +2677,6 @@ WinHttpQueryDataAvailable } else { - TRACE("%-35s:%-8d:%-16p available = %lu\n", __func__, __LINE__, (void*)request, available); DWORD lpvStatusInformation = available; TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, (void*)request, lpvStatusInformation); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), @@ -2718,6 +2728,7 @@ WinHttpReadData { LPVOID StatusInformation = (LPVOID)lpBuffer; + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%d\n", __func__, __LINE__, (void*)request, lpBuffer, 0); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, 0, (LPVOID)StatusInformation, sizeof(StatusInformation), false); } return TRUE; @@ -2745,9 +2756,9 @@ WinHttpReadData } else { - TRACE("%-35s:%-8d:%-16p lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)request, lpBuffer, readLength); LPVOID StatusInformation = (LPVOID)lpBuffer; + TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)request, lpBuffer, readLength); request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, readLength, (LPVOID)StatusInformation, sizeof(StatusInformation), false); } } From 1cd5cd13cd8d84a49e83d89131f99e0bd850b6e2 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 5 Aug 2019 01:07:03 -0400 Subject: [PATCH 31/41] Fix typo --- Release/tests/functional/http/client/outside_tests.cpp | 4 ++-- Release/tests/functional/http/client/request_uri_tests.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Release/tests/functional/http/client/outside_tests.cpp b/Release/tests/functional/http/client/outside_tests.cpp index 8897af7eac..a30d813a72 100644 --- a/Release/tests/functional/http/client/outside_tests.cpp +++ b/Release/tests/functional/http/client/outside_tests.cpp @@ -228,7 +228,7 @@ SUITE(outside_tests) TEST(server_hostname_mismatch) { test_failed_ssl_cert(U("https://wrong.host.badssl.com/")); } -#if !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_LISTENER_CURL) +#if !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_CURL) TEST(server_hostname_host_override) { handle_timeout([] { @@ -253,7 +253,7 @@ SUITE(outside_tests) const auto statusCode = response.status_code(); CHECK(statusCode == status_codes::OK || statusCode == status_codes::MovedPermanently); } -#endif // !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_LISTENER_CURL) +#endif // !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_CURL) TEST(server_cert_expired) { test_failed_ssl_cert(U("https://expired.badssl.com/")); } diff --git a/Release/tests/functional/http/client/request_uri_tests.cpp b/Release/tests/functional/http/client/request_uri_tests.cpp index 18a5555c3f..14de35a626 100644 --- a/Release/tests/functional/http/client/request_uri_tests.cpp +++ b/Release/tests/functional/http/client/request_uri_tests.cpp @@ -88,7 +88,7 @@ SUITE(request_uri_tests) // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/heheh?key1=value2#fragment"); -#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_CURL) +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) expected_value = percent_encode_pound(expected_value); #endif @@ -137,7 +137,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1?key1=value1&key2=value2#frag"); -#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_CURL) +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); @@ -157,7 +157,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1/path2?key2=value2#fragmentfg2"); -#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_LISTENER_CURL) +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); From 89ed468c5cc7821fba0b414e37e897ed704b9195 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 5 Aug 2019 23:00:49 -0400 Subject: [PATCH 32/41] Support proxies --- .../src/http/client/winhttppal/winhttppal.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index 0050b303b1..d1fa234745 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -484,8 +484,10 @@ class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this DWORD m_NotificationFlags = 0; std::vector m_OutstandingWrites; std::vector m_OutstandingReads; + bool m_Secure = false; public: + bool &GetSecure() { return m_Secure; } std::vector &GetOutstandingWrites() { return m_OutstandingWrites; } std::vector &GetOutstandingReads() { return m_OutstandingReads; } @@ -1907,9 +1909,14 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpen if (!pszProxyW) return NULL; + TRACE("%-35s:%-8d:%-16p proxy: " STRING_LITERAL " \n", __func__, __LINE__, (void*) (session.get()), pszProxyW); ConvertCstrAssign(pszProxyW, WCTLEN(pszProxyW), session->GetProxy()); std::vector proxies = Split(session->GetProxy(), ';'); + if (proxies.empty()) + { + proxies.push_back(session->GetProxy()); + } session->SetProxies(proxies); } @@ -2061,9 +2068,15 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( const char *prefix; std::string server = session->GetServerName(); if (dwFlags & WINHTTP_FLAG_SECURE) + { prefix = "https://"; + request->GetSecure() = true; + } else + { prefix = "http://"; + request->GetSecure() = false; + } if (server.find("http://") == std::string::npos) server = prefix + server; @@ -2930,7 +2943,13 @@ BOOLAPI WinHttpQueryHeaders( #endif if (dwInfoLevel == WINHTTP_QUERY_STATUS_CODE) { - return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_RESPONSE_CODE, returnDWORD); + WinHttpSessionImp *session; + + session = GetImp(request); + if (!session->GetProxies().empty() && request->GetSecure()) + return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_HTTP_CONNECTCODE, returnDWORD); + else + return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_RESPONSE_CODE, returnDWORD); } if (dwInfoLevel == WINHTTP_QUERY_STATUS_TEXT) From 8a4d80886463a4db0433d38f655d1d69ed669024 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 5 Aug 2019 23:01:38 -0400 Subject: [PATCH 33/41] Fix spacing --- .../src/http/client/winhttppal/winhttppal.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp index d1fa234745..f1c3ba6e5f 100644 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ b/Release/src/http/client/winhttppal/winhttppal.cpp @@ -1296,7 +1296,7 @@ THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) { void *ptr = request->GetResponseString().data(); size_t available = request->GetResponseString().size(); - size_t totalread = 0; + size_t totalread = 0; request->ConsumeIncoming(srequest, ptr, available, totalread); if (totalread) { @@ -1611,7 +1611,7 @@ void WinHttpRequestImp::ConsumeIncoming(std::shared_ptr &sreq } size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { - size_t read = 0; + size_t read = 0; WinHttpRequestImp *request = static_cast(rqst); if (!request) return 0; @@ -1624,7 +1624,7 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb std::lock_guard lck(request->GetBodyStringMutex()); void *buf = request->GetResponseString().data(); size_t available = request->GetResponseString().size(); - size_t totalread = 0; + size_t totalread = 0; request->ConsumeIncoming(srequest, buf, available, totalread); if (totalread) @@ -1636,7 +1636,7 @@ size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb } } - size_t available = size * nmemb; + size_t available = size * nmemb; { std::lock_guard lck(request->GetBodyStringMutex()); void *buf = ptr; @@ -1913,10 +1913,10 @@ WINHTTPAPI HINTERNET WINAPI WinHttpOpen ConvertCstrAssign(pszProxyW, WCTLEN(pszProxyW), session->GetProxy()); std::vector proxies = Split(session->GetProxy(), ';'); - if (proxies.empty()) - { - proxies.push_back(session->GetProxy()); - } + if (proxies.empty()) + { + proxies.push_back(session->GetProxy()); + } session->SetProxies(proxies); } From 2124784a82384ecba1a03d7c56fdd61eb71a6325 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 2 Sep 2019 18:46:16 +0000 Subject: [PATCH 34/41] Move winhttppal to its own library --- Release/CMakeLists.txt | 2 +- Release/cmake/cpprest_find_curl.cmake | 17 - Release/cmake/cpprest_find_winhttppal.cmake | 17 + Release/cmake/cpprestsdk-config.in.cmake | 4 +- Release/src/CMakeLists.txt | 18 +- ...nt_curl.cpp => http_client_winhttppal.cpp} | 6 +- .../src/http/client/winhttppal/winhttppal.cpp | 3644 ----------------- .../src/http/client/winhttppal/winhttppal.h | 454 -- .../functional/http/client/outside_tests.cpp | 4 +- .../http/client/request_uri_tests.cpp | 6 +- azure-pipelines.yml | 14 +- 11 files changed, 48 insertions(+), 4138 deletions(-) delete mode 100644 Release/cmake/cpprest_find_curl.cmake create mode 100644 Release/cmake/cpprest_find_winhttppal.cmake rename Release/src/http/client/{http_client_curl.cpp => http_client_winhttppal.cpp} (99%) delete mode 100644 Release/src/http/client/winhttppal/winhttppal.cpp delete mode 100644 Release/src/http/client/winhttppal/winhttppal.h diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 3df8be3c9e..4896443583 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -61,7 +61,7 @@ endif() include(cmake/cpprest_find_boost.cmake) include(cmake/cpprest_find_zlib.cmake) -include(cmake/cpprest_find_curl.cmake) +include(cmake/cpprest_find_winhttppal.cmake) include(cmake/cpprest_find_openssl.cmake) include(cmake/cpprest_find_websocketpp.cmake) include(cmake/cpprest_find_brotli.cmake) diff --git a/Release/cmake/cpprest_find_curl.cmake b/Release/cmake/cpprest_find_curl.cmake deleted file mode 100644 index bc572e233c..0000000000 --- a/Release/cmake/cpprest_find_curl.cmake +++ /dev/null @@ -1,17 +0,0 @@ -function(cpprest_find_curl) - if(TARGET cpprestsdk_curl_internal) - return() - endif() - - if(NOT CURL_LIBRARY OR NOT CURL_INCLUDE_DIRS) - find_package(CURL REQUIRED) - endif() - - add_library(cpprestsdk_curl_internal INTERFACE) - if(TARGET CURL::CURL) - target_link_libraries(cpprestsdk_curl_internal INTERFACE CURL::CURL) - else() - target_link_libraries(cpprestsdk_curl_internal INTERFACE "$") - target_include_directories(cpprestsdk_curl_internal INTERFACE "$") - endif() -endfunction() diff --git a/Release/cmake/cpprest_find_winhttppal.cmake b/Release/cmake/cpprest_find_winhttppal.cmake new file mode 100644 index 0000000000..072fc5049e --- /dev/null +++ b/Release/cmake/cpprest_find_winhttppal.cmake @@ -0,0 +1,17 @@ +function(cpprest_find_winhttppal) + if(TARGET cpprestsdk_winhttppal_internal) + return() + endif() + + if(NOT winhttppal_LIBRARY OR NOT winhttppal_INCLUDE_DIRS) + find_package(winhttppal REQUIRED) + endif() + + add_library(cpprestsdk_winhttppal_internal INTERFACE) + if(TARGET winhttppal::winhttppal) + target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE winhttppal::winhttppal) + else() + target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$") + target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$") + endif() +endfunction() diff --git a/Release/cmake/cpprestsdk-config.in.cmake b/Release/cmake/cpprestsdk-config.in.cmake index 9e7db64dfb..be95abf99a 100644 --- a/Release/cmake/cpprestsdk-config.in.cmake +++ b/Release/cmake/cpprestsdk-config.in.cmake @@ -11,8 +11,8 @@ if(@CPPREST_USES_OPENSSL@) find_dependency(OpenSSL) endif() -if(@CPPREST_USES_CURL@) - find_dependency(CURL) +if(@CPPREST_USES_WINHTTPPAL@) + find_dependency(WINHTTPPAL) endif() if(@CPPREST_USES_BOOST@ AND OFF) diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt index 82eed60672..c5a1c1a0e6 100644 --- a/Release/src/CMakeLists.txt +++ b/Release/src/CMakeLists.txt @@ -131,13 +131,13 @@ if(CPPREST_HTTP_CLIENT_IMPL STREQUAL "asio") target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_ASIO) target_sources(cpprest PRIVATE http/client/http_client_asio.cpp http/client/x509_cert_utilities.cpp) target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal) -elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "curl") +elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttppal") cpprest_find_boost() cpprest_find_openssl() - cpprest_find_curl() - target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_CURL) - target_sources(cpprest PRIVATE http/client/http_client_curl.cpp http/client/winhttppal/winhttppal.cpp http/client/x509_cert_utilities.cpp) - target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal cpprestsdk_curl_internal) + cpprest_find_winhttppal() + target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) + target_sources(cpprest PRIVATE http/client/http_client_winhttppal.cpp http/client/x509_cert_utilities.cpp) + target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal cpprestsdk_winhttppal_internal) elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttp") target_link_libraries(cpprest PRIVATE httpapi.lib @@ -244,7 +244,7 @@ if(CPPREST_INSTALL) set(CPPREST_USES_ZLIB OFF) set(CPPREST_USES_BROTLI OFF) set(CPPREST_USES_OPENSSL OFF) - set(CPPREST_USES_CURL OFF) + set(CPPREST_USES_WINHTTPPAL OFF) set(CPPREST_TARGETS cpprest) if(TARGET cpprestsdk_boost_internal) @@ -263,9 +263,9 @@ if(CPPREST_INSTALL) list(APPEND CPPREST_TARGETS cpprestsdk_openssl_internal) set(CPPREST_USES_OPENSSL ON) endif() - if(TARGET cpprestsdk_curl_internal) - list(APPEND CPPREST_TARGETS cpprestsdk_curl_internal) - set(CPPREST_USES_CURL ON) + if(TARGET cpprestsdk_winhttppal_internal) + list(APPEND CPPREST_TARGETS cpprestsdk_winhttppal_internal) + set(CPPREST_USES_WINHTTPPAL ON) endif() if(TARGET cpprestsdk_websocketpp_internal) list(APPEND CPPREST_TARGETS cpprestsdk_websocketpp_internal) diff --git a/Release/src/http/client/http_client_curl.cpp b/Release/src/http/client/http_client_winhttppal.cpp similarity index 99% rename from Release/src/http/client/http_client_curl.cpp rename to Release/src/http/client/http_client_winhttppal.cpp index 62cd9a10eb..dbbf866c2c 100644 --- a/Release/src/http/client/http_client_curl.cpp +++ b/Release/src/http/client/http_client_winhttppal.cpp @@ -18,7 +18,7 @@ #include "../common/internal_http_helpers.h" #include "cpprest/http_headers.h" #include "http_client_impl.h" -#include "winhttppal/winhttppal.h" +#include "winhttppal.h" #include #include @@ -620,7 +620,7 @@ struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG }; -// CURL client. +// WINHTTPPAL client. class curl_client final : public _http_client_communicator { public: @@ -923,7 +923,7 @@ class curl_client final : public _http_client_communicator DWORD ignoredCertificateValidationSteps = 0; if (client_config().validate_certificates()) { - // Revocation is enabled by default in CURL + // Revocation is enabled by default in WINHTTPPAL // check if the user has overridden the desired Common Name with the host header const auto hostHeader = headers.find(_XPLATSTR("Host")); if (hostHeader != headers.end()) diff --git a/Release/src/http/client/winhttppal/winhttppal.cpp b/Release/src/http/client/winhttppal/winhttppal.cpp deleted file mode 100644 index f1c3ba6e5f..0000000000 --- a/Release/src/http/client/winhttppal/winhttppal.cpp +++ /dev/null @@ -1,3644 +0,0 @@ -// winhttp.cpp : This file contains the 'main' function. Program execution begins and ends there. -// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef UNICODE -#include -#endif - -#ifdef WIN32 -#define localtime_r(_Time, _Tm) localtime_s(_Tm, _Time) -#endif - -#define _WINHTTP_INTERNAL_ -#ifdef _MSC_VER -#include -#include "winhttp.h" -#else -#include -#include -#include "winhttppal.h" -#endif - -#include -extern "C" -{ -#include -#include -} - -class WinHttpSessionImp; -class WinHttpRequestImp; - -#ifdef _MSC_VER -#pragma comment(lib, "Ws2_32.lib") -#endif - -static const std::map StatusCodeMap = { -{ 100, TEXT("Continue") }, -{ 101, TEXT("Switching Protocols") }, -{ 200, TEXT("OK") }, -{ 201, TEXT("Created") }, -{ 202, TEXT("Accepted") }, -{ 203, TEXT("Non-Authoritative Information") }, -{ 204, TEXT("No Content") }, -{ 205, TEXT("Reset Content") }, -{ 206, TEXT("Partial Content") }, -{ 300, TEXT("Multiple Choices") }, -{ 301, TEXT("Moved Permanently") }, -{ 302, TEXT("Found") }, -{ 303, TEXT("See Other") }, -{ 304, TEXT("Not Modified") }, -{ 305, TEXT("Use Proxy") }, -{ 306, TEXT("(Unused)") }, -{ 307, TEXT("Temporary Redirect") }, -{ 400, TEXT("Bad Request") }, -{ 401, TEXT("Unauthorized") }, -{ 402, TEXT("Payment Required") }, -{ 403, TEXT("Forbidden") }, -{ 404, TEXT("Not Found") }, -{ 405, TEXT("Method Not Allowed") }, -{ 406, TEXT("Not Acceptable") }, -{ 407, TEXT("Proxy Authentication Required") }, -{ 408, TEXT("Request Timeout") }, -{ 409, TEXT("Conflict") }, -{ 410, TEXT("Gone") }, -{ 411, TEXT("Length Required") }, -{ 412, TEXT("Precondition Failed") }, -{ 413, TEXT("Request Entity Too Large") }, -{ 414, TEXT("Request-URI Too Long") }, -{ 415, TEXT("Unsupported Media Type") }, -{ 416, TEXT("Requested Range Not Satisfiable") }, -{ 417, TEXT("Expectation Failed") }, -{ 500, TEXT("Internal Server Error") }, -{ 501, TEXT("Not Implemented") }, -{ 502, TEXT("Bad Gateway") }, -{ 503, TEXT("Service Unavailable") }, -{ 504, TEXT("Gateway Timeout") }, -{ 505, TEXT("HTTP Version Not Supported") }, -}; - -enum -{ - WINHTTP_CLASS_SESSION, - WINHTTP_CLASS_REQUEST, - WINHTTP_CLASS_IMP, -}; - -static int winhttp_tracing = false; -static int winhttp_tracing_verbose = false; - -#ifdef _MSC_VER -int gettimeofday(struct timeval * tp, struct timezone * tzp); -#endif - -static std::mutex trcmtx; - -#ifndef _MSC_VER -static void TRACE_INTERNAL(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); -#endif - -static void TRACE_INTERNAL(const char *fmt, ...) -{ - std::lock_guard lck(trcmtx); - va_list args; - va_start(args, fmt); - - tm localTime; - std::chrono::system_clock::time_point t = std::chrono::system_clock::now(); - time_t now = std::chrono::system_clock::to_time_t(t); - localtime_r(&now, &localTime); - - const std::chrono::duration tse = t.time_since_epoch(); - std::chrono::seconds::rep milliseconds = std::chrono::duration_cast(tse).count() % 1000; - - char szBuffer[512]; - vsnprintf(szBuffer, sizeof szBuffer - 1, fmt, args); - szBuffer[sizeof(szBuffer) / sizeof(szBuffer[0]) - 1] = '\0'; - - std::cout << (1900 + localTime.tm_year) << '-' - << std::setfill('0') << std::setw(2) << (localTime.tm_mon + 1) << '-' - << std::setfill('0') << std::setw(2) << localTime.tm_mday << ' ' - << std::setfill('0') << std::setw(2) << localTime.tm_hour << ':' - << std::setfill('0') << std::setw(2) << localTime.tm_min << ':' - << std::setfill('0') << std::setw(2) << localTime.tm_sec << '.' - << std::setfill('0') << std::setw(3) << milliseconds << " " << szBuffer; - - va_end(args); -} - -#define TRACE(fmt, ...) \ - do { if (winhttp_tracing) TRACE_INTERNAL(fmt, __VA_ARGS__); } while (0) - -#define TRACE_VERBOSE(fmt, ...) \ - do { if (winhttp_tracing_verbose) TRACE_INTERNAL(fmt, __VA_ARGS__); } while (0) - -#define CURL_BAILOUT_ONERROR(res, request, retval) \ - if ((res) != CURLE_OK) \ - { \ - TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)(request), (res)); \ - return (retval); \ - } - -typedef void (*CompletionCb)(std::shared_ptr &, DWORD status); - -#define MUTEX_TYPE std::mutex -#define MUTEX_SETUP(x) -#define MUTEX_CLEANUP(x) -#define MUTEX_LOCK(x) x.lock() -#define MUTEX_UNLOCK(x) x.unlock() -#ifdef WIN32 -#define THREAD_ID GetCurrentThreadId() -#define THREAD_HANDLE HANDLE -#define THREADPARAM LPVOID -#define CREATETHREAD(func, param, id) \ - CreateThread( \ - NULL, \ - 0, \ - (LPTHREAD_START_ROUTINE)func, \ - param, \ - 0, \ - id) - -#define THREADJOIN(h) WaitForSingleObject(h, INFINITE); -#define THREADRETURN DWORD -#else -#define THREADPARAM void* -#define THREADRETURN void* -#define THREAD_ID pthread_self() -#define THREAD_HANDLE pthread_t -#define THREADJOIN(x) pthread_join(x, NULL) - -typedef void* (*LPTHREAD_START_ROUTINE)(void *); -static inline THREAD_HANDLE CREATETHREAD(LPTHREAD_START_ROUTINE func, LPVOID param, pthread_t *id) -{ - pthread_t inc_x_thread; - pthread_create(&inc_x_thread, NULL, func, param); - return inc_x_thread; -} -#endif - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -/* This array will store all of the mutexes available to OpenSSL. */ -static MUTEX_TYPE *mutex_buf = NULL; - -static void locking_function(int mode, int n, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) - MUTEX_LOCK(mutex_buf[n]); - else - MUTEX_UNLOCK(mutex_buf[n]); -} - -static unsigned long id_function() -{ - return static_cast(THREAD_ID); -} -#endif - -static int thread_setup() -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L - int i; - - mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()]; - if (!mutex_buf) - return 0; - for (i = 0; i < CRYPTO_num_locks(); i++) - MUTEX_SETUP(mutex_buf[i]); - CRYPTO_set_id_callback(id_function); - CRYPTO_set_locking_callback(locking_function); -#endif - return 1; -} - -static int thread_cleanup() -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L - int i; - - if (!mutex_buf) - return 0; - CRYPTO_set_id_callback(NULL); - CRYPTO_set_locking_callback(NULL); - for (i = 0; i < CRYPTO_num_locks(); i++) - MUTEX_CLEANUP(mutex_buf[i]); - delete [] mutex_buf; - mutex_buf = NULL; -#endif - return 1; -} - -class UserCallbackContext -{ - std::shared_ptr m_request; - DWORD m_dwInternetStatus; - DWORD m_dwStatusInformationLength; - DWORD m_dwNotificationFlags; - WINHTTP_STATUS_CALLBACK m_cb; - LPVOID m_userdata; - LPVOID m_StatusInformationVal = NULL; - BYTE* m_StatusInformation = NULL; - bool m_allocate = FALSE; - BOOL m_AsyncResultValid = false; - CompletionCb m_requestCompletionCb; - - BOOL SetAsyncResult(LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate) { - - if (allocate) - { - m_StatusInformation = new BYTE[statusInformationCopySize]; - if (m_StatusInformation) - { - memcpy(m_StatusInformation, statusInformation, statusInformationCopySize); - } - else - return FALSE; - } - else - { - m_StatusInformationVal = statusInformation; - } - m_AsyncResultValid = true; - m_allocate = allocate; - - return TRUE; - } - -public: - UserCallbackContext(std::shared_ptr &request, - DWORD dwInternetStatus, - DWORD dwStatusInformationLength, - DWORD dwNotificationFlags, - WINHTTP_STATUS_CALLBACK cb, - LPVOID userdata, - LPVOID statusInformation, - DWORD statusInformationCopySize, - bool allocate, - CompletionCb completion); - ~UserCallbackContext(); - std::shared_ptr &GetRequestRef() { return m_request; } - WinHttpRequestImp *GetRequest() { return m_request.get(); } - DWORD GetInternetStatus() const { return m_dwInternetStatus; } - DWORD GetStatusInformationLength() const { return m_dwStatusInformationLength; } - DWORD GetNotificationFlags() const { return m_dwNotificationFlags; } - WINHTTP_STATUS_CALLBACK &GetCb() { return m_cb; } - CompletionCb &GetRequestCompletionCb() { return m_requestCompletionCb; } - LPVOID GetUserdata() { return m_userdata; } - LPVOID GetStatusInformation() { - if (m_allocate) - return m_StatusInformation; - else - return m_StatusInformationVal; - } - BOOL GetStatusInformationValid() const { return m_AsyncResultValid; } - -private: - UserCallbackContext(const UserCallbackContext&); - UserCallbackContext& operator=(const UserCallbackContext&); -}; - -static void ConvertCstrAssign(const TCHAR *lpstr, size_t cLen, std::string &target) -{ -#ifdef UNICODE - std::wstring_convert> conv; - target = conv.to_bytes(std::wstring(lpstr, cLen)); -#else - target.assign(lpstr, cLen); -#endif -} - -static std::vector Split(std::string &str, char delimiter) { - std::vector internal; - std::stringstream ss(str); // Turn the string into a stream. - std::string tok; - - while (getline(ss, tok, delimiter)) { - internal.push_back(tok); - } - - return internal; -} - -static BOOL SizeCheck(LPVOID lpBuffer, LPDWORD lpdwBufferLength, DWORD Required) -{ - if (!lpBuffer) - { - if (lpdwBufferLength) - *lpdwBufferLength = Required; - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; - } - - if (lpdwBufferLength && (*lpdwBufferLength < Required)) { - *lpdwBufferLength = Required; - - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; - } - - if ((Required == 0) || (lpdwBufferLength && (*lpdwBufferLength == 0))) - return FALSE; - - if (!lpBuffer) - return FALSE; - - if (lpdwBufferLength && (*lpdwBufferLength < Required)) - return FALSE; - - return TRUE; -} - -class WinHttpBase -{ -public: - virtual ~WinHttpBase() {} -}; - -class WinHttpConnectImp :public WinHttpBase -{ - WinHttpSessionImp *m_Handle = NULL; - void *m_UserBuffer = NULL; - -public: - void SetHandle(WinHttpSessionImp *session) { m_Handle = session; } - WinHttpSessionImp *GetHandle() { return m_Handle; } - - BOOL SetUserData(void **data) - { - if (!data) - return FALSE; - - m_UserBuffer = *data; - return TRUE; - } - void *GetUserData() { return m_UserBuffer; } -}; - -struct BufferRequest -{ - LPVOID m_Buffer = NULL; - size_t m_Length = 0; - size_t m_Used = 0; -}; - -static void QueueBufferRequest(std::vector &store, LPVOID buffer, size_t length) -{ - BufferRequest shr; - shr.m_Buffer = buffer; - shr.m_Length = length; - shr.m_Used = 0; - store.push_back(shr); -} - -static BufferRequest GetBufferRequest(std::vector &store) -{ - if (store.empty()) - return BufferRequest(); - BufferRequest shr = store.front(); - store.erase(store.begin()); - return shr; -} - -static BufferRequest &PeekBufferRequest(std::vector &store) -{ - return store.front(); -} - -class WinHttpRequestImp :public WinHttpBase, public std::enable_shared_from_this -{ - CURL *m_curl = NULL; - std::vector m_ResponseString; - std::string m_HeaderString; - std::string m_Header; - std::string m_FullPath; - std::string m_OptionalData; - size_t m_TotalSize = 0; - size_t m_TotalReceiveSize = 0; - std::vector m_ReadData; - - std::mutex m_ReadDataEventMtx; - DWORD m_ReadDataEventCounter = 0; - THREAD_HANDLE m_UploadCallbackThread; - bool m_UploadThreadExitStatus = false; - - DWORD m_SecureProtocol = 0; - DWORD m_MaxConnections = 0; - - std::string m_Type; - LPVOID m_UserBuffer = NULL; - bool m_HeaderReceiveComplete = false; - - struct curl_slist *m_HeaderList = NULL; - WinHttpConnectImp *m_Session = NULL; - std::mutex m_HeaderStringMutex; - std::mutex m_BodyStringMutex; - - std::mutex m_QueryDataEventMtx; - bool m_QueryDataEventState = false; - std::atomic m_QueryDataPending; - - std::mutex m_ReceiveCompletionEventMtx; - - std::mutex m_ReceiveResponseMutex; - std::atomic m_ReceiveResponsePending; - std::atomic m_ReceiveResponseEventCounter; - int m_ReceiveResponseSendCounter = 0; - - std::atomic m_RedirectPending; - - bool m_closing = false; - bool m_closed = false; - bool m_Completion = false; - bool m_Async = false; - CURLcode m_CompletionCode = CURLE_OK; - long m_VerifyPeer = 1; - long m_VerifyHost = 2; - bool m_Uploading = false; - - WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; - DWORD m_NotificationFlags = 0; - std::vector m_OutstandingWrites; - std::vector m_OutstandingReads; - bool m_Secure = false; - -public: - bool &GetSecure() { return m_Secure; } - std::vector &GetOutstandingWrites() { return m_OutstandingWrites; } - std::vector &GetOutstandingReads() { return m_OutstandingReads; } - - long &VerifyPeer() { return m_VerifyPeer; } - long &VerifyHost() { return m_VerifyHost; } - - static size_t WriteHeaderFunction(void *ptr, size_t size, size_t nmemb, void* rqst); - static size_t WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst); - void ConsumeIncoming(std::shared_ptr &srequest, void* &ptr, size_t &available, size_t &read); - void SetCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags) { - m_InternetCallback = lpfnInternetCallback; - m_NotificationFlags = dwNotificationFlags; - } - WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) - { - if (dwNotificationFlags) - *dwNotificationFlags = m_NotificationFlags; - return m_InternetCallback; - } - - void SetAsync() { m_Async = TRUE; } - BOOL GetAsync() { return m_Async; } - - WinHttpRequestImp(); - - bool &GetQueryDataEventState() { return m_QueryDataEventState; } - std::mutex &GetQueryDataEventMtx() { return m_QueryDataEventMtx; } - - bool HandleQueryDataNotifications(std::shared_ptr &, size_t available); - void WaitAsyncQueryDataCompletion(std::shared_ptr &); - - void HandleReceiveNotifications(std::shared_ptr &srequest); - void WaitAsyncReceiveCompletion(std::shared_ptr &srequest); - - CURLcode &GetCompletionCode() { return m_CompletionCode; } - bool &GetCompletionStatus() { return m_Completion; } - bool &GetClosing() { return m_closing; } - bool &GetClosed() { return m_closed; } - void CleanUp(); - ~WinHttpRequestImp(); - - bool &GetUploadThreadExitStatus() { return m_UploadThreadExitStatus; } - std::atomic &GetQueryDataPending() { return m_QueryDataPending; } - - THREAD_HANDLE &GetUploadThread() { - return m_UploadCallbackThread; - } - - static THREADRETURN UploadThreadFunction(THREADPARAM lpThreadParameter) - { - WinHttpRequestImp *request = static_cast(lpThreadParameter); - if (!request) - return NULL; - - CURL *curl = request->GetCurl(); - CURLcode res; - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - /* Perform the request, res will get the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - CURL_BAILOUT_ONERROR(res, request, NULL); - - TRACE("%-35s:%-8d:%-16p res:%d\n", __func__, __LINE__, (void*)request, res); - request->GetUploadThreadExitStatus() = true; - - #if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_remove_thread_state(NULL); - #elif OPENSSL_VERSION_NUMBER < 0x10000000L - ERR_remove_state(0); - #endif - return 0; - } - - // used to wake up CURL ReadCallback triggered on a upload request - std::mutex &GetReadDataEventMtx() { return m_ReadDataEventMtx; } - DWORD &GetReadDataEventCounter() { return m_ReadDataEventCounter; } - - std::mutex &GetReceiveCompletionEventMtx() { return m_ReceiveCompletionEventMtx; } - - // counters used to see which one of these events are observed: ResponseCallbackEventCounter - // counters used to see which one of these events are broadcasted: ResponseCallbackSendCounter - // WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE - // WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED - // WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE - // - // These counters need to be 3 and identical under normal circumstances - std::atomic &ResponseCallbackEventCounter() { return m_ReceiveResponseEventCounter; } - int &ResponseCallbackSendCounter() { return m_ReceiveResponseSendCounter; } - std::atomic &GetReceiveResponsePending() { return m_ReceiveResponsePending; } - std::mutex &GetReceiveResponseMutex() { return m_ReceiveResponseMutex; } - - std::atomic &GetRedirectPending() { return m_RedirectPending; } - - std::mutex &GetHeaderStringMutex() { return m_HeaderStringMutex; } - std::mutex &GetBodyStringMutex() { return m_HeaderStringMutex; } - - BOOL AsyncQueue(std::shared_ptr &, - DWORD dwInternetStatus, size_t statusInformationLength, - LPVOID statusInformation, DWORD statusInformationCopySize, bool allocate); - - void SetHeaderReceiveComplete() { m_HeaderReceiveComplete = TRUE; } - - BOOL SetSecureProtocol(DWORD *data) - { - if (!data) - return FALSE; - - m_SecureProtocol = *data; - return TRUE; - } - DWORD GetSecureProtocol() { return m_SecureProtocol; } - - BOOL SetMaxConnections(DWORD *data) - { - if (!data) - return FALSE; - - m_MaxConnections = *data; - return TRUE; - } - DWORD GetMaxConnections() { return m_MaxConnections; } - - BOOL SetUserData(void **data) - { - if (!data) - return FALSE; - - m_UserBuffer = *data; - return TRUE; - } - LPVOID GetUserData() { return m_UserBuffer; } - - void SetFullPath(std::string &server, std::string &relative) - { - m_FullPath.append(server); - m_FullPath.append(relative); - } - std::string &GetFullPath() { return m_FullPath; } - std::string &GetType() { return m_Type; } - bool &Uploading() { return m_Uploading; } - - void SetSession(WinHttpConnectImp *connect) { m_Session = connect; } - WinHttpConnectImp *GetSession() { return m_Session; } - - struct curl_slist *GetHeaderList() { return m_HeaderList; } - - void SetHeaderList(struct curl_slist *list) { m_HeaderList = list; } - std::string &GetOutgoingHeaderList() { return m_Header; } - void AddHeader(std::string &headers) { - std::stringstream check1(headers); - std::string str; - - while(getline(check1, str, '\n')) - { - str.erase(std::remove(str.begin(), str.end(), '\r'), str.end()); - SetHeaderList(curl_slist_append(GetHeaderList(), str.c_str())); - } - } - - std::vector &GetResponseString() { return m_ResponseString; } - std::string &GetHeaderString() { return m_HeaderString; } - CURL *GetCurl() { return m_curl; } - - BOOL SetProxy(std::vector &proxies) - { - std::vector::iterator it; - - for (it = proxies.begin(); it != proxies.end(); it++) - { - std::string &urlstr = (*it); - CURLcode res; - - res = curl_easy_setopt(GetCurl(), CURLOPT_PROXY, urlstr.c_str()); - CURL_BAILOUT_ONERROR(res, this, FALSE); - } - - return TRUE; - } - - BOOL SetServer(std::string &ServerName, int nServerPort) - { - CURLcode res; - - res = curl_easy_setopt(GetCurl(), CURLOPT_URL, ServerName.c_str()); - CURL_BAILOUT_ONERROR(res, this, FALSE); - - res = curl_easy_setopt(GetCurl(), CURLOPT_PORT, nServerPort); - CURL_BAILOUT_ONERROR(res, this, FALSE); - - return TRUE; - } - - size_t &GetTotalLength() { return m_TotalSize; } - size_t &GetReadLength() { return m_TotalReceiveSize; } - - std::string &GetOptionalData() { return m_OptionalData; } - BOOL SetOptionalData(void *lpOptional, size_t dwOptionalLength) - { - if (!lpOptional || !dwOptionalLength) - return FALSE; - - m_OptionalData.assign(&(static_cast(lpOptional))[0], dwOptionalLength); - return TRUE; - } - - std::vector &GetReadData() { return m_ReadData; } - - void AppendReadData(const void *data, size_t len) - { - std::lock_guard lck(GetReadDataEventMtx()); - m_ReadData.insert(m_ReadData.end(), static_cast(data), static_cast(data) + len); - } - - static int SocketCallback(CURL *handle, curl_infotype type, - char *data, size_t size, - void *userp); - - static size_t ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp); -}; - -typedef std::vector UserCallbackQueue; - -class UserCallbackContainer -{ - UserCallbackQueue m_Queue; - - // used to protect GetCallbackMap() - std::mutex m_MapMutex; - - THREAD_HANDLE m_hThread; - - // used by UserCallbackThreadFunction as a wake-up signal - std::mutex m_hEventMtx; - std::condition_variable m_hEvent; - - // cleared by UserCallbackThreadFunction, set by multiple event providers - std::atomic m_EventCounter; - - bool m_closing = false; - UserCallbackQueue &GetCallbackQueue() { return m_Queue; } - - -public: - - bool GetClosing() const { return m_closing; } - - UserCallbackContext* GetNext() - { - UserCallbackContext *ctx = NULL; - // show content: - if (!GetCallbackQueue().empty()) - { - ctx = GetCallbackQueue().front(); - GetCallbackQueue().erase(GetCallbackQueue().begin()); - } - - return ctx; - } - - static THREADRETURN UserCallbackThreadFunction(LPVOID lpThreadParameter); - - BOOL Queue(UserCallbackContext *ctx) - { - if (!ctx) - return FALSE; - - { - std::lock_guard lck(m_MapMutex); - - TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p userdata = %p dwInternetStatus = %p\n", - __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), - ctx->GetUserdata(), ctx->GetStatusInformation()); - GetCallbackQueue().push_back(ctx); - } - { - std::lock_guard lck(m_hEventMtx); - m_EventCounter++; - m_hEvent.notify_all(); - } - return TRUE; - } - - void DrainQueue() - { - while (true) - { - m_MapMutex.lock(); - UserCallbackContext *ctx = GetNext(); - m_MapMutex.unlock(); - - if (!ctx) - break; - - delete ctx; - } - } - - UserCallbackContainer(): m_EventCounter(0) - { - DWORD thread_id; - m_hThread = CREATETHREAD( - UserCallbackThreadFunction, // thread function name - this, // argument to thread function - &thread_id - ); - } - ~UserCallbackContainer() - { - m_closing = true; - { - std::lock_guard lck(m_hEventMtx); - m_EventCounter++; - m_hEvent.notify_all(); - } - THREADJOIN(m_hThread); - DrainQueue(); - } - static UserCallbackContainer &GetInstance() - { - static UserCallbackContainer the_instance; - return the_instance; - } -private: - UserCallbackContainer(const UserCallbackContainer&); - UserCallbackContainer& operator=(const UserCallbackContainer&); -}; - -class EnvInit -{ -public: - EnvInit() - { - if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG")) - winhttp_tracing = std::stoi(std::string(env_p)); - - if (const char* env_p = std::getenv("WINHTTP_PAL_DEBUG_VERBOSE")) - winhttp_tracing_verbose = std::stoi(std::string(env_p)); - } -}; - -static EnvInit envinit; - -class ComContainer -{ - THREAD_HANDLE m_hAsyncThread; - std::mutex m_hAsyncEventMtx; - - // used to wake up the Async Thread - // Set by external components, cleared by the Async thread - std::atomic m_hAsyncEventCounter; - std::condition_variable m_hAsyncEvent; - std::mutex m_MultiMutex; - CURLM *m_curlm = NULL; - std::vector< CURL *> m_ActiveCurl; - std::vector> m_ActiveRequests; - BOOL m_closing = FALSE; - - // used to protect CURLM data structures - BOOL GetThreadClosing() const { return m_closing; } - -public: - CURL *AllocCURL() - { - CURL *ptr; - - m_MultiMutex.lock(); - ptr = curl_easy_init(); - m_MultiMutex.unlock(); - - return ptr; - } - - void FreeCURL(CURL *ptr) - { - m_MultiMutex.lock(); - curl_easy_cleanup(ptr); - m_MultiMutex.unlock(); - } - - static ComContainer &GetInstance() - { - static ComContainer the_instance; - return the_instance; - } - - void ResumeTransfer(CURL *handle, int bitmask) - { - int still_running; - - m_MultiMutex.lock(); - curl_easy_pause(handle, bitmask); - curl_multi_perform(m_curlm, &still_running); - m_MultiMutex.unlock(); - } - - BOOL AddHandle(std::shared_ptr &srequest, CURL *handle) - { - CURLMcode mres = CURLM_OK; - - m_MultiMutex.lock(); - if (std::find(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle) != m_ActiveCurl.end()) { - mres = curl_multi_remove_handle(m_curlm, handle); - m_ActiveCurl.erase(std::remove(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle), m_ActiveCurl.end()); - } - if (std::find(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest) != m_ActiveRequests.end()) { - m_ActiveRequests.erase(std::remove(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest), m_ActiveRequests.end()); - } - m_MultiMutex.unlock(); - if (mres != CURLM_OK) - { - TRACE("curl_multi_remove_handle() failed: %s\n", curl_multi_strerror(mres)); - return FALSE; - } - - m_MultiMutex.lock(); - mres = curl_multi_add_handle(m_curlm, handle); - m_ActiveCurl.push_back(handle); - m_ActiveRequests.push_back(srequest); - m_MultiMutex.unlock(); - if (mres != CURLM_OK) - { - TRACE("curl_multi_add_handle() failed: %s\n", curl_multi_strerror(mres)); - return FALSE; - } - - return TRUE; - } - BOOL RemoveHandle(std::shared_ptr &srequest, CURL *handle, bool clearPrivate) - { - CURLMcode mres; - - m_MultiMutex.lock(); - mres = curl_multi_remove_handle(m_curlm, handle); - - if (clearPrivate) - curl_easy_getinfo(handle, CURLINFO_PRIVATE, NULL); - - m_ActiveCurl.erase(std::remove(m_ActiveCurl.begin(), m_ActiveCurl.end(), handle), m_ActiveCurl.end()); - m_ActiveRequests.erase(std::remove(m_ActiveRequests.begin(), m_ActiveRequests.end(), srequest), m_ActiveRequests.end()); - m_MultiMutex.unlock(); - if (mres != CURLM_OK) - { - TRACE("curl_multi_add_handle() failed: %s\n", curl_multi_strerror(mres)); - return FALSE; - } - - return TRUE; - } - - long GetTimeout() - { - long curl_timeo; - - m_MultiMutex.lock(); - curl_multi_timeout(m_curlm, &curl_timeo); - m_MultiMutex.unlock(); - - return curl_timeo; - } - - void KickStart() - { - std::lock_guard lck(m_hAsyncEventMtx); - m_hAsyncEventCounter++; - m_hAsyncEvent.notify_all(); - } - - ComContainer(): m_hAsyncEventCounter(0) - { - curl_global_init(CURL_GLOBAL_ALL); - thread_setup(); - - m_curlm = curl_multi_init(); - - DWORD thread_id; - m_hAsyncThread = CREATETHREAD( - AsyncThreadFunction, // thread function name - this, &thread_id); // argument to thread function - } - - ~ComContainer() - { - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); - m_closing = true; - { - std::lock_guard lck(m_hAsyncEventMtx); - m_hAsyncEventCounter++; - m_hAsyncEvent.notify_all(); - } - THREADJOIN(m_hAsyncThread); - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); - - curl_multi_cleanup(m_curlm); - curl_global_cleanup(); - thread_cleanup(); - } - - - static THREADRETURN AsyncThreadFunction(THREADPARAM lpThreadParameter); - - int QueryData(int *still_running) - { - if (!still_running) - return 0; - - int rc = 0; - struct timeval timeout; - - fd_set fdread; - fd_set fdwrite; - fd_set fdexcep; - int maxfd = -1; - - long curl_timeo = -1; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - FD_ZERO(&fdexcep); - - /* set a suitable timeout to play around with */ - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - m_MultiMutex.lock(); - curl_multi_timeout(m_curlm, &curl_timeo); - m_MultiMutex.unlock(); - if (curl_timeo < 0) - /* no set timeout, use a default */ - curl_timeo = 10000; - - if (curl_timeo > 0) { - timeout.tv_sec = curl_timeo / 1000; - if (timeout.tv_sec > 1) - timeout.tv_sec = 1; - else - timeout.tv_usec = (curl_timeo % 1000) * 1000; - } - - /* get file descriptors from the transfers */ - m_MultiMutex.lock(); - rc = curl_multi_fdset(m_curlm, &fdread, &fdwrite, &fdexcep, &maxfd); - m_MultiMutex.unlock(); - - if ((maxfd == -1) || (rc != CURLM_OK)) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - else - rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); - - switch (rc) { - case -1: - /* select error */ - *still_running = 0; - TRACE("%s\n", "select() returns error, this is badness\n"); - break; - case 0: - default: - /* timeout or readable/writable sockets */ - m_MultiMutex.lock(); - curl_multi_perform(m_curlm, still_running); - m_MultiMutex.unlock(); - break; - } - - return rc; - } -private: - ComContainer(const ComContainer&); - ComContainer& operator=(const ComContainer&); -}; - -template -class WinHttpHandleContainer -{ - std::mutex m_ActiveRequestMtx; - std::vector> m_ActiveRequests; - -public: - static WinHttpHandleContainer &Instance() - { - static WinHttpHandleContainer the_instance; - return the_instance; - } - - typedef std::shared_ptr t_shared; - void UnRegister(T *val) - { - typename std::vector::iterator findit; - bool found = false; - std::lock_guard lck(m_ActiveRequestMtx); - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)val); - - for (auto it = m_ActiveRequests.begin(); it != m_ActiveRequests.end(); ++it) - { - auto v = (*it); - if (v.get() == val) - { - findit = it; - found = true; - break; - } - } - if (found) - m_ActiveRequests.erase(findit); - } - - bool IsRegistered(T *val) - { - bool found = false; - std::lock_guard lck(m_ActiveRequestMtx); - - for (auto it = m_ActiveRequests.begin(); it != m_ActiveRequests.end(); ++it) - { - auto v = (*it); - if (v.get() == val) - { - found = true; - break; - } - } - TRACE("%-35s:%-8d:%-16p found:%d\n", __func__, __LINE__, (void*)val, found); - - return found; - } - - void Register(std::shared_ptr rqst) - { - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)(rqst.get())); - std::lock_guard lck(m_ActiveRequestMtx); - m_ActiveRequests.push_back(rqst); - } -}; - -class WinHttpSessionImp :public WinHttpBase -{ - std::string m_ServerName; - std::string m_Referrer; - std::vector m_Proxies; - std::string m_Proxy; - WINHTTP_STATUS_CALLBACK m_InternetCallback = NULL; - DWORD m_NotificationFlags = 0; - - int m_ServerPort = 0; - long m_Timeout = 15000; - BOOL m_Async = false; - - bool m_closing = false; - DWORD m_MaxConnections = 0; - DWORD m_SecureProtocol = 0; - void *m_UserBuffer = NULL; - -public: - - BOOL SetUserData(void **data) - { - if (!data) - return FALSE; - - m_UserBuffer = *data; - return TRUE; - } - void *GetUserData() { return m_UserBuffer; } - - BOOL SetSecureProtocol(DWORD *data) - { - if (!data) - return FALSE; - - m_SecureProtocol = *data; - return TRUE; - } - DWORD GetSecureProtocol() const { return m_SecureProtocol; } - - BOOL SetMaxConnections(DWORD *data) - { - if (!data) - return FALSE; - - m_MaxConnections = *data; - return TRUE; - } - DWORD GetMaxConnections() const { return m_MaxConnections; } - - void SetAsync() { m_Async = TRUE; } - BOOL GetAsync() const { return m_Async; } - - BOOL GetThreadClosing() const { return m_closing; } - - std::vector &GetProxies() { return m_Proxies; } - void SetProxies(std::vector &proxies) { m_Proxies = proxies; } - - std::string &GetProxy() { return m_Proxy; } - - int GetServerPort() const { return m_ServerPort; } - void SetServerPort(int port) { m_ServerPort = port; } - - long GetTimeout() const { - if (m_Timeout) - return m_Timeout; - - long curl_timeo; - - curl_timeo = ComContainer::GetInstance().GetTimeout(); - - if (curl_timeo < 0) - curl_timeo = 10000; - - return curl_timeo; - } - void SetTimeout(long timeout) { m_Timeout = timeout; } - - std::string &GetServerName() { return m_ServerName; } - - std::string &GetReferrer() { return m_Referrer; } - - void SetCallback(WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags) { - m_InternetCallback = lpfnInternetCallback; - m_NotificationFlags = dwNotificationFlags; - } - WINHTTP_STATUS_CALLBACK GetCallback(DWORD *dwNotificationFlags) - { - if (dwNotificationFlags) - *dwNotificationFlags = m_NotificationFlags; - return m_InternetCallback; - } - - ~WinHttpSessionImp() - { - TRACE("%-35s:%-8d:%-16p sesion\n", __func__, __LINE__, (void*)this); - SetCallback(NULL, 0); - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); - } -}; - -THREADRETURN ComContainer::AsyncThreadFunction(LPVOID lpThreadParameter) -{ - ComContainer *comContainer = static_cast(lpThreadParameter); - - while (true) - { - { - std::unique_lock getAsyncEventHndlMtx(comContainer->m_hAsyncEventMtx); - while (!comContainer->m_hAsyncEventCounter) - comContainer->m_hAsyncEvent.wait(getAsyncEventHndlMtx); - getAsyncEventHndlMtx.unlock(); - - comContainer->m_hAsyncEventCounter--; - } - - int still_running = 1; - - while (still_running && !comContainer->GetThreadClosing()) - { - WinHttpRequestImp *request = NULL; - - comContainer->QueryData(&still_running); - - //TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - struct CURLMsg *m; - - /* call curl_multi_perform or curl_multi_socket_action first, then loop - through and check if there are any transfers that have completed */ - - do { - int msgq = 0; - request = NULL; - std::shared_ptr srequest; - - comContainer->m_MultiMutex.lock(); - m = curl_multi_info_read(comContainer->m_curlm, &msgq); - if (m) - curl_easy_getinfo(m->easy_handle, CURLINFO_PRIVATE, &request); - if (request) - { - srequest = request->shared_from_this(); - } - comContainer->m_MultiMutex.unlock(); - - if (m && (m->msg == CURLMSG_DONE) && request && srequest) { - WINHTTP_ASYNC_RESULT result = { 0, 0 }; - DWORD dwInternetStatus; - - TRACE("%-35s:%-8d:%-16p type:%s result:%d\n", __func__, __LINE__, (void*)request, request->GetType().c_str(), m->data.result); - request->GetCompletionCode() = m->data.result; - - if (m->data.result == CURLE_OK) - { - if (request->HandleQueryDataNotifications(srequest, 0)) - { - TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); - } - { - std::lock_guard lck(request->GetQueryDataEventMtx()); - request->GetQueryDataEventState() = true; - } - request->HandleQueryDataNotifications(srequest, 0); - } - - request->GetBodyStringMutex().lock(); - - request->GetCompletionStatus() = true; - - if (m->data.result == CURLE_OK) - { - void *ptr = request->GetResponseString().data(); - size_t available = request->GetResponseString().size(); - size_t totalread = 0; - request->ConsumeIncoming(srequest, ptr, available, totalread); - if (totalread) - { - TRACE("%-35s:%-8d:%-16p consumed length:%lu\n", __func__, __LINE__, (void*)srequest.get(), totalread); - } - } - else if (m->data.result == CURLE_OPERATION_TIMEDOUT) - { - result.dwError = ERROR_WINHTTP_TIMEOUT; - dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; - request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); - TRACE("%-35s:%-8d:%-16p request done type = %s CURLE_OPERATION_TIMEDOUT\n", - __func__, __LINE__, (void*)request, request->GetType().c_str()); - } - else - { - result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; - dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; - TRACE("%-35s:%-8d:%-16p unknown async request done m->data.result = %d\n", - __func__, __LINE__, (void*)request, m->data.result); -#ifdef _DEBUG - assert(0); -#endif - request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); - } - - request->GetBodyStringMutex().unlock(); - - } else if (m && (m->msg != CURLMSG_DONE)) { - TRACE("%-35s:%-8d:%-16p unknown async request done\n", __func__, __LINE__, (void*)request); - DWORD dwInternetStatus; - WINHTTP_ASYNC_RESULT result = { 0, 0 }; - result.dwError = ERROR_WINHTTP_OPERATION_CANCELLED; - dwInternetStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; -#ifdef _DEBUG - assert(0); -#endif - if (request) - request->AsyncQueue(srequest, dwInternetStatus, 0, &result, sizeof(result), true); - } - - if (request) - { - comContainer->RemoveHandle(srequest, request->GetCurl(), true); - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - } - } while (m); - } - if (comContainer->GetThreadClosing()) - { - TRACE("%s:%d exiting\n", __func__, __LINE__); - break; - } - } - -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_remove_thread_state(NULL); -#elif OPENSSL_VERSION_NUMBER < 0x10000000L - ERR_remove_state(0); -#endif - return 0; -} - -THREADRETURN UserCallbackContainer::UserCallbackThreadFunction(LPVOID lpThreadParameter) -{ - UserCallbackContainer *cbContainer = static_cast(lpThreadParameter); - - while (true) - { - { - std::unique_lock GetEventHndlMtx(cbContainer->m_hEventMtx); - while (!cbContainer->m_EventCounter) - cbContainer->m_hEvent.wait(GetEventHndlMtx); - GetEventHndlMtx.unlock(); - - cbContainer->m_EventCounter--; - } - - if (cbContainer->GetClosing()) - { - TRACE("%s", "exiting\n"); - break; - } - - while (true) - { - cbContainer->m_MapMutex.lock(); - UserCallbackContext *ctx = NULL; - ctx = cbContainer->GetNext(); - cbContainer->m_MapMutex.unlock(); - if (!ctx) - break; - - void *statusInformation = NULL; - - if (ctx->GetStatusInformationValid()) - statusInformation = ctx->GetStatusInformation(); - - if (!ctx->GetRequestRef()->GetClosed() && ctx->GetCb()) { - TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", - __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), - ctx->GetUserdata(), ctx->GetInternetStatus(), statusInformation, ctx->GetRequestRef().use_count()); - ctx->GetCb()(ctx->GetRequest(), - (DWORD_PTR)(ctx->GetUserdata()), - ctx->GetInternetStatus(), - statusInformation, - ctx->GetStatusInformationLength()); - - TRACE_VERBOSE("%-35s:%-8d:%-16p ctx = %p cb = %p ctx->GetUserdata() = %p dwInternetStatus:0x%lx statusInformation=%p refcount:%lu\n", - __func__, __LINE__, (void*)ctx->GetRequest(), reinterpret_cast(ctx), reinterpret_cast(ctx->GetCb()), - ctx->GetUserdata(), ctx->GetInternetStatus(), statusInformation, ctx->GetRequestRef().use_count()); - } - ctx->GetRequestCompletionCb()(ctx->GetRequestRef(), ctx->GetInternetStatus()); - delete ctx; - } - } - -#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_remove_thread_state(NULL); -#elif OPENSSL_VERSION_NUMBER < 0x10000000L - ERR_remove_state(0); -#endif - - return 0; -} - -UserCallbackContext::UserCallbackContext(std::shared_ptr &request, - DWORD dwInternetStatus, - DWORD dwStatusInformationLength, - DWORD dwNotificationFlags, - WINHTTP_STATUS_CALLBACK cb, - LPVOID userdata, - LPVOID statusInformation, - DWORD statusInformationCopySize, bool allocate, - CompletionCb completion) - : m_request(request), m_dwInternetStatus(dwInternetStatus), - m_dwStatusInformationLength(dwStatusInformationLength), - m_dwNotificationFlags(dwNotificationFlags), m_cb(cb), m_userdata(userdata), - m_requestCompletionCb(completion) -{ - if (statusInformation) - SetAsyncResult(statusInformation, statusInformationCopySize, allocate); -} - -WinHttpSessionImp *GetImp(WinHttpBase *base) -{ - WinHttpConnectImp *connect; - WinHttpSessionImp *session; - WinHttpRequestImp *request; - - if ((connect = dynamic_cast(base))) - { - session = connect->GetHandle(); - } - else if ((request = dynamic_cast(base))) - { - connect = request->GetSession(); - if (!connect) - return NULL; - session = connect->GetHandle(); - } - else - { - session = dynamic_cast(base); - } - - return session; -} - - -UserCallbackContext::~UserCallbackContext() -{ - delete [] m_StatusInformation; -} - -void WinHttpRequestImp::HandleReceiveNotifications(std::shared_ptr &srequest) -{ - bool expected = true; - - bool result = std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, false); - if (result) - { - bool redirectPending; - { - expected = true; - redirectPending = GetRedirectPending(); - TRACE("%-35s:%-8d:%-16p redirectPending = %d ResponseCallbackSendCounter = %d\n", __func__, __LINE__, (void*)this, - redirectPending, (int)ResponseCallbackSendCounter()); - } - GetReceiveResponseMutex().lock(); - if (redirectPending) - { - if (ResponseCallbackSendCounter() == 0) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)this); - AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); - ResponseCallbackSendCounter()++; - } - if (ResponseCallbackSendCounter() == 1) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)this); - AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, GetHeaderString().length(), NULL, 0, false); - ResponseCallbackSendCounter()++; - } - AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REDIRECT, 0, NULL, 0, false); - ResponseCallbackSendCounter()++; - } - if (ResponseCallbackSendCounter() == (0 + redirectPending * 3)) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE\n", __func__, __LINE__, (void*)this); - AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0, NULL, 0, false); - ResponseCallbackSendCounter()++; - } - if (ResponseCallbackSendCounter() == (1 + redirectPending * 3)) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED\n", __func__, __LINE__, (void*)this); - AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, GetHeaderString().length(), NULL, 0, false); - ResponseCallbackSendCounter()++; - } - if ((ResponseCallbackSendCounter() == (2 + redirectPending * 3)) && (GetCompletionCode() == CURLE_OK)) - { - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE\n", __func__, __LINE__, (void*)this); - SetHeaderReceiveComplete(); - AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, 0, NULL, 0, false); - ResponseCallbackSendCounter()++; - } - GetReceiveResponseMutex().unlock(); - } -} - -size_t WinHttpRequestImp::WriteHeaderFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { - WinHttpRequestImp *request = static_cast(rqst); - if (!request) - return 0; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return size * nmemb; - bool EofHeaders = false; - - TRACE("%-35s:%-8d:%-16p %zu\n", __func__, __LINE__, (void*)request, size * nmemb); - { - std::lock_guard lck(request->GetHeaderStringMutex()); - - request->GetHeaderString().append(static_cast(ptr), size * nmemb); - - if (request->GetHeaderString().find("\r\n\r\n") != std::string::npos) - EofHeaders = true; - if (request->GetHeaderString().find("\n\n") != std::string::npos) - EofHeaders = true; - } - if (EofHeaders && request->GetAsync()) - { - DWORD retValue; - - curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &retValue); - if (retValue == 302) - { - std::lock_guard lck(request->GetHeaderStringMutex()); - request->GetHeaderString() = ""; - TRACE("%-35s:%-8d:%-16p Redirect \n", __func__, __LINE__, (void*)request); - request->GetRedirectPending() = true; - return size * nmemb; - } - - if (retValue != 100) - { - std::lock_guard lck(request->GetReceiveCompletionEventMtx()); - request->ResponseCallbackEventCounter()++; - request->HandleReceiveNotifications(srequest); - TRACE("%-35s:%-8d:%-16p GetReceiveCompletionEvent().notify_all\n", __func__, __LINE__, (void*)request); - } - else - { - TRACE("%-35s:%-8d:%-16p retValue = %lu \n", __func__, __LINE__, (void*)request, retValue); - } - - } - return size * nmemb; -} - -void WinHttpRequestImp::ConsumeIncoming(std::shared_ptr &srequest, void* &ptr, size_t &available, size_t &read) -{ - while (available) - { - BufferRequest buf = GetBufferRequest(srequest->GetOutstandingReads()); - if (!buf.m_Length) - break; - - size_t len = MIN(buf.m_Length, available); - - if (len) - { - TRACE("%-35s:%-8d:%-16p reading length:%lu written:%lu %p %ld\n", - __func__, __LINE__, (void*)srequest.get(), len, read, buf.m_Buffer, buf.m_Length); - memcpy(static_cast(buf.m_Buffer), ptr, len); - } - - ptr = static_cast(ptr) + len; - available -= len; - - if (len) - { - LPVOID StatusInformation = buf.m_Buffer; - - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)srequest.get(), buf.m_Buffer, len); - srequest->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, len, StatusInformation, sizeof(StatusInformation), false); - } - - read += len; - } -} - -size_t WinHttpRequestImp::WriteBodyFunction(void *ptr, size_t size, size_t nmemb, void* rqst) { - size_t read = 0; - WinHttpRequestImp *request = static_cast(rqst); - if (!request) - return 0; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return 0; - - { - std::lock_guard lck(request->GetBodyStringMutex()); - void *buf = request->GetResponseString().data(); - size_t available = request->GetResponseString().size(); - size_t totalread = 0; - - request->ConsumeIncoming(srequest, buf, available, totalread); - if (totalread) - { - TRACE("%-35s:%-8d:%-16p consumed length:%lu\n", __func__, __LINE__, (void*)srequest.get(), totalread); - request->GetReadLength() += totalread; - request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + totalread); - request->GetReadData().shrink_to_fit(); - } - } - - size_t available = size * nmemb; - { - std::lock_guard lck(request->GetBodyStringMutex()); - void *buf = ptr; - - request->ConsumeIncoming(srequest, buf, available, read); - - if (available) - { - request->GetResponseString().insert(request->GetResponseString().end(), - reinterpret_cast(buf), - reinterpret_cast(buf) + available); - - read += available; - } - } - - if (request->GetAsync()) { - if (available && request->HandleQueryDataNotifications(srequest, available)) - TRACE("%-35s:%-8d:%-16p GetQueryDataEvent().notify_all\n", __func__, __LINE__, (void*)request); - } - - return read; -} - -void WinHttpRequestImp::CleanUp() -{ - m_CompletionCode = CURLE_OK; - m_ResponseString.clear(); - m_HeaderString.clear(); - m_TotalReceiveSize = 0; - m_ReadData.clear(); - m_ReadDataEventCounter = 0; - m_UploadThreadExitStatus = false; - m_QueryDataPending = false; - m_ReceiveResponsePending = false; - m_RedirectPending = false; - m_ReceiveResponseEventCounter = 0; - m_ReceiveResponseSendCounter = 0; - m_OutstandingWrites.clear(); - m_OutstandingReads.clear(); - m_Completion = false; -} - -WinHttpRequestImp::WinHttpRequestImp(): - m_UploadCallbackThread(), - m_QueryDataPending(false), - m_ReceiveResponsePending(false), - m_ReceiveResponseEventCounter(0), - m_RedirectPending(false) -{ - m_curl = ComContainer::GetInstance().AllocCURL(); - if (!m_curl) - return; -} - -WinHttpRequestImp::~WinHttpRequestImp() -{ - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); - - m_closed = true; - - if (!GetAsync() && Uploading()) - { - { - std::lock_guard lck(GetReadDataEventMtx()); - GetReadDataEventCounter()++; - } - THREADJOIN(m_UploadCallbackThread); - } - - if (GetAsync()) { - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); - } - else - curl_easy_getinfo(GetCurl(), CURLINFO_PRIVATE, NULL); - - /* always cleanup */ - ComContainer::GetInstance().FreeCURL(m_curl); - - /* free the custom headers */ - if (m_HeaderList) - curl_slist_free_all(m_HeaderList); - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)this); -} - -static void RequestCompletionCb(std::shared_ptr &requestRef, DWORD status) -{ - if (status == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) - { - TRACE_VERBOSE("%-35s:%-8d:%-16p status:0x%lx\n", __func__, __LINE__, (void*)(requestRef.get()), status); - requestRef->GetClosed() = true; - } -} - -BOOL WinHttpRequestImp::AsyncQueue(std::shared_ptr &requestRef, - DWORD dwInternetStatus, size_t statusInformationLength, - LPVOID statusInformation, DWORD statusInformationCopySize, - bool allocate) -{ - UserCallbackContext* ctx = NULL; - DWORD dwNotificationFlags; - WINHTTP_STATUS_CALLBACK cb = GetCallback(&dwNotificationFlags); - LPVOID userdata = NULL; - - userdata = GetUserData(); - - ctx = new UserCallbackContext(requestRef, dwInternetStatus, static_cast(statusInformationLength), - dwNotificationFlags, cb, userdata, statusInformation, - statusInformationCopySize, allocate, RequestCompletionCb); - if (ctx) { - UserCallbackContainer::GetInstance().Queue(ctx); - } - else - return FALSE; - - - return TRUE; -} - - -size_t WinHttpRequestImp::ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp) -{ - WinHttpRequestImp *request = static_cast(userp); - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return size * nmemb; - - size_t len = 0; - - TRACE("request->GetTotalLength(): %lu\n", request->GetTotalLength()); - if (request->GetOptionalData().length() > 0) - { - len = MIN(request->GetOptionalData().length(), size * nmemb); - TRACE("%-35s:%-8d:%-16p writing optional length of %lu\n", __func__, __LINE__, (void*)request, len); - std::copy(request->GetOptionalData().begin(), request->GetOptionalData().end(), static_cast(ptr)); - request->GetOptionalData().erase(0, len); - request->GetReadLength() += len; - return len; - } - - if (request->GetClosed()) - return -1; - - TRACE("%-35s:%-8d:%-16p request->GetTotalLength():%lu request->GetReadLength():%lu\n", __func__, __LINE__, (void*)request, request->GetTotalLength(), request->GetReadLength()); - if (((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && request->Uploading()) || - (request->GetTotalLength() != request->GetReadLength())) - { - std::unique_lock getReadDataEventHndlMtx(request->GetReadDataEventMtx()); - if (!request->GetReadDataEventCounter()) - { - TRACE("%-35s:%-8d:%-16p transfer paused:%lu\n", __func__, __LINE__, (void*)request, size * nmemb); - return CURL_READFUNC_PAUSE; - } - TRACE("%-35s:%-8d:%-16p transfer resumed as already signalled:%lu\n", __func__, __LINE__, (void*)request, size * nmemb); - } - - if (request->GetAsync()) - { - std::lock_guard lck(request->GetReadDataEventMtx()); - if (!request->GetOutstandingWrites().empty()) - - { - BufferRequest &buf = PeekBufferRequest(request->GetOutstandingWrites()); - len = MIN(buf.m_Length - buf.m_Used, size * nmemb); - - if (request->GetTotalLength()) - { - size_t remaining = request->GetTotalLength() - request->GetReadLength(); - len = MIN(len, remaining); - } - - request->GetReadLength() += len; - TRACE("%-35s:%-8d:%-16p writing additional length:%lu buf.m_Length:%lu buf.m_Buffer:%p buf.m_Used:%lu\n", - __func__, __LINE__, (void*)request, len, buf.m_Length, buf.m_Buffer, buf.m_Used); - - if (len) - { - memcpy(static_cast(ptr), static_cast(buf.m_Buffer) + buf.m_Used, len); - } - - buf.m_Used += len; - - if (buf.m_Used == buf.m_Length) - { - DWORD dwInternetStatus; - DWORD result; - - result = buf.m_Length; - dwInternetStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE; - request->AsyncQueue(srequest, dwInternetStatus, sizeof(dwInternetStatus), &result, sizeof(result), true); - GetBufferRequest(request->GetOutstandingWrites()); - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:%p buf.m_Length:%lu\n", __func__, __LINE__, (void*)request, buf.m_Buffer, buf.m_Length); - request->GetReadDataEventCounter()--; - } - - TRACE("%-35s:%-8d:%-16p chunk written:%lu\n", __func__, __LINE__, (void*)request, len); - } - } - else - { - request->GetReadDataEventMtx().lock(); - request->GetReadDataEventCounter()--; - len = MIN(request->GetReadData().size(), size * nmemb); - TRACE("%-35s:%-8d:%-16p writing additional length:%lu\n", __func__, __LINE__, (void*)request, len); - std::copy(request->GetReadData().begin(), request->GetReadData().begin() + len, static_cast(ptr)); - request->GetReadLength() += len; - request->GetReadData().erase(request->GetReadData().begin(), request->GetReadData().begin() + len); - request->GetReadData().shrink_to_fit(); - request->GetReadDataEventMtx().unlock(); - } - - return len; -} - -int WinHttpRequestImp::SocketCallback(CURL *handle, curl_infotype type, - char *data, size_t size, - void *userp) -{ - const char *text = ""; - - switch (type) { - case CURLINFO_TEXT: - TRACE_VERBOSE("%-35s:%-8d:%-16p == Info: %s", __func__, __LINE__, (void*)userp, data); - return 0; - default: /* in case a new one is introduced to shock us */ - TRACE_VERBOSE("%-35s:%-8d:%-16p type:%d\n", __func__, __LINE__, (void*)userp, type); - return 0; - - case CURLINFO_HEADER_IN: - text = "=> Receive header"; - break; - case CURLINFO_HEADER_OUT: - text = "=> Send header"; - break; - case CURLINFO_DATA_OUT: - text = "=> Send data"; - break; - case CURLINFO_SSL_DATA_OUT: - text = "=> Send SSL data"; - break; - case CURLINFO_DATA_IN: - text = "<= Recv data"; - break; - case CURLINFO_SSL_DATA_IN: - text = "<= Recv SSL data"; - break; - } - TRACE_VERBOSE("%-35s:%-8d:%-16p %s\n", __func__, __LINE__, (void*)userp, text); - - return 0; -} - -WINHTTPAPI HINTERNET WINAPI WinHttpOpen -( - LPCTSTR pszAgentW, - DWORD dwAccessType, - LPCTSTR pszProxyW, - LPCTSTR pszProxyBypassW, - DWORD dwFlags -) -{ - std::shared_ptr session = std::make_shared (); - if (!session) - return NULL; - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*) (session.get())); - if (dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) - { - if (!pszProxyW) - return NULL; - - TRACE("%-35s:%-8d:%-16p proxy: " STRING_LITERAL " \n", __func__, __LINE__, (void*) (session.get()), pszProxyW); - ConvertCstrAssign(pszProxyW, WCTLEN(pszProxyW), session->GetProxy()); - - std::vector proxies = Split(session->GetProxy(), ';'); - if (proxies.empty()) - { - proxies.push_back(session->GetProxy()); - } - session->SetProxies(proxies); - } - - if (dwFlags & WINHTTP_FLAG_ASYNC) { - session->SetAsync(); - } - - WinHttpHandleContainer::Instance().Register(session); - return session.get(); -} - -WINHTTPAPI HINTERNET WINAPI WinHttpConnect -( - HINTERNET hSession, - LPCTSTR pswzServerName, - INTERNET_PORT nServerPort, - DWORD dwReserved -) -{ - WinHttpSessionImp *session = static_cast(hSession); - if (!session) - return NULL; - - TRACE("%-35s:%-8d:%-16p pswzServerName: " STRING_LITERAL " nServerPort:%d\n", - __func__, __LINE__, (void*)session, pswzServerName, nServerPort); - ConvertCstrAssign(pswzServerName, WCTLEN(pswzServerName), session->GetServerName()); - - session->SetServerPort(nServerPort); - std::shared_ptr connect = std::make_shared (); - if (!connect) - return NULL; - - connect->SetHandle(session); - WinHttpHandleContainer::Instance().Register(connect); - return connect.get(); -} - - -BOOLAPI WinHttpCloseHandle( - HINTERNET hInternet -) -{ - WinHttpBase *base = static_cast(hInternet); - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); - if (!base) - return FALSE; - - if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) - { - WinHttpRequestImp *request = dynamic_cast(base); - if (!request) - return FALSE; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - request->GetClosing() = true; - WinHttpHandleContainer::Instance().UnRegister(request); - return TRUE; - } - - if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) - { - WinHttpSessionImp *session = dynamic_cast(base); - if (session) - { - WinHttpHandleContainer::Instance().UnRegister(session); - return TRUE; - } - return TRUE; - } - - if (WinHttpHandleContainer::Instance().IsRegistered(static_cast(hInternet))) - { - WinHttpConnectImp *connect = dynamic_cast(base); - if (connect) - { - WinHttpHandleContainer::Instance().UnRegister(connect); - return TRUE; - } - } - - return TRUE; -} - -WINHTTPAPI HINTERNET WINAPI WinHttpOpenRequest( - HINTERNET hConnect, - LPCTSTR pwszVerb, /* include "GET", "POST", and "HEAD" */ - LPCTSTR pwszObjectName, - LPCTSTR pwszVersion, - LPCTSTR pwszReferrer, - LPCTSTR * ppwszAcceptTypes, - DWORD dwFlags /* isSecurePort ? (WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE) : WINHTTP_FLAG_REFRESH */ -) -{ - WinHttpConnectImp *connect = static_cast(hConnect); - if (!connect) - return NULL; - - WinHttpSessionImp *session = connect->GetHandle(); - if (!session) - return NULL; - - CURLcode res; - - if (!pwszVerb) - pwszVerb = TEXT("GET"); - - TRACE("%-35s:%-8d:%-16p pwszVerb = " STRING_LITERAL " pwszObjectName: " STRING_LITERAL " pwszVersion = " STRING_LITERAL " pwszReferrer = " STRING_LITERAL "\n", - __func__, __LINE__, (void*)session, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer); - - std::shared_ptr srequest(new WinHttpRequestImp, [](WinHttpRequestImp *request) { - - std::shared_ptr close_request(request); - TRACE("%-35s:%-8d:%-16p reference count reached to 0\n", __func__, __LINE__, (void*)request); - - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING\n", __func__, __LINE__, (void*)request); - request->AsyncQueue(close_request, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0, NULL, 0, false); - }); - - if (!srequest) - return NULL; - WinHttpRequestImp *request = srequest.get(); - if (!request) - return NULL; - - if (session->GetAsync()) - { - DWORD flag; - WINHTTP_STATUS_CALLBACK cb; - LPVOID userdata = NULL; - - cb = session->GetCallback(&flag); - request->SetAsync(); - request->SetCallback(cb, flag); - - if (connect->GetUserData()) - userdata = (LPVOID)connect->GetUserData(); - else if (session->GetUserData()) - userdata = (LPVOID)session->GetUserData(); - - if (userdata) - request->SetUserData(&userdata); - - } - - const char *prefix; - std::string server = session->GetServerName(); - if (dwFlags & WINHTTP_FLAG_SECURE) - { - prefix = "https://"; - request->GetSecure() = true; - } - else - { - prefix = "http://"; - request->GetSecure() = false; - } - - if (server.find("http://") == std::string::npos) - server = prefix + server; - if (pwszObjectName) - { - std::string objectname; - - ConvertCstrAssign(pwszObjectName, WCTLEN(pwszObjectName), objectname); - - size_t index = 0; - // convert # to %23 to support links to fragments - while (true) { - /* Locate the substring to replace. */ - index = objectname.find('#', index); - if (index == std::string::npos) break; - /* Make the replacement. */ - objectname.replace(index, 1, "%23"); - /* Advance index forward so the next iteration doesn't pick it up as well. */ - index += 3; - } - request->SetFullPath(server, objectname); - if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { - return NULL; - } - } - else - { - std::string nullstr; - request->SetFullPath(server, nullstr); - if (!request->SetServer(request->GetFullPath(), session->GetServerPort())) { - return NULL; - } - } - - if (session->GetTimeout() > 0) - { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, session->GetTimeout()); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_TIME, 60L); - CURL_BAILOUT_ONERROR(res, request, NULL); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_LOW_SPEED_LIMIT, 1L); - CURL_BAILOUT_ONERROR(res, request, NULL); - - request->SetProxy(session->GetProxies()); - TSTRING verb; - verb.assign(pwszVerb); - if (verb == TEXT("PUT")) - { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_PUT, 1L); - CURL_BAILOUT_ONERROR(res, request, NULL); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); - CURL_BAILOUT_ONERROR(res, request, NULL); - - request->Uploading() = true; - } - else if (verb == TEXT("GET")) - { - } - else if (verb == TEXT("POST")) - { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_POST, 1L); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - else - { - std::string verbcst; - - ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), verbcst); - TRACE("%-35s:%-8d:%-16p setting custom header %s\n", __func__, __LINE__, (void*)request, verbcst.c_str()); - res = curl_easy_setopt(request->GetCurl(), CURLOPT_CUSTOMREQUEST, verbcst.c_str()); - CURL_BAILOUT_ONERROR(res, request, NULL); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_UPLOAD, 1L); - CURL_BAILOUT_ONERROR(res, request, NULL); - - request->Uploading() = true; - } - -#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) - if (pwszVersion) - { - int ver; - TSTRING version; - version.assign(pwszVersion); - - if (version == TEXT("1.0")) - ver = CURL_HTTP_VERSION_1_0; - else if (version == TEXT("1.1")) - ver = CURL_HTTP_VERSION_1_1; - else if (version == TEXT("2.0")) - ver = CURL_HTTP_VERSION_2_0; - else - return NULL; - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTP_VERSION, ver); - CURL_BAILOUT_ONERROR(res, request, NULL); - } -#endif - - if (pwszReferrer) - { - ConvertCstrAssign(pwszReferrer, WCTLEN(pwszReferrer), session->GetReferrer()); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_REFERER, session->GetReferrer().c_str()); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - - if (dwFlags & WINHTTP_FLAG_SECURE) - { - const char *pKeyName; - const char *pKeyType; - const char *pEngine; - static const char *pCertFile = "testcert.pem"; - static const char *pCACertFile = "cacert.pem"; - //static const char *pHeaderFile = "dumpit"; - const char *pPassphrase = NULL; - - pKeyName = NULL; - pKeyType = "PEM"; - pCertFile = NULL; - pCACertFile = NULL; - pEngine = NULL; - - if (const char* env_p = std::getenv("WINHTTP_PAL_KEYNAME")) - pKeyName = env_p; - - if (const char* env_p = std::getenv("WINHTTP_PAL_KEYTYPE")) - pKeyType = env_p; - - if (const char* env_p = std::getenv("WINHTTP_PAL_CERTFILE")) - pCertFile = env_p; - - if (const char* env_p = std::getenv("WINHTTP_PAL_CACERTFILE")) - pCACertFile = env_p; - - if (const char* env_p = std::getenv("WINHTTP_PAL_ENGINE")) - pEngine = env_p; - - if (const char* env_p = std::getenv("WINHTTP_PAL_KEY_PASSPHRASE")) - pPassphrase = env_p; - - if (pEngine) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE, pEngine); - CURL_BAILOUT_ONERROR(res, request, NULL); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLENGINE_DEFAULT, 1L); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - /* cert is stored PEM coded in file... */ - /* since PEM is default, we needn't set it for PEM */ - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERTTYPE, "PEM"); - CURL_BAILOUT_ONERROR(res, request, NULL); - - /* set the cert for client authentication */ - if (pCertFile) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLCERT, pCertFile); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - - /* sorry, for engine we must set the passphrase - (if the key has one...) */ - if (pPassphrase) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_KEYPASSWD, pPassphrase); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - - /* if we use a key stored in a crypto engine, - we must set the key type to "ENG" */ - if (pKeyType) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEYTYPE, pKeyType); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - - /* set the private key (file or ID in engine) */ - if (pKeyName) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLKEY, pKeyName); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - - /* set the file with the certs vaildating the server */ - if (pCACertFile) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_CAINFO, pCACertFile); - CURL_BAILOUT_ONERROR(res, request, NULL); - } - } - if (pwszVerb) - { - ConvertCstrAssign(pwszVerb, WCTLEN(pwszVerb), request->GetType()); - } - request->SetSession(connect); - WinHttpHandleContainer::Instance().Register(srequest); - - return request; -} - -BOOLAPI -WinHttpAddRequestHeaders -( - HINTERNET hRequest, - LPCTSTR lpszHeaders, - DWORD dwHeadersLength, - DWORD dwModifiers -) -{ - CURLcode res; - WinHttpRequestImp *request = static_cast(hRequest); - if (!request) - return FALSE; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - TRACE("%-35s:%-8d:%-16p lpszHeaders = " STRING_LITERAL " dwModifiers: %lu\n", __func__, __LINE__, (void*)request, - lpszHeaders ? lpszHeaders: TEXT(""), dwModifiers); - - if (dwHeadersLength == (DWORD)-1) - { - dwHeadersLength = lpszHeaders ? WCTLEN(lpszHeaders): 0; - } - - if (lpszHeaders) - { - std::string &headers = request->GetOutgoingHeaderList(); - - ConvertCstrAssign(lpszHeaders, dwHeadersLength, headers); - request->AddHeader(headers); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_HTTPHEADER, request->GetHeaderList()); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - - return TRUE; -} - -BOOLAPI WinHttpSendRequest -( - HINTERNET hRequest, - LPCTSTR lpszHeaders, - DWORD dwHeadersLength, - LPVOID lpOptional, - DWORD dwOptionalLength, - DWORD dwTotalLength, - DWORD_PTR dwContext -) -{ - CURLcode res; - WinHttpRequestImp *request = static_cast(hRequest); - if (!request) - return FALSE; - - WinHttpConnectImp *connect = request->GetSession(); - if (!connect) - return FALSE; - - WinHttpSessionImp *session = connect->GetHandle(); - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - TSTRING customHeader; - - if (dwHeadersLength == (DWORD)-1) - { - dwHeadersLength = lpszHeaders ? WCTLEN(lpszHeaders): 0; - } - - if (lpszHeaders) - customHeader.assign(lpszHeaders, dwHeadersLength); - - if ((dwTotalLength == 0) && (dwOptionalLength == 0) && request->Uploading()) - customHeader += TEXT("Transfer-Encoding: chunked\r\n"); - - TRACE("%-35s:%-8d:%-16p lpszHeaders:%p dwHeadersLength:%lu lpOptional:%p dwOptionalLength:%lu dwTotalLength:%lu\n", - __func__, __LINE__, (void*)request, (const void*)lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength, dwTotalLength); - - if (!customHeader.empty() && !WinHttpAddRequestHeaders(hRequest, customHeader.c_str(), customHeader.length(), 0)) - return FALSE; - - if (lpOptional) - { - if (dwOptionalLength == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - request->SetOptionalData(lpOptional, dwOptionalLength); - - if (request->GetType() == "POST") - { - /* Now specify the POST data */ - res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDS, request->GetOptionalData().c_str()); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - } - - if (request->Uploading() || (request->GetType() == "POST")) - { - if (!request->GetAsync() && (dwTotalLength == 0)) { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - } - - if (dwOptionalLength == (DWORD)-1) - dwOptionalLength = request->GetOptionalData().length(); - - DWORD totalsize = MAX(dwOptionalLength, dwTotalLength); - /* provide the size of the upload, we specicially typecast the value - to curl_off_t since we must be sure to use the correct data size */ - if (request->GetType() == "POST") - { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_POSTFIELDSIZE, (long)totalsize); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - else if (totalsize) - { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_INFILESIZE_LARGE, (curl_off_t)totalsize); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - - request->GetTotalLength() = dwTotalLength; - res = curl_easy_setopt(request->GetCurl(), CURLOPT_READDATA, request); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_READFUNCTION, request->ReadCallback); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGFUNCTION, request->SocketCallback); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_DEBUGDATA, request); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEFUNCTION, request->WriteBodyFunction); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_WRITEDATA, request); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERFUNCTION, request->WriteHeaderFunction); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_HEADERDATA, request); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_PRIVATE, request); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_FOLLOWLOCATION, 1L); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - if (winhttp_tracing_verbose) - { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_VERBOSE, 1); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - - /* enable TCP keep-alive for this transfer */ - res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPALIVE, 1L); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - /* keep-alive idle time to 120 seconds */ - res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPIDLE, 120L); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - /* interval time between keep-alive probes: 60 seconds */ - res = curl_easy_setopt(request->GetCurl(), CURLOPT_TCP_KEEPINTVL, 60L); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYPEER, request->VerifyPeer()); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSL_VERIFYHOST, request->VerifyHost()); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - DWORD maxConnections = 0; - - if (request->GetMaxConnections()) - maxConnections = request->GetMaxConnections(); - else if (session->GetMaxConnections()) - maxConnections = session->GetMaxConnections(); - - if (maxConnections) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_MAXCONNECTS, maxConnections); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - - if (dwContext) - request->SetUserData(reinterpret_cast(&dwContext)); - - DWORD securityProtocols = 0; - - if (request->GetSecureProtocol()) - securityProtocols = request->GetSecureProtocol(); - else if (session->GetSecureProtocol()) - securityProtocols = session->GetSecureProtocol(); - - if (securityProtocols) { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_SSLVERSION, securityProtocols); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - - if (request->GetAsync()) - { - if (request->GetClosing()) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } - request->CleanUp(); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0, NULL, 0, false); - - if (!ComContainer::GetInstance().AddHandle(srequest, request->GetCurl())) - return FALSE; - - ComContainer::GetInstance().KickStart(); - - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0, NULL, 0, false); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, 0, NULL, 0, false); - - TRACE("%-35s:%-8d:%-16p use_count = %lu\n", __func__, __LINE__, (void*)request, srequest.use_count()); - } - else - { - if (dwTotalLength && (request->GetType() != "POST")) - { - DWORD thread_id; - request->GetUploadThread() = CREATETHREAD( - WinHttpRequestImp::UploadThreadFunction, // thread function name - request, &thread_id); // argument to thread function - } - else - { - /* Perform the request, res will get the return code */ - res = curl_easy_perform(request->GetCurl()); - /* Check for errors */ - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - } - - return TRUE; -} - -void WinHttpRequestImp::WaitAsyncReceiveCompletion(std::shared_ptr &srequest) -{ - bool expected = false; - std::atomic_compare_exchange_strong(&GetReceiveResponsePending(), &expected, true); - - TRACE("%-35s:%-8d:%-16p GetReceiveResponsePending() = %d\n", __func__, __LINE__, (void*)this, (int) GetReceiveResponsePending()); - { - std::lock_guard lck(GetReceiveCompletionEventMtx()); - if (ResponseCallbackEventCounter()) - { - TRACE("%-35s:%-8d:%-16p HandleReceiveNotifications \n", __func__, __LINE__, (void*)this); - HandleReceiveNotifications(srequest); - } - } -} - -WINHTTPAPI -BOOL -WINAPI -WinHttpReceiveResponse -( - HINTERNET hRequest, - LPVOID lpReserved -) -{ - WinHttpRequestImp *request = static_cast(hRequest); - if (!request) - return FALSE; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - - if ((request->GetTotalLength() == 0) && (request->GetOptionalData().length() == 0) && request->Uploading()) - { - if (request->GetAsync()) - { - { - std::lock_guard lck(request->GetReadDataEventMtx()); - request->GetReadDataEventCounter()++; - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - } - - ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); - } - } - - if (request->GetAsync()) - { - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - request->WaitAsyncReceiveCompletion(srequest); - } - else - { - if (request->Uploading()) - { - while (request->GetTotalLength() != request->GetReadLength()) { - if (request->GetUploadThreadExitStatus()) { - if (request->GetTotalLength() != request->GetReadLength()) { - SetLastError(ERROR_WINHTTP_OPERATION_CANCELLED); - return FALSE; - } - } - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - } - - return TRUE; - } - size_t headerLength; - { - std::lock_guard lck(request->GetHeaderStringMutex()); - headerLength = request->GetHeaderString().length(); - } - - return headerLength > 0; - } - return TRUE; -} - -bool WinHttpRequestImp::HandleQueryDataNotifications(std::shared_ptr &srequest, size_t available) -{ - bool expected = true; - bool result = std::atomic_compare_exchange_strong(&GetQueryDataPending(), &expected, false); - if (result) - { - if (!available) - { - size_t length; - - GetBodyStringMutex().lock(); - length = GetResponseString().size(); - available = length; - GetBodyStringMutex().unlock(); - } - - DWORD lpvStatusInformation = static_cast(available); - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, (void*)this, lpvStatusInformation); - AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), - (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); - } - return result; -} - -void WinHttpRequestImp::WaitAsyncQueryDataCompletion(std::shared_ptr &srequest) -{ - bool completed; - GetQueryDataPending() = true; - TRACE("%-35s:%-8d:%-16p GetQueryDataPending() = %d\n", __func__, __LINE__, (void*)this, (int)GetQueryDataPending()); - { - std::lock_guard lck(GetQueryDataEventMtx()); - completed = GetQueryDataEventState(); - } - - if (completed) { - TRACE("%-35s:%-8d:%-16p transfer already finished\n", __func__, __LINE__, (void*)this); - HandleQueryDataNotifications(srequest, 0); - } -} - -BOOLAPI -WinHttpQueryDataAvailable -( - HINTERNET hRequest, - LPDWORD lpdwNumberOfBytesAvailable -) -{ - WinHttpRequestImp *request = static_cast(hRequest); - if (!request) - return FALSE; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - if (request->GetClosing()) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } - - size_t length; - - request->GetBodyStringMutex().lock(); - length = request->GetResponseString().size(); - size_t available = length; - request->GetBodyStringMutex().unlock(); - - if (request->GetAsync()) - { - if (available == 0) - { - TRACE("%-35s:%-8d:%-16p !!!!!!!\n", __func__, __LINE__, (void*)request); - request->WaitAsyncQueryDataCompletion(srequest); - request->GetBodyStringMutex().lock(); - length = request->GetResponseString().size(); - available = length; - TRACE("%-35s:%-8d:%-16p available = %lu\n", __func__, __LINE__, (void*)request, available); - request->GetBodyStringMutex().unlock(); - } - else - { - DWORD lpvStatusInformation = available; - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:%lu\n", __func__, __LINE__, (void*)request, lpvStatusInformation); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, sizeof(lpvStatusInformation), - (LPVOID)&lpvStatusInformation, sizeof(lpvStatusInformation), true); - - } - - } - - - if (lpdwNumberOfBytesAvailable) - *lpdwNumberOfBytesAvailable = available; - - return TRUE; -} - -BOOLAPI -WinHttpReadData -( - HINTERNET hRequest, - LPVOID lpBuffer, - DWORD dwNumberOfBytesToRead, - LPDWORD lpdwNumberOfBytesRead -) -{ - WinHttpRequestImp *request = static_cast(hRequest); - if (!request) - return FALSE; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - if (request->GetClosing()) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } - - size_t readLength; - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - if (dwNumberOfBytesToRead == 0) - { - if (lpdwNumberOfBytesRead) - *lpdwNumberOfBytesRead = 0; - - if (request->GetAsync()) - { - LPVOID StatusInformation = (LPVOID)lpBuffer; - - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%d\n", __func__, __LINE__, (void*)request, lpBuffer, 0); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, 0, (LPVOID)StatusInformation, sizeof(StatusInformation), false); - } - return TRUE; - } - - request->GetBodyStringMutex().lock(); - readLength = request->GetResponseString().size(); - if (readLength) - { - if (readLength > dwNumberOfBytesToRead) - { - readLength = dwNumberOfBytesToRead; - } - std::copy(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength, static_cast(lpBuffer)); - request->GetResponseString().erase(request->GetResponseString().begin(), request->GetResponseString().begin() + readLength); - request->GetResponseString().shrink_to_fit(); - } - - if (request->GetAsync()) - { - if ((readLength == 0) && (!request->GetCompletionStatus())) - { - TRACE("%-35s:%-8d:%-16p Queueing pending reads %p %ld\n", __func__, __LINE__, (void*)request, lpBuffer, dwNumberOfBytesToRead); - QueueBufferRequest(request->GetOutstandingReads(), lpBuffer, dwNumberOfBytesToRead); - } - else - { - LPVOID StatusInformation = (LPVOID)lpBuffer; - - TRACE("%-35s:%-8d:%-16p WINHTTP_CALLBACK_STATUS_READ_COMPLETE lpBuffer: %p length:%lu\n", __func__, __LINE__, (void*)request, lpBuffer, readLength); - request->AsyncQueue(srequest, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, readLength, (LPVOID)StatusInformation, sizeof(StatusInformation), false); - } - } - request->GetBodyStringMutex().unlock(); - - if (lpdwNumberOfBytesRead) - *lpdwNumberOfBytesRead = (DWORD)readLength; - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)request); - return TRUE; -} - -BOOLAPI -WinHttpSetTimeouts -( - HINTERNET hInternet, // Session/Request handle. - int nResolveTimeout, - int nConnectTimeout, - int nSendTimeout, - int nReceiveTimeout -) -{ - WinHttpBase *base = static_cast(hInternet); - WinHttpSessionImp *session; - WinHttpRequestImp *request; - CURLcode res; - - TRACE("%-35s:%-8d:%-16p nResolveTimeout:%d nConnectTimeout:%d nSendTimeout:%d nReceiveTimeout:%d\n", - __func__, __LINE__, (void*)base, nResolveTimeout, nConnectTimeout, nSendTimeout, nReceiveTimeout); - if ((session = dynamic_cast(base))) - { - session->SetTimeout(nReceiveTimeout); - } - else if ((request = dynamic_cast(base))) - { - res = curl_easy_setopt(request->GetCurl(), CURLOPT_TIMEOUT_MS, nReceiveTimeout); - CURL_BAILOUT_ONERROR(res, request, FALSE); - } - else - return FALSE; - - return TRUE; -} - -static TSTRING FindRegex(const TSTRING &subject,const TSTRING ®str) -{ - TSTRING result; - try { - TREGEX re(regstr, std::regex_constants::icase); - TREGEX_MATCH match; - if (TREGEX_SEARCH(subject, match, re)) { - result = match.str(0); - } else { - result = TEXT(""); - return TEXT(""); - } - } catch (std::regex_error&) { - return TEXT(""); - } - return result; -} - -bool is_newline(char i) -{ - return (i == '\n') || (i == '\r'); -} - -template -std::basic_string nullize_newlines(const std::basic_string& str) { - std::basic_string result; - result.reserve(str.size()); - - auto cursor = str.begin(); - const auto end = str.end(); - for (;;) { - cursor = std::find_if_not(cursor, end, is_newline); - if (cursor == end) { - return result; - } - - const auto nextNewline = std::find_if(cursor, end, is_newline); - result.append(cursor, nextNewline); - result.push_back(CharT{}); - cursor = nextNewline; - } -} - -static BOOL ReadCurlValue(WinHttpRequestImp *request, LPVOID lpBuffer, LPDWORD lpdwBufferLength, - CURLINFO curlparam, bool returnDWORD) -{ - CURLcode res; - DWORD retValue; - - res = curl_easy_getinfo(request->GetCurl(), curlparam, &retValue); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - if (!returnDWORD) - { - TSTRING str = TO_STRING(retValue); - if (SizeCheck(lpBuffer, lpdwBufferLength, (str.size() + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - } - else - { - if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) - return FALSE; - } - - if (returnDWORD) - { - memcpy(lpBuffer, &retValue, sizeof(retValue)); - } - else - { - TCHAR *outbuf = static_cast(lpBuffer); - TSTRING str = TO_STRING(retValue); - std::copy(str.begin(), str.end(), outbuf); - outbuf[str.size()] = TEXT('\0'); - - if (lpdwBufferLength) - *lpdwBufferLength = (str.size() + 1) * sizeof(TCHAR); - } - TRACE("%-35s:%-8d:%-16p curlparam:%d code :%lu\n", __func__, __LINE__, (void*)request, curlparam, retValue); - return TRUE; -} - -BOOLAPI WinHttpQueryHeaders( - HINTERNET hRequest, - DWORD dwInfoLevel, - LPCTSTR pwszName, - LPVOID lpBuffer, - LPDWORD lpdwBufferLength, - LPDWORD lpdwIndex -) -{ - WinHttpRequestImp *request = static_cast(hRequest); - if (!request) - return FALSE; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - bool returnDWORD = false; - - if (pwszName != WINHTTP_HEADER_NAME_BY_INDEX) - return FALSE; - - if (lpdwIndex != WINHTTP_NO_HEADER_INDEX) - return FALSE; - - if (request->GetHeaderString().length() == 0) - return FALSE; - - if (dwInfoLevel & WINHTTP_QUERY_FLAG_NUMBER) - { - dwInfoLevel &= ~WINHTTP_QUERY_FLAG_NUMBER; - returnDWORD = true; - } - TRACE("%-35s:%-8d:%-16p dwInfoLevel = 0x%lx\n", __func__, __LINE__, (void*)request, dwInfoLevel); - - if (returnDWORD && SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) - return FALSE; - -#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) - if (dwInfoLevel == WINHTTP_QUERY_VERSION) - { - return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_HTTP_VERSION, returnDWORD); - } -#endif - if (dwInfoLevel == WINHTTP_QUERY_STATUS_CODE) - { - WinHttpSessionImp *session; - - session = GetImp(request); - if (!session->GetProxies().empty() && request->GetSecure()) - return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_HTTP_CONNECTCODE, returnDWORD); - else - return ReadCurlValue(request, lpBuffer, lpdwBufferLength, CURLINFO_RESPONSE_CODE, returnDWORD); - } - - if (dwInfoLevel == WINHTTP_QUERY_STATUS_TEXT) - { - CURLcode res; - DWORD responseCode; - - res = curl_easy_getinfo(request->GetCurl(), CURLINFO_RESPONSE_CODE, &responseCode); - CURL_BAILOUT_ONERROR(res, request, FALSE); - - std::lock_guard lck(request->GetHeaderStringMutex()); - -#ifdef UNICODE - TSTRING subject; - - std::wstring_convert> conv; - subject = conv.from_bytes(request->GetHeaderString()); -#else - TSTRING &subject = request->GetHeaderString(); -#endif - - TSTRING regstr; - - regstr.append(TEXT("HTTP.*")); - regstr.append(TO_STRING(responseCode)); - - TSTRING result = FindRegex(subject, regstr); - if (!result.empty()) - { - size_t offset = subject.find(result); - if (offset == std::string::npos) - return FALSE; - - size_t offsetendofline = subject.find(TEXT("\r\n"), offset); - if (offsetendofline == std::string::npos) - return FALSE; - - size_t startpos = offset + result.length() + 1; - if (offsetendofline <= startpos) - return FALSE; - - size_t linelength = offsetendofline - startpos; - if (SizeCheck(lpBuffer, lpdwBufferLength, (linelength + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - - std::copy(subject.begin() + startpos, subject.begin() + offsetendofline, - (TCHAR*)lpBuffer); - ((TCHAR*)lpBuffer)[linelength] = TEXT('\0'); - return TRUE; - } - else - { - TSTRING retStr = StatusCodeMap.at(responseCode); - if (retStr.empty()) - return FALSE; - - if (SizeCheck(lpBuffer, lpdwBufferLength, (retStr.size() + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - - std::copy(retStr.begin(), retStr.end(), (TCHAR*)lpBuffer); - ((TCHAR*)lpBuffer)[retStr.size()] = TEXT('\0'); - } - return TRUE; - } - - - if (dwInfoLevel == WINHTTP_QUERY_RAW_HEADERS) - { - std::string header; - TCHAR *wbuffer = static_cast(lpBuffer); - - std::lock_guard lck(request->GetHeaderStringMutex()); - header.append(request->GetHeaderString()); - - header = nullize_newlines(header); - header.resize(header.size() + 1); - - if (SizeCheck(lpBuffer, lpdwBufferLength, (header.length() + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - -#ifdef UNICODE - std::wstring_convert> conv; - std::wstring wc = conv.from_bytes(header); - - if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) - return FALSE; - - std::copy(wc.begin(), wc.end(), wbuffer); -#else - std::copy(header.begin(), header.end(), wbuffer); -#endif - wbuffer[header.length()] = TEXT('\0'); - if (lpdwBufferLength) - *lpdwBufferLength = (DWORD)header.length(); - return TRUE; - } - - if (dwInfoLevel == WINHTTP_QUERY_RAW_HEADERS_CRLF) - { - std::lock_guard lck(request->GetHeaderStringMutex()); - size_t length; - TCHAR *wbuffer = static_cast(lpBuffer); - - length = request->GetHeaderString().length(); - - if (SizeCheck(lpBuffer, lpdwBufferLength, (length + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - -#ifdef UNICODE - std::wstring_convert> conv; - std::wstring wc = conv.from_bytes(request->GetHeaderString()); - if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) - return FALSE; - - std::copy(wc.begin(), wc.end(), wbuffer); -#else - std::copy(request->GetHeaderString().begin(), request->GetHeaderString().end(), wbuffer); -#endif - wbuffer[length] = TEXT('\0'); - if (lpdwBufferLength) - *lpdwBufferLength = (DWORD)(length * sizeof(TCHAR)); - return TRUE; - } - - - return FALSE; -} - -static DWORD ConvertSecurityProtocol(DWORD offered) -{ - DWORD min = 0; - DWORD max = 0; - - if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) { - min = CURL_SSLVERSION_SSLv2; - } - else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) { - min = CURL_SSLVERSION_SSLv3; - } - else - min = CURL_SSLVERSION_DEFAULT; - - if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { - min = CURL_SSLVERSION_TLSv1_0; - - } - else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { - min = CURL_SSLVERSION_TLSv1_1; - } - else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { - min = CURL_SSLVERSION_TLSv1_2; - } - -#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 61) - if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) { - max = CURL_SSLVERSION_MAX_TLSv1_2; - } - else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) { - max = CURL_SSLVERSION_MAX_TLSv1_1; - } - else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) { - max = CURL_SSLVERSION_MAX_TLSv1_0; - } - else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) { - max = CURL_SSLVERSION_SSLv3; - } - else if (offered & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) { - max = CURL_SSLVERSION_SSLv2; - } - else - max = CURL_SSLVERSION_MAX_DEFAULT; -#endif - - return min | max; -} - - -template -static BOOL CallMemberFunction(WinHttpBase *base, std::function fn, LPVOID lpBuffer) -{ - T *obj; - - if (!lpBuffer) - return FALSE; - - if ((obj = dynamic_cast(base))) - { - if (fn(obj, static_cast(lpBuffer))) - return TRUE; - } - return FALSE; -} - -BOOLAPI WinHttpSetOption( - HINTERNET hInternet, - DWORD dwOption, - LPVOID lpBuffer, - DWORD dwBufferLength -) -{ - WinHttpBase *base = static_cast(hInternet); - - TRACE("%-35s:%-8d:%-16p dwOption:%lu\n", __func__, __LINE__, (void*)base, dwOption); - - if (dwOption == WINHTTP_OPTION_MAX_CONNS_PER_SERVER) - { - if (dwBufferLength != sizeof(DWORD)) - return FALSE; - - if (CallMemberFunction(base, &WinHttpSessionImp::SetMaxConnections, lpBuffer)) - return TRUE; - - if (CallMemberFunction(base, &WinHttpRequestImp::SetMaxConnections, lpBuffer)) - return TRUE; - - return FALSE; - } - else if (dwOption == WINHTTP_OPTION_CONTEXT_VALUE) - { - if (dwBufferLength != sizeof(void*)) - return FALSE; - - if (CallMemberFunction(base, &WinHttpConnectImp::SetUserData, lpBuffer)) - return TRUE; - - if (CallMemberFunction(base, &WinHttpSessionImp::SetUserData, lpBuffer)) - return TRUE; - - if (CallMemberFunction(base, &WinHttpRequestImp::SetUserData, lpBuffer)) - return TRUE; - - return FALSE; - } - else if (dwOption == WINHTTP_OPTION_SECURE_PROTOCOLS) - { - if (dwBufferLength != sizeof(DWORD)) - return FALSE; - - DWORD curlOffered = ConvertSecurityProtocol(*static_cast(lpBuffer)); - if (CallMemberFunction(base, &WinHttpSessionImp::SetSecureProtocol, &curlOffered)) - return TRUE; - - if (CallMemberFunction(base, &WinHttpRequestImp::SetSecureProtocol, &curlOffered)) - return TRUE; - - return FALSE; - } - else if (dwOption == WINHTTP_OPTION_SECURITY_FLAGS) - { - WinHttpRequestImp *request; - - if (dwBufferLength != sizeof(DWORD)) - return FALSE; - - if ((request = dynamic_cast(base))) - { - if (!lpBuffer) - return FALSE; - - DWORD value = *static_cast(lpBuffer); - if (value == (SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | - SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE)) - { - request->VerifyPeer() = 0L; - request->VerifyHost() = 0L; - } - else if (!value) - { - request->VerifyPeer() = 1L; - request->VerifyHost() = 2L; - } - else - return FALSE; - - return TRUE; - } - else - return FALSE; - } - - return FALSE; -} - -WINHTTPAPI -WINHTTP_STATUS_CALLBACK -WINAPI -WinHttpSetStatusCallback -( - HINTERNET hInternet, - WINHTTP_STATUS_CALLBACK lpfnInternetCallback, - DWORD dwNotificationFlags, - DWORD_PTR dwReserved -) -{ - WinHttpSessionImp *session = static_cast(hInternet); - WINHTTP_STATUS_CALLBACK oldcb; - DWORD olddwNotificationFlags; - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)session); - - if (hInternet == NULL) - return WINHTTP_INVALID_STATUS_CALLBACK; - - oldcb = session->GetCallback(&olddwNotificationFlags); - session->SetCallback(lpfnInternetCallback, dwNotificationFlags); - return oldcb; -} - -BOOLAPI -WinHttpQueryOption -( - HINTERNET hInternet, - DWORD dwOption, - LPVOID lpBuffer, - LPDWORD lpdwBufferLength -) -{ - WinHttpBase *base = static_cast(hInternet); - WinHttpSessionImp *session; - - TRACE("%-35s:%-8d:%-16p\n", __func__, __LINE__, (void*)base); - - if (!base) - return FALSE; - - if (WINHTTP_OPTION_CONNECT_TIMEOUT == dwOption) - { - if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(DWORD)) == FALSE) - return FALSE; - - session = GetImp(base); - if (!session) - return FALSE; - - *static_cast(lpBuffer) = session->GetTimeout(); - } - if (WINHTTP_OPTION_CALLBACK == dwOption) - { - if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(LPVOID)) == FALSE) - return FALSE; - - session = GetImp(base); - if (!session) - return FALSE; - - DWORD dwNotificationFlags; - WINHTTP_STATUS_CALLBACK cb = session->GetCallback(&dwNotificationFlags); - *static_cast(lpBuffer) = cb; - } - else if (WINHTTP_OPTION_URL == dwOption) - { - WinHttpRequestImp *request; - - if (!(request = dynamic_cast(base))) - return FALSE; - - char *url = NULL; - curl_easy_getinfo(request->GetCurl(), CURLINFO_EFFECTIVE_URL, &url); - if (!url) - return FALSE; - - if (SizeCheck(lpBuffer, lpdwBufferLength, (strlen(url) + 1) * sizeof(TCHAR)) == FALSE) - return FALSE; - - TCHAR *wbuffer = static_cast(lpBuffer); - size_t length = strlen(url); -#ifdef UNICODE - std::wstring_convert> conv; - std::wstring wc = conv.from_bytes(std::string(url)); - if (lpdwBufferLength && (*lpdwBufferLength < ((wc.length() + 1) * sizeof(TCHAR)))) - return FALSE; - - std::copy(wc.begin(), wc.end(), wbuffer); -#else - std::string urlstr; - urlstr.assign(url); - std::copy(urlstr.begin(), urlstr.end(), wbuffer); -#endif - wbuffer[strlen(url)] = TEXT('\0'); - if (lpdwBufferLength) - *lpdwBufferLength = (DWORD)length; - } - else if (WINHTTP_OPTION_HTTP_VERSION == dwOption) - { - WinHttpRequestImp *request; - - if (!(request = dynamic_cast(base))) - return FALSE; - - if (SizeCheck(lpBuffer, lpdwBufferLength, sizeof(HTTP_VERSION_INFO)) == FALSE) - return FALSE; - - HTTP_VERSION_INFO version; - -#if (LIBCURL_VERSION_MAJOR>=7) && (LIBCURL_VERSION_MINOR >= 50) - long curlversion; - CURLcode rc; - - rc = curl_easy_getinfo(request->GetCurl(), CURLINFO_HTTP_VERSION, &curlversion); - if (rc != CURLE_OK) - return FALSE; - - - if (curlversion == CURL_HTTP_VERSION_1_0) - { - version.dwMinorVersion = 0; - version.dwMajorVersion = 1; - } - else if (curlversion == CURL_HTTP_VERSION_1_1) - { - version.dwMinorVersion = 1; - version.dwMajorVersion = 1; - } - else if (curlversion == CURL_HTTP_VERSION_2_0) - { - version.dwMinorVersion = 0; - version.dwMajorVersion = 2; - } - else - return FALSE; -#else - version.dwMinorVersion = 1; - version.dwMajorVersion = 1; -#endif - - memcpy(lpBuffer, &version, sizeof(version)); - if (lpdwBufferLength) - *lpdwBufferLength = (DWORD)sizeof(version); - } - - return TRUE; -} - -#ifdef CURL_SUPPORTS_URL_API -BOOLAPI WinHttpCrackUrl( - LPCTSTR pwszUrl, - DWORD dwUrlLength, - DWORD dwFlags, - LPURL_COMPONENTS lpUrlComponents -) -{ - DWORD urlLen; - - if (!pwszUrl || !lpUrlComponents) - return FALSE; - - if (!lpUrlComponents->dwStructSize) - return FALSE; - - if (lpUrlComponents->dwStructSize != sizeof(*lpUrlComponents)) - return FALSE; - - if (dwUrlLength == 0) - urlLen = WCTLEN(pwszUrl); - else - urlLen = dwUrlLength; - - std::string urlstr; - ConvertCstrAssign(pwszUrl, urlLen, urlstr); - - CURLUcode rc; - CURLU *url = curl_url(); - rc = curl_url_set(url, CURLUPART_URL, urlstr.c_str(), 0); - if (rc) - return FALSE; - - char *host; - rc = curl_url_get(url, CURLUPART_HOST, &host, 0); - if (!rc) { - size_t pos = urlstr.find(host); - if (pos != std::string::npos) - { - if (lpUrlComponents->dwHostNameLength != (DWORD)-1) - { - if (lpUrlComponents->dwHostNameLength >= (strlen(host) + 1)) { - TSTRING hoststr; - hoststr.assign((TCHAR*)pwszUrl + pos, strlen(host)); - - std::copy(hoststr.begin(), hoststr.end(), lpUrlComponents->lpszHostName); - lpUrlComponents->lpszHostName[strlen(host)] = TEXT('\0'); - lpUrlComponents->dwHostNameLength = strlen(host); - } - } - else - { - lpUrlComponents->lpszHostName = const_cast(pwszUrl) + pos; - lpUrlComponents->dwHostNameLength = strlen(host); - } - } - curl_free(host); - } - - char *scheme; - rc = curl_url_get(url, CURLUPART_SCHEME, &scheme, 0); - if (!rc) { - size_t pos = urlstr.find(scheme); - if (pos != std::string::npos) - { - if (lpUrlComponents->dwSchemeLength != (DWORD)-1) - { - if (lpUrlComponents->dwSchemeLength >= (strlen(scheme) + 1)) { - TSTRING schemestr; - schemestr.assign((TCHAR*)pwszUrl + pos, strlen(scheme)); - - std::copy(schemestr.begin(), schemestr.end(), lpUrlComponents->lpszScheme); - lpUrlComponents->lpszScheme[strlen(scheme)] = TEXT('\0'); - lpUrlComponents->dwSchemeLength = strlen(scheme); - } - } - else - { - lpUrlComponents->lpszScheme = const_cast(pwszUrl) + pos; - lpUrlComponents->dwSchemeLength = strlen(scheme); - } - - if (strcmp(scheme, "http") == 0) { - lpUrlComponents->nPort = 80; - lpUrlComponents->nScheme = INTERNET_SCHEME_HTTP; - } - else if (strcmp(scheme, "https") == 0) - { - lpUrlComponents->nPort = 443; - lpUrlComponents->nScheme = INTERNET_SCHEME_HTTPS; - } - } - curl_free(scheme); - } - char *path; - rc = curl_url_get(url, CURLUPART_PATH, &path, 0); - if (!rc) { - size_t pos = urlstr.find(path); - if (pos != std::string::npos) - { - if (lpUrlComponents->dwUrlPathLength != (DWORD)-1) - { - if (lpUrlComponents->dwUrlPathLength >= (strlen(path) + 1)) { - TSTRING urlstr; - urlstr.assign((TCHAR*)pwszUrl + pos, strlen(path)); - - std::copy(urlstr.begin(), urlstr.end(), lpUrlComponents->lpszUrlPath); - lpUrlComponents->lpszUrlPath[strlen(path)] = TEXT('\0'); - lpUrlComponents->dwUrlPathLength = strlen(path); - } - } - else - { - if (strcmp(path, "/") == 0) - { - lpUrlComponents->lpszUrlPath = (LPWSTR)TEXT(""); - lpUrlComponents->dwUrlPathLength = 0; - } - else - { - lpUrlComponents->lpszUrlPath = const_cast(pwszUrl) + pos; - lpUrlComponents->dwUrlPathLength = strlen(path); - } - } - } - curl_free(path); - } - char *query; - rc = curl_url_get(url, CURLUPART_QUERY, &query, 0); - if (!rc) { - size_t pos = urlstr.find(query); - if (pos != std::string::npos) - { - if (lpUrlComponents->dwExtraInfoLength != (DWORD)-1) - { - if (lpUrlComponents->dwExtraInfoLength >= (strlen(query) + 1)) { - TSTRING extrainfo; - extrainfo.assign((TCHAR*)pwszUrl + pos - 1, strlen(query)); - - std::copy(extrainfo.begin(), extrainfo.end(), lpUrlComponents->lpszExtraInfo); - lpUrlComponents->lpszExtraInfo[strlen(query)] = TEXT('\0'); - lpUrlComponents->dwExtraInfoLength = strlen(query); - } - } - else - { - lpUrlComponents->lpszExtraInfo = const_cast(pwszUrl) + pos - 1; - lpUrlComponents->dwExtraInfoLength = strlen(query) + 1; - } - } - curl_free(query); - } - char *user; - rc = curl_url_get(url, CURLUPART_USER, &user, 0); - if (!rc) { - size_t pos = urlstr.find(user); - if (pos != std::string::npos) - { - if (lpUrlComponents->dwUserNameLength != (DWORD)-1) - { - if (lpUrlComponents->dwUserNameLength >= (strlen(user) + 1)) { - TSTRING userstr; - userstr.assign((TCHAR*)pwszUrl + pos - 1, strlen(user)); - - std::copy(userstr.begin(), userstr.end(), lpUrlComponents->lpszUserName); - lpUrlComponents->lpszUserName[strlen(user)] = TEXT('\0'); - lpUrlComponents->dwUserNameLength = strlen(user); - } - } - else - { - lpUrlComponents->lpszUserName = const_cast(pwszUrl) + pos - 1; - lpUrlComponents->dwUserNameLength = strlen(user); - } - } - curl_free(user); - } - char *pw; - rc = curl_url_get(url, CURLUPART_PASSWORD, &pw, 0); - if (!rc) { - size_t pos = urlstr.find(pw); - if (pos != std::string::npos) - { - if (lpUrlComponents->dwPasswordLength != (DWORD)-1) - { - if (lpUrlComponents->dwPasswordLength >= (strlen(pw) + 1)) { - TSTRING pwstr; - pwstr.assign((TCHAR*)pwszUrl + pos - 1, strlen(pw)); - - std::copy(pwstr.begin(), pwstr.end(), lpUrlComponents->lpszPassword); - - lpUrlComponents->lpszPassword[strlen(pw)] = TEXT('\0'); - lpUrlComponents->dwPasswordLength = strlen(pw); - } - } - else - { - lpUrlComponents->lpszPassword = const_cast(pwszUrl) + pos - 1; - lpUrlComponents->dwPasswordLength = strlen(pw); - } - } - curl_free(pw); - } - curl_url_cleanup(url); - - return TRUE; -} - -#endif - - -BOOLAPI WinHttpWriteData -( - HINTERNET hRequest, - LPCVOID lpBuffer, - DWORD dwNumberOfBytesToWrite, - LPDWORD lpdwNumberOfBytesWritten -) -{ - WinHttpRequestImp *request = static_cast(hRequest); - if (!request) - return FALSE; - - std::shared_ptr srequest = request->shared_from_this(); - if (!srequest) - return FALSE; - - if (request->GetClosing()) - { - TRACE("%-35s:%-8d:%-16p \n", __func__, __LINE__, (void*)request); - return FALSE; - } - - TRACE("%-35s:%-8d:%-16p dwNumberOfBytesToWrite:%lu\n", __func__, __LINE__, (void*)request, dwNumberOfBytesToWrite); - if (request->GetAsync()) - { - { - std::lock_guard lck(request->GetReadDataEventMtx()); - QueueBufferRequest(request->GetOutstandingWrites(), const_cast(lpBuffer), dwNumberOfBytesToWrite); - request->GetReadDataEventCounter()++; - } - - ComContainer::GetInstance().ResumeTransfer(request->GetCurl(), CURLPAUSE_CONT); - } - else - { - request->AppendReadData(lpBuffer, dwNumberOfBytesToWrite); - { - std::lock_guard lck(request->GetReadDataEventMtx()); - request->GetReadDataEventCounter()++; - } - } - - if (lpdwNumberOfBytesWritten) - *lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite; - - return TRUE; -} - diff --git a/Release/src/http/client/winhttppal/winhttppal.h b/Release/src/http/client/winhttppal/winhttppal.h deleted file mode 100644 index 5089e7f985..0000000000 --- a/Release/src/http/client/winhttppal/winhttppal.h +++ /dev/null @@ -1,454 +0,0 @@ -typedef void VOID; -typedef void* LPVOID; -typedef unsigned long DWORD; -typedef const void* LPCVOID; -typedef long LONG; -typedef unsigned char BYTE; -#define __int3264 long int -typedef unsigned __int3264 ULONG_PTR; -typedef ULONG_PTR DWORD_PTR; -typedef DWORD* PDWORD; -typedef DWORD* LPDWORD; -typedef char* LPSTR; -typedef const char* LPCSTR; - -#ifdef UNICODE -typedef wchar_t TCHAR; -typedef const wchar_t* LPCTSTR; -typedef wchar_t* LPTSTR; -typedef wchar_t* PTSTR; -typedef const wchar_t* LPCTSTR; -typedef const wchar_t* PCTSTR; -#define TEXT(a) L##a -#else -typedef char TCHAR; -typedef const char* LPCTSTR; -typedef char* LPTSTR; -typedef char* PTSTR; -typedef const char* LPCTSTR; -typedef const char* PCTSTR; -#define TEXT(a) a -#endif - -typedef LPVOID HINTERNET; -typedef bool BOOL; - -typedef VOID (* WINHTTP_STATUS_CALLBACK)( - HINTERNET hInternet, - DWORD_PTR dwContext, - DWORD dwInternetStatus, - LPVOID lpvStatusInformation, - DWORD dwStatusInformationLength -); - -enum -{ - API_RECEIVE_RESPONSE = 1, - API_QUERY_DATA_AVAILABLE, - API_READ_DATA, - API_WRITE_DATA, - API_SEND_REQUEST, -}; - -typedef struct -{ - DWORD_PTR dwResult; - DWORD dwError; -} -WINHTTP_ASYNC_RESULT, * LPWINHTTP_ASYNC_RESULT; - -typedef struct -{ - DWORD dwMajorVersion; - DWORD dwMinorVersion; -} -HTTP_VERSION_INFO; - -enum -{ - WINHTTP_QUERY_RAW_HEADERS, - WINHTTP_QUERY_STATUS_CODE, - WINHTTP_QUERY_VERSION, - WINHTTP_QUERY_RAW_HEADERS_CRLF, - WINHTTP_QUERY_STATUS_TEXT, - WINHTTP_QUERY_FLAG_NUMBER = 0x80000000, -}; - -enum -{ - WINHTTP_AUTH_SCHEME_NEGOTIATE, - WINHTTP_AUTH_SCHEME_NTLM, - WINHTTP_AUTH_SCHEME_PASSPORT, - WINHTTP_AUTH_SCHEME_DIGEST, - WINHTTP_AUTH_SCHEME_BASIC, - WINHTTP_AUTH_TARGET_PROXY, - WINHTTP_AUTH_TARGET_SERVER, -}; - -#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0 -#define WINHTTP_INVALID_STATUS_CALLBACK (WINHTTP_STATUS_CALLBACK)-1 - -#define WINHTTP_NO_REFERER NULL -#define WINHTTP_DEFAULT_ACCEPT_TYPES NULL -#define WINHTTP_NO_ADDITIONAL_HEADERS NULL -#define WINHTTP_NO_REQUEST_DATA NULL -#define WINHTTP_NO_PROXY_NAME NULL -#define WINHTTP_NO_PROXY_BYPASS NULL -#define WINHTTP_NO_HEADER_INDEX NULL -#define WINHTTP_HEADER_NAME_BY_INDEX NULL -#define WINHTTP_NO_OUTPUT_BUFFER NULL - -#define WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH 0 -#define WINHTTP_ADDREQ_FLAG_ADD 0 -#define WINHTTP_ENABLE_SSL_REVOCATION 0 - -#define INTERNET_DEFAULT_HTTP_PORT 80 -#define ARRAYSIZE(n) sizeof(n)/sizeof(n[0]) - -#define GetLastError() errno -#define CALLBACK - -#define FALSE 0 -#define TRUE 1 - -#define INTERNET_DEFAULT_HTTPS_PORT 443 -#define S_OK 0 - -#define ERROR_INSUFFICIENT_BUFFER ENOMEM -#define ERROR_WINHTTP_RESEND_REQUEST EBUSY -#define ERROR_SUCCESS 0 -#define ERROR_OPERATION_ABORTED EINVAL -#define ERROR_NOT_ENOUGH_MEMORY ENOMEM -#define ERROR_WINHTTP_TIMEOUT ETIMEDOUT -#define ERROR_INVALID_PARAMETER EINVAL - -#define BOOLAPI BOOL -#define SetLastError(val) errno = val -#define WINHTTPAPI -#define WINAPI - -#ifdef min -#define MIN min -#define MAX max -#else -#define MIN std::min -#define MAX std::max -#endif - -#ifdef UNICODE -#define WCTLEN wcslen -#define WCTCMP wcscmp -#define WCTCPY wcscpy -#define WCTNCPY wcsncpy -#define STNPRINTF swprintf -#define TSTRING std::wstring -#define STRING_LITERAL "%S" -#define TO_STRING std::to_wstring -#define TREGEX std::wregex -#define TREGEX_SEARCH std::regex_search -#define TREGEX_MATCH std::wsmatch -#else -#define WCTLEN strlen -#define WCTCMP strcmp -#define WCTCPY strcpy -#define WCTNCPY strncpy -#define STNPRINTF snprintf -#define TSTRING std::string -#define STRING_LITERAL "%s" -#define TO_STRING std::to_string -#define TREGEX std::regex -#define TREGEX_SEARCH std::regex_search -#define TREGEX_MATCH std::smatch -#endif - -enum -{ - WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = 0x00080000, - WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = 0x00100000, - WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = 0x00200000, - WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 = 0x00400000, - WINHTTP_FLAG_SECURE_PROTOCOL_SSL2 = 0x00040000, - WINHTTP_FLAG_SECURE = 0x00800000, - WINHTTP_FLAG_ASYNC = 0x10000000, - WINHTTP_FLAG_REFRESH = 0x00000100, - WINHTTP_FLAG_ESCAPE_DISABLE = 0x20000000, -}; - -enum -{ - WINHTTP_CALLBACK_STATUS_READ_COMPLETE = (1 << 0), - WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE = (1 << 1), - WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE = (1 << 2), - WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE = (1 << 3), - WINHTTP_CALLBACK_STATUS_REQUEST_ERROR = (1 << 4), - WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING = (1 << 5), - WINHTTP_CALLBACK_STATUS_SECURE_FAILURE = (1 << 6), - WINHTTP_CALLBACK_STATUS_SENDING_REQUEST = (1 << 7), - WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE = (1 << 8), - WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED = (1 << 9), - WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT = (1 << 10), - WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED = (1 << 11), - WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA = (1 << 12), - WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID = (1 << 13), - WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID = (1 << 14), - WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR = (1 << 15), - WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE = (1 << 16), - WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED = (1 << 17), - WINHTTP_CALLBACK_STATUS_REQUEST_SENT = (1 << 18), - WINHTTP_CALLBACK_STATUS_REDIRECT = (1 << 19), - WINHTTP_CALLBACK_STATUS_RESOLVING_NAME = (1 << 20), - WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS = (1 << 24), - WINHTTP_CALLBACK_FLAG_HANDLES = (1 << 25), - WINHTTP_CALLBACK_FLAG_SECURE_FAILURE = (1 << 26), - WINHTTP_CALLBACK_FLAG_SEND_REQUEST = (1 << 27), -}; - -enum -{ - WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS = (1 << 0), -}; - -enum -{ - WINHTTP_OPTION_CONTEXT_VALUE = 1, - WINHTTP_OPTION_CONNECT_TIMEOUT, - WINHTTP_OPTION_CALLBACK, - WINHTTP_OPTION_URL, - WINHTTP_OPTION_HTTP_VERSION, - WINHTTP_OPTION_MAX_CONNS_PER_SERVER, - WINHTTP_OPTION_SECURE_PROTOCOLS, - WINHTTP_OPTION_PROXY, - WINHTTP_OPTION_AUTOLOGON_POLICY, - WINHTTP_OPTION_ENABLE_FEATURE, - WINHTTP_OPTION_SECURITY_FLAGS, -}; - -enum -{ - SECURITY_FLAG_IGNORE_UNKNOWN_CA, - SECURITY_FLAG_IGNORE_CERT_DATE_INVALID, - SECURITY_FLAG_IGNORE_CERT_CN_INVALID, - SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE, -}; -enum -{ - ERROR_WINHTTP_OPERATION_CANCELLED = 12017, -}; - -enum -{ - WINHTTP_ACCESS_TYPE_NAMED_PROXY = 1, - WINHTTP_ACCESS_TYPE_DEFAULT_PROXY =2, - WINHTTP_ACCESS_TYPE_NO_PROXY = 3, - WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY = 4, -}; - -typedef unsigned int INTERNET_PORT; - -BOOL WinHttpCloseHandle( - HINTERNET hInternet -); - - -BOOL -WinHttpReceiveResponse -( - HINTERNET hRequest, - LPVOID lpReserved -); - -BOOL -WinHttpSetTimeouts -( - HINTERNET hInternet, // Session/Request handle. - int nResolveTimeout, - int nConnectTimeout, - int nSendTimeout, - int nReceiveTimeout -); - - -WINHTTP_STATUS_CALLBACK -WinHttpSetStatusCallback -( - HINTERNET hInternet, - WINHTTP_STATUS_CALLBACK lpfnInternetCallback, - DWORD dwNotificationFlags, - DWORD_PTR dwReserved -); - -BOOL WinHttpSetOption( - HINTERNET hInternet, - DWORD dwOption, - LPVOID lpBuffer, - DWORD dwBufferLength -); - -BOOL WinHttpSendRequest -( - HINTERNET hRequest, - LPCTSTR lpszHeaders, - DWORD dwHeadersLength, - LPVOID lpOptional, - DWORD dwOptionalLength, - DWORD dwTotalLength, - DWORD_PTR dwContext -); - -BOOL -WinHttpReadData -( - HINTERNET hRequest, - LPVOID lpBuffer, - DWORD dwNumberOfBytesToRead, - LPDWORD lpdwNumberOfBytesRead -); - -BOOL WinHttpQueryHeaders( - HINTERNET hRequest, - DWORD dwInfoLevel, - LPCTSTR pwszName, - LPVOID lpBuffer, - LPDWORD lpdwBufferLength, - LPDWORD lpdwIndex -); - - -BOOL -WinHttpQueryDataAvailable -( - HINTERNET hRequest, - LPDWORD lpdwNumberOfBytesAvailable -); - - -HINTERNET WinHttpOpenRequest -( - HINTERNET hConnect, - LPCTSTR pwszVerb, - LPCTSTR pwszObjectName, - LPCTSTR pwszVersion, - LPCTSTR pwszReferrer, - LPCTSTR * ppwszAcceptTypes, - DWORD dwFlags -); - -HINTERNET WinHttpOpen -( - LPCTSTR pszAgentW, - DWORD dwAccessType, - LPCTSTR pszProxyW, - LPCTSTR pszProxyBypassW, - DWORD dwFlags -); - -HINTERNET WinHttpConnect -( - HINTERNET hSession, - LPCTSTR pswzServerName, - INTERNET_PORT nServerPort, - DWORD dwReserved -); - -BOOL -WinHttpAddRequestHeaders -( - HINTERNET hRequest, - LPCTSTR lpszHeaders, - DWORD dwHeadersLength, - DWORD dwModifiers -); - -BOOL -WinHttpQueryOption -( - HINTERNET hInternet, - DWORD dwOption, - LPVOID lpBuffer, - LPDWORD lpdwBufferLength -); - -BOOL WinHttpWriteData( - HINTERNET hRequest, - LPCVOID lpBuffer, - DWORD dwNumberOfBytesToWrite, - LPDWORD lpdwNumberOfBytesWritten -); - - -typedef struct { - DWORD dwAccessType; - LPTSTR lpszProxy; - LPTSTR lpszProxyBypass; -} WINHTTP_PROXY_INFO, *LPWINHTTP_PROXY_INFO; - -typedef struct { - BOOL fAutoDetect; - LPTSTR lpszAutoConfigUrl; - LPTSTR lpszProxy; - LPTSTR lpszProxyBypass; -} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; - -typedef struct { - DWORD dwFlags; - DWORD dwAutoDetectFlags; - LPCTSTR lpszAutoConfigUrl; - LPVOID lpvReserved; - DWORD dwReserved; - BOOL fAutoLogonIfChallenged; -} WINHTTP_AUTOPROXY_OPTIONS; - -BOOL WinHttpGetProxyForUrl( - HINTERNET hSession, - LPCTSTR lpcwszUrl, - WINHTTP_AUTOPROXY_OPTIONS *pAutoProxyOptions, - WINHTTP_PROXY_INFO *pProxyInfo -); - - -BOOL WinHttpQueryAuthSchemes( - HINTERNET hRequest, - LPDWORD lpdwSupportedSchemes, - LPDWORD lpdwFirstScheme, - LPDWORD pdwAuthTarget -); - -BOOL WinHttpSetCredentials( - HINTERNET hRequest, - DWORD AuthTargets, - DWORD AuthScheme, - LPCTSTR pwszUserName, - LPCTSTR pwszPassword, - LPVOID pAuthParams -); - -enum -{ - WINHTTP_AUTOPROXY_AUTO_DETECT, - WINHTTP_AUTO_DETECT_TYPE_DHCP, - WINHTTP_AUTO_DETECT_TYPE_DNS_A, - WINHTTP_AUTOPROXY_CONFIG_URL, -}; - -typedef enum { - INTERNET_SCHEME_HTTP = 1, - INTERNET_SCHEME_HTTPS, -} INTERNET_SCHEME; - -typedef struct { - DWORD dwStructSize; - LPTSTR lpszScheme; - DWORD dwSchemeLength; - INTERNET_SCHEME nScheme; - LPTSTR lpszHostName; - DWORD dwHostNameLength; - INTERNET_PORT nPort; - LPTSTR lpszUserName; - DWORD dwUserNameLength; - LPTSTR lpszPassword; - DWORD dwPasswordLength; - LPTSTR lpszUrlPath; - DWORD dwUrlPathLength; - LPTSTR lpszExtraInfo; - DWORD dwExtraInfoLength; -} URL_COMPONENTS, *LPURL_COMPONENTS; diff --git a/Release/tests/functional/http/client/outside_tests.cpp b/Release/tests/functional/http/client/outside_tests.cpp index a30d813a72..05bb306e7a 100644 --- a/Release/tests/functional/http/client/outside_tests.cpp +++ b/Release/tests/functional/http/client/outside_tests.cpp @@ -228,7 +228,7 @@ SUITE(outside_tests) TEST(server_hostname_mismatch) { test_failed_ssl_cert(U("https://wrong.host.badssl.com/")); } -#if !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#if !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) TEST(server_hostname_host_override) { handle_timeout([] { @@ -253,7 +253,7 @@ SUITE(outside_tests) const auto statusCode = response.status_code(); CHECK(statusCode == status_codes::OK || statusCode == status_codes::MovedPermanently); } -#endif // !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#endif // !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) TEST(server_cert_expired) { test_failed_ssl_cert(U("https://expired.badssl.com/")); } diff --git a/Release/tests/functional/http/client/request_uri_tests.cpp b/Release/tests/functional/http/client/request_uri_tests.cpp index 14de35a626..5d61556991 100644 --- a/Release/tests/functional/http/client/request_uri_tests.cpp +++ b/Release/tests/functional/http/client/request_uri_tests.cpp @@ -88,7 +88,7 @@ SUITE(request_uri_tests) // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/heheh?key1=value2#fragment"); -#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) expected_value = percent_encode_pound(expected_value); #endif @@ -137,7 +137,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1?key1=value1&key2=value2#frag"); -#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); @@ -157,7 +157,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1/path2?key2=value2#fragmentfg2"); -#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_CURL) +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6073063e21..5c022ae799 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -312,7 +312,7 @@ jobs: # cd Build_iOS # ./configure.sh # displayName: 'Build for iOS' - - job: Ubuntu_1604_Apt_Curl + - job: Ubuntu_1604_Apt_winhttppal pool: vmImage: 'Ubuntu 16.04' steps: @@ -333,13 +333,21 @@ jobs: make sudo make install cd .. + git clone https://github.com/microsoft/WinHttpPAL.git + cd WinHttpPAL + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .. + make + make install + cd ../.. mkdir build.debug cd build.debug - /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCPPREST_HTTP_CLIENT_IMPL=curl .. + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCPPREST_HTTP_CLIENT_IMPL=winhttppal .. cd .. mkdir build.release cd build.release - /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCPPREST_HTTP_CLIENT_IMPL=curl .. + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCPPREST_HTTP_CLIENT_IMPL=winhttppal .. cd .. ninja -C build.debug ninja -C build.release From 78ef46b96c81d757a8d68b041c274f0516bc26f1 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Mon, 2 Sep 2019 21:12:30 +0000 Subject: [PATCH 35/41] Delta Platform category --- Release/include/cpprest/http_client.h | 8 +- Release/src/http/client/http_client.cpp | 4 +- .../http/client/http_client_winhttppal.cpp | 282 +++++++++--------- 3 files changed, 152 insertions(+), 142 deletions(-) diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index 2b6d011db2..82fa3222b6 100644 --- a/Release/include/cpprest/http_client.h +++ b/Release/include/cpprest/http_client.h @@ -104,7 +104,7 @@ class http_client_config #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) , m_tlsext_sni_enabled(true) #endif -#if (defined(_WIN32) && !defined(__cplusplus_winrt)) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) , m_buffer_request(false) #endif { @@ -262,7 +262,7 @@ class http_client_config void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; } #endif -#if (defined(_WIN32) && !defined(__cplusplus_winrt)) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) /// /// Checks if request data buffering is turned on, the default is off. /// @@ -389,7 +389,7 @@ class http_client_config std::function m_ssl_context_callback; bool m_tlsext_sni_enabled; #endif -#if (defined(_WIN32) && !defined(__cplusplus_winrt)) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) bool m_buffer_request; #endif }; @@ -716,7 +716,7 @@ class http_client namespace details { -#if defined(_WIN32) +#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) extern const utility::char_t* get_with_body_err_msg; #endif diff --git a/Release/src/http/client/http_client.cpp b/Release/src/http/client/http_client.cpp index f3174272bb..09e3eed15a 100644 --- a/Release/src/http/client/http_client.cpp +++ b/Release/src/http/client/http_client.cpp @@ -41,8 +41,8 @@ static void verify_uri(const uri& uri) namespace details { -#if defined(_WIN32) -extern const utility::char_t* get_with_body_err_msg = +#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) +const utility::char_t* get_with_body_err_msg = _XPLATSTR("A GET or HEAD request should not have an entity body."); #endif diff --git a/Release/src/http/client/http_client_winhttppal.cpp b/Release/src/http/client/http_client_winhttppal.cpp index dbbf866c2c..525d34f203 100644 --- a/Release/src/http/client/http_client_winhttppal.cpp +++ b/Release/src/http/client/http_client_winhttppal.cpp @@ -23,6 +23,9 @@ #include #define GlobalFree free +#if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA) +#include +#endif namespace { @@ -99,18 +102,6 @@ static http::status_code parse_status_code(HINTERNET request_handle) return (unsigned short)atoi((LPCTSTR)(buffer.c_str())); } -// Helper function to trim leading and trailing null characters from a string. -static void trim_nulls(utility::string_t& str) -{ - size_t index; - for (index = 0; index < str.size() && str[index] == 0; ++index) - ; - str.erase(0, index); - for (index = str.size(); index > 0 && str[index - 1] == 0; --index) - ; - str.erase(index); -} - // Helper function to get the reason phrase from a WinHTTP response. static utility::string_t parse_reason_phrase(HINTERNET request_handle) { @@ -126,7 +117,7 @@ static utility::string_t parse_reason_phrase(HINTERNET request_handle) &length, WINHTTP_NO_HEADER_INDEX); // WinHTTP reports back the wrong length, trim any null characters. - trim_nulls(phrase); + ::web::http::details::trim_nulls(phrase); return phrase; } @@ -153,7 +144,7 @@ static void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::htt /// /// Parses a string containing HTTP headers. /// -static void parse_curl_headers(HINTERNET request_handle, _In_z_ utility::char_t* headersStr, http_response& response) +static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utility::char_t* headersStr, http_response& response) { // Clear the header map for each new response; otherwise, the header values will be combined. response.headers().clear(); @@ -171,6 +162,8 @@ static std::string build_error_msg(unsigned long code, const std::string& locati std::string msg(location); msg.append(": "); msg.append(std::to_string(code)); + msg.append(": "); + msg.append(utility::details::platform_category().message(static_cast(code))); return msg; } @@ -229,20 +222,38 @@ enum msg_body_type transfer_encoding_chunked }; +static DWORD WinHttpDefaultProxyConstant() CPPREST_NOEXCEPT +{ +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +#if _WIN32_WINNT < _WIN32_WINNT_WINBLUE + if (!IsWindows8Point1OrGreater()) + { + // Not Windows 8.1 or later, use the default proxy setting + return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + } +#endif // _WIN32_WINNT < _WIN32_WINNT_WINBLUE + + // Windows 8.1 or later, use the automatic proxy setting + return WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; +#else // ^^^ _WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ // vvv _WIN32_WINNT < _WIN32_WINNT_VISTA vvv + return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA +} + // Additional information necessary to track a WinHTTP request. -class curl_request_context final : public request_context +class winhttp_request_context final : public request_context { public: // Factory function to create requests on the heap. static std::shared_ptr create_request_context( const std::shared_ptr<_http_client_communicator>& client, const http_request& request) { - std::shared_ptr ret(new curl_request_context(client, request)); + std::shared_ptr ret(new winhttp_request_context(client, request)); ret->m_self_reference = ret; return std::move(ret); } - ~curl_request_context() { cleanup(); } + ~winhttp_request_context() { cleanup(); } void allocate_request_space(_In_opt_ uint8_t* block, size_t length) { @@ -263,8 +274,8 @@ class curl_request_context final : public request_context bool is_externally_allocated() const { return !m_body_data.is_internally_allocated(); } HINTERNET m_request_handle; - std::weak_ptr* - m_request_handle_context; // owned by m_request_handle to be delete'd by WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING + std::weak_ptr* + m_request_handle_context; // owned by m_request_handle to be deleted by WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING bool m_proxy_authentication_tried; bool m_server_authentication_tried; @@ -283,7 +294,7 @@ class curl_request_context final : public request_context virtual concurrency::streams::streambuf _get_readbuffer() { return m_readStream.streambuf(); } // This self reference will keep us alive until finish() is called. - std::shared_ptr m_self_reference; + std::shared_ptr m_self_reference; memory_holder m_body_data; // Compress/decompress-related processing state lives here @@ -298,10 +309,10 @@ class curl_request_context final : public request_context , m_started(false) , m_done(false) , m_chunked(false) - , m_chunk_bytes(0) { } +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) compression_state(const compression_state&) = delete; compression_state(compression_state&& other) : m_buffer(std::move(other.m_buffer)) @@ -331,6 +342,7 @@ class curl_request_context final : public request_context m_chunk = std::move(other.m_chunk); return *this; } +#endif // defined(_MSC_VER) && _MSC_VER < 1900 // Minimal state for on-the-fly decoding of "chunked" encoded data class _chunk_helper @@ -482,7 +494,7 @@ class curl_request_context final : public request_context if (m_bytes_remaining) { // We're at the offset of a chunk of consumable data; let the caller process it - l = std::min(m_bytes_remaining, buffer_size - n); + l = (std::min)(m_bytes_remaining, buffer_size - n); m_bytes_remaining -= l; if (!m_bytes_remaining) { @@ -580,7 +592,7 @@ class curl_request_context final : public request_context std::vector m_cachedEncodedCert; // Can only create on the heap using factory function. - curl_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) + winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) : request_context(client, request) , m_request_handle(nullptr) , m_proxy_authentication_tried(false) @@ -619,12 +631,11 @@ struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG } }; - -// WINHTTPPAL client. -class curl_client final : public _http_client_communicator +// WinHTTP client. +class winhttp_client final : public _http_client_communicator { public: - curl_client(http::uri address, http_client_config client_config) + winhttp_client(http::uri address, http_client_config client_config) : _http_client_communicator(std::move(address), std::move(client_config)) , m_opened(false) , m_hSession(nullptr) @@ -633,11 +644,11 @@ class curl_client final : public _http_client_communicator { } - curl_client(const curl_client&) = delete; - curl_client& operator=(const curl_client&) = delete; + winhttp_client(const winhttp_client&) = delete; + winhttp_client& operator=(const winhttp_client&) = delete; // Closes session. - ~curl_client() + ~winhttp_client() { if (m_hConnection != nullptr) { @@ -656,7 +667,7 @@ class curl_client final : public _http_client_communicator virtual pplx::task propagate(http_request request) override { auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this()); - auto context = details::curl_request_context::create_request_context(self, request); + auto context = details::winhttp_request_context::create_request_context(self, request); // Use a task to externally signal the final result and completion of the task. auto result_task = pplx::create_task(context->m_request_completion); @@ -693,8 +704,12 @@ class curl_client final : public _http_client_communicator http::uri uri; const auto& config = client_config(); - - if (config.proxy().is_disabled()) + const auto& proxy = config.proxy(); + if (proxy.is_default()) + { + access_type = WinHttpDefaultProxyConstant(); + } + else if (proxy.is_disabled()) { access_type = WINHTTP_ACCESS_TYPE_NO_PROXY; proxy_name = WINHTTP_NO_PROXY_NAME; @@ -759,6 +774,7 @@ class curl_client final : public _http_client_communicator } // Enable TLS 1.1 and 1.2 +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) BOOL win32_result(FALSE); DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | @@ -769,13 +785,14 @@ class curl_client final : public _http_client_communicator { return GetLastError(); } +#endif config._invoke_nativesessionhandle_options(m_hSession); // Register asynchronous callback. if (WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback(m_hSession, - &curl_client::completion_callback, + &winhttp_client::completion_callback, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_HANDLES | WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, 0)) @@ -814,9 +831,9 @@ class curl_client final : public _http_client_communicator http_request& msg = request->m_request; http_headers& headers = msg.headers(); - std::shared_ptr curl_context = - std::static_pointer_cast(request); - std::weak_ptr weak_curl_context = curl_context; + std::shared_ptr winhttp_context = + std::static_pointer_cast(request); + std::weak_ptr weak_winhttp_context = winhttp_context; proxy_info info; bool proxy_info_required = false; @@ -828,27 +845,12 @@ class curl_client final : public _http_client_communicator // and host won't resolve if (!::web::http::details::validate_method(method)) { - request->report_exception(http_exception("The method string is invalid.")); - return; + request->report_exception(http_exception("The method string is invalid.")); + return; } if (m_proxy_auto_config) { - WINHTTP_AUTOPROXY_OPTIONS autoproxy_options; - memset(&autoproxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS)); - - if (m_proxy_auto_config_url.empty()) - { - autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; - autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; - } - else - { - autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; - autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str(); - } - - autoproxy_options.fAutoLogonIfChallenged = TRUE; } // Need to form uri path, query, and fragment for this request. @@ -857,9 +859,9 @@ class curl_client final : public _http_client_communicator http::uri_builder(m_uri).append(msg.relative_uri()).to_uri().resource().to_string(); // Open the request. - curl_context->m_request_handle_context = new std::weak_ptr(curl_context); + winhttp_context->m_request_handle_context = new std::weak_ptr(winhttp_context); - curl_context->m_request_handle = + winhttp_context->m_request_handle = WinHttpOpenRequest(m_hConnection, msg.method().c_str(), encoded_resource.c_str(), @@ -867,23 +869,23 @@ class curl_client final : public _http_client_communicator WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_ESCAPE_DISABLE | (m_secure ? WINHTTP_FLAG_SECURE : 0)); - if (curl_context->m_request_handle == nullptr) + if (winhttp_context->m_request_handle == nullptr) { auto errorCode = GetLastError(); - delete curl_context->m_request_handle_context; - curl_context->m_request_handle_context = 0; + delete winhttp_context->m_request_handle_context; + winhttp_context->m_request_handle_context = 0; request->report_error(errorCode, build_error_msg(errorCode, "WinHttpOpenRequest")); return; } - if (!WinHttpSetOption(curl_context->m_request_handle, + if (!WinHttpSetOption(winhttp_context->m_request_handle, WINHTTP_OPTION_CONTEXT_VALUE, - &curl_context->m_request_handle_context, + &winhttp_context->m_request_handle_context, sizeof(void*))) { auto errorCode = GetLastError(); - delete curl_context->m_request_handle_context; - curl_context->m_request_handle_context = 0; + delete winhttp_context->m_request_handle_context; + winhttp_context->m_request_handle_context = 0; request->report_error(errorCode, build_error_msg(errorCode, "WinHttpSetOption request context")); return; } @@ -891,7 +893,7 @@ class curl_client final : public _http_client_communicator if (proxy_info_required) { auto result = WinHttpSetOption( - curl_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO)); + winhttp_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO)); if (!result) { auto errorCode = GetLastError(); @@ -908,7 +910,7 @@ class curl_client final : public _http_client_communicator DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; auto result = WinHttpSetOption( - curl_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data)); + winhttp_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data)); if (!result) { auto errorCode = GetLastError(); @@ -931,7 +933,7 @@ class curl_client final : public _http_client_communicator const auto& requestHost = hostHeader->second; if (!utility::details::str_iequal(requestHost, m_uri.host())) { - curl_context->install_custom_cn_check(requestHost); + winhttp_context->install_custom_cn_check(requestHost); ignoredCertificateValidationSteps = SECURITY_FLAG_IGNORE_CERT_CN_INVALID; } } @@ -943,7 +945,7 @@ class curl_client final : public _http_client_communicator SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; } - if (ignoredCertificateValidationSteps && !WinHttpSetOption(curl_context->m_request_handle, + if (ignoredCertificateValidationSteps && !WinHttpSetOption(winhttp_context->m_request_handle, WINHTTP_OPTION_SECURITY_FLAGS, &ignoredCertificateValidationSteps, sizeof(ignoredCertificateValidationSteps))) @@ -968,38 +970,38 @@ class curl_client final : public _http_client_communicator { if (msg.method() == http::methods::GET || msg.method() == http::methods::HEAD) { - request->report_exception(http_exception("get_with_body_err_msg")); + request->report_exception(http_exception(get_with_body_err_msg)); return; } // There is a request body that needs to be transferred. - if (content_length == std::numeric_limits::max()) + if (content_length == (std::numeric_limits::max)()) { // The content length is not set and the application set a stream. This is an // indication that we will use transfer encoding chunked. We still want to // know that stream's effective length if possible for memory efficiency. - curl_context->m_bodyType = transfer_encoding_chunked; - curl_context->m_remaining_to_write = msg._get_impl()->_get_stream_length(); + winhttp_context->m_bodyType = transfer_encoding_chunked; + winhttp_context->m_remaining_to_write = msg._get_impl()->_get_stream_length(); } else { // While we won't be transfer-encoding the data, we will write it in portions. - curl_context->m_bodyType = content_length_chunked; - curl_context->m_remaining_to_write = content_length; + winhttp_context->m_bodyType = content_length_chunked; + winhttp_context->m_remaining_to_write = content_length; } } utility::string_t flattened_headers = web::http::details::flatten_http_headers(headers); - if (curl_context->m_request.method() == http::methods::GET) + if (winhttp_context->m_request.method() == http::methods::GET) { // Prepare to request a compressed response from the server if necessary. - flattened_headers += curl_context->get_compression_header(); + flattened_headers += winhttp_context->get_compression_header(); } // Add headers. if (!flattened_headers.empty()) { - if (!WinHttpAddRequestHeaders(curl_context->m_request_handle, + if (!WinHttpAddRequestHeaders(winhttp_context->m_request_handle, flattened_headers.c_str(), static_cast(flattened_headers.length()), WINHTTP_ADDREQ_FLAG_ADD)) @@ -1014,12 +1016,12 @@ class curl_client final : public _http_client_communicator if (msg._cancellation_token() != pplx::cancellation_token::none()) { // cancellation callback is unregistered when request is completed. - curl_context->m_cancellationRegistration = - msg._cancellation_token().register_callback([weak_curl_context]() { + winhttp_context->m_cancellationRegistration = + msg._cancellation_token().register_callback([weak_winhttp_context]() { // Call the WinHttpSendRequest API after WinHttpCloseHandle will give invalid handle error and we // throw this exception. Call the cleanup to make the m_request_handle as nullptr, otherwise, // Application Verifier will give AV exception on m_request_handle. - auto lock = weak_curl_context.lock(); + auto lock = weak_winhttp_context.lock(); if (!lock) return; lock->cleanup(); }); @@ -1028,7 +1030,7 @@ class curl_client final : public _http_client_communicator // Call the callback function of user customized options. try { - client_config().invoke_nativehandle_options(curl_context->m_request_handle); + client_config().invoke_nativehandle_options(winhttp_context->m_request_handle); } catch (...) { @@ -1036,49 +1038,57 @@ class curl_client final : public _http_client_communicator return; } - _start_request_send(curl_context, content_length); + // Only need to cache the request body if user specified and the request stream doesn't support seeking. + if (winhttp_context->m_bodyType != no_body && client_config().buffer_request() && + !winhttp_context->_get_readbuffer().can_seek()) + { + winhttp_context->m_readBufferCopy = + ::utility::details::make_unique<::concurrency::streams::container_buffer>>(); + } + + _start_request_send(winhttp_context, content_length); return; } private: - void _start_request_send(const std::shared_ptr& curl_context, size_t content_length) + void _start_request_send(const std::shared_ptr& winhttp_context, size_t content_length) { DWORD totalLength; - if (curl_context->m_bodyType == no_body) + if (winhttp_context->m_bodyType == no_body) { totalLength = 0; } else { // Capture the current read position of the stream. - auto rbuf = curl_context->_get_readbuffer(); + auto rbuf = winhttp_context->_get_readbuffer(); // Record starting position in case request is challenged for authorization // and needs to seek back to where reading is started from. - curl_context->m_startingPosition = rbuf.getpos(std::ios_base::in); + winhttp_context->m_startingPosition = rbuf.getpos(std::ios_base::in); // If we find ourselves here, we either don't know how large the message - totalLength = curl_context->m_bodyType == content_length_chunked ? (DWORD)content_length + totalLength = winhttp_context->m_bodyType == content_length_chunked ? (DWORD)content_length : WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH; } - const auto requestSuccess = WinHttpSendRequest(curl_context->m_request_handle, + const auto requestSuccess = WinHttpSendRequest(winhttp_context->m_request_handle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, nullptr, 0, totalLength, - (DWORD_PTR)curl_context->m_request_handle_context); + (DWORD_PTR)winhttp_context->m_request_handle_context); if (!requestSuccess) { auto errorCode = GetLastError(); - curl_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest")); + winhttp_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest")); } } // Helper function to query/read next part of response data from winhttp. - static void read_next_response_chunk(curl_request_context* pContext, DWORD bytesRead, bool firstRead = false) + static void read_next_response_chunk(winhttp_request_context* pContext, DWORD bytesRead, bool firstRead = false) { const bool defaultChunkSize = pContext->m_http_client->client_config().is_default_chunksize(); @@ -1130,7 +1140,7 @@ class curl_client final : public _http_client_communicator } } - static void _transfer_encoding_chunked_write_data(_In_ curl_request_context* p_request_context) + static void _transfer_encoding_chunked_write_data(_In_ winhttp_request_context* p_request_context) { size_t chunk_size; std::unique_ptr& compressor = p_request_context->m_request.compressor(); @@ -1146,11 +1156,11 @@ class curl_client final : public _http_client_communicator chunk_size = p_request_context->m_body_data.size() - http::details::chunked_encoding::additional_encoding_space; } - else if (p_request_context->m_remaining_to_write != std::numeric_limits::max()) + else if (p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { // Choose a semi-intelligent size based on how much total data is left to compress - chunk_size = std::min(static_cast(p_request_context->m_remaining_to_write) + 128, - p_request_context->m_http_client->client_config().chunksize()); + chunk_size = (std::min)(static_cast(p_request_context->m_remaining_to_write) + 128, + p_request_context->m_http_client->client_config().chunksize()); } else { @@ -1162,8 +1172,8 @@ class curl_client final : public _http_client_communicator { // We're not compressing; use the smaller of the remaining data (if known) and the configured (or default) // chunk size - chunk_size = std::min(static_cast(p_request_context->m_remaining_to_write), - p_request_context->m_http_client->client_config().chunksize()); + chunk_size = (std::min)(static_cast(p_request_context->m_remaining_to_write), + p_request_context->m_http_client->client_config().chunksize()); } p_request_context->allocate_request_space( nullptr, chunk_size + http::details::chunked_encoding::additional_encoding_space); @@ -1201,7 +1211,7 @@ class curl_client final : public _http_client_communicator chunk_size + http::details::chunked_encoding::additional_encoding_space, bytes_read); - if (!compressor && p_request_context->m_remaining_to_write != std::numeric_limits::max()) + if (!compressor && p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { if (bytes_read == 0 && p_request_context->m_remaining_to_write) { @@ -1256,7 +1266,6 @@ class curl_client final : public _http_client_communicator { return pplx::task_from_exception(std::current_exception()); } - // _ASSERTE(bytes_read >= 0); uint8_t* buffer = p_request_context->m_compression_state.m_acquired; if (buffer == nullptr) @@ -1296,7 +1305,7 @@ class curl_client final : public _http_client_communicator p_request_context->m_compression_state.m_bytes_read) { if (p_request_context->m_remaining_to_write && - p_request_context->m_remaining_to_write != std::numeric_limits::max()) + p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { // The stream ended earlier than we detected it should return pplx::task_from_exception(http_exception( @@ -1349,7 +1358,7 @@ class curl_client final : public _http_client_communicator p_request_context->m_compression_state.m_bytes_processed += r.input_bytes_processed; _ASSERTE(p_request_context->m_compression_state.m_bytes_processed <= p_request_context->m_compression_state.m_bytes_read); - if (p_request_context->m_remaining_to_write != std::numeric_limits::max()) + if (p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { _ASSERTE(p_request_context->m_remaining_to_write >= r.input_bytes_processed); p_request_context->m_remaining_to_write -= r.input_bytes_processed; @@ -1399,7 +1408,7 @@ class curl_client final : public _http_client_communicator return; } else if (p_request_context->m_remaining_to_write && - p_request_context->m_remaining_to_write != std::numeric_limits::max()) + p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { // Unexpected end-of-stream. p_request_context->report_error(GetLastError(), @@ -1417,8 +1426,8 @@ class curl_client final : public _http_client_communicator } else { - length = std::min(static_cast(p_request_context->m_remaining_to_write), - p_request_context->m_http_client->client_config().chunksize()); + length = (std::min)(static_cast(p_request_context->m_remaining_to_write), + p_request_context->m_http_client->client_config().chunksize()); if (p_request_context->m_compression_state.m_buffer.capacity() < length) { p_request_context->m_compression_state.m_buffer.reserve(length); @@ -1439,7 +1448,7 @@ class curl_client final : public _http_client_communicator } } - static void _multiple_segment_write_data(_In_ curl_request_context* p_request_context) + static void _multiple_segment_write_data(_In_ winhttp_request_context* p_request_context) { auto rbuf = p_request_context->_get_readbuffer(); msl::safeint3::SafeInt safeCount = p_request_context->m_remaining_to_write; @@ -1529,16 +1538,16 @@ class curl_client final : public _http_client_communicator } } - static std::string get_request_url(HINTERNET hRequestHandle) + static utility::string_t get_request_url(HINTERNET hRequestHandle) { - std::string url; + utility::string_t url; auto urlSize = static_cast(url.capacity()) * 2; // use initial small string optimization capacity for (;;) { url.resize(urlSize / sizeof(utility::char_t)); if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize)) { - url.resize(strlen(url.c_str())); + url.resize(url.length()); return url; } @@ -1555,7 +1564,7 @@ class curl_client final : public _http_client_communicator // Returns true if we handle successfully and resending the request // or false if we fail to handle. static bool handle_authentication_failure(HINTERNET hRequestHandle, - const std::shared_ptr& p_request_context, + const std::shared_ptr& p_request_context, _In_ DWORD error = 0) { http_request& request = p_request_context->m_request; @@ -1588,7 +1597,7 @@ class curl_client final : public _http_client_communicator } } } - p_request_context->m_compression_state = curl_request_context::compression_state(); + p_request_context->m_compression_state = winhttp_request_context::compression_state(); // If we got ERROR_WINHTTP_RESEND_REQUEST, the response header is not available, // we cannot call WinHttpQueryAuthSchemes and WinHttpSetCredentials. @@ -1606,7 +1615,7 @@ class curl_client final : public _http_client_communicator if (content_length > 0) { // There is a request body that needs to be transferred. - if (content_length == std::numeric_limits::max()) + if (content_length == (std::numeric_limits::max)()) { // The content length is unknown and the application set a stream. This is an // indication that we will need to chunk the data. @@ -1626,7 +1635,7 @@ class curl_client final : public _http_client_communicator } // We're good. - curl_client* winclnt = reinterpret_cast(p_request_context->m_http_client.get()); + winhttp_client* winclnt = reinterpret_cast(p_request_context->m_http_client.get()); winclnt->_start_request_send(p_request_context, content_length); // We will not complete the request. Instead wait for the response to the request that was resent @@ -1637,8 +1646,10 @@ class curl_client final : public _http_client_communicator static void CALLBACK completion_callback( HINTERNET hRequestHandle, DWORD_PTR context, DWORD statusCode, _In_ void* statusInfo, DWORD statusInfoLength) { - std::weak_ptr* p_weak_request_context = - reinterpret_cast*>(context); + (void)statusInfoLength; + + std::weak_ptr* p_weak_request_context = + reinterpret_cast*>(context); if (p_weak_request_context == nullptr) { @@ -1800,7 +1811,7 @@ class curl_client final : public _http_client_communicator } http_response& response = p_request_context->m_response; - parse_curl_headers(hRequestHandle, header_buffer, response); + parse_winhttp_headers(hRequestHandle, header_buffer, response); if (response.status_code() == status_codes::Unauthorized /*401*/ || response.status_code() == status_codes::ProxyAuthRequired /*407*/) @@ -1823,7 +1834,7 @@ class curl_client final : public _http_client_communicator !p_request_context->m_http_client->client_config().request_compressed_response()) { p_request_context->m_compression_state.m_chunk = - ::utility::details::make_unique(); + ::utility::details::make_unique(); p_request_context->m_compression_state.m_chunked = true; } @@ -1970,8 +1981,8 @@ class curl_client final : public _http_client_communicator if (p_request_context->m_decompressor) { - size_t chunk_size = std::max(static_cast(bytesRead), - p_request_context->m_http_client->client_config().chunksize()); + size_t chunk_size = (std::max)(static_cast(bytesRead), + p_request_context->m_http_client->client_config().chunksize()); p_request_context->m_compression_state.m_bytes_read = static_cast(bytesRead); p_request_context->m_compression_state.m_chunk_bytes = 0; @@ -1982,7 +1993,7 @@ class curl_client final : public _http_client_communicator // Oddly enough, WinHttp doesn't de-chunk for us if "chunked" isn't the only // encoding, so we need to do so on the fly as we process the received data auto process_buffer = - [chunk_size](curl_request_context* c, size_t bytes_produced, bool outer) -> bool { + [chunk_size](winhttp_request_context* c, size_t bytes_produced, bool outer) -> bool { if (!c->m_compression_state.m_chunk_bytes) { if (c->m_compression_state.m_chunked) @@ -2114,7 +2125,7 @@ class curl_client final : public _http_client_communicator .then([p_request_context, buffer, chunk_size, process_buffer]( pplx::task op) { auto r = op.get(); - auto keep_going = [&r, process_buffer](curl_request_context* c) -> pplx::task { + auto keep_going = [&r, process_buffer](winhttp_request_context* c) -> pplx::task { _ASSERTE(r.input_bytes_processed <= c->m_compression_state.m_chunk_bytes); c->m_compression_state.m_chunk_bytes -= r.input_bytes_processed; c->m_compression_state.m_bytes_processed += r.input_bytes_processed; @@ -2158,24 +2169,23 @@ class curl_client final : public _http_client_communicator return keep_going(p_request_context.get()); }); }); - }) - .then([p_request_context](pplx::task op) { - try - { - op.get(); - } - catch (...) + }).then([p_request_context](pplx::task op) { + try + { + op.get(); + } + catch (...) + { + // We're only here to pick up any exception that may have been thrown, and to clean up + // if needed + if (p_request_context->m_compression_state.m_acquired) { - // We're only here to pick up any exception that may have been thrown, and to clean up - // if needed - if (p_request_context->m_compression_state.m_acquired) - { - p_request_context->_get_writebuffer().commit(0); - p_request_context->m_compression_state.m_acquired = nullptr; - } - p_request_context->report_exception(std::current_exception()); + p_request_context->_get_writebuffer().commit(0); + p_request_context->m_compression_state.m_acquired = nullptr; } - }); + p_request_context->report_exception(std::current_exception()); + } + }); } else { @@ -2235,7 +2245,7 @@ class curl_client final : public _http_client_communicator std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri, http_client_config&& client_config) { - return std::make_shared(std::move(base_uri), std::move(client_config)); + return std::make_shared(std::move(base_uri), std::move(client_config)); } From df34d8ffc94ccb73d2eb0c65c0eb25603114bcf2 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 3 Sep 2019 00:19:46 +0000 Subject: [PATCH 36/41] Share parse_headers_string --- Release/src/http/client/http_client_impl.h | 4 +--- .../http/client/http_client_winhttppal.cpp | 22 +------------------ Release/src/http/common/http_msg.cpp | 20 ++++++++--------- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/Release/src/http/client/http_client_impl.h b/Release/src/http/client/http_client_impl.h index b3f9329e3a..d9e7d4829e 100644 --- a/Release/src/http/client/http_client_impl.h +++ b/Release/src/http/client/http_client_impl.h @@ -30,12 +30,10 @@ namespace details /// Serialize the http_headers into name:value pairs separated by a carriage return and line feed. /// utility::string_t flatten_http_headers(const http_headers& headers); -#if defined(_WIN32) /// /// Parses a string containing Http headers. /// -void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers); -#endif +void parse_headers_string(_Inout_z_ utility::char_t* headersStr, http_headers& headers); } // namespace details } // namespace http } // namespace web diff --git a/Release/src/http/client/http_client_winhttppal.cpp b/Release/src/http/client/http_client_winhttppal.cpp index 525d34f203..d17174cdc9 100644 --- a/Release/src/http/client/http_client_winhttppal.cpp +++ b/Release/src/http/client/http_client_winhttppal.cpp @@ -121,26 +121,6 @@ static utility::string_t parse_reason_phrase(HINTERNET request_handle) return phrase; } - -static void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers) -{ - utility::char_t* line = strtok (headersStr, "\r\n"); - while (line != nullptr) - { - const utility::string_t header_line(line); - const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":")); - if (colonIndex != utility::string_t::npos) - { - utility::string_t key = header_line.substr(0, colonIndex); - utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1); - web::http::details::trim_whitespace(key); - web::http::details::trim_whitespace(value); - headers.add(key, value); - } - line = strtok(nullptr, "\r\n"); - } -} - /// /// Parses a string containing HTTP headers. /// @@ -153,7 +133,7 @@ static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utility::char response.set_status_code(parse_status_code(request_handle)); response.set_reason_phrase(parse_reason_phrase(request_handle)); - parse_headers_string(headersStr, response.headers()); + web::http::details::parse_headers_string(headersStr, response.headers()); } // Helper function to build error messages. diff --git a/Release/src/http/common/http_msg.cpp b/Release/src/http/common/http_msg.cpp index c32dfcfaaa..a3c51c62a1 100644 --- a/Release/src/http/common/http_msg.cpp +++ b/Release/src/http/common/http_msg.cpp @@ -225,27 +225,27 @@ utility::string_t flatten_http_headers(const http_headers& headers) return flattened_headers; } -#if defined(_WIN32) -void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers) +void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers) { - utf16char* context = nullptr; - utf16char* line = wcstok_s(headersStr, CRLF, &context); - while (line != nullptr) + utility::string_t str(headersStr); + std::size_t pos = str.find_first_of(_XPLATSTR("\r\n")); + std::size_t startpos = 0; + while (pos!=std::string::npos) { - const utility::string_t header_line(line); + const utility::string_t header_line(str, startpos, pos - startpos); const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":")); if (colonIndex != utility::string_t::npos) { utility::string_t key = header_line.substr(0, colonIndex); utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1); - http::details::trim_whitespace(key); - http::details::trim_whitespace(value); + web::http::details::trim_whitespace(key); + web::http::details::trim_whitespace(value); headers.add(key, value); } - line = wcstok_s(nullptr, CRLF, &context); + startpos = pos + 1; + pos = str.find_first_of(_XPLATSTR("\r\n"), pos + 1); } } -#endif } // namespace details From 4332fe58e415a0ff6bfb4f567f323868cb7c23eb Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 3 Sep 2019 01:48:12 +0000 Subject: [PATCH 37/41] Implement auth scheme --- .../http/client/http_client_winhttppal.cpp | 287 +++++++++++++++++- 1 file changed, 273 insertions(+), 14 deletions(-) diff --git a/Release/src/http/client/http_client_winhttppal.cpp b/Release/src/http/client/http_client_winhttppal.cpp index d17174cdc9..fc2bfbae8c 100644 --- a/Release/src/http/client/http_client_winhttppal.cpp +++ b/Release/src/http/client/http_client_winhttppal.cpp @@ -18,11 +18,14 @@ #include "../common/internal_http_helpers.h" #include "cpprest/http_headers.h" #include "http_client_impl.h" +#ifdef WIN32 +#include +#endif +#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) #include "winhttppal.h" +#endif #include -#include -#define GlobalFree free #if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA) #include #endif @@ -99,7 +102,7 @@ static http::status_code parse_status_code(HINTERNET request_handle) &buffer[0], &length, WINHTTP_NO_HEADER_INDEX); - return (unsigned short)atoi((LPCTSTR)(buffer.c_str())); + return (unsigned short)stoi(buffer); } // Helper function to get the reason phrase from a WinHTTP response. @@ -555,7 +558,106 @@ class winhttp_request_context final : public request_context void on_send_request_validate_cn() { +#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) // we do the validation inside curl + return; +#else + if (m_customCnCheck.empty()) + { + // no custom validation selected; either we've delegated that to winhttp or + // certificate checking is completely disabled + return; + } + + winhttp_cert_context certContext; + DWORD bufferSize = sizeof(certContext.raw); + if (!WinHttpQueryOption(m_request_handle, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &certContext.raw, &bufferSize)) + { + auto errorCode = GetLastError(); + if (errorCode == HRESULT_CODE(WININET_E_INCORRECT_HANDLE_STATE)) + { + // typically happens when given a custom host with an initially HTTP connection + return; + } + + report_error(errorCode, + build_error_msg(errorCode, "WinHttpQueryOption WINHTTP_OPTION_SERVER_CERT_CONTEXT")); + cleanup(); + return; + } + + const auto encodedFirst = certContext.raw->pbCertEncoded; + const auto encodedLast = encodedFirst + certContext.raw->cbCertEncoded; + if (certContext.raw->cbCertEncoded == m_cachedEncodedCert.size() && + std::equal(encodedFirst, encodedLast, m_cachedEncodedCert.begin())) + { + // already validated OK + return; + } + + char oidPkixKpServerAuth[] = szOID_PKIX_KP_SERVER_AUTH; + char oidServerGatedCrypto[] = szOID_SERVER_GATED_CRYPTO; + char oidSgcNetscape[] = szOID_SGC_NETSCAPE; + char* chainUses[] = { + oidPkixKpServerAuth, + oidServerGatedCrypto, + oidSgcNetscape, + }; + + winhttp_cert_chain_context chainContext; + CERT_CHAIN_PARA chainPara = {sizeof(chainPara)}; + chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + chainPara.RequestedUsage.Usage.cUsageIdentifier = sizeof(chainUses) / sizeof(char*); + chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = chainUses; + + // note that the following chain only checks the end certificate; we + // assume WinHTTP already validated everything but the common name. + if (!CertGetCertificateChain(NULL, + certContext.raw, + nullptr, + certContext.raw->hCertStore, + &chainPara, + CERT_CHAIN_CACHE_END_CERT, + NULL, + &chainContext.raw)) + { + auto errorCode = GetLastError(); + report_error(errorCode, build_error_msg(errorCode, "CertGetCertificateChain")); + cleanup(); + return; + } + + HTTPSPolicyCallbackData policyData = { + {sizeof(policyData)}, + AUTHTYPE_SERVER, + // we assume WinHTTP already checked these: + 0x00000080 /* SECURITY_FLAG_IGNORE_REVOCATION */ + | 0x00000100 /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */ + | 0x00000200 /* SECURITY_FLAG_IGNORE_WRONG_USAGE */ + | 0x00002000 /* SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */, + &m_customCnCheck[0], + }; + CERT_CHAIN_POLICY_PARA policyPara = {sizeof(policyPara)}; + policyPara.pvExtraPolicyPara = &policyData; + + CERT_CHAIN_POLICY_STATUS policyStatus = {sizeof(policyStatus)}; + if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext.raw, &policyPara, &policyStatus)) + { + auto errorCode = GetLastError(); + report_error(errorCode, build_error_msg(errorCode, "CertVerifyCertificateChainPolicy")); + cleanup(); + return; + } + + if (policyStatus.dwError) + { + report_error(policyStatus.dwError, build_error_msg(policyStatus.dwError, "Incorrect common name")); + cleanup(); + return; + } + + m_cachedEncodedCert.assign(encodedFirst, encodedLast); +#endif } protected: @@ -586,6 +688,29 @@ class winhttp_request_context final : public request_context } }; +static DWORD ChooseAuthScheme(DWORD dwSupportedSchemes) +{ + // It is the server's responsibility only to accept + // authentication schemes that provide a sufficient + // level of security to protect the servers resources. + // + // The client is also obligated only to use an authentication + // scheme that adequately protects its username and password. + // + if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE) + return WINHTTP_AUTH_SCHEME_NEGOTIATE; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM) + return WINHTTP_AUTH_SCHEME_NTLM; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT) + return WINHTTP_AUTH_SCHEME_PASSPORT; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST) + return WINHTTP_AUTH_SCHEME_DIGEST; + else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_BASIC) + return WINHTTP_AUTH_SCHEME_BASIC; + else + return 0; +} + // Small RAII helper to ensure that the fields of this struct are always // properly freed. struct proxy_info : WINHTTP_PROXY_INFO @@ -678,8 +803,9 @@ class winhttp_client final : public _http_client_communicator ie_proxy_config proxyIE; DWORD access_type; - LPCTSTR proxy_name; + LPCTSTR proxy_name = WINHTTP_NO_PROXY_NAME; LPCTSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS; + m_proxy_auto_config = false; utility::string_t proxy_str; http::uri uri; @@ -692,15 +818,47 @@ class winhttp_client final : public _http_client_communicator else if (proxy.is_disabled()) { access_type = WINHTTP_ACCESS_TYPE_NO_PROXY; - proxy_name = WINHTTP_NO_PROXY_NAME; } - else if (config.proxy().is_default() || config.proxy().is_auto_discovery()) + else if (proxy.is_auto_discovery()) { - // Use the default WinHTTP proxy by default. - access_type = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; - proxy_name = WINHTTP_NO_PROXY_NAME; - m_proxy_auto_config = true; - access_type = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; + access_type = WinHttpDefaultProxyConstant(); + if (access_type != WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY) + { + // Windows 8 or earlier, do proxy autodetection ourselves + m_proxy_auto_config = true; + + proxy_info proxyDefault; + if (!WinHttpGetDefaultProxyConfiguration(&proxyDefault) || + proxyDefault.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY) + { + // ... then try to fall back on the default WinINET proxy, as + // recommended for the desktop applications (if we're not + // running under a user account, the function below will just + // fail, so there is no real need to check for this explicitly) + if (WinHttpGetIEProxyConfigForCurrentUser(&proxyIE)) + { + if (proxyIE.fAutoDetect) + { + m_proxy_auto_config = true; + } + else if (proxyIE.lpszAutoConfigUrl) + { + m_proxy_auto_config = true; + m_proxy_auto_config_url = proxyIE.lpszAutoConfigUrl; + } + else if (proxyIE.lpszProxy) + { + access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY; + proxy_name = proxyIE.lpszProxy; + + if (proxyIE.lpszProxyBypass) + { + proxy_bypass = proxyIE.lpszProxyBypass; + } + } + } + } + } } else { @@ -825,12 +983,35 @@ class winhttp_client final : public _http_client_communicator // and host won't resolve if (!::web::http::details::validate_method(method)) { - request->report_exception(http_exception("The method string is invalid.")); - return; + request->report_exception(http_exception("The method string is invalid.")); + return; } if (m_proxy_auto_config) { + WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {}; + if (m_proxy_auto_config_url.empty()) + { + autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } + else + { + autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; + autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str(); + } + + autoproxy_options.fAutoLogonIfChallenged = TRUE; + + auto result = WinHttpGetProxyForUrl(m_hSession, m_uri.to_string().c_str(), &autoproxy_options, &info); + if (result) + { + proxy_info_required = true; + } + else + { + // Failure to download the auto-configuration script is not fatal. Fall back to the default proxy. + } } // Need to form uri path, query, and fragment for this request. @@ -905,7 +1086,18 @@ class winhttp_client final : public _http_client_communicator DWORD ignoredCertificateValidationSteps = 0; if (client_config().validate_certificates()) { - // Revocation is enabled by default in WINHTTPPAL + // if we are validating certificates, also turn on revocation checking + DWORD dwEnableSSLRevocationOpt = WINHTTP_ENABLE_SSL_REVOCATION; + if (!WinHttpSetOption(winhttp_context->m_request_handle, + WINHTTP_OPTION_ENABLE_FEATURE, + &dwEnableSSLRevocationOpt, + sizeof(dwEnableSSLRevocationOpt))) + { + auto errorCode = GetLastError(); + request->report_error(errorCode, build_error_msg(errorCode, "Error enabling SSL revocation check")); + return; + } + // check if the user has overridden the desired Common Name with the host header const auto hostHeader = headers.find(_XPLATSTR("Host")); if (hostHeader != headers.end()) @@ -1581,6 +1773,73 @@ class winhttp_client final : public _http_client_communicator // If we got ERROR_WINHTTP_RESEND_REQUEST, the response header is not available, // we cannot call WinHttpQueryAuthSchemes and WinHttpSetCredentials. + if (error != ERROR_WINHTTP_RESEND_REQUEST) + { + // Obtain the supported and preferred schemes. + DWORD dwSupportedSchemes; + DWORD dwFirstScheme; + DWORD dwAuthTarget; + if (!WinHttpQueryAuthSchemes(hRequestHandle, &dwSupportedSchemes, &dwFirstScheme, &dwAuthTarget)) + { + // This will return the authentication failure to the user, without reporting fatal errors + return false; + } + + DWORD dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes); + if (dwSelectedScheme == 0) + { + // This will return the authentication failure to the user, without reporting fatal errors + return false; + } + + credentials cred; + if (dwAuthTarget == WINHTTP_AUTH_TARGET_SERVER && !p_request_context->m_server_authentication_tried) + { + cred = p_request_context->m_http_client->client_config().credentials(); + p_request_context->m_server_authentication_tried = true; + } + else if (dwAuthTarget == WINHTTP_AUTH_TARGET_PROXY) + { + bool is_redirect = false; + try + { + web::uri current_uri(get_request_url(hRequestHandle)); + is_redirect = p_request_context->m_request.absolute_uri().to_string() != current_uri.to_string(); + } + catch (const std::exception&) + { + } + + // If we have been redirected, then WinHttp needs the proxy credentials again to make the next request + // leg (which may be on a different server) + if (is_redirect || !p_request_context->m_proxy_authentication_tried) + { + cred = p_request_context->m_http_client->client_config().proxy().credentials(); + p_request_context->m_proxy_authentication_tried = true; + } + } + + // No credentials found so can't resend. + if (!cred.is_set()) + { + return false; + } + + // New scope to ensure plaintext password is cleared as soon as possible. + { + auto password = cred._internal_decrypt(); + if (!WinHttpSetCredentials(hRequestHandle, + dwAuthTarget, + dwSelectedScheme, + cred.username().c_str(), + password->c_str(), + nullptr)) + { + return false; + } + } + } + // Reset the request body type since it might have already started sending. size_t content_length; try From fce24809ae6eef124c3267e86dc9eb67fe5528b5 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 3 Sep 2019 01:49:55 +0000 Subject: [PATCH 38/41] Remove http_client_winhttppal.cpp --- Release/src/CMakeLists.txt | 2 +- .../src/http/client/http_client_winhttp.cpp | 68 +- .../http/client/http_client_winhttppal.cpp | 2494 ----------------- 3 files changed, 46 insertions(+), 2518 deletions(-) delete mode 100644 Release/src/http/client/http_client_winhttppal.cpp diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt index c5a1c1a0e6..c9d0349a87 100644 --- a/Release/src/CMakeLists.txt +++ b/Release/src/CMakeLists.txt @@ -136,7 +136,7 @@ elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttppal") cpprest_find_openssl() cpprest_find_winhttppal() target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) - target_sources(cpprest PRIVATE http/client/http_client_winhttppal.cpp http/client/x509_cert_utilities.cpp) + target_sources(cpprest PRIVATE http/client/http_client_winhttp.cpp http/client/x509_cert_utilities.cpp) target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal cpprestsdk_winhttppal_internal) elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttp") target_link_libraries(cpprest PRIVATE diff --git a/Release/src/http/client/http_client_winhttp.cpp b/Release/src/http/client/http_client_winhttp.cpp index f4b97f509a..fc2bfbae8c 100644 --- a/Release/src/http/client/http_client_winhttp.cpp +++ b/Release/src/http/client/http_client_winhttp.cpp @@ -18,10 +18,15 @@ #include "../common/internal_http_helpers.h" #include "cpprest/http_headers.h" #include "http_client_impl.h" +#ifdef WIN32 #include +#endif +#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) +#include "winhttppal.h" +#endif #include -#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +#if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA) #include #endif @@ -97,7 +102,7 @@ static http::status_code parse_status_code(HINTERNET request_handle) &buffer[0], &length, WINHTTP_NO_HEADER_INDEX); - return (unsigned short)_wtoi(buffer.c_str()); + return (unsigned short)stoi(buffer); } // Helper function to get the reason phrase from a WinHTTP response. @@ -122,7 +127,7 @@ static utility::string_t parse_reason_phrase(HINTERNET request_handle) /// /// Parses a string containing HTTP headers. /// -static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utf16char* headersStr, http_response& response) +static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utility::char_t* headersStr, http_response& response) { // Clear the header map for each new response; otherwise, the header values will be combined. response.headers().clear(); @@ -141,7 +146,7 @@ static std::string build_error_msg(unsigned long code, const std::string& locati msg.append(": "); msg.append(std::to_string(code)); msg.append(": "); - msg.append(utility::details::windows_category().message(code)); + msg.append(utility::details::platform_category().message(static_cast(code))); return msg; } @@ -159,6 +164,7 @@ static std::string build_error_msg(_In_ WINHTTP_ASYNC_RESULT* error_result) } } + class memory_holder { uint8_t* m_externalData; @@ -289,7 +295,7 @@ class winhttp_request_context final : public request_context { } -#if defined(_MSC_VER) && _MSC_VER < 1900 +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) compression_state(const compression_state&) = delete; compression_state(compression_state&& other) : m_buffer(std::move(other.m_buffer)) @@ -552,6 +558,10 @@ class winhttp_request_context final : public request_context void on_send_request_validate_cn() { +#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) + // we do the validation inside curl + return; +#else if (m_customCnCheck.empty()) { // no custom validation selected; either we've delegated that to winhttp or @@ -647,6 +657,7 @@ class winhttp_request_context final : public request_context } m_cachedEncodedCert.assign(encodedFirst, encodedLast); +#endif } protected: @@ -666,13 +677,13 @@ class winhttp_request_context final : public request_context winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) : request_context(client, request) , m_request_handle(nullptr) - , m_bodyType(no_body) - , m_startingPosition(std::char_traits::eof()) - , m_body_data() - , m_remaining_to_write(0) , m_proxy_authentication_tried(false) , m_server_authentication_tried(false) + , m_bodyType(no_body) + , m_remaining_to_write(0) + , m_startingPosition(std::char_traits::eof()) , m_readStream(request.body()) + , m_body_data() { } }; @@ -731,10 +742,10 @@ class winhttp_client final : public _http_client_communicator public: winhttp_client(http::uri address, http_client_config client_config) : _http_client_communicator(std::move(address), std::move(client_config)) - , m_secure(m_uri.scheme() == _XPLATSTR("https")) , m_opened(false) , m_hSession(nullptr) , m_hConnection(nullptr) + , m_secure(m_uri.scheme() == _XPLATSTR("https")) { } @@ -752,7 +763,7 @@ class winhttp_client final : public _http_client_communicator if (m_hSession != nullptr) { // Unregister the callback. - WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL); + WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0); WinHttpCloseHandle(m_hSession); } @@ -792,8 +803,8 @@ class winhttp_client final : public _http_client_communicator ie_proxy_config proxyIE; DWORD access_type; - LPCWSTR proxy_name = WINHTTP_NO_PROXY_NAME; - LPCWSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS; + LPCTSTR proxy_name = WINHTTP_NO_PROXY_NAME; + LPCTSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS; m_proxy_auto_config = false; utility::string_t proxy_str; http::uri uri; @@ -901,7 +912,7 @@ class winhttp_client final : public _http_client_communicator } // Enable TLS 1.1 and 1.2 -#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) BOOL win32_result(FALSE); DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | @@ -965,6 +976,17 @@ class winhttp_client final : public _http_client_communicator proxy_info info; bool proxy_info_required = false; + const auto& method = msg.method(); + + // stop injection of headers via method + // resource should be ok, since it's been encoded + // and host won't resolve + if (!::web::http::details::validate_method(method)) + { + request->report_exception(http_exception("The method string is invalid.")); + return; + } + if (m_proxy_auto_config) { WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {}; @@ -1416,7 +1438,6 @@ class winhttp_client final : public _http_client_communicator { return pplx::task_from_exception(std::current_exception()); } - _ASSERTE(bytes_read >= 0); uint8_t* buffer = p_request_context->m_compression_state.m_acquired; if (buffer == nullptr) @@ -1689,16 +1710,16 @@ class winhttp_client final : public _http_client_communicator } } - static std::wstring get_request_url(HINTERNET hRequestHandle) + static utility::string_t get_request_url(HINTERNET hRequestHandle) { - std::wstring url; + utility::string_t url; auto urlSize = static_cast(url.capacity()) * 2; // use initial small string optimization capacity for (;;) { - url.resize(urlSize / sizeof(wchar_t)); - if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], &urlSize)) + url.resize(urlSize / sizeof(utility::char_t)); + if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize)) { - url.resize(wcslen(url.c_str())); + url.resize(url.length()); return url; } @@ -2014,7 +2035,7 @@ class winhttp_client final : public _http_client_communicator // Now allocate buffer for headers and query for them. std::vector header_raw_buffer; header_raw_buffer.resize(headerBufferLength); - utf16char* header_buffer = reinterpret_cast(&header_raw_buffer[0]); + utility::char_t* header_buffer = reinterpret_cast(&header_raw_buffer[0]); if (!WinHttpQueryHeaders(hRequestHandle, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, @@ -2052,7 +2073,7 @@ class winhttp_client final : public _http_client_communicator !p_request_context->m_http_client->client_config().request_compressed_response()) { p_request_context->m_compression_state.m_chunk = - std::make_unique(); + ::utility::details::make_unique(); p_request_context->m_compression_state.m_chunked = true; } @@ -2390,7 +2411,7 @@ class winhttp_client final : public _http_client_communicator }).then([p_request_context](pplx::task op) { try { - bool ignored = op.get(); + op.get(); } catch (...) { @@ -2466,6 +2487,7 @@ std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage( return std::make_shared(std::move(base_uri), std::move(client_config)); } + } // namespace details } // namespace client } // namespace http diff --git a/Release/src/http/client/http_client_winhttppal.cpp b/Release/src/http/client/http_client_winhttppal.cpp deleted file mode 100644 index fc2bfbae8c..0000000000 --- a/Release/src/http/client/http_client_winhttppal.cpp +++ /dev/null @@ -1,2494 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * HTTP Library: Client-side APIs. - * - * This file contains the implementation for Windows Desktop, based on WinHTTP. - * - * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ -#include "stdafx.h" - -#include "../common/x509_cert_utilities.h" -#include "../common/internal_http_helpers.h" -#include "cpprest/http_headers.h" -#include "http_client_impl.h" -#ifdef WIN32 -#include -#endif -#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) -#include "winhttppal.h" -#endif -#include - -#if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA) -#include -#endif - -namespace -{ -struct security_failure_message -{ - std::uint32_t flag; - const char* text; -}; - -CPPREST_CONSTEXPR security_failure_message g_security_failure_messages[] = { - {WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED, - "WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED failed to check revocation status."}, - {WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT, - "WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT SSL certificate is invalid."}, - {WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED, - "WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED SSL certificate was revoked."}, - {WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA, "WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA SSL invalid CA."}, - {WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID, - "WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID SSL common name does not match."}, - {WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID, - "WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID SLL certificate is expired."}, - {WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR, - "WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR internal error."}, -}; - -std::string generate_security_failure_message(std::uint32_t flags) -{ - std::string result("SSL Error:"); - for (const auto& message : g_security_failure_messages) - { - if (flags & message.flag) - { - result.push_back(' '); - result.append(message.text); - } - } - - return result; -} - -} // unnamed namespace -namespace web -{ -namespace http -{ -namespace client -{ -namespace details -{ -// Helper function to query for the size of header values. -static void query_header_length(HINTERNET request_handle, DWORD header, DWORD& length) -{ - WinHttpQueryHeaders(request_handle, - header, - WINHTTP_HEADER_NAME_BY_INDEX, - WINHTTP_NO_OUTPUT_BUFFER, - &length, - WINHTTP_NO_HEADER_INDEX); -} - -// Helper function to get the status code from a WinHTTP response. -static http::status_code parse_status_code(HINTERNET request_handle) -{ - DWORD length = 0; - query_header_length(request_handle, WINHTTP_QUERY_STATUS_CODE, length); - utility::string_t buffer; - buffer.resize(length); - WinHttpQueryHeaders(request_handle, - WINHTTP_QUERY_STATUS_CODE, - WINHTTP_HEADER_NAME_BY_INDEX, - &buffer[0], - &length, - WINHTTP_NO_HEADER_INDEX); - return (unsigned short)stoi(buffer); -} - -// Helper function to get the reason phrase from a WinHTTP response. -static utility::string_t parse_reason_phrase(HINTERNET request_handle) -{ - utility::string_t phrase; - DWORD length = 0; - - query_header_length(request_handle, WINHTTP_QUERY_STATUS_TEXT, length); - phrase.resize(length); - WinHttpQueryHeaders(request_handle, - WINHTTP_QUERY_STATUS_TEXT, - WINHTTP_HEADER_NAME_BY_INDEX, - &phrase[0], - &length, - WINHTTP_NO_HEADER_INDEX); - // WinHTTP reports back the wrong length, trim any null characters. - ::web::http::details::trim_nulls(phrase); - return phrase; -} - -/// -/// Parses a string containing HTTP headers. -/// -static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utility::char_t* headersStr, http_response& response) -{ - // Clear the header map for each new response; otherwise, the header values will be combined. - response.headers().clear(); - - // Status code and reason phrase. - response.set_status_code(parse_status_code(request_handle)); - response.set_reason_phrase(parse_reason_phrase(request_handle)); - - web::http::details::parse_headers_string(headersStr, response.headers()); -} - -// Helper function to build error messages. -static std::string build_error_msg(unsigned long code, const std::string& location) -{ - std::string msg(location); - msg.append(": "); - msg.append(std::to_string(code)); - msg.append(": "); - msg.append(utility::details::platform_category().message(static_cast(code))); - return msg; -} - -// Helper function to build an error message from a WinHTTP async result. -static std::string build_error_msg(_In_ WINHTTP_ASYNC_RESULT* error_result) -{ - switch (error_result->dwResult) - { - case API_RECEIVE_RESPONSE: return build_error_msg(error_result->dwError, "WinHttpReceiveResponse"); - case API_QUERY_DATA_AVAILABLE: return build_error_msg(error_result->dwError, "WinHttpQueryDataAvaliable"); - case API_READ_DATA: return build_error_msg(error_result->dwError, "WinHttpReadData"); - case API_WRITE_DATA: return build_error_msg(error_result->dwError, "WinHttpWriteData"); - case API_SEND_REQUEST: return build_error_msg(error_result->dwError, "WinHttpSendRequest"); - default: return build_error_msg(error_result->dwError, "Unknown WinHTTP Function"); - } -} - - -class memory_holder -{ - uint8_t* m_externalData; - std::vector m_internalData; - size_t m_size; - -public: - memory_holder() : m_externalData(nullptr), m_size(0) {} - - void allocate_space(size_t length) - { - if (length > m_internalData.size()) - { - m_internalData.resize(length); - } - m_externalData = nullptr; - } - - inline void reassign_to(_In_opt_ uint8_t* block, size_t length) - { - assert(block != nullptr); - m_externalData = block; - m_size = length; - } - - inline bool is_internally_allocated() const { return m_externalData == nullptr; } - - inline uint8_t* get() { return is_internally_allocated() ? &m_internalData[0] : m_externalData; } - - inline size_t size() const { return is_internally_allocated() ? m_internalData.size() : m_size; } -}; - -// Possible ways a message body can be sent/received. -enum msg_body_type -{ - no_body, - content_length_chunked, - transfer_encoding_chunked -}; - -static DWORD WinHttpDefaultProxyConstant() CPPREST_NOEXCEPT -{ -#if _WIN32_WINNT >= _WIN32_WINNT_VISTA -#if _WIN32_WINNT < _WIN32_WINNT_WINBLUE - if (!IsWindows8Point1OrGreater()) - { - // Not Windows 8.1 or later, use the default proxy setting - return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; - } -#endif // _WIN32_WINNT < _WIN32_WINNT_WINBLUE - - // Windows 8.1 or later, use the automatic proxy setting - return WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; -#else // ^^^ _WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ // vvv _WIN32_WINNT < _WIN32_WINNT_VISTA vvv - return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; -#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA -} - -// Additional information necessary to track a WinHTTP request. -class winhttp_request_context final : public request_context -{ -public: - // Factory function to create requests on the heap. - static std::shared_ptr create_request_context( - const std::shared_ptr<_http_client_communicator>& client, const http_request& request) - { - std::shared_ptr ret(new winhttp_request_context(client, request)); - ret->m_self_reference = ret; - return std::move(ret); - } - - ~winhttp_request_context() { cleanup(); } - - void allocate_request_space(_In_opt_ uint8_t* block, size_t length) - { - if (block == nullptr) - m_body_data.allocate_space(length); - else - m_body_data.reassign_to(block, length); - } - - void allocate_reply_space(_In_opt_ uint8_t* block, size_t length) - { - if (block == nullptr) - m_body_data.allocate_space(length); - else - m_body_data.reassign_to(block, length); - } - - bool is_externally_allocated() const { return !m_body_data.is_internally_allocated(); } - - HINTERNET m_request_handle; - std::weak_ptr* - m_request_handle_context; // owned by m_request_handle to be deleted by WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING - - bool m_proxy_authentication_tried; - bool m_server_authentication_tried; - - msg_body_type m_bodyType; - - utility::size64_t m_remaining_to_write; - - std::char_traits::pos_type m_startingPosition; - - // If the user specified that to guarantee data buffering of request data, in case of challenged authentication - // requests, etc... Then if the request stream buffer doesn't support seeking we need to copy the body chunks as it - // is sent. - concurrency::streams::istream m_readStream; - std::unique_ptr>> m_readBufferCopy; - virtual concurrency::streams::streambuf _get_readbuffer() { return m_readStream.streambuf(); } - - // This self reference will keep us alive until finish() is called. - std::shared_ptr m_self_reference; - memory_holder m_body_data; - - // Compress/decompress-related processing state lives here - class compression_state - { - public: - compression_state() - : m_acquired(nullptr) - , m_bytes_read(0) - , m_bytes_processed(0) - , m_needs_flush(false) - , m_started(false) - , m_done(false) - , m_chunked(false) - { - } - -#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) - compression_state(const compression_state&) = delete; - compression_state(compression_state&& other) - : m_buffer(std::move(other.m_buffer)) - , m_acquired(other.m_acquired) - , m_bytes_read(other.m_bytes_read) - , m_bytes_processed(other.m_bytes_processed) - , m_needs_flush(other.m_needs_flush) - , m_started(other.m_started) - , m_done(other.m_done) - , m_chunked(other.m_chunked) - , m_chunk_bytes(other.m_chunk_bytes) - , m_chunk(std::move(other.m_chunk)) - { - } - compression_state& operator=(const compression_state&) = delete; - compression_state& operator=(compression_state&& other) - { - m_buffer = std::move(other.m_buffer); - m_acquired = other.m_acquired; - m_bytes_read = other.m_bytes_read; - m_bytes_processed = other.m_bytes_processed; - m_needs_flush = other.m_needs_flush; - m_started = other.m_started; - m_done = other.m_done; - m_chunked = other.m_chunked; - m_chunk_bytes = other.m_chunk_bytes; - m_chunk = std::move(other.m_chunk); - return *this; - } -#endif // defined(_MSC_VER) && _MSC_VER < 1900 - - // Minimal state for on-the-fly decoding of "chunked" encoded data - class _chunk_helper - { - public: - _chunk_helper() - : m_bytes_remaining(0) - , m_chunk_size(true) - , m_chunk_delim(false) - , m_expect_linefeed(false) - , m_ignore(false) - , m_trailer(false) - { - } - - // Returns true if the end of chunked data has been reached, specifically whether the 0-length - // chunk and its trailing delimiter has been processed. Otherwise, offset and length bound the - // portion of buffer that represents a contiguous (and possibly partial) chunk of consumable - // data; offset+length is the total number of bytes processed from the buffer on this pass. - bool process_buffer(uint8_t* buffer, size_t buffer_size, size_t& offset, size_t& length) - { - bool done = false; - size_t n = 0; - size_t l = 0; - - while (n < buffer_size) - { - if (m_ignore) - { - if (m_expect_linefeed) - { - _ASSERTE(m_chunk_delim && m_trailer); - if (buffer[n] != '\n') - { - // The data stream does not conform to "chunked" encoding - throw http_exception(status_codes::BadRequest, "Transfer-Encoding malformed trailer"); - } - - // Look for further trailer fields or the end of the stream - m_expect_linefeed = false; - m_trailer = false; - } - else if (buffer[n] == '\r') - { - if (!m_trailer) - { - // We're at the end of the data we need to ignore - _ASSERTE(m_chunk_size || m_chunk_delim); - m_ignore = false; - m_chunk_delim = false; // this is only set if we're at the end of the message - } // else we're at the end of a trailer field - m_expect_linefeed = true; - } - else if (m_chunk_delim) - { - // We're processing (and ignoring) a trailer field - m_trailer = true; - } - } - else if (m_expect_linefeed) - { - // We've already seen a carriage return; confirm the linefeed - if (buffer[n] != '\n') - { - // The data stream does not conform to "chunked" encoding - throw http_exception(status_codes::BadRequest, "Transfer-Encoding malformed delimiter"); - } - if (m_chunk_size) - { - if (!m_bytes_remaining) - { - // We're processing the terminating "empty" chunk; there's - // no data, we just need to confirm the final chunk delimiter, - // possibly ignoring a trailer part along the way - m_ignore = true; - m_chunk_delim = true; - } // else we move on to the chunk data itself - m_chunk_size = false; - } - else - { - // Now we move on to the next chunk size - _ASSERTE(!m_bytes_remaining); - if (m_chunk_delim) - { - // We expect a chunk size next - m_chunk_size = true; - } - else - { - // We just processed the end-of-input delimiter - done = true; - } - m_chunk_delim = false; - } - m_expect_linefeed = false; - } - else if (m_chunk_delim) - { - // We're processing a post-chunk delimiter - if (buffer[n] != '\r') - { - // The data stream does not conform to "chunked" encoding - throw http_exception(status_codes::BadRequest, - "Transfer-Encoding malformed chunk delimiter"); - } - - // We found the carriage return; look for the linefeed - m_expect_linefeed = true; - } - else if (m_chunk_size) - { - // We're processing an ASCII hexadecimal chunk size - if (buffer[n] >= 'a' && buffer[n] <= 'f') - { - m_bytes_remaining *= 16; - m_bytes_remaining += 10 + buffer[n] - 'a'; - } - else if (buffer[n] >= 'A' && buffer[n] <= 'F') - { - m_bytes_remaining *= 16; - m_bytes_remaining += 10 + buffer[n] - 'A'; - } - else if (buffer[n] >= '0' && buffer[n] <= '9') - { - m_bytes_remaining *= 16; - m_bytes_remaining += buffer[n] - '0'; - } - else if (buffer[n] == '\r') - { - // We've reached the end of the size, and there's no chunk extension - m_expect_linefeed = true; - } - else if (buffer[n] == ';') - { - // We've reached the end of the size, and there's a chunk extension; - // we don't support extensions, so we ignore them per RFC - m_ignore = true; - } - else - { - // The data stream does not conform to "chunked" encoding - throw http_exception(status_codes::BadRequest, - "Transfer-Encoding malformed chunk size or extension"); - } - } - else - { - if (m_bytes_remaining) - { - // We're at the offset of a chunk of consumable data; let the caller process it - l = (std::min)(m_bytes_remaining, buffer_size - n); - m_bytes_remaining -= l; - if (!m_bytes_remaining) - { - // We're moving on to the post-chunk delimiter - m_chunk_delim = true; - } - } - else - { - // We've previously processed the terminating empty chunk and its - // trailing delimiter; skip the entire buffer, and inform the caller - n = buffer_size; - done = true; - } - - // Let the caller process the result - break; - } - - // Move on to the next byte - n++; - } - - offset = n; - length = l; - return buffer_size ? done : (!m_bytes_remaining && !m_chunk_size && !m_chunk_delim); - } - - private: - size_t m_bytes_remaining; // the number of bytes remaining in the chunk we're currently processing - bool m_chunk_size; // if true, we're processing a chunk size or its trailing delimiter - bool m_chunk_delim; // if true, we're processing a delimiter between a chunk and the next chunk's size - bool m_expect_linefeed; // if true, we're processing a delimiter, and we've already seen its carriage return - bool m_ignore; // if true, we're processing a chunk extension or trailer, which we don't support - bool m_trailer; // if true, we're processing (and ignoring) a trailer field; m_ignore is also true - }; - - std::vector m_buffer; // we read data from the stream into this before compressing - uint8_t* m_acquired; // we use this in place of m_buffer if the stream has directly-accessible data available - size_t m_bytes_read; // we most recently read this many bytes, which may be less than m_buffer.size() - size_t m_bytes_processed; // we've compressed this many bytes of m_bytes_read so far - bool m_needs_flush; // we've read and compressed all bytes, but the compressor still has compressed bytes to - // give us - bool m_started; // we've sent at least some number of bytes to m_decompressor - bool m_done; // we've read, compressed, and consumed all bytes - bool m_chunked; // if true, we need to decode and decompress a transfer-encoded message - size_t m_chunk_bytes; // un-decompressed bytes remaining in the most-recently-obtained data from m_chunk - std::unique_ptr<_chunk_helper> m_chunk; - } m_compression_state; - - void cleanup() - { - if (m_compression_state.m_acquired != nullptr) - { - // We may still hold a piece of the buffer if we encountered an exception; release it here - if (m_decompressor) - { - _get_writebuffer().commit(0); - } - else - { - _get_readbuffer().release(m_compression_state.m_acquired, m_compression_state.m_bytes_processed); - } - m_compression_state.m_acquired = nullptr; - } - - if (m_request_handle != nullptr) - { - WinHttpCloseHandle(m_request_handle); - } - } - - void install_custom_cn_check(const utility::string_t& customHost) - { - m_customCnCheck = customHost; - utility::details::inplace_tolower(m_customCnCheck); - } - - void on_send_request_validate_cn() - { -#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) - // we do the validation inside curl - return; -#else - if (m_customCnCheck.empty()) - { - // no custom validation selected; either we've delegated that to winhttp or - // certificate checking is completely disabled - return; - } - - winhttp_cert_context certContext; - DWORD bufferSize = sizeof(certContext.raw); - if (!WinHttpQueryOption(m_request_handle, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &certContext.raw, &bufferSize)) - { - auto errorCode = GetLastError(); - if (errorCode == HRESULT_CODE(WININET_E_INCORRECT_HANDLE_STATE)) - { - // typically happens when given a custom host with an initially HTTP connection - return; - } - - report_error(errorCode, - build_error_msg(errorCode, "WinHttpQueryOption WINHTTP_OPTION_SERVER_CERT_CONTEXT")); - cleanup(); - return; - } - - const auto encodedFirst = certContext.raw->pbCertEncoded; - const auto encodedLast = encodedFirst + certContext.raw->cbCertEncoded; - if (certContext.raw->cbCertEncoded == m_cachedEncodedCert.size() && - std::equal(encodedFirst, encodedLast, m_cachedEncodedCert.begin())) - { - // already validated OK - return; - } - - char oidPkixKpServerAuth[] = szOID_PKIX_KP_SERVER_AUTH; - char oidServerGatedCrypto[] = szOID_SERVER_GATED_CRYPTO; - char oidSgcNetscape[] = szOID_SGC_NETSCAPE; - char* chainUses[] = { - oidPkixKpServerAuth, - oidServerGatedCrypto, - oidSgcNetscape, - }; - - winhttp_cert_chain_context chainContext; - CERT_CHAIN_PARA chainPara = {sizeof(chainPara)}; - chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; - chainPara.RequestedUsage.Usage.cUsageIdentifier = sizeof(chainUses) / sizeof(char*); - chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = chainUses; - - // note that the following chain only checks the end certificate; we - // assume WinHTTP already validated everything but the common name. - if (!CertGetCertificateChain(NULL, - certContext.raw, - nullptr, - certContext.raw->hCertStore, - &chainPara, - CERT_CHAIN_CACHE_END_CERT, - NULL, - &chainContext.raw)) - { - auto errorCode = GetLastError(); - report_error(errorCode, build_error_msg(errorCode, "CertGetCertificateChain")); - cleanup(); - return; - } - - HTTPSPolicyCallbackData policyData = { - {sizeof(policyData)}, - AUTHTYPE_SERVER, - // we assume WinHTTP already checked these: - 0x00000080 /* SECURITY_FLAG_IGNORE_REVOCATION */ - | 0x00000100 /* SECURITY_FLAG_IGNORE_UNKNOWN_CA */ - | 0x00000200 /* SECURITY_FLAG_IGNORE_WRONG_USAGE */ - | 0x00002000 /* SECURITY_FLAG_IGNORE_CERT_DATE_INVALID */, - &m_customCnCheck[0], - }; - CERT_CHAIN_POLICY_PARA policyPara = {sizeof(policyPara)}; - policyPara.pvExtraPolicyPara = &policyData; - - CERT_CHAIN_POLICY_STATUS policyStatus = {sizeof(policyStatus)}; - if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, chainContext.raw, &policyPara, &policyStatus)) - { - auto errorCode = GetLastError(); - report_error(errorCode, build_error_msg(errorCode, "CertVerifyCertificateChainPolicy")); - cleanup(); - return; - } - - if (policyStatus.dwError) - { - report_error(policyStatus.dwError, build_error_msg(policyStatus.dwError, "Incorrect common name")); - cleanup(); - return; - } - - m_cachedEncodedCert.assign(encodedFirst, encodedLast); -#endif - } - -protected: - virtual void finish() override - { - request_context::finish(); - assert(m_self_reference != nullptr); - auto dereference_self = std::move(m_self_reference); - // As the stack frame cleans up, this will be deleted if no other references exist. - } - -private: - utility::string_t m_customCnCheck; - std::vector m_cachedEncodedCert; - - // Can only create on the heap using factory function. - winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) - : request_context(client, request) - , m_request_handle(nullptr) - , m_proxy_authentication_tried(false) - , m_server_authentication_tried(false) - , m_bodyType(no_body) - , m_remaining_to_write(0) - , m_startingPosition(std::char_traits::eof()) - , m_readStream(request.body()) - , m_body_data() - { - } -}; - -static DWORD ChooseAuthScheme(DWORD dwSupportedSchemes) -{ - // It is the server's responsibility only to accept - // authentication schemes that provide a sufficient - // level of security to protect the servers resources. - // - // The client is also obligated only to use an authentication - // scheme that adequately protects its username and password. - // - if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE) - return WINHTTP_AUTH_SCHEME_NEGOTIATE; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM) - return WINHTTP_AUTH_SCHEME_NTLM; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT) - return WINHTTP_AUTH_SCHEME_PASSPORT; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST) - return WINHTTP_AUTH_SCHEME_DIGEST; - else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_BASIC) - return WINHTTP_AUTH_SCHEME_BASIC; - else - return 0; -} - -// Small RAII helper to ensure that the fields of this struct are always -// properly freed. -struct proxy_info : WINHTTP_PROXY_INFO -{ - proxy_info() { memset(this, 0, sizeof(WINHTTP_PROXY_INFO)); } - - ~proxy_info() - { - if (lpszProxy) ::GlobalFree(lpszProxy); - if (lpszProxyBypass) ::GlobalFree(lpszProxyBypass); - } -}; - -struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG -{ - ie_proxy_config() { memset(this, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG)); } - - ~ie_proxy_config() - { - if (lpszAutoConfigUrl) ::GlobalFree(lpszAutoConfigUrl); - if (lpszProxy) ::GlobalFree(lpszProxy); - if (lpszProxyBypass) ::GlobalFree(lpszProxyBypass); - } -}; - -// WinHTTP client. -class winhttp_client final : public _http_client_communicator -{ -public: - winhttp_client(http::uri address, http_client_config client_config) - : _http_client_communicator(std::move(address), std::move(client_config)) - , m_opened(false) - , m_hSession(nullptr) - , m_hConnection(nullptr) - , m_secure(m_uri.scheme() == _XPLATSTR("https")) - { - } - - winhttp_client(const winhttp_client&) = delete; - winhttp_client& operator=(const winhttp_client&) = delete; - - // Closes session. - ~winhttp_client() - { - if (m_hConnection != nullptr) - { - WinHttpCloseHandle(m_hConnection); - } - - if (m_hSession != nullptr) - { - // Unregister the callback. - WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0); - - WinHttpCloseHandle(m_hSession); - } - } - - virtual pplx::task propagate(http_request request) override - { - auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this()); - auto context = details::winhttp_request_context::create_request_context(self, request); - - // Use a task to externally signal the final result and completion of the task. - auto result_task = pplx::create_task(context->m_request_completion); - - // Asynchronously send the response with the HTTP client implementation. - this->async_send_request(context); - - return result_task; - } - -protected: - // Open session and connection with the server. - unsigned long open() - { - if (m_opened) - { - return 0; - } - - pplx::extensibility::scoped_critical_section_t l(m_client_lock); - if (m_opened) - { - return 0; - } - - // This object have lifetime greater than proxy_name and proxy_bypass - // which may point to its elements. - ie_proxy_config proxyIE; - - DWORD access_type; - LPCTSTR proxy_name = WINHTTP_NO_PROXY_NAME; - LPCTSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS; - m_proxy_auto_config = false; - utility::string_t proxy_str; - http::uri uri; - - const auto& config = client_config(); - const auto& proxy = config.proxy(); - if (proxy.is_default()) - { - access_type = WinHttpDefaultProxyConstant(); - } - else if (proxy.is_disabled()) - { - access_type = WINHTTP_ACCESS_TYPE_NO_PROXY; - } - else if (proxy.is_auto_discovery()) - { - access_type = WinHttpDefaultProxyConstant(); - if (access_type != WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY) - { - // Windows 8 or earlier, do proxy autodetection ourselves - m_proxy_auto_config = true; - - proxy_info proxyDefault; - if (!WinHttpGetDefaultProxyConfiguration(&proxyDefault) || - proxyDefault.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY) - { - // ... then try to fall back on the default WinINET proxy, as - // recommended for the desktop applications (if we're not - // running under a user account, the function below will just - // fail, so there is no real need to check for this explicitly) - if (WinHttpGetIEProxyConfigForCurrentUser(&proxyIE)) - { - if (proxyIE.fAutoDetect) - { - m_proxy_auto_config = true; - } - else if (proxyIE.lpszAutoConfigUrl) - { - m_proxy_auto_config = true; - m_proxy_auto_config_url = proxyIE.lpszAutoConfigUrl; - } - else if (proxyIE.lpszProxy) - { - access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY; - proxy_name = proxyIE.lpszProxy; - - if (proxyIE.lpszProxyBypass) - { - proxy_bypass = proxyIE.lpszProxyBypass; - } - } - } - } - } - } - else - { - _ASSERTE(config.proxy().is_specified()); - access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY; - // WinHttpOpen cannot handle trailing slash in the name, so here is some string gymnastics to keep - // WinHttpOpen happy proxy_str is intentionally declared at the function level to avoid pointing to the - // string in the destructed object - uri = config.proxy().address(); - if (uri.is_port_default()) - { - proxy_name = uri.host().c_str(); - } - else - { - proxy_str = uri.host(); - if (uri.port() > 0) - { - proxy_str.push_back(_XPLATSTR(':')); - proxy_str.append(::utility::conversions::details::to_string_t(uri.port())); - } - - proxy_name = proxy_str.c_str(); - } - } - - // Open session. - m_hSession = WinHttpOpen(NULL, access_type, proxy_name, proxy_bypass, WINHTTP_FLAG_ASYNC); - if (!m_hSession) - { - return GetLastError(); - } - - // Set timeouts. - int milliseconds = static_cast(config.timeout().count()); - milliseconds = std::max(milliseconds, 1); - if (!WinHttpSetTimeouts(m_hSession, milliseconds, milliseconds, milliseconds, milliseconds)) - { - return GetLastError(); - } - - if (config.guarantee_order()) - { - // Set max connection to use per server to 1. - DWORD maxConnections = 1; - if (!WinHttpSetOption( - m_hSession, WINHTTP_OPTION_MAX_CONNS_PER_SERVER, &maxConnections, sizeof(maxConnections))) - { - return GetLastError(); - } - } - - // Enable TLS 1.1 and 1.2 -#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) - BOOL win32_result(FALSE); - - DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | - WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2); - win32_result = ::WinHttpSetOption( - m_hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols)); - if (FALSE == win32_result) - { - return GetLastError(); - } -#endif - - config._invoke_nativesessionhandle_options(m_hSession); - - // Register asynchronous callback. - if (WINHTTP_INVALID_STATUS_CALLBACK == - WinHttpSetStatusCallback(m_hSession, - &winhttp_client::completion_callback, - WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_HANDLES | - WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, - 0)) - { - return GetLastError(); - } - - // Open connection. - unsigned int port = m_uri.is_port_default() - ? (m_secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT) - : m_uri.port(); - m_hConnection = WinHttpConnect(m_hSession, m_uri.host().c_str(), (INTERNET_PORT)port, 0); - - if (m_hConnection == nullptr) - { - return GetLastError(); - } - - m_opened = true; - return S_OK; - } - - // Start sending request. - void send_request(_In_ const std::shared_ptr& request) - { - // First see if we need to be opened. - unsigned long error = open(); - if (error != 0) - { - // DO NOT TOUCH the this pointer after completing the request - // This object could be freed along with the request as it could - // be the last reference to this object - request->report_error(error, _XPLATSTR("Open failed")); - return; - } - - http_request& msg = request->m_request; - http_headers& headers = msg.headers(); - std::shared_ptr winhttp_context = - std::static_pointer_cast(request); - std::weak_ptr weak_winhttp_context = winhttp_context; - - proxy_info info; - bool proxy_info_required = false; - - const auto& method = msg.method(); - - // stop injection of headers via method - // resource should be ok, since it's been encoded - // and host won't resolve - if (!::web::http::details::validate_method(method)) - { - request->report_exception(http_exception("The method string is invalid.")); - return; - } - - if (m_proxy_auto_config) - { - WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {}; - if (m_proxy_auto_config_url.empty()) - { - autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; - autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; - } - else - { - autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL; - autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str(); - } - - autoproxy_options.fAutoLogonIfChallenged = TRUE; - - auto result = WinHttpGetProxyForUrl(m_hSession, m_uri.to_string().c_str(), &autoproxy_options, &info); - if (result) - { - proxy_info_required = true; - } - else - { - // Failure to download the auto-configuration script is not fatal. Fall back to the default proxy. - } - } - - // Need to form uri path, query, and fragment for this request. - // Make sure to keep any path that was specified with the uri when the http_client was created. - const utility::string_t encoded_resource = - http::uri_builder(m_uri).append(msg.relative_uri()).to_uri().resource().to_string(); - - // Open the request. - winhttp_context->m_request_handle_context = new std::weak_ptr(winhttp_context); - - winhttp_context->m_request_handle = - WinHttpOpenRequest(m_hConnection, - msg.method().c_str(), - encoded_resource.c_str(), - nullptr, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_ESCAPE_DISABLE | (m_secure ? WINHTTP_FLAG_SECURE : 0)); - if (winhttp_context->m_request_handle == nullptr) - { - auto errorCode = GetLastError(); - delete winhttp_context->m_request_handle_context; - winhttp_context->m_request_handle_context = 0; - request->report_error(errorCode, build_error_msg(errorCode, "WinHttpOpenRequest")); - return; - } - - if (!WinHttpSetOption(winhttp_context->m_request_handle, - WINHTTP_OPTION_CONTEXT_VALUE, - &winhttp_context->m_request_handle_context, - sizeof(void*))) - { - auto errorCode = GetLastError(); - delete winhttp_context->m_request_handle_context; - winhttp_context->m_request_handle_context = 0; - request->report_error(errorCode, build_error_msg(errorCode, "WinHttpSetOption request context")); - return; - } - - if (proxy_info_required) - { - auto result = WinHttpSetOption( - winhttp_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO)); - if (!result) - { - auto errorCode = GetLastError(); - request->report_error(errorCode, build_error_msg(errorCode, "Setting proxy options")); - return; - } - } - - // If credentials are specified, use autologon policy: WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH - // => default credentials are not used. - // Else, the default autologon policy WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM will be used. - if (client_config().credentials().is_set()) - { - DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; - - auto result = WinHttpSetOption( - winhttp_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data)); - if (!result) - { - auto errorCode = GetLastError(); - request->report_error( - errorCode, - build_error_msg(errorCode, "Setting autologon policy to WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH")); - return; - } - } - - // Check to turn off server certificate verification. - DWORD ignoredCertificateValidationSteps = 0; - if (client_config().validate_certificates()) - { - // if we are validating certificates, also turn on revocation checking - DWORD dwEnableSSLRevocationOpt = WINHTTP_ENABLE_SSL_REVOCATION; - if (!WinHttpSetOption(winhttp_context->m_request_handle, - WINHTTP_OPTION_ENABLE_FEATURE, - &dwEnableSSLRevocationOpt, - sizeof(dwEnableSSLRevocationOpt))) - { - auto errorCode = GetLastError(); - request->report_error(errorCode, build_error_msg(errorCode, "Error enabling SSL revocation check")); - return; - } - - // check if the user has overridden the desired Common Name with the host header - const auto hostHeader = headers.find(_XPLATSTR("Host")); - if (hostHeader != headers.end()) - { - const auto& requestHost = hostHeader->second; - if (!utility::details::str_iequal(requestHost, m_uri.host())) - { - winhttp_context->install_custom_cn_check(requestHost); - ignoredCertificateValidationSteps = SECURITY_FLAG_IGNORE_CERT_CN_INVALID; - } - } - } - else - { - ignoredCertificateValidationSteps = - SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | - SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE; - } - - if (ignoredCertificateValidationSteps && !WinHttpSetOption(winhttp_context->m_request_handle, - WINHTTP_OPTION_SECURITY_FLAGS, - &ignoredCertificateValidationSteps, - sizeof(ignoredCertificateValidationSteps))) - { - auto errorCode = GetLastError(); - request->report_error(errorCode, - build_error_msg(errorCode, "Setting ignore server certificate verification")); - return; - } - - size_t content_length; - try - { - content_length = msg._get_impl()->_get_content_length_and_set_compression(); - } - catch (...) - { - request->report_exception(std::current_exception()); - return; - } - if (content_length > 0) - { - if (msg.method() == http::methods::GET || msg.method() == http::methods::HEAD) - { - request->report_exception(http_exception(get_with_body_err_msg)); - return; - } - - // There is a request body that needs to be transferred. - if (content_length == (std::numeric_limits::max)()) - { - // The content length is not set and the application set a stream. This is an - // indication that we will use transfer encoding chunked. We still want to - // know that stream's effective length if possible for memory efficiency. - winhttp_context->m_bodyType = transfer_encoding_chunked; - winhttp_context->m_remaining_to_write = msg._get_impl()->_get_stream_length(); - } - else - { - // While we won't be transfer-encoding the data, we will write it in portions. - winhttp_context->m_bodyType = content_length_chunked; - winhttp_context->m_remaining_to_write = content_length; - } - } - - utility::string_t flattened_headers = web::http::details::flatten_http_headers(headers); - if (winhttp_context->m_request.method() == http::methods::GET) - { - // Prepare to request a compressed response from the server if necessary. - flattened_headers += winhttp_context->get_compression_header(); - } - - // Add headers. - if (!flattened_headers.empty()) - { - if (!WinHttpAddRequestHeaders(winhttp_context->m_request_handle, - flattened_headers.c_str(), - static_cast(flattened_headers.length()), - WINHTTP_ADDREQ_FLAG_ADD)) - { - auto errorCode = GetLastError(); - request->report_error(errorCode, build_error_msg(errorCode, "WinHttpAddRequestHeaders")); - return; - } - } - - // Register for notification on cancellation to abort this request. - if (msg._cancellation_token() != pplx::cancellation_token::none()) - { - // cancellation callback is unregistered when request is completed. - winhttp_context->m_cancellationRegistration = - msg._cancellation_token().register_callback([weak_winhttp_context]() { - // Call the WinHttpSendRequest API after WinHttpCloseHandle will give invalid handle error and we - // throw this exception. Call the cleanup to make the m_request_handle as nullptr, otherwise, - // Application Verifier will give AV exception on m_request_handle. - auto lock = weak_winhttp_context.lock(); - if (!lock) return; - lock->cleanup(); - }); - } - - // Call the callback function of user customized options. - try - { - client_config().invoke_nativehandle_options(winhttp_context->m_request_handle); - } - catch (...) - { - request->report_exception(std::current_exception()); - return; - } - - // Only need to cache the request body if user specified and the request stream doesn't support seeking. - if (winhttp_context->m_bodyType != no_body && client_config().buffer_request() && - !winhttp_context->_get_readbuffer().can_seek()) - { - winhttp_context->m_readBufferCopy = - ::utility::details::make_unique<::concurrency::streams::container_buffer>>(); - } - - _start_request_send(winhttp_context, content_length); - - return; - } - -private: - void _start_request_send(const std::shared_ptr& winhttp_context, size_t content_length) - { - DWORD totalLength; - if (winhttp_context->m_bodyType == no_body) - { - totalLength = 0; - } - else - { - // Capture the current read position of the stream. - auto rbuf = winhttp_context->_get_readbuffer(); - - // Record starting position in case request is challenged for authorization - // and needs to seek back to where reading is started from. - winhttp_context->m_startingPosition = rbuf.getpos(std::ios_base::in); - - // If we find ourselves here, we either don't know how large the message - totalLength = winhttp_context->m_bodyType == content_length_chunked ? (DWORD)content_length - : WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH; - } - - const auto requestSuccess = WinHttpSendRequest(winhttp_context->m_request_handle, - WINHTTP_NO_ADDITIONAL_HEADERS, - 0, - nullptr, - 0, - totalLength, - (DWORD_PTR)winhttp_context->m_request_handle_context); - if (!requestSuccess) - { - auto errorCode = GetLastError(); - winhttp_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpSendRequest")); - } - } - - // Helper function to query/read next part of response data from winhttp. - static void read_next_response_chunk(winhttp_request_context* pContext, DWORD bytesRead, bool firstRead = false) - { - const bool defaultChunkSize = pContext->m_http_client->client_config().is_default_chunksize(); - - // If user specified a chunk size then read in chunks instead of using query data available. - if (defaultChunkSize) - { - if (!WinHttpQueryDataAvailable(pContext->m_request_handle, nullptr)) - { - auto errorCode = GetLastError(); - pContext->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryDataAvaliable")); - } - } - else - { - // If bytes read is less than the chunk size this request is done. - // Is it really, though? The WinHttpReadData docs suggest that less can be returned regardless... - const size_t chunkSize = pContext->m_http_client->client_config().chunksize(); - std::unique_ptr& decompressor = pContext->m_decompressor; - if (!decompressor && bytesRead < chunkSize && !firstRead) - { - pContext->complete_request(pContext->m_downloaded); - } - else - { - uint8_t* buffer; - - if (decompressor) - { - // m_buffer holds the compressed data; we'll decompress into the caller's buffer later - if (pContext->m_compression_state.m_buffer.capacity() < chunkSize) - { - pContext->m_compression_state.m_buffer.reserve(chunkSize); - } - buffer = pContext->m_compression_state.m_buffer.data(); - } - else - { - auto writebuf = pContext->_get_writebuffer(); - pContext->allocate_reply_space(writebuf.alloc(chunkSize), chunkSize); - buffer = pContext->m_body_data.get(); - } - - if (!WinHttpReadData(pContext->m_request_handle, buffer, static_cast(chunkSize), nullptr)) - { - auto errorCode = GetLastError(); - pContext->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData")); - } - } - } - } - - static void _transfer_encoding_chunked_write_data(_In_ winhttp_request_context* p_request_context) - { - size_t chunk_size; - std::unique_ptr& compressor = p_request_context->m_request.compressor(); - - // Set the chunk size up front; we need it before the lambda functions come into scope - if (compressor) - { - // We could allocate less than a chunk for the compressed data here, though that - // would result in more trips through this path for not-so-compressible data... - if (p_request_context->m_body_data.size() > http::details::chunked_encoding::additional_encoding_space) - { - // If we've previously allocated space for the compressed data, don't reduce it - chunk_size = - p_request_context->m_body_data.size() - http::details::chunked_encoding::additional_encoding_space; - } - else if (p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) - { - // Choose a semi-intelligent size based on how much total data is left to compress - chunk_size = (std::min)(static_cast(p_request_context->m_remaining_to_write) + 128, - p_request_context->m_http_client->client_config().chunksize()); - } - else - { - // Just base our allocation on the chunk size, since we don't have any other data available - chunk_size = p_request_context->m_http_client->client_config().chunksize(); - } - } - else - { - // We're not compressing; use the smaller of the remaining data (if known) and the configured (or default) - // chunk size - chunk_size = (std::min)(static_cast(p_request_context->m_remaining_to_write), - p_request_context->m_http_client->client_config().chunksize()); - } - p_request_context->allocate_request_space( - nullptr, chunk_size + http::details::chunked_encoding::additional_encoding_space); - - auto after_read = [p_request_context, chunk_size, &compressor](pplx::task op) { - size_t bytes_read; - try - { - bytes_read = op.get(); - // If the read buffer for copying exists then write to it. - if (p_request_context->m_readBufferCopy) - { - // We have raw memory here writing to a memory stream so it is safe to wait - // since it will always be non-blocking. - if (!compressor) - { - p_request_context->m_readBufferCopy - ->putn_nocopy( - &p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset], - bytes_read) - .wait(); - } - } - } - catch (...) - { - p_request_context->report_exception(std::current_exception()); - return; - } - - _ASSERTE(bytes_read != static_cast(-1)); - - size_t offset = http::details::chunked_encoding::add_chunked_delimiters( - p_request_context->m_body_data.get(), - chunk_size + http::details::chunked_encoding::additional_encoding_space, - bytes_read); - - if (!compressor && p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) - { - if (bytes_read == 0 && p_request_context->m_remaining_to_write) - { - // The stream ended earlier than we detected it should - http_exception ex( - U("Unexpected end of request body stream encountered before expected length met.")); - p_request_context->report_exception(ex); - return; - } - p_request_context->m_remaining_to_write -= bytes_read; - } - - // Stop writing chunks if we reached the end of the stream. - // Note that we could detect end-of-stream based on !m_remaining_to_write, and insert - // the last (0) chunk if we have enough extra space... though we currently don't. - if (bytes_read == 0) - { - p_request_context->m_bodyType = no_body; - if (p_request_context->m_readBufferCopy) - { - // Move the saved buffer into the read buffer, which now supports seeking. - p_request_context->m_readStream = - concurrency::streams::container_stream>::open_istream( - std::move(p_request_context->m_readBufferCopy->collection())); - p_request_context->m_readBufferCopy.reset(); - } - } - - const auto length = bytes_read + (http::details::chunked_encoding::additional_encoding_space - offset); - - if (!WinHttpWriteData(p_request_context->m_request_handle, - &p_request_context->m_body_data.get()[offset], - static_cast(length), - nullptr)) - { - auto errorCode = GetLastError(); - p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); - } - }; - - if (compressor) - { - auto do_compress = - [p_request_context, chunk_size, &compressor](pplx::task op) -> pplx::task { - size_t bytes_read; - - try - { - bytes_read = op.get(); - } - catch (...) - { - return pplx::task_from_exception(std::current_exception()); - } - - uint8_t* buffer = p_request_context->m_compression_state.m_acquired; - if (buffer == nullptr) - { - buffer = p_request_context->m_compression_state.m_buffer.data(); - } - - web::http::compression::operation_hint hint = web::http::compression::operation_hint::has_more; - - if (bytes_read) - { - // An actual read always resets compression state for the next chunk - _ASSERTE(p_request_context->m_compression_state.m_bytes_processed == - p_request_context->m_compression_state.m_bytes_read); - _ASSERTE(!p_request_context->m_compression_state.m_needs_flush); - p_request_context->m_compression_state.m_bytes_read = bytes_read; - p_request_context->m_compression_state.m_bytes_processed = 0; - if (p_request_context->m_readBufferCopy) - { - // If we've been asked to keep a copy of the raw data for restarts, do so here, pre-compression - p_request_context->m_readBufferCopy->putn_nocopy(buffer, bytes_read).wait(); - } - if (p_request_context->m_remaining_to_write == bytes_read) - { - // We've read to the end of the stream; finalize here if possible. We'll - // decrement the remaining count as we actually process the read buffer. - hint = web::http::compression::operation_hint::is_last; - } - } - else if (p_request_context->m_compression_state.m_needs_flush) - { - // All input has been consumed, but we still need to collect additional compressed output; - // this is done (in theory it can be multiple times) as a finalizing operation - hint = web::http::compression::operation_hint::is_last; - } - else if (p_request_context->m_compression_state.m_bytes_processed == - p_request_context->m_compression_state.m_bytes_read) - { - if (p_request_context->m_remaining_to_write && - p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) - { - // The stream ended earlier than we detected it should - return pplx::task_from_exception(http_exception( - U("Unexpected end of request body stream encountered before expected length met."))); - } - - // We think we're done; inform the compression library so it can finalize and/or give us any pending - // compressed bytes. Note that we may end up here multiple times if m_needs_flush is set, until all - // compressed bytes are drained. - hint = web::http::compression::operation_hint::is_last; - } - // else we're still compressing bytes from the previous read - - _ASSERTE(p_request_context->m_compression_state.m_bytes_processed <= - p_request_context->m_compression_state.m_bytes_read); - - uint8_t* in = buffer + p_request_context->m_compression_state.m_bytes_processed; - size_t inbytes = p_request_context->m_compression_state.m_bytes_read - - p_request_context->m_compression_state.m_bytes_processed; - return compressor - ->compress(in, - inbytes, - &p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset], - chunk_size, - hint) - .then([p_request_context, bytes_read, hint, chunk_size]( - pplx::task op) -> pplx::task { - http::compression::operation_result r; - - try - { - r = op.get(); - } - catch (...) - { - return pplx::task_from_exception(std::current_exception()); - } - - if (hint == web::http::compression::operation_hint::is_last) - { - // We're done reading all chunks, but the compressor may still have compressed bytes to - // drain from previous reads - _ASSERTE(r.done || r.output_bytes_produced == chunk_size); - p_request_context->m_compression_state.m_needs_flush = !r.done; - p_request_context->m_compression_state.m_done = r.done; - } - - // Update the number of bytes compressed in this read chunk; if it's been fully compressed, - // we'll reset m_bytes_processed and m_bytes_read after reading the next chunk - p_request_context->m_compression_state.m_bytes_processed += r.input_bytes_processed; - _ASSERTE(p_request_context->m_compression_state.m_bytes_processed <= - p_request_context->m_compression_state.m_bytes_read); - if (p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) - { - _ASSERTE(p_request_context->m_remaining_to_write >= r.input_bytes_processed); - p_request_context->m_remaining_to_write -= r.input_bytes_processed; - } - - if (p_request_context->m_compression_state.m_acquired != nullptr && - p_request_context->m_compression_state.m_bytes_processed == - p_request_context->m_compression_state.m_bytes_read) - { - // Release the acquired buffer back to the streambuf at the earliest possible point - p_request_context->_get_readbuffer().release( - p_request_context->m_compression_state.m_acquired, - p_request_context->m_compression_state.m_bytes_processed); - p_request_context->m_compression_state.m_acquired = nullptr; - } - - return pplx::task_from_result(r.output_bytes_produced); - }); - }; - - if (p_request_context->m_compression_state.m_bytes_processed < - p_request_context->m_compression_state.m_bytes_read || - p_request_context->m_compression_state.m_needs_flush) - { - // We're still working on data from a previous read; continue compression without reading new data - do_compress(pplx::task_from_result(0)).then(after_read); - } - else if (p_request_context->m_compression_state.m_done) - { - // We just need to send the last (zero-length) chunk; there's no sense in going through the compression - // path - after_read(pplx::task_from_result(0)); - } - else - { - size_t length; - - // We need to read from the input stream, then compress before sending - if (p_request_context->_get_readbuffer().acquire(p_request_context->m_compression_state.m_acquired, - length)) - { - if (length == 0) - { - if (p_request_context->_get_readbuffer().exception()) - { - p_request_context->report_exception(p_request_context->_get_readbuffer().exception()); - return; - } - else if (p_request_context->m_remaining_to_write && - p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) - { - // Unexpected end-of-stream. - p_request_context->report_error(GetLastError(), - _XPLATSTR("Outgoing HTTP body stream ended early.")); - return; - } - } - else if (length > p_request_context->m_remaining_to_write) - { - // The stream grew, but we won't - length = static_cast(p_request_context->m_remaining_to_write); - } - - do_compress(pplx::task_from_result(length)).then(after_read); - } - else - { - length = (std::min)(static_cast(p_request_context->m_remaining_to_write), - p_request_context->m_http_client->client_config().chunksize()); - if (p_request_context->m_compression_state.m_buffer.capacity() < length) - { - p_request_context->m_compression_state.m_buffer.reserve(length); - } - p_request_context->_get_readbuffer() - .getn(p_request_context->m_compression_state.m_buffer.data(), length) - .then(do_compress) - .then(after_read); - } - } - } - else - { - // We're not compressing; just read and chunk - p_request_context->_get_readbuffer() - .getn(&p_request_context->m_body_data.get()[http::details::chunked_encoding::data_offset], chunk_size) - .then(after_read); - } - } - - static void _multiple_segment_write_data(_In_ winhttp_request_context* p_request_context) - { - auto rbuf = p_request_context->_get_readbuffer(); - msl::safeint3::SafeInt safeCount = p_request_context->m_remaining_to_write; - safeCount = safeCount.Min(p_request_context->m_http_client->client_config().chunksize()); - - uint8_t* block = nullptr; - size_t length = 0; - if (rbuf.acquire(block, length)) - { - if (length == 0) - { - // Unexpected end-of-stream. - if (rbuf.exception() == nullptr) - { - p_request_context->report_error(GetLastError(), - _XPLATSTR("Error reading outgoing HTTP body from its stream.")); - } - else - { - p_request_context->report_exception(rbuf.exception()); - } - return; - } - - p_request_context->allocate_request_space(block, length); - - const size_t to_write = safeCount.Min(length); - - // Stop writing chunks after this one if no more data. - p_request_context->m_remaining_to_write -= to_write; - if (p_request_context->m_remaining_to_write == 0) - { - p_request_context->m_bodyType = no_body; - } - - if (!WinHttpWriteData(p_request_context->m_request_handle, - p_request_context->m_body_data.get(), - static_cast(to_write), - nullptr)) - { - auto errorCode = GetLastError(); - p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); - } - } - else - { - p_request_context->allocate_request_space(nullptr, safeCount); - - rbuf.getn(p_request_context->m_body_data.get(), safeCount) - .then([p_request_context, rbuf](pplx::task op) { - size_t read; - try - { - read = op.get(); - } - catch (...) - { - p_request_context->report_exception(std::current_exception()); - return; - } - _ASSERTE(read != static_cast(-1)); - - if (read == 0) - { - p_request_context->report_exception(http_exception( - U("Unexpected end of request body stream encountered before Content-Length met."))); - return; - } - - p_request_context->m_remaining_to_write -= read; - - // Stop writing chunks after this one if no more data. - if (p_request_context->m_remaining_to_write == 0) - { - p_request_context->m_bodyType = no_body; - } - - if (!WinHttpWriteData(p_request_context->m_request_handle, - p_request_context->m_body_data.get(), - static_cast(read), - nullptr)) - { - auto errorCode = GetLastError(); - p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpWriteData")); - } - }); - } - } - - static utility::string_t get_request_url(HINTERNET hRequestHandle) - { - utility::string_t url; - auto urlSize = static_cast(url.capacity()) * 2; // use initial small string optimization capacity - for (;;) - { - url.resize(urlSize / sizeof(utility::char_t)); - if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize)) - { - url.resize(url.length()); - return url; - } - - const auto lastError = GetLastError(); - if (lastError != ERROR_INSUFFICIENT_BUFFER || urlSize == 0) - { - url.clear(); - url.shrink_to_fit(); - return url; - } - } - } - - // Returns true if we handle successfully and resending the request - // or false if we fail to handle. - static bool handle_authentication_failure(HINTERNET hRequestHandle, - const std::shared_ptr& p_request_context, - _In_ DWORD error = 0) - { - http_request& request = p_request_context->m_request; - - _ASSERTE(p_request_context->m_response.status_code() == status_codes::Unauthorized || - p_request_context->m_response.status_code() == status_codes::ProxyAuthRequired || - error == ERROR_WINHTTP_RESEND_REQUEST); - - // Check if the saved read position is valid - auto rdpos = p_request_context->m_startingPosition; - if (rdpos != static_cast::pos_type>(std::char_traits::eof())) - { - // Try to seek back to the saved read position - auto rbuf = p_request_context->_get_readbuffer(); - if (rbuf.seekpos(rdpos, std::ios::ios_base::in) != rdpos) - { - return false; - } - - // We successfully seeked back; now reset the compression state, if any, to match - if (p_request_context->m_request.compressor()) - { - try - { - p_request_context->m_request.compressor()->reset(); - } - catch (...) - { - return false; - } - } - } - p_request_context->m_compression_state = winhttp_request_context::compression_state(); - - // If we got ERROR_WINHTTP_RESEND_REQUEST, the response header is not available, - // we cannot call WinHttpQueryAuthSchemes and WinHttpSetCredentials. - if (error != ERROR_WINHTTP_RESEND_REQUEST) - { - // Obtain the supported and preferred schemes. - DWORD dwSupportedSchemes; - DWORD dwFirstScheme; - DWORD dwAuthTarget; - if (!WinHttpQueryAuthSchemes(hRequestHandle, &dwSupportedSchemes, &dwFirstScheme, &dwAuthTarget)) - { - // This will return the authentication failure to the user, without reporting fatal errors - return false; - } - - DWORD dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes); - if (dwSelectedScheme == 0) - { - // This will return the authentication failure to the user, without reporting fatal errors - return false; - } - - credentials cred; - if (dwAuthTarget == WINHTTP_AUTH_TARGET_SERVER && !p_request_context->m_server_authentication_tried) - { - cred = p_request_context->m_http_client->client_config().credentials(); - p_request_context->m_server_authentication_tried = true; - } - else if (dwAuthTarget == WINHTTP_AUTH_TARGET_PROXY) - { - bool is_redirect = false; - try - { - web::uri current_uri(get_request_url(hRequestHandle)); - is_redirect = p_request_context->m_request.absolute_uri().to_string() != current_uri.to_string(); - } - catch (const std::exception&) - { - } - - // If we have been redirected, then WinHttp needs the proxy credentials again to make the next request - // leg (which may be on a different server) - if (is_redirect || !p_request_context->m_proxy_authentication_tried) - { - cred = p_request_context->m_http_client->client_config().proxy().credentials(); - p_request_context->m_proxy_authentication_tried = true; - } - } - - // No credentials found so can't resend. - if (!cred.is_set()) - { - return false; - } - - // New scope to ensure plaintext password is cleared as soon as possible. - { - auto password = cred._internal_decrypt(); - if (!WinHttpSetCredentials(hRequestHandle, - dwAuthTarget, - dwSelectedScheme, - cred.username().c_str(), - password->c_str(), - nullptr)) - { - return false; - } - } - } - - // Reset the request body type since it might have already started sending. - size_t content_length; - try - { - content_length = request._get_impl()->_get_content_length_and_set_compression(); - } - catch (...) - { - return false; - } - - if (content_length > 0) - { - // There is a request body that needs to be transferred. - if (content_length == (std::numeric_limits::max)()) - { - // The content length is unknown and the application set a stream. This is an - // indication that we will need to chunk the data. - p_request_context->m_bodyType = transfer_encoding_chunked; - p_request_context->m_remaining_to_write = request._get_impl()->_get_stream_length(); - } - else - { - // While we won't be transfer-encoding the data, we will write it in portions. - p_request_context->m_bodyType = content_length_chunked; - p_request_context->m_remaining_to_write = content_length; - } - } - else - { - p_request_context->m_bodyType = no_body; - } - - // We're good. - winhttp_client* winclnt = reinterpret_cast(p_request_context->m_http_client.get()); - winclnt->_start_request_send(p_request_context, content_length); - - // We will not complete the request. Instead wait for the response to the request that was resent - return true; - } - - // Callback used with WinHTTP to listen for async completions. - static void CALLBACK completion_callback( - HINTERNET hRequestHandle, DWORD_PTR context, DWORD statusCode, _In_ void* statusInfo, DWORD statusInfoLength) - { - (void)statusInfoLength; - - std::weak_ptr* p_weak_request_context = - reinterpret_cast*>(context); - - if (p_weak_request_context == nullptr) - { - return; - } - - if (statusCode == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) - { - // This callback is responsible for freeing the type-erased context. - // This particular status code indicates that this is the final callback call, suitable for context - // destruction. - delete p_weak_request_context; - return; - } - - auto p_request_context = p_weak_request_context->lock(); - if (!p_request_context) - { - // The request context was already released, probably due to cancellation - return; - } - - switch (statusCode) - { - case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: - { - WINHTTP_ASYNC_RESULT* error_result = reinterpret_cast(statusInfo); - const DWORD errorCode = error_result->dwError; - - // Some authentication schemes require multiple transactions. - // When ERROR_WINHTTP_RESEND_REQUEST is encountered, - // we should continue to resend the request until a response is received that does not contain a 401 or - // 407 status code. - if (errorCode == ERROR_WINHTTP_RESEND_REQUEST) - { - bool resending = handle_authentication_failure(hRequestHandle, p_request_context, errorCode); - if (resending) - { - // The request is resending. Wait until we get a new response. - return; - } - } - - p_request_context->report_error(errorCode, build_error_msg(error_result)); - return; - } - case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: - { - if (!p_request_context->m_request.body()) - { - // Report progress finished uploading with no message body. - auto progress = p_request_context->m_request._get_impl()->_progress_handler(); - if (progress) - { - try - { - (*progress)(message_direction::upload, 0); - } - catch (...) - { - p_request_context->report_exception(std::current_exception()); - return; - } - } - } - - if (p_request_context->m_bodyType == transfer_encoding_chunked) - { - _transfer_encoding_chunked_write_data(p_request_context.get()); - } - else if (p_request_context->m_bodyType == content_length_chunked) - { - _multiple_segment_write_data(p_request_context.get()); - } - else - { - if (!WinHttpReceiveResponse(hRequestHandle, nullptr)) - { - auto errorCode = GetLastError(); - p_request_context->report_error(errorCode, - build_error_msg(errorCode, "WinHttpReceiveResponse")); - } - } - return; - } - case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: p_request_context->on_send_request_validate_cn(); return; - case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: - p_request_context->report_exception(web::http::http_exception( - generate_security_failure_message(*reinterpret_cast(statusInfo)))); - return; - case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: - { - DWORD bytesWritten = *((DWORD*)statusInfo); - _ASSERTE(statusInfoLength == sizeof(DWORD)); - - if (bytesWritten > 0) - { - auto progress = p_request_context->m_request._get_impl()->_progress_handler(); - if (progress) - { - p_request_context->m_uploaded += bytesWritten; - try - { - (*progress)(message_direction::upload, p_request_context->m_uploaded); - } - catch (...) - { - p_request_context->report_exception(std::current_exception()); - return; - } - } - } - - if (p_request_context->is_externally_allocated()) - { - p_request_context->_get_readbuffer().release(p_request_context->m_body_data.get(), bytesWritten); - } - - if (p_request_context->m_bodyType == transfer_encoding_chunked) - { - _transfer_encoding_chunked_write_data(p_request_context.get()); - } - else if (p_request_context->m_bodyType == content_length_chunked) - { - _multiple_segment_write_data(p_request_context.get()); - } - else - { - if (!WinHttpReceiveResponse(hRequestHandle, nullptr)) - { - auto errorCode = GetLastError(); - p_request_context->report_error(errorCode, - build_error_msg(errorCode, "WinHttpReceiveResponse")); - } - } - return; - } - case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: - { - // First need to query to see what the headers size is. - DWORD headerBufferLength = 0; - query_header_length(hRequestHandle, WINHTTP_QUERY_RAW_HEADERS_CRLF, headerBufferLength); - - // Now allocate buffer for headers and query for them. - std::vector header_raw_buffer; - header_raw_buffer.resize(headerBufferLength); - utility::char_t* header_buffer = reinterpret_cast(&header_raw_buffer[0]); - if (!WinHttpQueryHeaders(hRequestHandle, - WINHTTP_QUERY_RAW_HEADERS_CRLF, - WINHTTP_HEADER_NAME_BY_INDEX, - header_buffer, - &headerBufferLength, - WINHTTP_NO_HEADER_INDEX)) - { - auto errorCode = GetLastError(); - p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryHeaders")); - ; - return; - } - - http_response& response = p_request_context->m_response; - parse_winhttp_headers(hRequestHandle, header_buffer, response); - - if (response.status_code() == status_codes::Unauthorized /*401*/ || - response.status_code() == status_codes::ProxyAuthRequired /*407*/) - { - bool resending = handle_authentication_failure(hRequestHandle, p_request_context); - if (resending) - { - // The request was not completed but resent with credentials. Wait until we get a new response - return; - } - } - - // Check whether the request is compressed, and if so, whether we're handling it. - if (!p_request_context->handle_compression()) - { - // false indicates report_exception was called - return; - } - if (p_request_context->m_decompressor && - !p_request_context->m_http_client->client_config().request_compressed_response()) - { - p_request_context->m_compression_state.m_chunk = - ::utility::details::make_unique(); - p_request_context->m_compression_state.m_chunked = true; - } - - // Signal that the headers are available. - p_request_context->complete_headers(); - - // If the method was 'HEAD,' the body of the message is by definition empty. No need to - // read it. Any headers that suggest the presence of a body can safely be ignored. - if (p_request_context->m_request.method() == methods::HEAD) - { - p_request_context->allocate_request_space(nullptr, 0); - p_request_context->complete_request(0); - return; - } - - // HTTP Specification states: - // If a message is received with both a Transfer-Encoding header field - // and a Content-Length header field, the latter MUST be ignored. - // If none of them is specified, the message length should be determined by the server closing the - // connection. http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 - - read_next_response_chunk(p_request_context.get(), 0, true); - return; - } - case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: - { - // Status information contains pointer to DWORD containing number of bytes available. - const DWORD num_bytes = *(PDWORD)statusInfo; - uint8_t* buffer; - - if (num_bytes > 0) - { - if (p_request_context->m_decompressor) - { - // Allocate space for the compressed data; we'll decompress it into the caller stream once it's - // been filled in - if (p_request_context->m_compression_state.m_buffer.capacity() < num_bytes) - { - p_request_context->m_compression_state.m_buffer.reserve(num_bytes); - } - buffer = p_request_context->m_compression_state.m_buffer.data(); - } - else - { - auto writebuf = p_request_context->_get_writebuffer(); - p_request_context->allocate_reply_space(writebuf.alloc(num_bytes), num_bytes); - buffer = p_request_context->m_body_data.get(); - } - - // Read in available body data all at once. - if (!WinHttpReadData(hRequestHandle, buffer, num_bytes, nullptr)) - { - auto errorCode = GetLastError(); - p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpReadData")); - } - } - else - { - if (p_request_context->m_decompressor) - { - if (p_request_context->m_compression_state.m_chunked) - { - // We haven't seen the 0-length chunk and/or trailing delimiter that indicate the end of - // chunked input - p_request_context->report_exception( - http_exception("Chunked response stream ended unexpectedly")); - return; - } - if (p_request_context->m_compression_state.m_started && - !p_request_context->m_compression_state.m_done) - { - p_request_context->report_exception( - http_exception("Received incomplete compressed stream")); - return; - } - } - - // No more data available, complete the request. - auto progress = p_request_context->m_request._get_impl()->_progress_handler(); - if (progress) - { - try - { - (*progress)(message_direction::download, p_request_context->m_downloaded); - } - catch (...) - { - p_request_context->report_exception(std::current_exception()); - return; - } - } - - p_request_context->complete_request(p_request_context->m_downloaded); - } - return; - } - case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: - { - // Status information length contains the number of bytes read. - DWORD bytesRead = statusInfoLength; - - // Report progress about downloaded bytes. - auto progress = p_request_context->m_request._get_impl()->_progress_handler(); - p_request_context->m_downloaded += statusInfoLength; - if (progress) - { - try - { - (*progress)(message_direction::download, p_request_context->m_downloaded); - } - catch (...) - { - p_request_context->report_exception(std::current_exception()); - return; - } - } - - // If no bytes have been read, then this is the end of the response. - if (bytesRead == 0) - { - if (p_request_context->m_decompressor) - { - if (p_request_context->m_compression_state.m_chunked) - { - // We haven't seen the 0-length chunk and/or trailing delimiter that indicate the end of - // chunked input - p_request_context->report_exception( - http_exception("Chunked response stream ended unexpectedly")); - return; - } - if (p_request_context->m_compression_state.m_started && - !p_request_context->m_compression_state.m_done) - { - p_request_context->report_exception( - http_exception("Received incomplete compressed stream")); - return; - } - } - p_request_context->complete_request(p_request_context->m_downloaded); - return; - } - - auto writebuf = p_request_context->_get_writebuffer(); - - if (p_request_context->m_decompressor) - { - size_t chunk_size = (std::max)(static_cast(bytesRead), - p_request_context->m_http_client->client_config().chunksize()); - p_request_context->m_compression_state.m_bytes_read = static_cast(bytesRead); - p_request_context->m_compression_state.m_chunk_bytes = 0; - - // Note, some servers seem to send a first chunk of body data that decompresses to nothing, but - // initializes the decompression state; this produces no decompressed output. Subsequent chunks - // will then begin emitting decompressed body data. - - // Oddly enough, WinHttp doesn't de-chunk for us if "chunked" isn't the only - // encoding, so we need to do so on the fly as we process the received data - auto process_buffer = - [chunk_size](winhttp_request_context* c, size_t bytes_produced, bool outer) -> bool { - if (!c->m_compression_state.m_chunk_bytes) - { - if (c->m_compression_state.m_chunked) - { - size_t offset; - bool done; - - // Process the next portion of this piece of the transfer-encoded message - done = c->m_compression_state.m_chunk->process_buffer( - c->m_compression_state.m_buffer.data() + c->m_compression_state.m_bytes_processed, - c->m_compression_state.m_bytes_read - c->m_compression_state.m_bytes_processed, - offset, - c->m_compression_state.m_chunk_bytes); - - // Skip chunk-related metadata; it isn't relevant to decompression - _ASSERTE(c->m_compression_state.m_bytes_processed + offset <= - c->m_compression_state.m_bytes_read); - c->m_compression_state.m_bytes_processed += offset; - - if (!c->m_compression_state.m_chunk_bytes) - { - if (done) - { - // We've processed/validated all bytes in this transfer-encoded message. - // Note that we currently ignore "extra" trailing bytes, i.e. - // c->m_compression_state.m_bytes_processed < - // c->m_compression_state.m_bytes_read - if (c->m_compression_state.m_done) - { - c->complete_request(c->m_downloaded); - return false; - } - else if (!outer && bytes_produced != chunk_size) - { - throw http_exception("Transfer ended before decompression completed"); - } - } - else if (!outer && bytes_produced != chunk_size) - { - // There should be more data to receive; look for it - c->m_compression_state.m_bytes_processed = 0; - read_next_response_chunk( - c, static_cast(c->m_compression_state.m_bytes_read)); - return false; - } - } - } - else - { - _ASSERTE(!c->m_compression_state.m_bytes_processed || - c->m_compression_state.m_bytes_processed == - c->m_compression_state.m_bytes_read); - if (c->m_compression_state.m_done) - { - // Decompression is done; complete the request - c->complete_request(c->m_downloaded); - return false; - } - else if (c->m_compression_state.m_bytes_processed != - c->m_compression_state.m_bytes_read) - { - // We still have more data to process in the current buffer - c->m_compression_state.m_chunk_bytes = - c->m_compression_state.m_bytes_read - c->m_compression_state.m_bytes_processed; - } - else if (!outer && bytes_produced != chunk_size) - { - // There should be more data to receive; look for it - c->m_compression_state.m_bytes_processed = 0; - read_next_response_chunk(c, - static_cast(c->m_compression_state.m_bytes_read)); - return false; - } - // Otherwise, we've processed all bytes in the input buffer, but there's a good chance - // that there are still decompressed bytes to emit; we'll do so before reading the next - // chunk - } - } - - // We're still processing the current message chunk - return true; - }; - - pplx::details::_do_while([p_request_context, chunk_size, process_buffer]() -> pplx::task { - uint8_t* buffer; - - try - { - if (!process_buffer(p_request_context.get(), 0, true)) - { - // The chunked request has been completely processed (or contains no data in the first - // place) - return pplx::task_from_result(false); - } - } - catch (...) - { - // The outer do-while requires an explicit task return to activate the then() clause - return pplx::task_from_exception(std::current_exception()); - } - - // If it's possible to know how much post-compression data we're expecting (for instance if we - // can discern how much total data the ostream can support, we could allocate (or at least - // attempt to acquire) based on that - p_request_context->m_compression_state.m_acquired = - p_request_context->_get_writebuffer().alloc(chunk_size); - if (p_request_context->m_compression_state.m_acquired) - { - buffer = p_request_context->m_compression_state.m_acquired; - } - else - { - // The streambuf couldn't accommodate our request; we'll use m_body_data's - // internal vector as temporary storage, then putn() to the caller's stream - p_request_context->allocate_reply_space(nullptr, chunk_size); - buffer = p_request_context->m_body_data.get(); - } - - uint8_t* in = p_request_context->m_compression_state.m_buffer.data() + - p_request_context->m_compression_state.m_bytes_processed; - size_t inbytes = p_request_context->m_compression_state.m_chunk_bytes; - if (inbytes) - { - p_request_context->m_compression_state.m_started = true; - } - return p_request_context->m_decompressor - ->decompress( - in, inbytes, buffer, chunk_size, web::http::compression::operation_hint::has_more) - .then([p_request_context, buffer, chunk_size, process_buffer]( - pplx::task op) { - auto r = op.get(); - auto keep_going = [&r, process_buffer](winhttp_request_context* c) -> pplx::task { - _ASSERTE(r.input_bytes_processed <= c->m_compression_state.m_chunk_bytes); - c->m_compression_state.m_chunk_bytes -= r.input_bytes_processed; - c->m_compression_state.m_bytes_processed += r.input_bytes_processed; - c->m_compression_state.m_done = r.done; - - try - { - // See if we still have more work to do for this section and/or for the response - // in general - return pplx::task_from_result( - process_buffer(c, r.output_bytes_produced, false)); - } - catch (...) - { - return pplx::task_from_exception(std::current_exception()); - } - }; - - _ASSERTE(p_request_context->m_compression_state.m_bytes_processed + - r.input_bytes_processed <= - p_request_context->m_compression_state.m_bytes_read); - - if (p_request_context->m_compression_state.m_acquired != nullptr) - { - // We decompressed directly into the output stream - p_request_context->m_compression_state.m_acquired = nullptr; - p_request_context->_get_writebuffer().commit(r.output_bytes_produced); - return keep_going(p_request_context.get()); - } - - // We decompressed into our own buffer; let the stream copy the data - return p_request_context->_get_writebuffer() - .putn_nocopy(buffer, r.output_bytes_produced) - .then([p_request_context, r, keep_going](pplx::task op) { - if (op.get() != r.output_bytes_produced) - { - return pplx::task_from_exception( - std::runtime_error("Response stream unexpectedly failed to write the " - "requested number of bytes")); - } - return keep_going(p_request_context.get()); - }); - }); - }).then([p_request_context](pplx::task op) { - try - { - op.get(); - } - catch (...) - { - // We're only here to pick up any exception that may have been thrown, and to clean up - // if needed - if (p_request_context->m_compression_state.m_acquired) - { - p_request_context->_get_writebuffer().commit(0); - p_request_context->m_compression_state.m_acquired = nullptr; - } - p_request_context->report_exception(std::current_exception()); - } - }); - } - else - { - // If the data was allocated directly from the buffer then commit, otherwise we still - // need to write to the response stream buffer. - if (p_request_context->is_externally_allocated()) - { - writebuf.commit(bytesRead); - read_next_response_chunk(p_request_context.get(), bytesRead); - } - else - { - writebuf.putn_nocopy(p_request_context->m_body_data.get(), bytesRead) - .then([hRequestHandle, p_request_context, bytesRead](pplx::task op) { - size_t written = 0; - try - { - written = op.get(); - } - catch (...) - { - p_request_context->report_exception(std::current_exception()); - return; - } - - // If we couldn't write everything, it's time to exit. - if (written != bytesRead) - { - p_request_context->report_exception(std::runtime_error( - "response stream unexpectedly failed to write the requested number of bytes")); - return; - } - - read_next_response_chunk(p_request_context.get(), bytesRead); - }); - } - } - return; - } - } - } - - std::atomic m_opened; - - // WinHTTP session and connection - HINTERNET m_hSession; - HINTERNET m_hConnection; - bool m_secure; - - // If auto config is true, dynamically find the proxy for each URL using - // the proxy configuration script at the given URL if it's not empty or - // using WPAD otherwise. - bool m_proxy_auto_config {false}; - utility::string_t m_proxy_auto_config_url; -}; - -std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri, - http_client_config&& client_config) -{ - return std::make_shared(std::move(base_uri), std::move(client_config)); -} - - -} // namespace details -} // namespace client -} // namespace http -} // namespace web From 17a75fc711c276f75e7cd3907e6cc9c9f3fd6151 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 3 Sep 2019 01:55:10 +0000 Subject: [PATCH 39/41] sudo install --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5c022ae799..44cf6115be 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -339,7 +339,7 @@ jobs: cd build cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .. make - make install + sudo make install cd ../.. mkdir build.debug cd build.debug From 8db16dae6dcc1d4a01c3c6470a31eaf2c87ff8dd Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 3 Sep 2019 02:08:29 +0000 Subject: [PATCH 40/41] Undo warning suppress --- Release/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 4896443583..4d4227915a 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -165,7 +165,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR IOS) elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") message("-- Setting gcc options") - set(WARNINGS -Wall -Wextra -Wno-unused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code) + set(WARNINGS -Wall -Wextra -Wunused-parameter -Wcast-align -Wcast-qual -Wconversion -Wformat=2 -Winit-self -Winvalid-pch -Wmissing-format-attribute -Wmissing-include-dirs -Wpacked -Wredundant-decls -Wunreachable-code) set(LD_FLAGS "${LD_FLAGS} -Wl,-z,defs") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-strict-aliasing") From cdb2244a0aafa40993d6b98b0166a853b33cadd0 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Tue, 3 Sep 2019 02:23:24 +0000 Subject: [PATCH 41/41] Fix capitalization --- Release/cmake/cpprest_find_winhttppal.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Release/cmake/cpprest_find_winhttppal.cmake b/Release/cmake/cpprest_find_winhttppal.cmake index 072fc5049e..9a6840fba2 100644 --- a/Release/cmake/cpprest_find_winhttppal.cmake +++ b/Release/cmake/cpprest_find_winhttppal.cmake @@ -3,7 +3,7 @@ function(cpprest_find_winhttppal) return() endif() - if(NOT winhttppal_LIBRARY OR NOT winhttppal_INCLUDE_DIRS) + if(NOT WINHTTPPAL_LIBRARY OR NOT WINHTTPPAL_INCLUDE_DIRS) find_package(winhttppal REQUIRED) endif() @@ -11,7 +11,7 @@ function(cpprest_find_winhttppal) if(TARGET winhttppal::winhttppal) target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE winhttppal::winhttppal) else() - target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$") - target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$") + target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$") + target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$") endif() endfunction()