Hi @Shyam Butani ,
Thank you for the details!
I looked through your flow and here’s what I think why the connection closes with 1229:
- After sending headers with
HttpSendHttpResponse, you call it again for each SSE event. Once headers are sent, HTTP.sys expects body-only writes; re-sending headers causesERROR_HTTP_INVALID_HEADER / ERROR_CONNECTION_INVALID. The correct flow is: headers once, then stream body via HttpSendResponseEntityBody. - Also, your first send uses
flags = 0, it should includeHTTP_SEND_RESPONSE_FLAG_MORE_DATAto keep the response open. Finally, dropConnection: keep-alive(invalid on HTTP/2, here's why) and usetext/event-stream; charset=utf-8per SSE spec.
Here is what I would try:
1. Send the headers only once, and mark the response as streaming.
First call:
ULONG flags = HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
HTTP_RESPONSE resp{};
resp.StatusCode = 200;
resp.pReason = "OK";
resp.ReasonLength = (USHORT)strlen("OK");
resp.Headers.KnownHeaders[HttpHeaderContentType].pRawValue = "text/event-stream; charset=utf-8";
resp.Headers.KnownHeaders[HttpHeaderContentType].RawValueLength = (USHORT)strlen("text/event-stream; charset=utf-8");
resp.Headers.KnownHeaders[HttpHeaderCacheControl].pRawValue = "no-cache";
resp.Headers.KnownHeaders[HttpHeaderCacheControl].RawValueLength = (USHORT)strlen("no-cache");
// DO NOT set Connection
ULONG result = HttpSendHttpResponse(g_reqQueue, request->RequestId, flags,
&resp, NULL, NULL, NULL, 0, NULL, NULL);
This tells HTTP.sys: more entity-body data will follow later.
2. Stream each SSE event with HttpSendResponseEntityBody (not HttpSendHttpResponse)
Inside your loop:
std::string evt = "data: {\"message\":\"Hello from SSE, count: " + std::to_string(i) + "\"}\n\n";
HTTP_DATA_CHUNK chunk{};
chunk.DataChunkType = HttpDataChunkFromMemory;
chunk.FromMemory.pBuffer = (PVOID)evt.data();
chunk.FromMemory.BufferLength = (ULONG)evt.size();
ULONG flags = HTTP_SEND_RESPONSE_FLAG_MORE_DATA; // keep streaming
result = HttpSendResponseEntityBody(g_reqQueue, request->RequestId, flags,
1, &chunk, NULL, NULL, 0, NULL, NULL);
Use MORE_DATA for every non-final chunk; on the last chunk, omit the flag so HTTP.sys finalizes the response.
3. Remove Connection: keep-alive from your headers.
It’s unnecessary for HTTP/1.1 and invalid for HTTP/2; letting HTTP.sys manage the connection avoids protocol errors.
4. Don’t send Content-Length.
When you stream with MORE_DATA, HTTP.sys handles the semantics; you don’t need (and shouldn’t set) Content-Length. The official overview shows the intended sequence: headers via HttpSendHttpResponse, then body via HttpSendResponseEntityBody.
Inside your event loop you call:
result = HttpSendHttpResponse(g_reqQueue, request->RequestId, 0, &response, ...);
This might be the core issue, switch this to HttpSendResponseEntityBody and stop re-sending headers. Also, on your very first HttpSendHttpResponse, set HTTP_SEND_RESPONSE_FLAG_MORE_DATA.
Disclaimer: Some of these links are not from Microsoft official documentation so only use for information. Do not click on or download any strange links that you might encounter.
I hope my suggestions help! If you have any questions, please comment below. I'll be happy to help!