Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Samples/GDK-Http/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 58 additions & 25 deletions Source/HTTP/WinHttp/winhttp_provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <XGameRuntimeFeature.h>
Expand Down Expand Up @@ -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<std::mutex> lock{ m_lock };
Expand Down Expand Up @@ -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<std::mutex> lock{ m_lock };
Expand Down Expand Up @@ -326,8 +327,25 @@ Result<XPlatSecurityInformation> WinHttpProvider::GetSecurityInformation(const c
#endif
}

Result<HINTERNET> WinHttpProvider::GetHSession(uint32_t securityProtocolFlags)
Result<HINTERNET> 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);
Comment thread
jasonsandlin marked this conversation as resolved.
}
#endif

std::lock_guard<std::mutex> lock(m_lock);
auto iter = m_hSessions.find(securityProtocolFlags);
if (iter != m_hSessions.end())
Expand All @@ -342,31 +360,40 @@ Result<HINTERNET> 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;
Comment thread
jasonsandlin marked this conversation as resolved.
}

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)
{
Expand All @@ -375,28 +402,34 @@ Result<HINTERNET> 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,
sizeof(enableFallback));
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())
Expand Down
2 changes: 1 addition & 1 deletion Source/HTTP/WinHttp/winhttp_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class WinHttpProvider
HRESULT CloseAllConnections();

Result<XPlatSecurityInformation> GetSecurityInformation(const char* url);
Result<HINTERNET> GetHSession(uint32_t securityProtolFlags);
Result<HINTERNET> 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);
Expand Down
52 changes: 52 additions & 0 deletions Tests/UnitTests/Tests/HttpTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include "../global/global.h"
#include <httpClient/httpProvider.h>

#if HC_PLATFORM == HC_PLATFORM_GDK
#include <winhttp.h>
#endif

#pragma warning(disable:4389)

using namespace xbox::httpclient;
Expand Down Expand Up @@ -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
Comment thread
jasonsandlin marked this conversation as resolved.
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