- 人工智能
- AI Agent
- 多模态
- 语音
- AI 应用
【免费下载链接】ten-framework
Open-source framework for conversational voice AI agents
本篇文章讲解 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()可以看出转换的大致步骤:
- 创建
lws_context(CONTEXT_PORT_NO_LISTEN,即不监听任何端口),调用lws_ss_policy_parse_begin(context, 0)开始一次策略解析会话; - 循环从 stdin
read()数据块,逐块交给lws_ss_policy_parse()做增量解析;若返回错误且不是LEJP_CONTINUE(表示数据尚未完整,需要继续喂入),则视为解析失败并退出; - 解析完成后,通过
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_stores | lws_ss_trust_store_t,内含ssx509[]证书指针数组 |
<streamtype>_http_respmap | http_resp_map | HTTP 响应码到 SS 状态的映射表 |
_ssau_<name> | auth条目 | lws_ss_auth_t,name/type/streamtype/blob_index |
_ssp_<streamtype> | s中的每个 streamtype | lws_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 输出分支与上述列表一一对应。
最佳实践与注意事项
- 工具跑在构建机,不在目标机:policy2c 需要在带全角色(H1/H2/WS/MQTT)的 lws 构建下编译运行,其输出才参与固件编译。目标设备侧则按需启用
LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY,只编译必需的协议角色。 - 一次转换,多处复用:policy2c 已做过去重优化,共享的 retry 方案、trust store、证书只会生成一份常量,多个 streamtype 通过指针引用,降低最终体积。
- JSON 仍是唯一事实来源:把
static-policy.json纳入版本管理,改策略时只改 JSON、重新运行转换,避免手改 C 产物——这既保持了 JSON 的可读可审,也保证生成物与策略严格同步。 - 关注体积预算:工具会回显原始 JSON 字节数并在结尾打印估计 footprint(如
estimated footprint 10720),可用于静态策略的 ROM 成本评估。 - 协议角色对齐:输入策略中出现
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
相关推荐
libwebsockets Secure Streams 静态策略示例实战:从 JSON 策略到 C 代码的完整链路
libwebsockets Secure Streams 静态策略示例实战:从 JSON 策略到 C 代码的完整链路 本篇指南围绕 libwebsockets
人工智能AI Agent多模态语音AI 应用Xinference 部署 llama-3-instruct 完全指南:模型规格、量化选项与多引擎启动命令详解
Xinference 部署 llama 3 instruct 完全指南:模型规格、量化选项与多引擎启动命令详解 本文基于 Xinference 内置模型文档 l
人工智能AI Agent多模态语音AI 应用仓颉宏原理实战:hystrix-cj的@ProtectedResrouce如何用Token插码实现零侵入熔断
仓颉宏原理实战:hystrix cj的@ProtectedResrouce如何用Token插码实现零侵入熔断 hystrix cj 是一个面向仓颉语言的熔断降级
人工智能AI Agent多模态语音AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考