首页 / 资讯中心 / 文章详情

TEN-framework 内置 libwebsockets 的 HTTP 流式压缩模块:deflate 与 brotli 变换实现解析

TEN-framework 内置 libwebsockets 的 HTTP 流式压缩模块:deflate 与 brotli 变换实现解析 ★ FEATURED ARTICLE
人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载本篇文章聚焦于 TEN-framework 仓库中随附的第三方库 libwebsockets 的 HTTP 压缩变换模块位于third_party/libwebsockets/lib/roles/http/compression/深入讲解其通用压缩变换接口ops 结构、buflist_comp预压缩缓冲机制、zlibdeflate与 brotlibr两种服务端压缩算法的实现以及它们在 h1/h2 角色中的接入与调用链。读者读完可掌握 libwebsockets 如何在 HTTP 响应头之后对内容流进行透明压缩、如何通过content-encoding协商选择压缩算法以及如何在自己的 HTTP 服务中复用这套变换框架。模块定位HTTP 内容流的通用压缩变换层该 README 明确指出lib/roles/http/compression/目录存放的是通用的压缩变换generic compression transforms可应用于 HTTP 内容流content stream即响应头header之后的正文部分无论底层承载是 h1 还是 h2。它区别于 WebSocket 的 permessage-deflate 扩展——后者压缩的是 WebSocket 帧负载而本模块压缩的是 HTTP 响应体。third_party/libwebsockets/lib/roles/http/compression/ ├── README.md # 模块说明文档 ├── private-lib-roles-http-compression.h # ops 接口与压缩上下文定义 ├── stream.c # 变换调度、协商与应用逻辑 ├── deflate/ │ └── deflate.c # zlib/miniz deflate 实现 └── brotli/ └── brotli.c # brotli br 实现模块启用前提是编译期宏LWS_WITH_HTTP_STREAM_COMPRESSION头文件 private-lib-roles-http-compression.h 的注释说明它由private-lib-core.h在该宏开启时引入。压缩变换的统一接口ops 结构体README 提到压缩变换对外暴露一个 ops 类型结构体以及压缩器名称用于content-encoding其定义位于./private-lib-roles-http-compression.h。在该头文件中接口被建模为struct lws_compression_supportstruct lws_compression_support { /** compression name as used by, eg, content-encoding */ const char *encoding_name; /** create a compression context for the compression method, or NULL */ int (*init_compression)(lws_comp_ctx_t *ctx, int decomp); /** pass data into the context to be processed */ int (*process)(lws_comp_ctx_t *ctx, const void *in, size_t *ilen_iused, void *out, size_t *olen_oused); /** destroy the de/compression context */ void (*destroy)(lws_comp_ctx_t *ctx); };四个字段各司其职encoding_name压缩算法的content-encoding名称。deflate 实现填写deflatebrotli 实现填写br见下文两个实现文件末尾的实例化结构体init_compression为某次连接创建解压缩上下文decomp为 0 表示压缩方向、非 0 表示解压缩方向process向上下文喂入输入数据、产出输出数据。通过ilen_iused/olen_oused两个 in/out 指针分别报告本次实际消费了多少输入和本次实际产出了多少输出这是实现流式分块处理的关键约定destroy释放解压缩上下文。两个已注册实现通过全局符号导出声明在头文件末尾extern struct lws_compression_support lcs_deflate; extern struct lws_compression_support lcs_brotli;压缩上下文与预压缩缓冲buflist_compREADME 特别解释了引入buflist_comp的原因压缩变换依赖先把输出发出去才能继续处理新输入because the compression transform depends on being able to send on its output before it can process new input因此新增了一类 buflist——wsi-buflist_comp用于保存已交付待处理、但当前无法被压缩器接收的数据即从压缩变换视角看属于输入数据。对应的上下文结构lws_comp_ctx_t定义如下同头文件typedef struct lws_compression_ctx { union { #if defined(LWS_WITH_HTTP_BROTLI) BrotliEncoderState *br_en; BrotliDecoderState *br_de; #endif z_stream *deflate; void *generic_ctx_ptr; } u; struct lws_buflist *buflist_comp; unsigned int is_decompression:1; unsigned int final_on_input_side:1; unsigned int may_have_more:1; unsigned int chunking:1; } lws_comp_ctx_t;各字段语义u联合体按编译配置容纳 brotli 编码器/解码器状态LWS_WITH_HTTP_BROTLI或 zlib 的z_stream并提供通用指针用于存在性判断buflist_comp预压缩变换缓冲保存因输出未发完而暂存下来的输入数据is_decompression当前是压缩还是解压缩方向final_on_input_side已在输入侧收到 FINAL末段标记但输出侧尚未确定最后一帧may_have_more压缩器仍可能产出更多输出如 deflate 的Z_SYNC_FLUSH语义、brotli 编码器内部尚有余量chunking与 h1 chunked 传输编码配合的标记。缓冲机制的驱动逻辑核心调度实现在 stream.c 的lws_http_compression_transform()中其工作方式如下若当前未启用压缩wsi-http.lcs为空或写入协议不是LWS_WRITE_HTTP/LWS_WRITE_HTTP_FINAL则原样透传数据不经过压缩当收到LWS_WRITE_HTTP_FINAL时先记录final_on_input_side 1并把写协议临时降级为普通LWS_WRITE_HTTP——因为最终输入经过压缩变换后可能产生多个输出帧只有最后一帧才是真正的 FINAL若buflist_comp中已有旧数据未处理完则把本次新输入追加到缓冲尾部转而从缓冲头部取出数据交给压缩器lws_buflist_next_segment_lenlws_buflist_use_segment组合边消费边缩减调用wsi-http.lcs-process()实际执行压缩若直接从调用方传入的数据未被全部消费ilen_iused ! len剩余部分追加到buflist_comp尾部等待下次可写事件只要buflist_comp非空或may_have_more为真就调用lws_callback_on_writable(wsi)请求下一次可写回调驱动压缩继续“吐”数据。这种输出受限即暂存输入、可写再续的机制正是流式压缩在事件驱动、非阻塞网络栈中得以正确工作的基础。两种服务端压缩算法deflate 与 brotliREADME 明确指出当前服务端server side支持 zlibdeflate与 brotlibr两种算法两者实现分列于deflate/与brotli/子目录。zlib deflate 实现deflate.c 的关键实现细节初始化lcs_init_compression_deflate压缩方向调用deflateInit2(ctx-u.deflate, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY)level 为 1最快档适合实时流式输出、windowBits 为-15负号表示输出 raw deflate 流不含 zlib 头这正是content-encoding: deflate惯用语义、memLevel 8、默认策略解压方向调用inflateInit2(..., 16 15)兼容 zlib/gzip 头头文件顶部按LWS_WITH_MINIZ决定引入miniz.h还是zlib.h即允许用 miniz 替代 zlib处理lcs_process_deflate将输入/输出指针与长度装配进z_stream后压缩走deflate(..., Z_SYNC_FLUSH)、解压走inflate(..., Z_SYNC_FLUSH)实现每轮调用尽量吐出可用输出对Z_NEED_DICT/Z_STREAM_ERROR/Z_DATA_ERROR/Z_MEM_ERROR均记录错误并返回 -1may_have_more (*olen_oused olen_oused_in)若本轮输出缓冲被完全用满说明压缩器可能还有更多输出待产注释明确这是 zlib 的歧义处理销毁lcs_destroy_deflate按方向调用deflateEnd/inflateEnd并释放z_stream内存。brotli br 实现brotli.c 的关键实现细节初始化lcs_init_compression_brotli压缩方向创建BrotliEncoderCreateInstance后把模式设为BROTLI_MODE_TEXT文本模式适合 HTML/JSON 等并把质量设为BROTLI_MIN_QUALITY——为实时流式场景选择了最低 CPU 开销档位解压方向创建BrotliDecoderCreateInstance处理lcs_process_brotli压缩走BrotliEncoderCompressStream默认操作为BROTLI_OPERATION_PROCESS当输入缓冲已空且final_on_input_side为真时切换为BROTLI_OPERATION_FINISH以输出收尾帧若a_in为 0 且编码器无更多输出直接短路返回goto bail避免空转解压走BrotliDecoderDecompressStream对BROTLI_DECODER_RESULT_ERROR记录错误结束判定使用BrotliEncoderIsFinished/BrotliDecoderIsFinished销毁分别调用BrotliEncoderDestroyInstance/BrotliDecoderDestroyInstance。算法优先级在 stream.c 顶部可用算法按偏好顺序登记struct lws_compression_support *lcs_available[] { #if defined(LWS_WITH_HTTP_BROTLI) lcs_brotli, #endif lcs_deflate, };即编译期启用 brotli 时 brotli 优先其次 deflate数组顺序决定了后续协商时的选择次序。编译开关与依赖HTTP 流式压缩由三个 CMake 选项控制见 CMakeLists.txtLWS_WITH_HTTP_STREAM_COMPRESSION总开关默认OFF开启后强制联动LWS_WITH_ZLIB 1第 498-500 行因为 deflate 必须依赖 zlib/minizLWS_WITH_HTTP_BROTLI附加开关需在LWS_WITH_HTTP_STREAM_COMPRESSION之上再开启 brotli 支持cmake option 注释明确 requires LWS_WITH_HTTP_STREAM_COMPRESSIONLWS_WITH_MINIZ用 miniz 替代系统 zlib配合LWS_WITH_ZLIB使用cmake 注释见第 290、324 行。在 CMakeLists-implied-options.txt 中可以确认跨平台构建时该特性被自动管理——某些目标平台默认开启libz and brotli if avail部分受限平台如特定交叉编译场景则显式置 0。生成的lws_config.h中对应出现LWS_WITH_HTTP_BROTLI、LWS_WITH_HTTP_STREAM_COMPRESSION、LWS_WITH_MINIZ等#cmakedefine见 lws_config.h.in。changelog 中也记载了这两个配置项-DLWS_WITH_HTTP_STREAM_COMPRESSION1、-DLWS_WITH_HTTP_BROTLI1。因此在构建本仓库随附的 libwebsockets 时若要启用该压缩功能需要以类似方式传入-DLWS_WITH_HTTP_STREAM_COMPRESSION1并按需附加-DLWS_WITH_HTTP_BROTLI1同时确保 zlib或 miniz开发库可用。协商与应用从 Accept-Encoding 到 Content-Encoding 的完整调用链README 强调压缩器名称“as used bycontent-encoding”意味着整个模块服务于 HTTP 的内容编码协商。完整流程分为协商、应用、变换、销毁四步分别对应 stream.c 与 header.c 中的实现。1. 协商lws_http_compression_validate服务器在解析完请求头后调用h2 路径见 http2.c 的lws_http_compression_validate(h2n-swsi)h1 路径见 server.c 第 2431 行附近int lws_http_compression_validate(struct lws *wsi) { ... a lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING); if (!a) return 0; for (n 0; n LWS_ARRAY_SIZE(lcs_available); n) if (strstr(a, lcs_available[n]-encoding_name)) wsi-http.comp_accept_mask ... | (1 n); return 0; }即从请求的Accept-Encoding头中用子串匹配逐一检测客户端声明支持的算法结果写入wsi-http.comp_accept_mask位掩码供后续apply阶段使用。2. 应用lws_http_compression_applyapply按名称name参数或按偏好顺序挑选算法name非 NULL 时只尝试指定算法否则遍历lcs_available服务端decomp 0还需确认该算法在comp_accept_mask中即客户端已声明支持。选中后调用该算法的init_compression创建上下文记录wsi-http.lcs、初始化may_have_more/final_on_input_side/chunking/is_decompression向响应头追加Content-Encoding: encoding_nameWSI_TOKEN_HTTP_CONTENT_ENCODING。该 API 面向用户代码公开见 lws-http.h 第 913-940 行的文档注释它允许对动态生成的 HTTP 内容做透明压缩仅当客户端头声明支持且库内具备对应算法时才生效否则为 NOPname传 NULL 时自动按Accept-Encoding选择第一个可用的受支持格式。h1 服务端的文件服务路径在 header.c 第 204 行与 server.c 第 2826 行附近调用lws_http_compression_apply(wsi, NULL, p, end, 0)。3. 变换lws_http_compression_transform写入路径在 h1 与 h2 中分别接入ops-h1.c 第 800 行、ops-h2.c 第 447 行。h1 侧的调用先分配一块 MTU 级输出缓冲1500 LWS_PRE chunk 头尾预留见 ops-h1.c 第 791-798 行再把缓冲交给lws_http_compression_transform变换后按实际产出字节数发送同时依据may_have_more决定是否请求再次可写。这正是上一节描述的 buflist 驱动循环的入口。4. 销毁lws_http_compression_destroy连接关闭或流结束时调用h1 见 ops-h1.c 第 895 行h2 见 ops-h2.c 第 664 行服务端清理见 server.c 第 2580 行若上下文已初始化则调用算法的destroy释放资源并清空wsi-http.lcs。该清理在 close.c、output.c、service.c 等LWS_WITH_HTTP_STREAM_COMPRESSION保护段中均有配套处理确保各平台unix/windows/freertos/optee 等生命周期一致。使用约定与注意事项必须成对使用写协议lws_http_compression_apply的 API 注释强调压缩变换与 h2 支持一样依赖用户代码先以LWS_WRITE_HTTP写出各段内容、最后一段用LWS_WRITE_HTTP_FINAL收尾libwebsockets 内置的文件服务代码已遵循此约定。若未启用LWS_WITH_HTTP_STREAM_COMPRESSION编译该 API 提供 NOP 空实现用户代码可在两种构建下共存并有则压缩、无则透传。FINAL 的语义降级transform对LWS_WRITE_HTTP_FINAL并不直接透传而是先记录final_on_input_side、降级为普通写入等压缩器真正吐完最后一帧!may_have_more final_on_input_side再恢复 FINAL 写协议从而保证 h1/h2 帧边界的正确性。缓冲区的存在当压缩器瞬时输出能力不足时未消费的输入会积压在wsi-buflist_comp中并在可写事件驱动下逐段处理——这是理解该模块内存行为与背压backpressure语义的关键。服务端语义README 明示当前压缩方向仅服务端可用validate中lwsi_role_server(wsi)的守卫也印证了这一点。h1 chunked 协同lws_comp_ctx_t.chunking字段与 h1 的 chunked 传输编码配合ops-h1.c 中为 chunk 头尾预留的缓冲空间正是为此预留。小结本模块用一套轻量的 ops 接口把 deflate 与 brotli 抽象为可插拔的 HTTP 流压缩变换通过buflist_comp解决输出受限、输入积压的流式背压问题并贯穿 h1/h2 的协商Accept-Encoding、应用Content-Encoding、变换与销毁全流程。在 TEN-framework 的构建环境中理解LWS_WITH_HTTP_STREAM_COMPRESSION、LWS_WITH_HTTP_BROTLI、LWS_WITH_MINIZ三个开关与 zlib/brotli 依赖关系即可在需要时按需启用这套透明压缩能力为语音 Agent 服务中的 HTTP 响应如配置下发、日志回传、文件服务节省带宽。赞分享人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载相关推荐MicroPython deflate 模块实战指南基于 DeflateIO 的压缩与解压流式处理MicroPython deflate 模块实战指南基于 DeflateIO 的压缩与解压流式处理 本文围绕 MicroPython 内置的 deflate嵌入式语言运行时编程语言解释器编译器物联网系统编程MyBatis-Plus版本兼容性从版本地狱到版本天堂的实战指南MyBatis Plus版本兼容性从版本地狱到版本天堂的实战指南 如果你曾经在凌晨3点被一个奇怪的 NoSuchMethodError 异常惊醒或者后端ORM代码生成PrusaSlicer 内置 Miniz 压缩库单文件 Deflate/zlib 实现与 3MF 流式写档实战解析PrusaSlicer 内置 Miniz 压缩库单文件 Deflate/zlib 实现与 3MF 流式写档实战解析 导读 PrusaSlicer 在 bund桌面应用3D渲染上一篇解决Spring Boot Thin Launcher常见问题依赖冲突与网络代理配置下一篇PGroonga高级技巧正则表达式与模糊搜索的完美实现方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站