aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/servers/httpsys.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenhttp/servers/httpsys.cpp')
-rw-r--r--src/zenhttp/servers/httpsys.cpp312
1 files changed, 81 insertions, 231 deletions
diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp
index 89d93e258..23d57af57 100644
--- a/src/zenhttp/servers/httpsys.cpp
+++ b/src/zenhttp/servers/httpsys.cpp
@@ -162,9 +162,7 @@ private:
# include <conio.h>
# include <mstcpip.h>
-# include <websocket.h>
# pragma comment(lib, "httpapi.lib")
-# pragma comment(lib, "websocket.lib")
std::wstring
UTF8_to_UTF16(const char* InPtr)
@@ -2133,258 +2131,110 @@ InitialRequestHandler::HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesT
{
HTTP_REQUEST* HttpReq = HttpRequest();
-# if 0
- for (int i = 0; i < HttpReq->RequestInfoCount; ++i)
- {
- auto& ReqInfo = HttpReq->pRequestInfo[i];
-
- switch (ReqInfo.InfoType)
- {
- case HttpRequestInfoTypeRequestTiming:
- {
- const HTTP_REQUEST_TIMING_INFO* TimingInfo = reinterpret_cast<HTTP_REQUEST_TIMING_INFO*>(ReqInfo.pInfo);
-
- ZEN_INFO("");
- }
- break;
- case HttpRequestInfoTypeAuth:
- ZEN_INFO("");
- break;
- case HttpRequestInfoTypeChannelBind:
- ZEN_INFO("");
- break;
- case HttpRequestInfoTypeSslProtocol:
- ZEN_INFO("");
- break;
- case HttpRequestInfoTypeSslTokenBindingDraft:
- ZEN_INFO("");
- break;
- case HttpRequestInfoTypeSslTokenBinding:
- ZEN_INFO("");
- break;
- case HttpRequestInfoTypeTcpInfoV0:
- {
- const TCP_INFO_v0* TcpInfo = reinterpret_cast<const TCP_INFO_v0*>(ReqInfo.pInfo);
-
- ZEN_INFO("");
- }
- break;
- case HttpRequestInfoTypeRequestSizing:
- {
- const HTTP_REQUEST_SIZING_INFO* SizingInfo = reinterpret_cast<const HTTP_REQUEST_SIZING_INFO*>(ReqInfo.pInfo);
- ZEN_INFO("");
- }
- break;
- case HttpRequestInfoTypeQuicStats:
- ZEN_INFO("");
- break;
- case HttpRequestInfoTypeTcpInfoV1:
- {
- const TCP_INFO_v1* TcpInfo = reinterpret_cast<const TCP_INFO_v1*>(ReqInfo.pInfo);
-
- ZEN_INFO("");
- }
- break;
- }
- }
-# endif
-
if (HttpService* Service = reinterpret_cast<HttpService*>(HttpReq->UrlContext))
{
// WebSocket upgrade detection
if (m_IsInitialRequest)
{
- auto& UpgradeHeader = HttpReq->Headers.KnownHeaders[HttpHeaderUpgrade];
+ const HTTP_KNOWN_HEADER& UpgradeHeader = HttpReq->Headers.KnownHeaders[HttpHeaderUpgrade];
if (UpgradeHeader.RawValueLength > 0 &&
StrCaseCompare(UpgradeHeader.pRawValue, "websocket", UpgradeHeader.RawValueLength) == 0)
{
if (IWebSocketHandler* WsHandler = dynamic_cast<IWebSocketHandler*>(Service))
{
- // Collect all request headers (known + unknown) for WebSocketBeginServerHandshake
- eastl::fixed_vector<WEB_SOCKET_HTTP_HEADER, 32> RequestHeaders;
-
- // Known headers
- static const char* KnownHeaderNames[] = {"Cache-Control",
- "Connection",
- "Date",
- "Keep-Alive",
- "Pragma",
- "Trailer",
- "Transfer-Encoding",
- "Upgrade",
- "Via",
- "Warning",
- "Allow",
- "Content-Length",
- "Content-Type",
- "Content-Encoding",
- "Content-Language",
- "Content-Location",
- "Content-MD5",
- "Content-Range",
- "Expires",
- "Last-Modified",
- "Accept",
- "Accept-Charset",
- "Accept-Encoding",
- "Accept-Language",
- "Authorization",
- "Cookie",
- "Expect",
- "From",
- "Host",
- "If-Match",
- "If-Modified-Since",
- "If-None-Match",
- "If-Range",
- "If-Unmodified-Since",
- "Max-Forwards",
- "Proxy-Authorization",
- "Referer",
- "Range",
- "TE",
- "Translate",
- "User-Agent"};
-
- for (int i = 0; i < HttpHeaderRequestMaximum; ++i)
+ // Extract Sec-WebSocket-Key from the unknown headers
+ // (http.sys has no known-header slot for it)
+ std::string_view SecWebSocketKey;
+ for (USHORT i = 0; i < HttpReq->Headers.UnknownHeaderCount; ++i)
{
- auto& Hdr = HttpReq->Headers.KnownHeaders[i];
- if (Hdr.RawValueLength > 0)
+ const HTTP_UNKNOWN_HEADER& Hdr = HttpReq->Headers.pUnknownHeaders[i];
+ if (Hdr.NameLength == 17 && _strnicmp(Hdr.pName, "Sec-WebSocket-Key", 17) == 0)
{
- WEB_SOCKET_HTTP_HEADER WsHdr;
- WsHdr.pcName = const_cast<PCHAR>(KnownHeaderNames[i]);
- WsHdr.ulNameLength = (ULONG)strlen(KnownHeaderNames[i]);
- WsHdr.pcValue = const_cast<PCHAR>(Hdr.pRawValue);
- WsHdr.ulValueLength = Hdr.RawValueLength;
- RequestHeaders.push_back(WsHdr);
+ SecWebSocketKey = std::string_view(Hdr.pRawValue, Hdr.RawValueLength);
+ break;
}
}
- // Unknown headers
- for (USHORT i = 0; i < HttpReq->Headers.UnknownHeaderCount; ++i)
+ if (SecWebSocketKey.empty())
{
- auto& Hdr = HttpReq->Headers.pUnknownHeaders[i];
- WEB_SOCKET_HTTP_HEADER WsHdr;
- WsHdr.pcName = const_cast<PCHAR>(Hdr.pName);
- WsHdr.ulNameLength = Hdr.NameLength;
- WsHdr.pcValue = const_cast<PCHAR>(Hdr.pRawValue);
- WsHdr.ulValueLength = Hdr.RawValueLength;
- RequestHeaders.push_back(WsHdr);
+ ZEN_WARN("WebSocket upgrade missing Sec-WebSocket-Key header");
+ return nullptr;
}
- // Use Windows WebSocket Protocol Component API for the handshake.
- // This produces the correct response headers that http.sys expects
- // when the OPAQUE flag is used.
- WEB_SOCKET_HANDLE WsHandle = nullptr;
- HRESULT Hr = WebSocketCreateServerHandle(nullptr, 0, &WsHandle);
- if (SUCCEEDED(Hr))
- {
- PWEB_SOCKET_HTTP_HEADER ResponseHeaders = nullptr;
- ULONG ResponseHeaderCount = 0;
-
- Hr = WebSocketBeginServerHandshake(WsHandle,
- nullptr, // no subprotocol
- nullptr,
- 0, // no extensions
- RequestHeaders.data(),
- (ULONG)RequestHeaders.size(),
- &ResponseHeaders,
- &ResponseHeaderCount);
-
- if (SUCCEEDED(Hr))
- {
- HANDLE RequestQueueHandle = Transaction().RequestQueueHandle();
- HTTP_REQUEST_ID RequestId = HttpReq->RequestId;
-
- // Build the 101 response with headers from the WS handshake API
- HTTP_RESPONSE Response = {};
- Response.StatusCode = 101;
- Response.pReason = "Switching Protocols";
- Response.ReasonLength = (USHORT)strlen(Response.pReason);
-
- // Convert WEB_SOCKET_HTTP_HEADER[] to HTTP_UNKNOWN_HEADER[]
- eastl::fixed_vector<HTTP_UNKNOWN_HEADER, 8> UnknownHeaders;
- for (ULONG i = 0; i < ResponseHeaderCount; ++i)
- {
- if (_strnicmp(ResponseHeaders[i].pcName, "Upgrade", ResponseHeaders[i].ulNameLength) == 0)
- {
- Response.Headers.KnownHeaders[HttpHeaderUpgrade].pRawValue = ResponseHeaders[i].pcValue;
- Response.Headers.KnownHeaders[HttpHeaderUpgrade].RawValueLength =
- (USHORT)ResponseHeaders[i].ulValueLength;
- }
- else
- {
- HTTP_UNKNOWN_HEADER UH = {};
- UH.pName = ResponseHeaders[i].pcName;
- UH.NameLength = (USHORT)ResponseHeaders[i].ulNameLength;
- UH.pRawValue = ResponseHeaders[i].pcValue;
- UH.RawValueLength = (USHORT)ResponseHeaders[i].ulValueLength;
- UnknownHeaders.push_back(UH);
- }
- }
-
- Response.Headers.UnknownHeaderCount = (USHORT)UnknownHeaders.size();
- Response.Headers.pUnknownHeaders = UnknownHeaders.data();
-
- ULONG Flags = HTTP_SEND_RESPONSE_FLAG_OPAQUE | HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
-
- // Use an OVERLAPPED with an event so we can wait synchronously.
- // The request queue is IOCP-associated, so passing NULL for pOverlapped
- // may return ERROR_IO_PENDING. Setting the low-order bit of hEvent
- // prevents IOCP delivery and lets us wait on the event directly.
- OVERLAPPED SendOverlapped = {};
- HANDLE SendEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
- SendOverlapped.hEvent = (HANDLE)((uintptr_t)SendEvent | 1);
-
- ULONG SendResult = HttpSendHttpResponse(RequestQueueHandle,
- RequestId,
- Flags,
- &Response,
- nullptr, // CachePolicy
- nullptr, // BytesSent
- nullptr, // Reserved1
- 0, // Reserved2
- &SendOverlapped,
- nullptr // LogData
- );
-
- if (SendResult == ERROR_IO_PENDING)
- {
- WaitForSingleObject(SendEvent, INFINITE);
- SendResult = (SendOverlapped.Internal == 0) ? NO_ERROR : ERROR_IO_INCOMPLETE;
- }
-
- CloseHandle(SendEvent);
-
- if (SendResult == NO_ERROR)
- {
- WebSocketEndServerHandshake(WsHandle);
- WebSocketDeleteHandle(WsHandle);
-
- Ref<WsHttpSysConnection> WsConn(
- new WsHttpSysConnection(RequestQueueHandle, RequestId, *WsHandler, Transaction().Iocp()));
- Ref<WebSocketConnection> WsConnRef(WsConn.Get());
-
- WsHandler->OnWebSocketOpen(std::move(WsConnRef));
- WsConn->Start();
-
- return nullptr;
- }
-
- ZEN_WARN("WebSocket 101 send failed: {}", SendResult);
- }
- else
- {
- ZEN_WARN("WebSocketBeginServerHandshake failed: {:#x}", (uint32_t)Hr);
- }
+ const std::string AcceptKey = WsFrameCodec::ComputeAcceptKey(SecWebSocketKey);
+
+ HANDLE RequestQueueHandle = Transaction().RequestQueueHandle();
+ HTTP_REQUEST_ID RequestId = HttpReq->RequestId;
+
+ // Build the 101 Switching Protocols response
+ HTTP_RESPONSE Response = {};
+ Response.StatusCode = 101;
+ Response.pReason = "Switching Protocols";
+ Response.ReasonLength = (USHORT)strlen(Response.pReason);
+
+ Response.Headers.KnownHeaders[HttpHeaderUpgrade].pRawValue = "websocket";
+ Response.Headers.KnownHeaders[HttpHeaderUpgrade].RawValueLength = 9;
+
+ eastl::fixed_vector<HTTP_UNKNOWN_HEADER, 8> UnknownHeaders;
+
+ // IMPORTANT: Due to some quirk in HttpSendHttpResponse, this cannot use KnownHeaders
+ // despite there being an entry for it there (HttpHeaderConnection). If you try to do
+ // that you get an ERROR_INVALID_PARAMETERS error from HttpSendHttpResponse below
- WebSocketDeleteHandle(WsHandle);
+ UnknownHeaders.push_back({.NameLength = 10, .RawValueLength = 7, .pName = "Connection", .pRawValue = "Upgrade"});
+
+ UnknownHeaders.push_back({.NameLength = 20,
+ .RawValueLength = (USHORT)AcceptKey.size(),
+ .pName = "Sec-WebSocket-Accept",
+ .pRawValue = AcceptKey.c_str()});
+
+ Response.Headers.UnknownHeaderCount = (USHORT)UnknownHeaders.size();
+ Response.Headers.pUnknownHeaders = UnknownHeaders.data();
+
+ const ULONG Flags = HTTP_SEND_RESPONSE_FLAG_OPAQUE | HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
+
+ // Use an OVERLAPPED with an event so we can wait synchronously.
+ // The request queue is IOCP-associated, so passing NULL for pOverlapped
+ // may return ERROR_IO_PENDING. Setting the low-order bit of hEvent
+ // prevents IOCP delivery and lets us wait on the event directly.
+ OVERLAPPED SendOverlapped = {};
+ HANDLE SendEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ SendOverlapped.hEvent = (HANDLE)((uintptr_t)SendEvent | 1);
+
+ ULONG SendResult = HttpSendHttpResponse(RequestQueueHandle,
+ RequestId,
+ Flags,
+ &Response,
+ nullptr, // CachePolicy
+ nullptr, // BytesSent
+ nullptr, // Reserved1
+ 0, // Reserved2
+ &SendOverlapped,
+ nullptr // LogData
+ );
+
+ if (SendResult == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(SendEvent, INFINITE);
+ SendResult = (SendOverlapped.Internal == 0) ? NO_ERROR : ERROR_IO_INCOMPLETE;
}
- else
+
+ CloseHandle(SendEvent);
+
+ if (SendResult == NO_ERROR)
{
- ZEN_WARN("WebSocketCreateServerHandle failed: {:#x}", (uint32_t)Hr);
+ Ref<WsHttpSysConnection> WsConn(
+ new WsHttpSysConnection(RequestQueueHandle, RequestId, *WsHandler, Transaction().Iocp()));
+ Ref<WebSocketConnection> WsConnRef(WsConn.Get());
+
+ WsHandler->OnWebSocketOpen(std::move(WsConnRef));
+ WsConn->Start();
+
+ return nullptr;
}
+ ZEN_WARN("WebSocket 101 send failed: {}", SendResult);
+
// WebSocket upgrade failed — return nullptr since ServerRequest()
// was never populated (no InvokeRequestHandler call)
return nullptr;