☰
libwebsockets Secure Streams policy2c:JSON 策略到 C 结构体的离线转换工具
2026/10/7 1:48:23 网站建设 项目流程
  • 人工智能
  • AI Agent
  • 多模态
  • 语音
  • AI 应用

【免费下载链接】ten-framework

Open-source framework for conversational voice AI agents

项目地址:https://gitcode.com/TEN-framework/ten-framework
点击查看免费下载

本篇文章讲解 libwebsockets(lws)Secure Streams 体系中一个特殊而实用的工具:lws-minimal-secure-streams-policy2c。它的作用是把一份以 JSON 书写的 Secure Streams 策略,从标准输入读入并解析,然后在标准输出上生成一份可直接参与编译的 C 结构体源码。这样,在那些内存受限、无法在运行时解析动态 JSON 策略的嵌入式平台上,依然可以继续用 JSON 维护和演进策略,再借助本工具把策略"烧录"成固件里的静态数据。读完本文,你将掌握这个转换工具的使用方式、生成产物的结构、以及它如何与LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY静态策略模式配合,实现"一份 JSON,多端部署"的落地路径。

这个工具解决什么问题

Secure Streams 是 lws 提供的网络 API,它的设计把"载荷"与"元数据"严格分离:连接的目标地址、TLS 信任链、甚至所使用的协议,都被收纳进一份策略数据库(policy database),应用代码只负责收发载荷与接收连接状态回调。策略通常以 JSON 形式在lws_context创建时传入,甚至可以从远端更新。

但并非所有平台都适合动态解析 JSON。对于 Flash/RAM 紧张的嵌入式目标,运行时解析 JSON 策略既不划算也有风险。policy2c 正是为这一场景准备的:

  • 你仍然用 JSON 维护和编写策略(便于阅读、审查、版本管理);
  • 在 PC/构建机上运行本工具,把 JSON 一次性地转换为可编译的 C 结构体;
  • 生成的 C 代码直接包含进固件,目标设备无需 JSON 解析器即可使用 Secure Streams。

从源码注释可以看到它定位的描述:"It's useful if your platform is too space-constrained for a JSON policy and needs to build a static policy in C via LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY... this way you can still create and maintain the JSON policy but implement it directly as C structs in your code."(见 minimal-secure-streams.c 文件头注释。)

依赖与构建

编译前提

policy2c 自身需要在支持完整协议角色的 lws 构建上编译,因为它必须能解析任意策略内容——无论是 HTTP/1、HTTP/2、WebSocket 还是 MQTT 的 streamtype。工具文档明确指出它依赖LWS_ROLE_H1、LWS_ROLE_H2、LWS_ROLE_WS与LWS_ROLE_MQTT四个构建选项。

这一依赖也反映在它的 CMakeLists.txt 中:

require_lws_config(LWS_ROLE_H1 1 requirements) require_lws_config(LWS_ROLE_H2 1 requirements) require_lws_config(LWS_ROLE_MQTT 1 requirements) require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)

其中最后一行很有意思:policy2c 作为"生成器"工具,运行在 PC 上,因此它自身不需要以静态策略模式构建;相反,静态策略模式(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY=1)是它生成物所服务的下游目标形态。

构建命令

与绝大多数 lws minimal example 相同,在示例目录中直接执行:

$ cmake . && make

构建产物是可执行文件lws-minimal-secure-streams-policy2c。

使用方式

命令行参数

命令行选项含义
-d <loglevel>调试输出详细程度(十进制),例如-d15

基本用法

工具从stdin读取 JSON 策略,向stdout输出等价的 C 结构体源码:

$ cat mypolicy.json | lws-minimal-secure-streams-policy2c (on stdout) static const uint32_t _rbo_bo_0[] = { 1000, 2000, 3000, 5000, 10000, }; static const lws_retry_bo_t _rbo_0 = { .retry_ms_table = _rbo_bo_0, .retry_ms_table_count = 5, .conceal_count = 5, .secs_since_valid_ping = 30, .secs_since_valid_hangup = 35, .jitter_percent = 20, }; static const uint8_t _ss_der_amazon_root_ca_1[] = { /* 0x 0 */ 0x30, 0x82, 0x03, 0x41, 0x30, 0x82, 0x02, 0x29, /* 0x 8 */ 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x06, /* 0x 10 */ 0x6C, 0x9F, 0xCF, 0x99, 0xBF, 0x8C, 0x0A, 0x39, /* 0x 18 */ 0xE2, 0xF0, 0x78, 0x8A, 0x43, 0xE6, 0x96, 0x36, /* 0x 20 */ 0x5B, 0xCA, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, ...

重定向即可保存:

$ cat mypolicy.json | lws-minimal-secure-streams-policy2c > static-policy.h

转换过程与源码级原理

整体流程

从 minimal-secure-streams.c 的main()可以看出转换的大致步骤:

  1. 创建lws_context(CONTEXT_PORT_NO_LISTEN,即不监听任何端口),调用lws_ss_policy_parse_begin(context, 0)开始一次策略解析会话;
  2. 循环从 stdinread()数据块,逐块交给lws_ss_policy_parse()做增量解析;若返回错误且不是LEJP_CONTINUE(表示数据尚未完整,需要继续喂入),则视为解析失败并退出;
  3. 解析完成后,通过lws_ss_policy_get(context)取得内存中的策略对象链表,逐类遍历并输出对应的 C 结构体。

过程中还会在输出头部把原始 JSON 原样回显,并用#if 0 ... #endif包起来,同时打印 "Original JSON size: xxx" 提示原始策略的体积;结尾会打印一段估计的 C 侧内存占用,例如:

/* estimated footprint 10720 (when sizeof void * = 8) */

这个估计值由工具内部累加各结构体sizeof与证书 DER 长度得到,方便评估静态策略的最终成本。

标识符清洗(purify_csymbol)

JSON 里的名称(streamtype、metadata 名、证书名、auth 名、trust store 名等)要变成合法的 C 标识符。工具用purify_csymbol()把任何既不是字母也不是数字的字符替换成下划线_,例如api.amazon.com这类名字在生成的结构体命名中会以_ss_ts_api_amazon_com的形式出现。这是从源码中可以明确确认的实现事实。

去重机制

转换器在遍历策略时维护了三个"已见过"映射(见 minimal-secure-streams.c 中的rbomap、trustmap、certmap):

  • retry/backoff 对象:多个 streamtype 往往共享同一个"retry": "default"重试方案,工具只输出一份_rbo_N(N 为递增序号);
  • trust store:同一信任库被多个 streamtype 引用时只生成一份_ss_ts_<name>;
  • x.509 证书:证书按名称去重,DER 字节只输出一份_ss_der_<name>数组,并伴随一个_ss_x509_<name>包装结构。

其余 streamtype 之间通过next指针串成链表,所有指针在输出时都指向已生成的static const对象,因此最终产物是一批相互引用的编译期常量,天然适合放进只读存储器。

各类生成物的结构

从工具源码与仓库内已生成的 static-policy.h 可以总结出产物涵盖的类型:

生成物对应 JSON 概念说明
_md_<streamtype>_<name>streamtype 的metadata条目lws_ss_metadata_t数组,含 name、可选默认值、长度等
_rbo_bo_<n>/_rbo_<n>retry[].backoff / conceal / jitterpc等uint32_t延时表 +lws_retry_bo_t重试/退避结构
_ss_der_<cert>/_ss_x509_<cert>certs中的 base64 DER 证书uint8_t字节数组 +lws_ss_x509_t包装
_ss_ts_<store>trust_storeslws_ss_trust_store_t,内含ssx509[]证书指针数组
<streamtype>_http_respmaphttp_resp_mapHTTP 响应码到 SS 状态的映射表
_ssau_<name>auth条目lws_ss_auth_t,name/type/streamtype/blob_index
_ssp_<streamtype>s中的每个 streamtypelws_ss_policy_t策略结构,含协议联合体u.http/u.mqtt

其中lws_ss_policy_t的u联合体按协议分支输出:

  • H1 / H2 / WS:输出.http.method/.url/.multipart_name/.auth_preamble/.respmap/.blob_header/.resp_expect/.fail_redirect等;若协议是 WS,还会嵌套输出.u.ws.subprotocol/.binary;
  • MQTT:输出.mqtt.topic/.subscribe/.will_*/.birth_*/.keep_alive/.qos/.clean_start/.aws_iot/.retain等;
  • 其余协议索引会触发错误退出("unknown ss protocol index"),这提示工具只支持这四类协议角色的策略内容。

streamtype 结构最后还会输出timeout_ms、flags、priority、port、metadata_count、protocol、client_cert、trust以及retry_bo指针引用;如果 lws 构建时启用了LWS_WITH_SECURE_STREAMS_AUTH_SIGV4,还会额外输出aws_region/aws_service字段(见源码中该宏条件编译块)。

输出链表的头结点在最后以宏形式标记出来:

#define _ss_static_policy_entry _ssp_api_amazon_com_auth

这个宏正是静态策略模式下,应用通过info.pss_policies = &_ss_static_policy_entry;挂载给lws_context的入口。

产物如何被下游使用:static policy 模式

配套示例 minimal-secure-streams-staticpolicy

仓库里有一个与 policy2c 直接配套的示例 minimal-secure-streams-staticpolicy,它演示了"用 policy2c 生成的静态策略跑真实 HTTP 客户端"的完整链路:应用访问https://warmcat.com/并读取其 index.html。

该目录下有三个关键文件:

  • static-policy.json:原始 JSON 策略(内含 retry 方案、多张 CA 证书、trust store、以及mintest、avs_*、mqtt_test*、captive_portal_detect等一批 streamtype);
  • static-policy.h:由 policy2c 从该 JSON 生成的 C 头文件(约 1500 行,头部#if 0块中嵌着原始 JSON 与 "Original JSON size: 15493"),文件末尾即为_ssp_*策略链表与#define _ss_static_policy_entry _ssp_api_amazon_com_auth;
  • minimal-secure-streams.c:应用本体,直接#include "static-policy.h"。

在应用侧,静态策略通过lws_context_creation_info的pss_policies字段挂载:

info.pss_policies = &_ss_static_policy_entry; info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;

然后照常创建mintest类型的 Secure Stream:ssi.streamtype = "mintest"; lws_ss_create(context, 0, &ssi, NULL, NULL, NULL, NULL);,在LWSSSCS_CREATING状态里通过lws_ss_set_metadata()设置uptag、ctype等元数据,再lws_ss_client_connect()发起连接,最后在 rx 回调中收到页面载荷。其 CMakeLists.txt 也印证了静态策略模式是构建前提:

require_lws_config(LWS_ROLE_H1 1 requirements) require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 1 requirements)

注意这个下游示例只需要LWS_ROLE_H1,而生成它的 policy2c 却要求四种角色——这正是"生成端全能力、运行端按需裁剪"的典型分工。

运行输出示例

README 中记录了该静态策略客户端的典型运行日志:

$ ./lws-minimal-secure-streams-staticpolicy [2020/03/26 15:49:12:6640] U: LWS secure streams static policy test client [-d<verb>] [2020/03/26 15:49:12:7067] N: lws_create_context: using ss proxy bind '(null)', port 0, ads '(null)' [2020/03/26 15:49:12:7567] N: lws_tls_client_create_vhost_context: using mem client CA cert 914 ... [2020/03/26 15:49:13:9625] N: ss_api_amazon_auth_rx: acquired 588-byte api.amazon.com auth token, exp 3600s [2020/03/26 15:49:13:9747] U: myss_state: LWSSSCS_CREATING, ord 0x0 [2020/03/26 15:49:13:9774] U: myss_state: LWSSSCS_CONNECTING, ord 0x0 [2020/03/26 15:49:14:1897] U: myss_state: LWSSSCS_CONNECTED, ord 0x0 [2020/03/26 15:49:14:1926] U: myss_rx: len 1520, flags: 1 ... [2020/03/26 15:49:14:2136] U: myss_rx: len 0, flags: 2 [2020/03/26 15:49:14:2137] U: myss_state: LWSSSCS_QOS_ACK_REMOTE, ord 0x0 [2020/03/26 15:49:14:2170] U: myss_state: LWSSSCS_DISCONNECTED, ord 0x0 [2020/03/26 15:49:14:2192] U: myss_state: LWSSSCS_DESTROYING, ord 0x0 [2020/03/26 15:49:14:2282] U: Completed: OK

可以看到,日志中 TLS vhost 使用的 CA 证书来自"mem"(内存中的静态 DER 数组,即 policy2c 生成物),且整个流程经历了CREATING → CONNECTING → CONNECTED → QOS_ACK_REMOTE → DISCONNECTED → DESTROYING的完整 Secure Streams 状态机。

构建模式说明

LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY模式下 lws 不会构建 JSON 策略解析器,若项目其他部分也用不到 LEJP 解析器,还可以顺带关闭 LEJP 选项,再省下约 2KB 空间(这一数量说明来自 libwebsockets 自带的 Secure Streams 总览文档 lib/secure-streams/README.md 中 "Using static policies" 一节)。这正是 policy2c 存在意义的量化注脚。

JSON 策略关键字段速览

由于 policy2c 的输入即 Secure Streams JSON 策略,掌握输入格式才能用好该工具。以下字段来自 lib/secure-streams/README.md 的 JSON Policy Database 一节,与仓库内 static-policy.json 相互印证:

字段作用
release/product/schema-version策略版本、适用产品、解析器最低版本要求
retry[]重试/退避方案集合,内部含backoff(逐次毫秒延时数组)、conceal(向上层隐藏的失败次数,65535 表示永不放弃)、jitterpc(延时随机抖动百分比,防重试风暴)等
certs校验所需的 CA 证书,格式为 base64 DER(即 PEM 首尾行之间的内容)
trust_stores由certs组成的命名证书链,每个条目会创建对应命名的 vhost + TLS 上下文
auth认证方案映射(name/type/streamtype/blob),如 sigv4
s各 streamtype 的策略定义数组,是转换的主体

streamtype 常用成员包括:endpoint(支持${metadata}运行时替换,+前缀表示 Unix Domain Socket)、port、protocol(h1/h2/ws/mqtt/raw)、tls、tls_trust_store、retry、opportunistic、nailed_up、timeout_ms、metadata[]、HTTP 类(http_method/http_url/http_expect/http_fail_redirect/http_auth_header/http_auth_preamble/http_resp_map/http_multipart_*)、WS 类(ws_subprotocol/ws_binary)、MQTT 类(mqtt_topic/mqtt_subscribe/mqtt_qos/mqtt_keep_alive/mqtt_will_*/mqtt_clean_start)等。转换器对这些字段的 C 输出分支与上述列表一一对应。

最佳实践与注意事项

  1. 工具跑在构建机,不在目标机:policy2c 需要在带全角色(H1/H2/WS/MQTT)的 lws 构建下编译运行,其输出才参与固件编译。目标设备侧则按需启用LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY,只编译必需的协议角色。
  2. 一次转换,多处复用:policy2c 已做过去重优化,共享的 retry 方案、trust store、证书只会生成一份常量,多个 streamtype 通过指针引用,降低最终体积。
  3. JSON 仍是唯一事实来源:把static-policy.json纳入版本管理,改策略时只改 JSON、重新运行转换,避免手改 C 产物——这既保持了 JSON 的可读可审,也保证生成物与策略严格同步。
  4. 关注体积预算:工具会回显原始 JSON 字节数并在结尾打印估计 footprint(如estimated footprint 10720),可用于静态策略的 ROM 成本评估。
  5. 协议角色对齐:输入策略中出现ws或mqttstreamtype 时,policy2c 的构建必须具备对应角色,否则解析会失败(源码在解析错误时输出"lws has WITH_ROLEs for what's in the JSON?"的提示)。

相关资源

  • 工具本体:minimal-secure-streams.c 与 CMakeLists.txt
  • 静态策略配套示例:minimal-secure-streams-staticpolicy,其 static-policy.h 是 policy2c 的真实产出样例,static-policy.json 是对应输入
  • Secure Streams 总览与 JSON 策略字段详解:lib/secure-streams/README.md
  • 相关 CMake 配置项在 lib/secure-streams/CMakeLists.txt 与 CMakeLists.txt 中定义
  • 人工智能
  • AI Agent
  • 多模态
  • 语音
  • AI 应用

【免费下载链接】ten-framework

Open-source framework for conversational voice AI agents

项目地址:https://gitcode.com/TEN-framework/ten-framework
点击查看免费下载

相关推荐

上一篇:UPNG.js 开源项目教程
下一篇:Moveable 开源项目教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询