feat: Restore Windows WebSocket functionality

This commit is contained in:
Ryan Hurey 2025-11-25 19:13:47 -08:00
parent 89fec2f168
commit 145eb4863e
2 changed files with 109 additions and 41 deletions

View File

@ -16,6 +16,7 @@
#include "azure/core/internal/unique_handle.hpp"
#include <memory>
#include <mutex>
#include <winhttp.h>
namespace Azure { namespace Core { namespace Http { namespace WebSockets {
@ -24,13 +25,13 @@ namespace Azure { namespace Core { namespace Http { namespace WebSockets {
*/
class WinHttpWebSocketTransport : public WebSocketTransport, public WinHttpTransport {
Azure::Core::_internal::UniqueHandle<void*> m_socketHandle; // HINTERNET is void*
Azure::Core::_internal::UniqueHandle<HINTERNET> m_socketHandle;
std::mutex m_sendMutex;
std::mutex m_receiveMutex;
// Fixed method signature to match current base class
void OnUpgradedConnection(
std::unique_ptr<_detail::WinHttpRequest> const& request) const override;
std::unique_ptr<_detail::WinHttpRequest> const& request) override;
public:
/**

View File

@ -8,6 +8,7 @@
#include "azure/core/internal/diagnostics/log.hpp"
#include "azure/core/platform.hpp"
#include "azure/core/request_failed_exception.hpp"
#include "win_http_request.hpp"
#if defined(AZ_PLATFORM_POSIX)
#include <poll.h> // for poll()
@ -21,22 +22,23 @@
#endif
#include <winapifamily.h>
#include <winsock2.h> // for WSAPoll();
#include <winhttp.h>
#endif
#include <shared_mutex>
namespace Azure { namespace Core { namespace Http { namespace WebSockets {
void WinHttpWebSocketTransport::OnUpgradedConnection(
std::unique_ptr<_detail::WinHttpRequest> const& request) const
std::unique_ptr<_detail::WinHttpRequest> const& request)
{
// TODO: Need to get HINTERNET handle from WinHttpRequest
// For now, this is a placeholder implementation to fix compilation
// The actual implementation needs to:
// 1. Get HINTERNET handle from request object
// 2. Call WinHttpWebSocketCompleteUpgrade
// 3. Store the WebSocket handle
// Convert the request handle into a WebSocket handle for us to use later.
m_socketHandle = Azure::Core::_internal::UniqueHandle<HINTERNET>(
WinHttpWebSocketCompleteUpgrade(request->GetRequestHandle(), 0));
throw Azure::Core::RequestFailedException("WebSocket upgrade not yet implemented on Windows");
if (!m_socketHandle)
{
GetErrorAndThrow("Error Upgrading HttpRequest handle to WebSocket handle.");
}
}
std::unique_ptr<Azure::Core::Http::RawResponse> WinHttpWebSocketTransport::Send(
@ -50,8 +52,10 @@ namespace Azure { namespace Core { namespace Http { namespace WebSockets {
* @brief Close the WebSocket cleanly.
*/
void WinHttpWebSocketTransport::Close() {
// TODO: Implement proper WebSocket close when Windows integration is complete
// m_socketHandle.reset();
if (m_socketHandle)
{
m_socketHandle.reset();
}
}
// Native WebSocket support methods.
@ -72,25 +76,22 @@ namespace Azure { namespace Core { namespace Http { namespace WebSockets {
{
context.ThrowIfCancelled();
// TODO: Windows WebSocket API integration needed
// auto err = WinHttpWebSocketClose(
// m_socketHandle.get(),
// status,
// disconnectReason.empty()
// ? nullptr
// : reinterpret_cast<PVOID>(const_cast<char*>(disconnectReason.c_str())),
// static_cast<DWORD>(disconnectReason.size()));
// if (err != 0)
// {
// throw Azure::Core::RequestFailedException("WinHttpWebSocketClose() failed");
// }
throw Azure::Core::RequestFailedException("Windows WebSocket native close not yet implemented");
auto err = WinHttpWebSocketClose(
m_socketHandle.get(),
status,
disconnectReason.empty()
? nullptr
: reinterpret_cast<PVOID>(const_cast<char*>(disconnectReason.c_str())),
static_cast<DWORD>(disconnectReason.size()));
if (err != 0)
{
GetErrorAndThrow("WinHttpWebSocketClose() failed", err);
}
context.ThrowIfCancelled();
// TODO: Windows WebSocket API integration needed
// auto closeInformation = NativeGetCloseSocketInformation(context);
(void)context;
// Make sure that the server responds gracefully to the close request.
auto closeInformation = NativeGetCloseSocketInformation(context);
// The server should return the same status we sent.
if (closeInformation.CloseReason != status)
@ -112,10 +113,28 @@ namespace Azure { namespace Core { namespace Http { namespace WebSockets {
WinHttpWebSocketTransport::NativeWebSocketCloseInformation
WinHttpWebSocketTransport::NativeGetCloseSocketInformation(Azure::Core::Context const& context)
{
(void)context;
// TODO: Windows WebSocket API integration needed
throw Azure::Core::RequestFailedException("Windows WebSocket get close info not yet implemented");
}
context.ThrowIfCancelled();
uint16_t closeStatus = 0;
// Handle missing constant definition in older Windows SDKs
#ifndef WINHTTP_WEB_SOCKET_MAX_CLOSE_REASON_LENGTH
#define WINHTTP_WEB_SOCKET_MAX_CLOSE_REASON_LENGTH 123
#endif
char closeReason[WINHTTP_WEB_SOCKET_MAX_CLOSE_REASON_LENGTH]{};
DWORD closeReasonLength;
auto err = WinHttpWebSocketQueryCloseStatus(
m_socketHandle.get(),
&closeStatus,
closeReason,
WINHTTP_WEB_SOCKET_MAX_CLOSE_REASON_LENGTH,
&closeReasonLength);
if (err != 0)
{
GetErrorAndThrow("WinHttpWebSocketQueryCloseStatus() failed", err);
}
return NativeWebSocketCloseInformation{closeStatus, std::string(closeReason)};
}
/**
@ -153,19 +172,67 @@ namespace Azure { namespace Core { namespace Http { namespace WebSockets {
"Unknown frame type: " + std::to_string(static_cast<uint32_t>(frameType)));
break;
}
// TODO: Windows WebSocket API integration needed
(void)frameType;
(void)frameData;
(void)context;
throw Azure::Core::RequestFailedException("Windows WebSocket send frame not yet implemented");
// Lock the socket to prevent concurrent writes. WinHTTP gets annoyed if
// there are multiple WinHttpWebSocketSend requests outstanding.
std::lock_guard<std::mutex> lock(m_sendMutex);
auto err = WinHttpWebSocketSend(
m_socketHandle.get(),
bufferType,
reinterpret_cast<PVOID>(const_cast<uint8_t*>(frameData.data())),
static_cast<DWORD>(frameData.size()));
if (err != 0)
{
GetErrorAndThrow("WinHttpWebSocketSend() failed", err);
}
}
WinHttpWebSocketTransport::NativeWebSocketReceiveInformation
WinHttpWebSocketTransport::NativeReceiveFrame(Azure::Core::Context const& context)
{
(void)context;
// TODO: Windows WebSocket API integration needed
throw Azure::Core::RequestFailedException("Windows WebSocket receive frame not yet implemented");
context.ThrowIfCancelled();
WINHTTP_WEB_SOCKET_BUFFER_TYPE bufferType;
NativeWebSocketFrameType frameTypeReceived;
DWORD bufferBytesRead;
std::vector<uint8_t> buffer(128);
std::lock_guard<std::mutex> lock(m_receiveMutex);
auto err = WinHttpWebSocketReceive(
m_socketHandle.get(),
reinterpret_cast<PVOID>(buffer.data()),
static_cast<DWORD>(buffer.size()),
&bufferBytesRead,
&bufferType);
if (err != 0 && err != ERROR_INSUFFICIENT_BUFFER)
{
GetErrorAndThrow("WinHttpWebSocketReceive() failed", err);
}
buffer.resize(bufferBytesRead);
switch (bufferType)
{
case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
frameTypeReceived = NativeWebSocketFrameType::Text;
break;
case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
frameTypeReceived = NativeWebSocketFrameType::Binary;
break;
case WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE:
frameTypeReceived = NativeWebSocketFrameType::BinaryFragment;
break;
case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
frameTypeReceived = NativeWebSocketFrameType::TextFragment;
break;
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
frameTypeReceived = NativeWebSocketFrameType::Closed;
break;
default:
throw std::runtime_error("Unknown frame type: " + std::to_string(bufferType));
break;
}
return NativeWebSocketReceiveInformation{frameTypeReceived, buffer};
}
}}}} // namespace Azure::Core::Http::WebSockets