网页事件流接口规范 0.1.0
本包让应用把当前入站 HTTP 请求回应成服务端事件流(text/event-stream):先立即提交响应头,程序继续运行,再逐块写入文字;写入受背压,能感知客户端断开,也能正常结束或异常终止。典型用途是持久对象向浏览器或本地软件推送事件、长任务的进度回报、向服务商的长连接下发任务。SSE 帧格式(id:、event:、data:、retry:、以冒号起首的注释行、以空行结尾)由应用自己拼;本包只把应用给出的文字按 UTF-8 写出,不解析、不校验、不补全帧。HEAD 与非 GET 请求由应用先判断,本包不替应用决定路由和方法。接口包只依赖标准库。
流柄
开始入站事件流回应 返回一个不透明的字符串(流柄),其余四个函数以它作第一个参数。流柄只在创建它的事件内有效:另一个事件里的流柄、伪造的字符串、从未创建的流柄一律使调用失败(豫言异常),不会误操作同一事件里的别的流。同一个事件至多开始一次事件流回应,重复开始失败。事件结束后流柄失效;流的结局见“生命周期”。
函数
开始入站事件流回应 接受状态码和附加标头列表,立即提交响应头,返回流柄,程序继续运行。状态码须在 200 至 299 之间且不是 204、205(这两个状态不能带正文)。内容类型固定为 text/event-stream; charset=utf-8,应用不可覆盖;Cache-Control: no-store 是默认值,应用可在附加标头里用同名标头覆盖。附加标头是(名,值)列表,至多 32 项,逐项严格校验:名是 1 至 64 字节的 RFC 9110 token(字母、数字与 !#$%&'*+-.^_|~);值只允许可见 ASCII、空格与制表(0x09、0x20 至 0x7E),至多 8192 字节,因此不含 CR、LF、NUL 与高位字节;名不区分大小写地重复则失败;下列名不允许应用附加:Content-Length、Transfer-Encoding、Connection、Keep-Alive、Upgrade、TE、Trailer、Content-Type、Content-Encoding,以及 Set-Cookie、Set-Cookie2`(凭据类响应用普通答复函数)。违反任何一项都抛豫言异常,此时响应尚未提交,应用仍可用别的答复函数回应。只能在 HTTP 事件里调用(其他事件抛豫言异常);当前事件若已由别的函数(网页答复等)提交过最终响应,再开始事件流是应用的违规,云工宿主会报错并中止本次运行,不一定能被「尝试运行」捕获;反过来,事件流开始之后本事件已有最终响应,再调用网页答复的任何答复函数同样是违规。
写入入站事件流文字 把文字按 UTF-8 立即写出,每次至多 65536 字节,超限抛豫言异常(先于状态判断);一帧比 64 KiB 长时,应用自行分几次写,字节序保持。返回整数:0 已写入;1 客户端已断开;2 流已由应用结束或中止。空文字不写出,只刷新并返回当前状态。写入受背压:缓冲已满时阻塞豫言,直到有空位或客户端断开;缓冲量由宿主与传输决定,规范不承诺具体块数,也不承诺写入调用与网络块一一对应,只承诺字节序。客户端断开之后应用仍可继续运行(例如完成计费结算、清理订阅),此后的写入只返回 1,不再阻塞。
入站事件流已断开 返回是否已经观察到客户端断开,不阻塞;一旦为阳恒为阳(此后调用了结束或中止也不变)。观察的来源:宿主报告请求已被客户端中止,或响应体读端已取消、出错。已由应用正常结束或中止、且此前未观察到断开的流返回阴。
结束入站事件流 正常终止事件流(客户端读到流的正常结束),幂等:已结束、已中止或已断开的流不报错。中止入站事件流 异常终止事件流,用于上游失败:宿主使响应体流以错误终止,原因(至多 1024 字节,超限抛异常)只供宿主诊断,不发给客户端;幂等。两者之后写入都返回 2。
失败分类:状态、标头、大小、流柄、事件种类等违规抛豫言异常;写入时客户端已断开、流已结束属可恢复的业务情形,用返回值 1、2 表达,不抛异常。
生命周期与宿主相关的事实
程序结束前必须调用结束或中止。 响应头已提交而程序返回时流不会自行结束(除非宿主在事件结束时收尾,见下),客户端会一直等待。断开的检测依赖宿主与传输:云工适配用请求的 AbortSignal(须开启兼容标志 enable_request_signal,见适配说明)与响应体流错误共同判定;HTTP/1.1 上空闲连接的断开只有在服务端再次写入之后才会被发现,因此需要长时间保持的流应按周期写入心跳(SSE 注释行 : ping)。存活时间以宿主为准:云工每个事件的豫言实例有墙钟时限(默认 30 秒,应用目录的 执行配置.json 可按事件种类改到至多 15 分钟),时限到达后下一次调用宿主失败、事件终止;当前宿主此时不会终止未结束的响应流(客户端看到流停止但连接仍开),这是宿主待改进项,改进前应用应在时限之前自行结束流并让客户端凭 Last-Event-ID 重连(如云端项目的 /events 20 秒后关闭)。异常终止在客户端的可见形态依传输而定:本地 workerd 的 HTTP/1.1 分块响应里客户端读到的是正常结束,宿主日志记一条未捕获异常;需要客户端可靠区分失败的应用应在流里先写一帧明确的错误事件再中止或结束。这些云端行为除注明本地 workerd 已验之外,均“待云端验证”。
跨宿主一致性要求
同一组调用在各宿主上必须得到相同的返回码与异常类别;宿主可以不同的只有缓冲量、块边界与断开被发现的时机。一旦宿主报告断开,其后的写入必须返回 1 且不得阻塞;结束与中止必须幂等;非法标头、超限写入、无效流柄必须在提交响应头之前(对开始)或不改变流状态(对其余)地失败。写入、结束不得因慢读者而使其他事件阻塞。
一致性样例
- 开始(200,空列表)→ 写入
data: 1加空行返回 0 → 结束 → 写入x返回 2 → 已断开返回阴;客户端依次读到响应头(内容类型固定)、一帧、正常结束。 - 开始 → 客户端断开 → 写入返回 1,应用继续记录结算 → 已断开返回阳 → 结束 → 写入返回 2、已断开仍为阳。
- 开始(200,
Content-Type或Set-Cookie或含换行的值)→ 抛豫言异常,响应未提交。 - 开始 → 写入两帧 → 中止 → 客户端在两帧之后不见正常结束(可见形态依传输)→ 写入返回 2,重复中止与结束都不报错。
- 写入 65536 字节返回 0,写入 65537 字节抛异常;多字节字符按字节计。
- 另一个事件拿到的流柄写入抛豫言异常,不影响本事件自己的流。
边界
不解析或生成 SSE 帧,不实现重连与 Last-Event-ID 语义(应用用网页入站读取该标头并自行回放),不做心跳、压缩、二进制流、WebSocket 或多路复用;不改变已提交的响应头;不承诺块边界;不在应用外自动结束流;不授予网络能力(授权仍由宿主资源许可管)。响应体只承载 UTF-8 文字。