diff --git a/Samples/GDK-Http/Game.cpp b/Samples/GDK-Http/Game.cpp index 7a763034..422c2e47 100644 --- a/Samples/GDK-Http/Game.cpp +++ b/Samples/GDK-Http/Game.cpp @@ -454,6 +454,14 @@ void Game::Update(DX::StepTimer const& /*timer*/) } break; + case 4: + { + // Test HTTP (non-HTTPS) protocol to verify WinHTTP flag handling + url = "http://neverssl.com/"; + PerformHttpCall(url, "", false, "", false, false, false); + } + break; + default: { // All HttpCalls complete diff --git a/Source/HTTP/WinHttp/winhttp_provider.cpp b/Source/HTTP/WinHttp/winhttp_provider.cpp index 0dcd4a9a..bbadc892 100644 --- a/Source/HTTP/WinHttp/winhttp_provider.cpp +++ b/Source/HTTP/WinHttp/winhttp_provider.cpp @@ -2,6 +2,7 @@ #include "HTTP/httpcall.h" #include "winhttp_provider.h" #include "winhttp_connection.h" +#include "uri.h" #if HC_PLATFORM == HC_PLATFORM_GDK #include @@ -97,7 +98,7 @@ HRESULT WinHttpProvider::PerformAsync( RETURN_IF_FAILED(getSecurityInfoResult.hr); // Get HSession for the call - auto getHSessionResult = GetHSession(getSecurityInfoResult.Payload().enabledHttpSecurityProtocolFlags); + auto getHSessionResult = GetHSession(getSecurityInfoResult.Payload().enabledHttpSecurityProtocolFlags, callHandle->url.data()); RETURN_IF_FAILED(getHSessionResult.hr); std::unique_lock lock{ m_lock }; @@ -152,7 +153,7 @@ HRESULT WinHttpProvider::ConnectAsync( RETURN_IF_FAILED(getSecurityInfoResult.hr); // Get HSession for the call - auto getHSessionResult = GetHSession(getSecurityInfoResult.Payload().enabledHttpSecurityProtocolFlags); + auto getHSessionResult = GetHSession(getSecurityInfoResult.Payload().enabledHttpSecurityProtocolFlags, uri.data()); RETURN_IF_FAILED(getHSessionResult.hr); std::unique_lock lock{ m_lock }; @@ -326,8 +327,25 @@ Result WinHttpProvider::GetSecurityInformation(const c #endif } -Result WinHttpProvider::GetHSession(uint32_t securityProtocolFlags) +Result WinHttpProvider::GetHSession(uint32_t securityProtocolFlags, const char* url) { + // Parse URL to determine scheme + xbox::httpclient::Uri uri(url); + if (!uri.IsValid()) + { + return E_INVALIDARG; + } + + bool isHttps = uri.IsSecure(); + +#if HC_PLATFORM == HC_PLATFORM_GDK + // Log warning for insecure HTTP requests on GDK for console certification reasons + if (!isHttps) + { + HC_TRACE_WARNING(HTTPCLIENT, "WARNING: Insecure HTTP request \"%s\"", url); + } +#endif + std::lock_guard lock(m_lock); auto iter = m_hSessions.find(securityProtocolFlags); if (iter != m_hSessions.end()) @@ -342,31 +360,40 @@ Result WinHttpProvider::GetHSession(uint32_t securityProtocolFlags) m_proxyType = get_ie_proxy_info(proxy_protocol::https, proxyUri); GetProxyName(m_proxyType, proxyUri, accessType, wProxyName); + // Determine WinHTTP flags based on URL scheme + // Use WINHTTP_FLAG_SECURE_DEFAULTS for HTTPS and WINHTTP_FLAG_ASYNC for HTTP + DWORD openFlags; + if (isHttps) + { + // For HTTPS, use secure defaults which implies WINHTTP_FLAG_ASYNC + openFlags = WINHTTP_FLAG_SECURE_DEFAULTS; + } + else + { + // For HTTP, use async only (allow insecure connections) + openFlags = WINHTTP_FLAG_ASYNC; + } + HINTERNET hSession = WinHttpOpen( nullptr, accessType, wProxyName.length() > 0 ? wProxyName.c_str() : WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, -#if HC_PLATFORM == HC_PLATFORM_GDK - WINHTTP_FLAG_SECURE_DEFAULTS -#else - WINHTTP_FLAG_ASYNC -#endif + openFlags ); -#if HC_PLATFORM == HC_PLATFORM_GDK DWORD error = GetLastError(); - if (error == ERROR_INVALID_PARAMETER) + if (error == ERROR_INVALID_PARAMETER && isHttps) { - // This might happen on older Win10 PC versions that don't support WINHTTP_FLAG_SECURE_DEFAULTS + // WINHTTP_FLAG_SECURE_DEFAULTS exists only on newer Windows versions; + // on earlier OS releases we will receive ERROR_INVALID_PARAMETER and should continue without it. hSession = WinHttpOpen( nullptr, - WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, - WINHTTP_NO_PROXY_NAME, + accessType, + wProxyName.length() > 0 ? wProxyName.c_str() : WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC); } -#endif if (hSession == nullptr) { @@ -375,20 +402,26 @@ Result WinHttpProvider::GetHSession(uint32_t securityProtocolFlags) return hr; } - auto result = WinHttpSetOption( - hSession, - WINHTTP_OPTION_SECURE_PROTOCOLS, - &securityProtocolFlags, - sizeof(securityProtocolFlags)); - if (!result) + // Only set secure protocols for HTTPS requests + // For HTTP requests, ignore the security protocol settings as they don't apply + if (isHttps) { - HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); - HC_TRACE_ERROR_HR(HTTPCLIENT, hr, "WinHttpProvider WinHttpSetOption"); - return hr; + auto result = WinHttpSetOption( + hSession, + WINHTTP_OPTION_SECURE_PROTOCOLS, + &securityProtocolFlags, + sizeof(securityProtocolFlags)); + if (!result) + { + HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); + HC_TRACE_ERROR_HR(HTTPCLIENT, hr, "WinHttpProvider WinHttpSetOption WINHTTP_OPTION_SECURE_PROTOCOLS"); + WinHttpCloseHandle(hSession); + return hr; + } } BOOL enableFallback = TRUE; - result = WinHttpSetOption( + auto result = WinHttpSetOption( hSession, WINHTTP_OPTION_IPV6_FAST_FALLBACK, &enableFallback, @@ -396,7 +429,7 @@ Result WinHttpProvider::GetHSession(uint32_t securityProtocolFlags) if (!result) { HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); - HC_TRACE_WARNING_HR(HTTPCLIENT, hr, "WinHttpProvider WinHttpSetOption"); + HC_TRACE_WARNING_HR(HTTPCLIENT, hr, "WinHttpProvider WinHttpSetOption WINHTTP_OPTION_IPV6_FAST_FALLBACK"); } if (!m_globalProxy.empty()) diff --git a/Source/HTTP/WinHttp/winhttp_provider.h b/Source/HTTP/WinHttp/winhttp_provider.h index 698cd6b4..7ed94820 100644 --- a/Source/HTTP/WinHttp/winhttp_provider.h +++ b/Source/HTTP/WinHttp/winhttp_provider.h @@ -111,7 +111,7 @@ class WinHttpProvider HRESULT CloseAllConnections(); Result GetSecurityInformation(const char* url); - Result GetHSession(uint32_t securityProtolFlags); + Result GetHSession(uint32_t securityProtocolFlags, const char* url); static HRESULT SetGlobalProxyForHSession(HINTERNET hSession, const char* proxyUri); static HRESULT GetProxyName(_In_ proxy_type proxyType, _In_ Uri proxyUri, _Out_ DWORD& pAccessType, _Out_ http_internal_wstring& pwProxyName); diff --git a/Tests/UnitTests/Tests/HttpTests.cpp b/Tests/UnitTests/Tests/HttpTests.cpp index 3e9dee1f..f1751e52 100644 --- a/Tests/UnitTests/Tests/HttpTests.cpp +++ b/Tests/UnitTests/Tests/HttpTests.cpp @@ -9,6 +9,10 @@ #include "../global/global.h" #include +#if HC_PLATFORM == HC_PLATFORM_GDK +#include +#endif + #pragma warning(disable:4389) using namespace xbox::httpclient; @@ -449,6 +453,54 @@ DEFINE_TEST_CLASS(HttpTests) HCHttpCallCloseHandle(call); HCCleanup(); } + + DEFINE_TEST_CASE(TestHttpProtocol) + { + DEFINE_TEST_CASE_PROPERTIES(TestHttpProtocol); + + // Test to verify HTTP (non-HTTPS) requests work correctly + // + // Background: This test was added to ensure HTTP protocol support remains functional + // after changes to provider flag configuration that could potentially affect HTTP vs HTTPS handling. + + VERIFY_ARE_EQUAL(S_OK, HCInitialize(nullptr)); + + HCCallHandle call = nullptr; + VERIFY_ARE_EQUAL(S_OK, HCHttpCallCreate(&call)); + + // Use HTTP protocol to verify it works correctly + VERIFY_ARE_EQUAL(S_OK, HCHttpCallRequestSetUrl(call, "GET", "http://example.com")); + VERIFY_ARE_EQUAL(S_OK, HCHttpCallRequestSetRetryAllowed(call, false)); + + // Create a mock response for the HTTP call + HCMockCallHandle mockCall; + VERIFY_ARE_EQUAL(S_OK, HCMockCallCreate(&mockCall)); + VERIFY_ARE_EQUAL(S_OK, HCMockResponseSetStatusCode(mockCall, 200)); + std::string responseBody = "HTTP test successful"; + VERIFY_ARE_EQUAL(S_OK, HCMockResponseSetResponseBodyBytes(mockCall, (uint8_t*)responseBody.c_str(), (uint32_t)responseBody.length())); + VERIFY_ARE_EQUAL(S_OK, HCMockAddMock(mockCall, "GET", "http://example.com", nullptr, 0)); + + XAsyncBlock asyncBlock = {}; + asyncBlock.context = call; + + // Perform the HTTP call + VERIFY_ARE_EQUAL(S_OK, HCHttpCallPerformAsync(call, &asyncBlock)); + + // Wait for completion + HRESULT hr = XAsyncGetStatus(&asyncBlock, true); + + // Verify the HTTP request succeeded + VERIFY_ARE_EQUAL(S_OK, hr); + + // Verify we got a successful HTTP response + uint32_t statusCode = 0; + VERIFY_ARE_EQUAL(S_OK, HCHttpCallResponseGetStatusCode(call, &statusCode)); + VERIFY_ARE_EQUAL(200, statusCode); + + HCHttpCallCloseHandle(call); + HCCleanup(); + } + }; NAMESPACE_XBOX_HTTP_CLIENT_TEST_END