diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 6d0764fe46..dfd31ebb80 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -119,12 +119,17 @@ jobs: run: ctest --test-dir build --output-on-failure -L badssl windows: - name: ${{ matrix.os }} ${{ matrix.build_type }} + name: ${{ matrix.os }} ${{ matrix.build_type }}${{ matrix.http == 'asio' && ' Asio without WebSockets' || '' }} strategy: fail-fast: false matrix: os: [windows-2025-vs2026, windows-2022] build_type: [Debug, Release] + http: [default] + include: + - os: windows-2025-vs2026 + build_type: Release + http: asio runs-on: ${{ matrix.os }} defaults: run: @@ -173,6 +178,7 @@ jobs: run: >- cmake -B build -A x64 -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" + ${{ matrix.http == 'asio' && '-DCPPREST_HTTP_CLIENT_IMPL=asio -DCPPREST_HTTP_LISTENER_IMPL=asio -DCPPREST_EXCLUDE_WEBSOCKETS=ON' || '' }} - name: Build run: cmake --build build --config ${{ matrix.build_type }} diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt index e15aeb7fc3..52934ad2b0 100644 --- a/Release/src/CMakeLists.txt +++ b/Release/src/CMakeLists.txt @@ -106,7 +106,7 @@ elseif(CPPREST_PPLX_IMPL STREQUAL "linux") endif() elseif(CPPREST_PPLX_IMPL STREQUAL "win") target_sources(cpprest PRIVATE pplx/pplxwin.cpp) - if(CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp") + if(CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp" OR CPPREST_HTTP_CLIENT_IMPL STREQUAL "asio" OR CPPREST_HTTP_LISTENER_IMPL STREQUAL "asio") target_sources(cpprest PRIVATE pplx/threadpool.cpp ../include/pplx/threadpool.h) if(CPPREST_INSTALL_HEADERS) install(FILES ../include/pplx/threadpool.h DESTINATION include/pplx) diff --git a/Release/src/http/listener/http_server_asio.cpp b/Release/src/http/listener/http_server_asio.cpp index 7409864c7e..fe4549ada7 100644 --- a/Release/src/http/listener/http_server_asio.cpp +++ b/Release/src/http/listener/http_server_asio.cpp @@ -571,6 +571,8 @@ void asio_server_connection::shutdown() { m_close = true; boost::system::error_code ec; + // On Windows, shutdown does not complete a pending read, so cancel it + m_socket->cancel(ec); m_socket->shutdown(tcp::socket::shutdown_both, ec); get_request()._reply_if_not_already(status_codes::InternalError); } @@ -794,9 +796,22 @@ will_deref_and_erase_t asio_server_connection::handle_headers() auto colon = header.find(':'); if (colon != std::string::npos && colon != 0) { - auto name = utility::conversions::to_string_t(header.substr(0, colon)); - auto value = utility::conversions::to_string_t( - header.substr(colon + 1, header.length() - (colon + 1))); // also exclude '\r' + utility::string_t name; + utility::string_t value; + try + { + name = utility::conversions::to_string_t(header.substr(0, colon)); + value = utility::conversions::to_string_t( + header.substr(colon + 1, header.length() - (colon + 1))); // also exclude '\r' + } + catch (const std::range_error& e) // invalid Unicode + { + currentRequest.reply(status_codes::BadRequest, e.what()); + m_close = true; + (will_erase_from_parent_t) do_bad_response(); + (will_deref_t) deref(); + return will_deref_and_erase_t {}; + } web::http::details::trim_whitespace(name); web::http::details::trim_whitespace(value); diff --git a/Release/src/pplx/threadpool.cpp b/Release/src/pplx/threadpool.cpp index b7602df7b3..73943ef768 100644 --- a/Release/src/pplx/threadpool.cpp +++ b/Release/src/pplx/threadpool.cpp @@ -4,7 +4,7 @@ **/ #include "stdafx.h" -#if !defined(CPPREST_EXCLUDE_WEBSOCKETS) || !defined(_WIN32) +#if !defined(_WIN32) || !defined(CPPREST_EXCLUDE_WEBSOCKETS) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) #include "pplx/threadpool.h" #include #include @@ -232,4 +232,4 @@ std::unique_ptr crossplat::threadpool::construct(size_t n { return std::unique_ptr(new threadpool_impl(num_threads)); } -#endif // !defined(CPPREST_EXCLUDE_WEBSOCKETS) || !defined(_WIN32) +#endif // !defined(_WIN32) || !defined(CPPREST_EXCLUDE_WEBSOCKETS) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) diff --git a/Release/tests/functional/http/client/authentication_tests.cpp b/Release/tests/functional/http/client/authentication_tests.cpp index f75e8ab60e..c90046be37 100644 --- a/Release/tests/functional/http/client/authentication_tests.cpp +++ b/Release/tests/functional/http/client/authentication_tests.cpp @@ -58,7 +58,7 @@ namespace client { SUITE(authentication_tests) { - TEST_FIXTURE(uri_address, auth_no_data, "Ignore:Linux", "89", "Ignore:Apple", "89") + TEST_FIXTURE(uri_address, auth_no_data, IGNORE_IF_HTTP_CLIENT_ASIO, "89") { pplx::task t, t2; { @@ -115,7 +115,7 @@ SUITE(authentication_tests) // TFS 648783 #ifndef __cplusplus_winrt - TEST_FIXTURE(uri_address, proxy_auth_known_contentlength, "Ignore:Linux", "88", "Ignore:Apple", "88") + TEST_FIXTURE(uri_address, proxy_auth_known_contentlength, IGNORE_IF_HTTP_CLIENT_ASIO, "88") { pplx::task t, t2; { @@ -169,7 +169,7 @@ SUITE(authentication_tests) } #endif - TEST_FIXTURE(uri_address, proxy_auth_noseek, "Ignore:Linux", "88", "Ignore:Apple", "88") + TEST_FIXTURE(uri_address, proxy_auth_noseek, IGNORE_IF_HTTP_CLIENT_ASIO, "88") { web::http::uri uri(U("http://localhost:24567/")); test_http_server::scoped_server scoped(uri); @@ -200,7 +200,7 @@ SUITE(authentication_tests) // Must specify content length with winrt client, so this test case isn't possible. #ifndef __cplusplus_winrt - TEST_FIXTURE(uri_address, proxy_auth_unknown_contentlength, "Ignore:Linux", "88", "Ignore:Apple", "88") + TEST_FIXTURE(uri_address, proxy_auth_unknown_contentlength, IGNORE_IF_HTTP_CLIENT_ASIO, "88") { pplx::task t; { @@ -275,7 +275,7 @@ SUITE(authentication_tests) // Fails on WinRT due to TFS 648278 // Accessing a server that supports auth, but returns 401, even after the user has provided valid creds // We're making sure the error is reported properly, and the response data from the second response is received - TEST_FIXTURE(uri_address, error_after_valid_credentials, "Ignore:Linux", "89", "Ignore:Apple", "89") + TEST_FIXTURE(uri_address, error_after_valid_credentials, IGNORE_IF_HTTP_CLIENT_ASIO, "89") { pplx::task t; { @@ -477,7 +477,7 @@ SUITE(authentication_tests) } #endif // __cplusplus_winrt -#ifdef _WIN32 +#if defined(_WIN32) && !defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) #if !defined(__cplusplus_winrt) TEST_FIXTURE(server_properties, set_user_options, "Requires", "Server;UserName;Password") { @@ -633,7 +633,7 @@ SUITE(authentication_tests) http_client client(m_uri, config); VERIFY_THROWS(client.request(methods::GET).get(), std::runtime_error); } -#endif // _WIN32 +#endif // defined(_WIN32) && !defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) // Fix for 522831 AV after failed authentication attempt // apis.live.net now returns 400 Bad Request instead of 401 Unauthorized diff --git a/Release/tests/functional/http/listener/connections_and_errors.cpp b/Release/tests/functional/http/listener/connections_and_errors.cpp index 87f4a7cee6..802010b8a3 100644 --- a/Release/tests/functional/http/listener/connections_and_errors.cpp +++ b/Release/tests/functional/http/listener/connections_and_errors.cpp @@ -310,7 +310,7 @@ SUITE(connections_and_errors) VERIFY_THROWS(listener.open().wait(), http_exception); } - TEST_FIXTURE(uri_address, try_port_already_in_use, "Ignore:Linux", "Bug 879077", "Ignore:Apple", "Bug 879077") + TEST_FIXTURE(uri_address, try_port_already_in_use, IGNORE_IF_HTTP_LISTENER_ASIO, "Bug 879077") { test_http_server::scoped_server scoped(m_uri); http_listener listener(m_uri); @@ -450,12 +450,7 @@ SUITE(connections_and_errors) return errorCode; } - TEST_FIXTURE(uri_address, - request_content_ready_timeout, - "Ignore:Linux", - "Unsuitable until 813276", - "Ignore:Apple", - "Unsuitable until 813276") + TEST_FIXTURE(uri_address, request_content_ready_timeout, IGNORE_IF_HTTP_LISTENER_ASIO, "Unsuitable until 813276") { #if !defined(_WIN32) || defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) throw std::runtime_error( diff --git a/Release/tests/functional/http/utilities/include/http_test_utilities.h b/Release/tests/functional/http/utilities/include/http_test_utilities.h index 2f66e56a88..36428f3f0d 100644 --- a/Release/tests/functional/http/utilities/include/http_test_utilities.h +++ b/Release/tests/functional/http/utilities/include/http_test_utilities.h @@ -16,3 +16,16 @@ #include "test_http_client.h" #include "test_http_server.h" #include "test_server_utilities.h" + +// Test properties that ignore a test when the library uses the Asio HTTP client or listener, on any platform. +// The test runner cannot tell which implementations the library uses. +#if defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) +#define IGNORE_IF_HTTP_CLIENT_ASIO "Ignore" +#else +#define IGNORE_IF_HTTP_CLIENT_ASIO "Ignore:None" +#endif +#if defined(CPPREST_FORCE_HTTP_LISTENER_ASIO) +#define IGNORE_IF_HTTP_LISTENER_ASIO "Ignore" +#else +#define IGNORE_IF_HTTP_LISTENER_ASIO "Ignore:None" +#endif