-
Notifications
You must be signed in to change notification settings - Fork 0
API SSE
命名空间:wknet::sse
头文件:wknet/sse/Sse.h · 配置类型在 wknet/http/Types.h 与 Sse.h 内并列
Connect / Receive / Close,依赖已有 http::Session。实现为 WHATWG text/event-stream 客户端:解析事件、跟踪 Last-Event-ID、可选断线自动重连。
ConnectConfig DefaultConnectConfig() noexcept;
struct ConnectConfig final {
const char* Url = nullptr;
SIZE_T UrlLength = 0;
const Header* Headers = nullptr;
SIZE_T HeaderCount = 0;
wknet::http::TlsConfig Tls = {};
wknet::http::AddressFamily Family = wknet::http::AddressFamily::Any;
const char* LastEventId = nullptr;
SIZE_T LastEventIdLength = 0;
bool AutoReconnect = true;
ULONG MaxReconnectAttempts = 0; // 0 = 直到 Close 无上限
ULONG InitialReconnectDelayMs = 1000;
ULONG MaxReconnectDelayMs = 30000;
ULONG ConnectTimeoutMs = 30000;
ULONG IdleTimeoutMs = 0; // 0 = 不限 body 空闲
ULONG ReceiveTimeoutMs = 0; // 0 = 不限单次 Receive 等待
SIZE_T MaxEventBytes = 1 * 1024 * 1024;
SIZE_T MaxParserBufferBytes = 256 * 1024;
bool RequireEventStreamContentType = true;
EventCallback OnEvent = nullptr;
ReconnectCallback OnReconnect = nullptr;
void* CallbackContext = nullptr;
};| 字段 | 说明 |
|---|---|
Url / UrlLength
|
http:// 或 https:// 流端点(GET only) |
Headers / HeaderCount
|
调用方 opening 头(如 Authorization);库控制头 Accept / Last-Event-ID / Cache-Control 冲突时拒绝 |
Tls / Family
|
HTTPS TLS 与地址族 |
LastEventId |
初始 Last-Event-ID(可选) |
AutoReconnect |
断线后自动重连;4xx open 失败不重连 |
MaxReconnectAttempts |
0 表示直到 Close
|
InitialReconnectDelayMs / MaxReconnectDelayMs
|
指数退避;尊重服务端 retry: 字段 |
ConnectTimeoutMs |
等到响应头 / open 完成的上限 |
IdleTimeoutMs |
body 字节空闲超时;0 禁用 |
ReceiveTimeoutMs |
单次 Receive 等待事件上限;0 不限 |
MaxEventBytes / MaxParserBufferBytes
|
单事件与解析缓冲上限 |
RequireEventStreamContentType |
要求 Content-Type 以 text/event-stream 开头 |
OnEvent / OnReconnect
|
可选回调(在交付路径上调用) |
struct Event final {
const char* Type = nullptr; SIZE_T TypeLength = 0; // 默认 "message"
const char* Data = nullptr; SIZE_T DataLength = 0; // 多行 data 以 \n 拼接
const char* Id = nullptr; SIZE_T IdLength = 0;
};
struct Header final {
const char* Name = nullptr; SIZE_T NameLength = 0;
const char* Value = nullptr; SIZE_T ValueLength = 0;
};
typedef NTSTATUS (*EventCallback)(void* context, const Event* event);
typedef void (*ReconnectCallback)(
void* context, ULONG attempt, ULONG delayMs, NTSTATUS lastError,
const char* lastEventId, SIZE_T lastEventIdLength);
struct ReceiveOptions final {
SIZE_T MaxEventBytes = 0;
EventCallback OnEvent = nullptr;
void* CallbackContext = nullptr;
};NTSTATUS Connect(
_In_ wknet::http::Session* session,
_In_reads_bytes_(urlLength) const char* url, SIZE_T urlLength,
_Out_ SseClient** client) noexcept;
NTSTATUS Connect(
_In_ wknet::http::Session* session,
_In_ const ConnectConfig* config,
_Out_ SseClient** client) noexcept;
NTSTATUS ConnectEx(
_In_ wknet::http::Session* session,
_In_ const ConnectConfig* config,
_Out_ SseClient** client) noexcept;
NTSTATUS ConnectAsync(...); // 返回 AsyncOp*
NTSTATUS ConnectAsyncEx(...);
NTSTATUS AsyncGetSseClient(
_In_ wknet::http::AsyncOp* operation,
_Out_ SseClient** client) noexcept;成功时 *client 有效:已收到 2xx 且(若要求)Content-Type 可接受。库注入 Accept: text/event-stream 与 Cache-Control: no-cache;有 last-event-id 时注入 Last-Event-ID。
| 典型失败 | 含义 |
|---|---|
STATUS_INVALID_PARAMETER |
URL/头/地址族非法,或调用方覆盖库控制头 |
STATUS_INVALID_NETWORK_RESPONSE |
非 event-stream Content-Type(当 Require 时) |
STATUS_ACCESS_DENIED |
HTTP 4xx(不自动重连) |
STATUS_IO_TIMEOUT |
open 超时 |
NTSTATUS Receive(_In_ SseClient* client, _Out_ Event* event) noexcept;
NTSTATUS ReceiveEx(
_In_ SseClient* client,
_In_opt_ const ReceiveOptions* options,
_Out_opt_ Event* event) noexcept;阻塞直到:
- 有完整事件可交付 →
STATUS_SUCCESS,Event字段指向客户端内部缓冲(下次成功 Receive / Close 前有效);或 - 流结束且不可/不再重连 → 断开或最终错误状态;或
-
ReceiveTimeoutMs到期 →STATUS_IO_TIMEOUT;或 -
Close→STATUS_CANCELLED。
Event.Type 缺省为 "message"。多行 data: 以 \n 拼接。id: 更新 last-event-id;含 \0 的 id 按规范忽略。retry: 影响后续重连 delay。
NTSTATUS GetLastEventId(
_In_ SseClient* client,
_Outptr_result_bytebuffer_(*idLength) const char** id,
_Out_ SIZE_T* idLength) noexcept;
NTSTATUS GetReconnectAttempt(
_In_ SseClient* client,
_Out_ ULONG* attempt) noexcept;
NTSTATUS Close(_In_opt_ SseClient* client) noexcept;Close 接受 nullptr,可中止阻塞 Receive 与重连 delay。关闭后句柄失效。
| 主题 | 行为 |
|---|---|
| 方法 | 仅 GET |
| Content-Encoding | 首版要求 identity(非 identity 在流路径拒绝) |
| 协议 | 与 Session HTTP 栈一致,可走 H1 / H2 / H3(受 Session / TLS / Alt-Svc 策略约束) |
| 重连 | open 失败 4xx 不重连;其它可恢复断开可重连并带 Last-Event-ID
|
| 连接策略 | 每次 open 使用 ForceNew,避免半死 keep-alive |
| 并发 | 同一 SseClient 不要多线程并行 Receive
|
| IRQL | 全部入口 PASSIVE_LEVEL
|
SSE 建立在 session 真增量响应体之上:SendOptions.OnBody 在启用时会按到达顺序多次回调,finalChunk 仅在真正结束时为 true。若只要聚合 body,不要设 OnBody。详见 同步 HTTP。
Namespace: wknet::sse
Headers: wknet/sse/Sse.h · types live in Sse.h (alongside wknet/http/Types.h patterns)
Connect / Receive / Close on an existing http::Session. WHATWG text/event-stream client: parse events, track Last-Event-ID, optional auto-reconnect.
ConnectConfig DefaultConnectConfig() noexcept;
struct ConnectConfig final {
const char* Url = nullptr;
SIZE_T UrlLength = 0;
const Header* Headers = nullptr;
SIZE_T HeaderCount = 0;
wknet::http::TlsConfig Tls = {};
wknet::http::AddressFamily Family = wknet::http::AddressFamily::Any;
const char* LastEventId = nullptr;
SIZE_T LastEventIdLength = 0;
bool AutoReconnect = true;
ULONG MaxReconnectAttempts = 0; // 0 = unlimited until Close
ULONG InitialReconnectDelayMs = 1000;
ULONG MaxReconnectDelayMs = 30000;
ULONG ConnectTimeoutMs = 30000;
ULONG IdleTimeoutMs = 0; // 0 = no body idle limit
ULONG ReceiveTimeoutMs = 0; // 0 = no single Receive wait limit
SIZE_T MaxEventBytes = 1 * 1024 * 1024;
SIZE_T MaxParserBufferBytes = 256 * 1024;
bool RequireEventStreamContentType = true;
EventCallback OnEvent = nullptr;
ReconnectCallback OnReconnect = nullptr;
void* CallbackContext = nullptr;
};| Field | Notes |
|---|---|
Url / UrlLength
|
http:// or https:// stream endpoint (GET only) |
Headers / HeaderCount
|
Caller opening headers (e.g. Authorization); library-controlled Accept / Last-Event-ID / Cache-Control conflicts are rejected |
Tls / Family
|
HTTPS TLS and address family |
LastEventId |
Initial Last-Event-ID (optional) |
AutoReconnect |
Reconnect after disconnect; 4xx open failures never reconnect |
MaxReconnectAttempts |
0 means until Close
|
InitialReconnectDelayMs / MaxReconnectDelayMs
|
Exponential backoff; honors server retry:
|
ConnectTimeoutMs |
Wait for response headers / open |
IdleTimeoutMs |
Idle gap between body bytes; 0 disables |
ReceiveTimeoutMs |
Per-Receive wait; 0 unlimited |
MaxEventBytes / MaxParserBufferBytes
|
Per-event and parser buffer caps |
RequireEventStreamContentType |
Require Content-Type prefix text/event-stream
|
OnEvent / OnReconnect
|
Optional callbacks on the delivery path |
struct Event final {
const char* Type = nullptr; SIZE_T TypeLength = 0; // default "message"
const char* Data = nullptr; SIZE_T DataLength = 0; // multi-line data joined with \n
const char* Id = nullptr; SIZE_T IdLength = 0;
};
struct Header final {
const char* Name = nullptr; SIZE_T NameLength = 0;
const char* Value = nullptr; SIZE_T ValueLength = 0;
};
typedef NTSTATUS (*EventCallback)(void* context, const Event* event);
typedef void (*ReconnectCallback)(
void* context, ULONG attempt, ULONG delayMs, NTSTATUS lastError,
const char* lastEventId, SIZE_T lastEventIdLength);
struct ReceiveOptions final {
SIZE_T MaxEventBytes = 0;
EventCallback OnEvent = nullptr;
void* CallbackContext = nullptr;
};NTSTATUS Connect(
_In_ wknet::http::Session* session,
_In_reads_bytes_(urlLength) const char* url, SIZE_T urlLength,
_Out_ SseClient** client) noexcept;
NTSTATUS Connect(
_In_ wknet::http::Session* session,
_In_ const ConnectConfig* config,
_Out_ SseClient** client) noexcept;
NTSTATUS ConnectEx(
_In_ wknet::http::Session* session,
_In_ const ConnectConfig* config,
_Out_ SseClient** client) noexcept;
NTSTATUS ConnectAsync(...); // returns AsyncOp*
NTSTATUS ConnectAsyncEx(...);
NTSTATUS AsyncGetSseClient(
_In_ wknet::http::AsyncOp* operation,
_Out_ SseClient** client) noexcept;On success *client is open: 2xx received and (if required) Content-Type accepted. The library injects Accept: text/event-stream and Cache-Control: no-cache; injects Last-Event-ID when known.
| Typical failure | Meaning |
|---|---|
STATUS_INVALID_PARAMETER |
Illegal URL/headers/family, or caller overrode library-controlled headers |
STATUS_INVALID_NETWORK_RESPONSE |
Non event-stream Content-Type when required |
STATUS_ACCESS_DENIED |
HTTP 4xx (no auto-reconnect) |
STATUS_IO_TIMEOUT |
Open timeout |
NTSTATUS Receive(_In_ SseClient* client, _Out_ Event* event) noexcept;
NTSTATUS ReceiveEx(
_In_ SseClient* client,
_In_opt_ const ReceiveOptions* options,
_Out_opt_ Event* event) noexcept;Blocks until:
- A full event is ready →
STATUS_SUCCESS;Eventfields point at client-owned storage (valid until next successful Receive / Close); or - Stream ended and reconnect is not attempted / exhausted → disconnect or final error; or
-
ReceiveTimeoutMselapsed →STATUS_IO_TIMEOUT; or -
Close→STATUS_CANCELLED.
Default type is "message". Multi-line data: lines are joined with \n. id: updates last-event-id (ids containing \0 are ignored per WHATWG). retry: affects reconnect delay.
NTSTATUS GetLastEventId(
_In_ SseClient* client,
_Outptr_result_bytebuffer_(*idLength) const char** id,
_Out_ SIZE_T* idLength) noexcept;
NTSTATUS GetReconnectAttempt(
_In_ SseClient* client,
_Out_ ULONG* attempt) noexcept;
NTSTATUS Close(_In_opt_ SseClient* client) noexcept;Close accepts nullptr and aborts a blocked Receive and reconnect delay. The handle is invalid after close.
| Topic | Behavior |
|---|---|
| Method | GET only |
| Content-Encoding | First release requires identity |
| Protocols | Same session HTTP stack (H1 / H2 / H3 subject to Session / TLS / Alt-Svc policy) |
| Reconnect | 4xx open failures never reconnect; other recoverable disconnects may reconnect with Last-Event-ID
|
| Connection policy | Each open uses ForceNew
|
| Concurrency | Do not call Receive concurrently on the same SseClient
|
| IRQL | All entry points at PASSIVE_LEVEL
|
SSE sits on true incremental response bodies: when SendOptions.OnBody is set, the library invokes it multiple times in arrival order; finalChunk is true only at the real end. For aggregated body only, leave OnBody null. See Sync HTTP.
-
开始使用 / Get Started
-
概念与行为 / Concepts
-
协议指南 / Protocols
-
API 参考 / API Reference
-
贡献与项目 / Contribute