ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

libwebsockets 单端口 HTTP(s) 与 Raw TCP 代理双协议复用实战:minimal-raw-proxy-fallback 深度解析

libwebsockets 单端口 HTTP(s) 与 Raw TCP 代理双协议复用实战:minimal-raw-proxy-fallback 深度解析 人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载导读本文基于 libwebsockets下称 lws官方示例minimal-raw-proxy-fallback展开讲解如何让同一个监听端口同时承担两套完全不同的协议对外表现得像一个普通 HTTP/HTTPS 服务器而一旦收到无法解析为 HTTP 或 TLS 的首包例如 SSH 客户端的握手数据则自动降级为原始 TCP 数据包代理raw proxy把流量透明转发到指定后端。读完本文你将掌握 lws vhost 的fallback机制、raw-proxy插件的配置与转发原理以及如何在 TEN-framework 仓库所内置的 libwebsockets 源码树中构建、运行和改造这套一端口两用的实战方案。说明libwebsockets 作为第三方依赖随 TEN-framework 仓库一并分发源码位于third_party/libwebsockets本文所有源码与文档引用均以该目录下的真实文件为准。一、示例想解决什么问题常规服务器通常一个端口只服务一种协议。但有些场景希望把两种流量合并到一个端口上既想让浏览器通过 80/443 访问 Web 服务又不想为另一种私有协议如 SSH、自定义二进制协议额外开放端口、暴露更多攻击面或增加防火墙/负载均衡的配置负担。本示例演示的正是这种能力一个 vhost 在承担正常 http 或 https 职责的同时若传入的协议对 tls 或 http 来说是意外的就把连接降级fallback绑定到指定的角色role与协议protocol上。示例中选择的降级目标是一个 lws 插件——raw-proxy它执行原始数据包代理。从示例源码 minimal-raw-proxy-fallback.c 的文件头注释可以看到作者对行为的精确描述这演示了一个正常的 http / https 服务器如果它在连接开始时收到无法理解的内容就回退成为到指定地址和端口的原始 tcp 代理。传入的连接会触发发起一个出站连接一旦成功建立从一侧进入的流量会放入 ringbuffer并尽快从另一侧发出去如果收到的是 http(s) 连接的预期数据包则像正常的 h1 / h2 服务器一样工作。默认行为演示按默认配置运行时示例会把127.0.0.1:22通常就是本机 sshd 的监听端口代理到本机127.0.0.1:7681。效果是用ssh -p 7681连接本机与直连 22 端口效果一致可以正常登录与此同时用浏览器访问http://127.0.0.1:7681也能正常打开网页若追加-s参数启用 TLS浏览器可访问https://127.0.0.1:7681而 SSH 客户端依然可以连接同一端口。也就是说同一个端口 7681 上HTTP 流量走 Web 服务非 HTTP 流量走 SSH 代理互不干扰。二、构建让 cmake 找到 raw-proxy 插件源码该示例依赖 raw-proxy 插件因此单独构建时必须通过-DLWS_PLUGINS_DIR告诉 cmake lws 源码树中plugins目录的位置因为 CMakeLists.txt 需要把插件源码编入示例$ cmake . -DLWS_PLUGINS_DIR~/libwebsockets/plugins make配套的构建文件还做了两项关键检查见 CMakeLists.txtrequire_lws_config(LWS_ROLE_RAW_PROXY 1 requirements)要求当前 lws 构建启用了LWS_ROLE_RAW_PROXY角色。若未启用需在配置 libwebsockets 本体时加上-DLWS_ROLE_RAW_PROXY1参见 raw-proxy 插件 README。find_package(libwebsockets CONFIG REQUIRED)示例链接的是已安装的 lws 库共享或静态均可构建脚本会自动选择websockets_shared或websockets。TEN-framework 仓库内的源码树同时包含示例与插件third_party/libwebsockets/plugins/raw-proxy/读者可以直接对照阅读无需单独下载。三、命令行参数一张表掌握全部开关示例通过lws_cmdline_option()声明见 lws-misc.h解析命令行完整参数如下参数含义-d loglevel日志详细程度十进制例如-d15。日志级别位定义见 log.h15对应LLL_USER \| LLL_ERR \| LLL_WARN \| LLL_NOTICE-r ipv4:address:port配置被代理的远端地址与端口默认ipv4:127.0.0.1:22。该值会写入 raw-proxy 插件的onwardpvo见下文第四节-s让服务器启用 TLS/HTTPS并设置LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT使非 SSL 明文连接也能连到 TLS 端口-h需配合-s让 vhost 同时设置LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER允许在 TLS 端口上提供 http 服务⚠️ 这会使该端口失去 TLS 的加密保护作者在注释中明确不推荐-u需配合-s让 vhost 设置LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS对尝试以 http 访问 TLS 端口的客户端发出 302 重定向强制其改用 https这几个 option 位均定义于 lws-context-vhost.h含义可查阅头文件内联注释LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORTL53-L60允许非 SSL 明文连接连到 SSL 监听端口若与REDIRECT_HTTP_TO_HTTPS组合会尝试把 http 连接重定向到显式 https 连接。LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIGL115-L127当首行内容不是合法 http 时放弃按 http 处理转而在连接上应用listen_accept_role/listen_accept_protocol指定的角色与协议。必须与ALLOW_NON_SSL_ON_SSL_PORT组合才能在 TLS 监听端口上工作。LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENERL198-L205允许 http 连接进入 tls 监听端口但作者提示这可能造成 tls 安全保证的意外丢失。LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPSL77-L87用 http 重定向强制客户端改用 https。LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCEL181-L196为每次 http 响应附加 Mozilla Observatory 推荐的默认安全头严格 CSP 等。四、源码拆解fallback 是如何挂上去的示例的核心配置集中在 main() 函数 中几处关键设置如下。1. 协议表与静态插件#define LWS_PLUGIN_STATIC #include ../plugins/raw-proxy/protocol_lws_raw_proxy.c static struct lws_protocols protocols[] { LWS_PLUGIN_PROTOCOL_RAW_PROXY, LWS_PROTOCOL_LIST_TERM };这里通过宏LWS_PLUGIN_STATIC把 raw-proxy 插件静态编入示例而非运行时动态加载 .so。LWS_PLUGIN_PROTOCOL_RAW_PROXY宏在插件源码末尾定义protocol_lws_raw_proxy.c其声明的协议名为raw-proxy并指定了 rx/tx 缓冲各 8192 字节。2. 通过 pvo 传递代理目标static struct lws_protocol_vhost_options pvo1 { NULL, NULL, onward, /* pvo name */ ipv4:127.0.0.1:22 /* pvo value */ }; static const struct lws_protocol_vhost_options pvo { NULL, pvo1, raw-proxy, /* protocol name we belong to on this vhost */ /* ignored */ };pvoprotocol vhost options是 lws 给 vhost 上某个协议传递配置的标准机制。raw-proxy协议在LWS_CALLBACK_PROTOCOL_INIT回调中通过lws_pvo_get_str(in, onward, cp)读取该选项protocol_lws_raw_proxy.c并用lws_tokenize解析成地址与端口格式必须为ipv4:addr[:port]或ipv6:addr解析失败会打印onward pvo format must be ipv4:addr[:port] or ipv6:addr并拒绝初始化。-r参数在运行时覆盖pvo1.value。3. vhost 的第二人格配置info.options LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG; info.listen_accept_role raw-proxy; info.listen_accept_protocol raw-proxy;LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG打开降级开关listen_accept_role与listen_accept_protocol指定降级后绑定的角色与协议。二者都设为raw-proxy含义是当首包无法被判定为合法 HTTP或 TLS时把该连接按 raw-proxy 角色 raw-proxy 协议处理。若这两个字段留空lws 会按向后兼容的优先级依次尝试名为raw的 pvo 协议、名为default的 pvo 协议、最后是protocols[0]见 lws-context-vhost.h 注释。4. HTTP 静态挂载与 404 文档static const struct lws_http_mount mount { /* .mountpoint */ /, /* .origin */ ./mount-origin, /* .def */ index.html, /* .origin_protocol */ LWSMPRO_FILE, ... }; info.mounts mount; info.error_document_404 /404.html;vhost 的 Web 部分从./mount-origin目录提供静态文件默认页为index.html内容见 mount-origin/index.html包含 lws logo 与 strict-csp 图标并提供了一个指向不存在页面的链接用于验证 404 处理器404 时返回mount-origin/404.html。5. 事件循环while (n 0 !interrupted) n lws_service(context, 0);lws_service驱动事件循环SIGINT时置interrupted标志退出并销毁 context这是所有 lws minimal 示例的标准骨架。五、运行与验证SSH 与浏览器共用 76811. 默认纯 HTTP raw 代理模式启动示例$ ./lws-minimal-raw-proxy [2018/11/30 19:22:35:7290] USER: LWS minimal raw proxy-fallback [2018/11/30 19:22:35:7291] NOTICE: Creating Vhost default port 7681, 1 protocols, IPv6 off [2018/11/30 19:22:35:7336] NOTICE: callback_raw_proxy: onward ipv4 127.0.0.1:22 ...日志中的callback_raw_proxy: onward ipv4 127.0.0.1:22正是插件PROTOCOL_INIT解析 pvo 后的输出protocol_lws_raw_proxy.c。随后从另一终端验证 SSH 代理$ ssh -p7681 me127.0.0.1 Last login: Fri Nov 30 19:29:23 2018 from 127.0.0.1 [melearn ~]$此时再打开浏览器访问http://127.0.0.1:7681同样正常显示页面。同一端口、同一进程SSH 二进制流与 HTTP 文本请求被自动分流。2. TLS/HTTPS 模式追加-s参数示例自带localhost-100y.cert与localhost-100y.key一对本地测试证书见 示例目录$ ./lws-minimal-raw-proxy -s此时浏览器可访问https://127.0.0.1:7681而 SSH 客户端依然能连接同一端口——因为ALLOW_NON_SSL_ON_SSL_PORT允许非 SSL 连接进入 TLS 监听端口再由 fallback 逻辑接管。组合开关的行为差异-s -u对尝试以http://访问该端口的浏览器返回重定向强制跳转到https://-s -h在 TLS 端口上额外放行明文 HTTP 服务作者明确警示这会失去 TLS 保护生产环境慎用。注意-s分支被#if defined(LWS_WITH_TLS)保护minimal-raw-proxy-fallback.c若当前 lws 构建未启用 TLS 则这些开关不生效。六、raw-proxy 插件的转发原理示例的代理能力完全来自插件 protocol_lws_raw_proxy.c其实现要点如下。1. 双 wsi 双 ringbuffer 架构插件为每个代理会话分配一个共享的struct conn内含两条 socketwsi[2]ACC为被接受的入站侧ONW为发起的出站侧和两条lws_ring环形缓冲r[2]以及用于保证有序发送的票据计数ticket_next / ticket_retired。代码注释protocol_lws_raw_proxy.c特别说明双方各有一个 pss但共享同一个 conn最后关闭的一方才释放它从而避免一侧关闭后另一侧持有悬空指针。2. 连接建立流程LWS_CALLBACK_RAW_PROXY_SRV_ADOPT入站连接被采纳时创建struct conn、两条 ring先关闭入站侧接收流控然后调用connect_client()向onward目标发起RAW方法客户端连接i.method RAW见 protocol_lws_raw_proxy.c。LWS_CALLBACK_RAW_PROXY_CLI_ADOPT出站连接建立成功标记established[ONW] 1重新开启入站侧接收流控flow_control(conn, ACC, 1)。3. 数据搬运rx 入环、writeable 出环任一侧_RX回调把收到的数据malloc后整体放入对应 ringlws_ring_insert并带递增 ticket若 ring 空闲元素不足 2 个则对该侧执行接收流控flow_control(side, 0)形成背压。任一侧_WRITEABLE回调从对侧 ring 取出队首元素校验 ticket 严格递增保证字节序不乱序后用lws_write(..., LWS_WRITE_RAW)写出ring 空闲超过 2 个时恢复该侧接收流控。当某侧关闭且 ring 已全部发送完调用lws_raw_transaction_completed结束会话若两侧都已关闭则destroy_conn释放共享结构。简单说这不是逐字节直通的 socket 转发而是基于环形缓冲 流控的有序消息转发天然避免了背压时的内存无限增长。读者可结合RING_DEPTH 8protocol_lws_raw_proxy.c理解缓冲容量。4. 吞吐量提示插件 README 建议因为代理的一侧往往是对大包毫无压力的 localhost若以吞吐量为目标创建 context 时应设置info.pt_serv_buf_size 8192;lwsws 默认已这么做。示例通过LWS_PLUGIN_PROTOCOL_RAW_PROXY宏中的 8192 缓冲已隐含该考量。七、vhost 选择限制与注意事项原始包没有 SNI 或 Host 头可供 vhost 分流raw-proxy 以raw方式转发无法像 HTTP 那样按 Host:、像 TLS 那样按 SNI 在多个 vhost 间选路。因此与 raw-proxy 协议关联的 vhost 必须独占其监听端口见 raw-proxy 插件 README。fallback 效果对同端口的所有 vhost 全局生效虽然FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG设置在第一个 vhost 上但文档 README.http-fallback.md 明确指出由于非 HTTP/TLS 协议的首包既不携带 Host: 也不携带 SNI无法做 vhost 选择因此同端口所有 vhost 都会执行 fallback 动作。判定时机明文 HTTP 端口在首包不是合法 HTTP 方法GET/POST 等时触发 fallbackTLS 端口则在 TLS 隧道建立阶段更早失败并触发详见 README.http-fallback.md。HTTPS 端口必须组合两个 flagFALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG需与ALLOW_NON_SSL_ON_SSL_PORT同时使用fallback 才能在 TLS 监听 socket 上生效头文件注释明确要求见 lws-context-vhost.h。八、在 lwsws 中配置等价能力若不想写 C 代码lws 的配置化守护进程 lwswslejp-conf 格式提供等价配置。在插件独占整个 vhost的场景下先启用协议并指定onwardpvoraw-proxy 插件 READMEws-protocols: [{ raw-proxy: { status: ok, onward: ipv4:remote.address.com:port } }]再在 vhost 上声明连接一被接受就应用该角色与协议apply-listen-accept: 1, listen-accept-role: raw-proxy, listen-accept-protocol: raw-proxy若改为先尝试 http(s)失败再降级则将apply-listen-accept换成fallback-listen-accept: 1对应 C API 的FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG见 README.http-fallback.md——这正是本文示例在纯 C 代码中所做的事。九、小结minimal-raw-proxy-fallback是理解 lws 多协议复用能力的极佳入口它把LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG、listen_accept_role / listen_accept_protocol、静态插件嵌入与 pvo 配置、ringbuffer 流控代理这几大机制浓缩在一个可运行的最小示例中。读者可以基于该示例把默认的127.0.0.1:22换成任意 TCP 后端如-r ipv4:192.168.1.10:8080从而在 TEN-framework 自带的 libwebsockets 源码树内快速验证HTTP(S) 与任意二进制协议共享端口的部署思路。赞分享人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载相关推荐libwebsockets 多协议单端口实战Http Fallback 与 Raw 代理机制全解析libwebsockets 多协议单端口实战Http Fallback 与 Raw 代理机制全解析 导读 在 libwebsocketslws中一个监听人工智能AI Agent多模态语音AI 应用libwebsockets 原始协议回退实战minimal-raw-fallback-http-server 示例源码级解析libwebsockets 原始协议回退实战minimal raw fallback http server 示例源码级解析 这篇技术指南围绕 libwebs人工智能AI Agent多模态语音AI 应用libwebsockets 原始 TCP 代理示例使用 raw-proxy 角色与协议插件实现端口转发libwebsockets 原始 TCP 代理示例使用 raw proxy 角色与协议插件实现端口转发 导读 minimal raw proxy 是 libw人工智能AI Agent多模态语音AI 应用上一篇KOReader 实战指南5 步让墨水屏阅读跑得更快下一篇FluentValidation 测试扩展指南用 TestValidate 与 TestHelper 编写健壮的校验器单元测试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表