diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 6d2521b016..000516e1be 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_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_winhttppal.cmake b/Release/cmake/cpprest_find_winhttppal.cmake new file mode 100644 index 0000000000..9a6840fba2 --- /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 8b5e8a6ff3..be95abf99a 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_WINHTTPPAL@) + find_dependency(WINHTTPPAL) +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/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index 0bedfbd42a..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/CMakeLists.txt b/Release/src/CMakeLists.txt index 119b07afe2..c9d0349a87 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 "winhttppal") + cpprest_find_boost() + cpprest_find_openssl() + cpprest_find_winhttppal() + target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) + 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 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_WINHTTPPAL 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_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) 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_impl.h b/Release/src/http/client/http_client_impl.h index 384c9b2de6..d9e7d4829e 100644 --- a/Release/src/http/client/http_client_impl.h +++ b/Release/src/http/client/http_client_impl.h @@ -30,13 +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_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/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 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/outside_tests.cpp b/Release/tests/functional/http/client/outside_tests.cpp index 3ff1a809ec..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) +#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) +#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 c856b600e3..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"); -#ifdef __cplusplus_winrt +#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"); -#ifdef __cplusplus_winrt +#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"); -#ifdef __cplusplus_winrt +#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 8fc6f54365..a5e397d0e8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -214,3 +214,47 @@ jobs: # cd Build_iOS # ./configure.sh # displayName: 'Build for iOS' + - job: Ubuntu_1604_Apt_winhttppal + pool: + 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 + 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 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 + 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 + sudo make install + cd ../.. + mkdir build.debug + cd build.debug + /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=winhttppal .. + 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