【Linux 网络基础】libwebsockets HTTPS 服务端实现机制详解

libwebsockets HTTPS 服务端实现机制详解

概览

  • 代码根路径:libwebsockets`
  • 涉及模块:HTTP 监听与解析、TLS 初始化与握手、HTTPS 管道整合与连接管理、性能优化与缓存

模块流程图

HTTP 连接初始化

lws_create_context lws_create_vhost _lws_vhost_init_server AF _lws_vhost_init_server_af lws_plat_set_socket_options lws_socket_bind listen insert listen wsi accept lws_adopt_descriptor_vhost enter HTTP/WS role

HTTP/1 解析状态机

method line continue end START WSI_TOKEN_NAME_PART URI / method WSI_TOKEN_HTTP WSI_TOKEN_SKIPPING CRLF WSI_PARSING_COMPLETE lws_handshake_server

TLS 握手(服务端)

App LWS SSL Client lws_create_context 1 SSL_CTX_new + configure 2 load certs / key 3 accept() 4 SSL_new + set_fd + set_nbio 5 SSL_accept (non-blocking) 6 WANT_READ / WANT_WRITE 7 handshake done + ALPN 8 enter HTTP/WS callbacks 9 App LWS SSL Client

HTTPS 管道整合

yes no yes no accept SSL SSL_new SSL_accept done HTTP parse lws_handshake_server protocol callback

关键数据结构

struct lws_protocols

  • 定义位置:include/libwebsockets/lws-protocols-plugins.h:44
  • 关键字段:namecallbackper_session_data_sizerx_buffer_sizeidusertx_packet_size
  • 作用:声明协议名与对应回调,服务器在路由后通过该结构触发应用层处理(HTTP/WS 等)。

struct lws_vhost

  • 定义位置:lib/core-net/private-lib-core-net.h:425
  • 关键字段:
    • protocols:协议数组引用
    • listen_wsi:该 vhost 的监听 wsi 列表
    • listen_portiface:监听端口与绑定接口
    • tls:TLS 相关上下文(含 SSL_CTX*、策略、缓存等)
    • options:行为选项位(如允许共享监听、IPv6 配置、TLS 选项)

struct lws

  • 定义位置:lib/core-net/private-lib-core-net.h:634
  • 关键字段:
    • a.contexta.vhosta.protocol:上下文、虚拟主机、协议绑定
    • http.ah:HTTP 头解析缓冲与状态机
    • tls.ssl:SSL 会话句柄与握手状态
    • desc.sockfd:底层套接字 fd

HTTP 实现细节

监听套接字创建与接受

  • 上下文创建入口:lib/core/context.c:393 中的 lws_create_context
  • vhost 初始化调用:lib/core-net/vhost.c:1030 调用 _lws_vhost_init_server
  • 地址族与监听创建:lib/roles/http/server/server.c:44989 _lws_vhost_init_server/_af
  • 绑定与监听:
    • bind() 封装:lib/core-net/network.c:274 lws_socket_bind
    • listen()lib/roles/http/server/server.c:403
  • 接受连接:lib/roles/listen/ops-listen.c:28 rops_handle_POLLIN_listen,调用 accept() 后经 lws_adopt_descriptor_vhost 进入角色与协议处理(147)。

HTTP/1 解析器状态机

  • 入口:lib/roles/h1/ops-h1.c:41 lws_read_h1
  • 服务器握手驱动:lib/roles/http/server/server.c:2285 lws_handshake_server
  • 解析核心:lib/roles/http/parsers.c:1018 lws_parse
  • 机制要点:
    • 单字节状态机,逐字节推进,支持分片与未知头处理
    • 方法行解析:请求行遇空格切换到 WSI_TOKEN_HTTP 版本解析
    • 头名/值解析:利用 lextable_h1 做头名识别,存储片段索引
    • 结束检测:CRLF 进入 WSI_PARSING_COMPLETE,随后进行路由与协议确认

路由与协议回调触发

  • 服务器握手后路由:lib/roles/http/server/server.c:2240 起,lws_bind_protocol 绑定协议,进入 LRS_ESTABLISHED
  • 默认 HTTP 处理:lib/roles/http/server/server.c:2530 lws_http_action
  • 升级处理:检测 Upgrade 头,进入 WS 或 H2C(24992536),并发出 LWS_CALLBACK_HTTP_CONFIRM_UPGRADE
  • 应用回调枚举:include/libwebsockets/lws-callbacks.h:217 LWS_CALLBACK_HTTPHTTP_BODYHTTP_BODY_COMPLETION

TLS 握手实现

服务器握手流程分阶段

  • 数据报文与握手消息概览(以TLS1.2/1.3为准)
    • ClientHello:客户端发起,包含支持的协议版本、随机数、支持的密码套件、扩展(如SNI、ALPN)。
    • ServerHello:服务器选择版本与套件(TLS1.3还会下发EncryptedExtensionsCertificateCertificateVerifyFinished)。TLS1.2通常下发ServerKeyExchangeCertificateServerHelloDone等。
    • 证书链与验证:服务器发送证书链;若要求客户端证书,客户端在后续发送其证书供验证。
    • 秘钥协商:基于选定套件完成密钥交换(ECDHE等),双方派生主密钥与会话密钥。
    • Finished:双方发送Finished报文确认握手完整性并切换至应用数据加密通道。
  • 初始化阶段(配置SSL_CTX)
    • lws_tls_server_vhost_backend_init:创建 SSL_CTXSSLv23_server_method),关闭SSLv2/3并禁用压缩,启用SSL_OP_CIPHER_SERVER_PREFERENCE;配置ECDH/DH自动参数;按需设置cipher_list(TLS1.2)与ciphersuites(TLS1.3);注册SNI回调以在握手时根据servername切换SSL_CTX
    • 证书链与私钥加载:lws_tls_server_certs_load 支持从文件或内存(DER/PEM)载入,并通过SSL_CTX_check_private_key完成匹配校验;可选加载CA用于客户端证书验证。
  • 接入阶段(TCP层)
    • 监听fd上accept()得到会话fd,应用通用套接字选项(非阻塞、TCP_NODELAY等)。
  • 会话建立阶段(SSL对象创建与非阻塞BIO)
    • lws_tls_server_new_nonblockingSSL_new(ctx)创建SSL对象;SSL_set_fd(fd)关联底层fd;SSL_get_rbio/wbio + BIO_set_nbio(1)将读写BIO均设为非阻塞;可启用SSL_set_info_callback捕获握手事件。
  • 握手推进阶段(非阻塞驱动)
    • lws_tls_server_accept:调用SSL_accept,如返回WANT_READ/WRITE,通过lws_change_pollfd切换事件关注(POLLIN/POLLOUT),等待下一次可读/可写再继续调用SSL_accept
    • 握手成功后:
      • 提取对端证书信息(如Common Name);
      • 记录协商的套件与ALPN;
      • SSL_pending()存在握手期间缓冲的明文,加入TLS缓冲队列并合成POLLIN以尽快消费。
    • ALPN选择:在服务器端,lws_context_init_alpn 设置 SSL_CTX_set_alpn_select_cb,握手过程中由OpenSSL在 alpn_cb 基于vhost配置选择协议(如h2http/1.1);完成后通过 lws_role_call_alpn_negotiated 让对应角色执行协商后逻辑(例如h2切换为HTTP/2状态机)。
  • 失败与关闭阶段
    • 错误(SSL_ERROR_SYSCALL/SSL_ERROR_SSL)则中止;正常关闭使用SSL_shutdown并释放资源。

客户端证书与SNI

  • 客户端证书校验:当启用LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT时,SSL_CTX_set_verify结合自定义回调OpenSSL_verify_callback把验证结果报告到LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,用户代码可参与决策。
  • SNI(Server Name Indication):lws_ssl_server_name_cb在握手阶段读取servername,基于端口与名称选择匹配的vhost并切换其SSL_CTX,实现同端口多证书与多虚拟主机。

加密/解密数据路径

  • 加密写路径
    • 应用层通过lws_write提交明文;底层在TLS会话活跃时,写入由SSL_write完成,将明文经记录层分片、MAC(或AEAD)与加密后通过套接字发送;非阻塞下若WANT_WRITE,由POLLOUT继续驱动直到完成。
  • 解密读路径
    • 网络数据进入读BIO;调用SSL_read将密文经记录层处理、解密与完整性校验得到明文;若内部有缓存(SSL_pending),通过合成POLLIN尽快继续上送;明文最终进入HTTP/WS解析路径(lws_read_h1 / lws_ws_rx_sm)。

会话复用与性能

  • 会话缓存:openssl-session.c基于vhost维护LRU缓存,lws_tls_session_new_cb在新会话建立时插入条目,lws_tls_reuse_session在新连接时尝试复用,必要时延长会话有效期;TTL到期由定时器清理。
  • 连接层优化:监听侧SO_REUSEPORT便于多线程负载分担;TCP_FASTOPEN缩短连接建立延迟;TCP_NODELAY减少Nagle影响。TLS层采用服务器优先套件以选择更高性能的组合(如AES-GCM/CHACHA20-Poly1305)。

HTTPS 通信整合与连接管理

HTTP over TLS 管道

  • TLS 监听接入:lws_server_socket_service_ssl() 在握手状态 LRS_SSL_INIT/ACK_PENDING 间推进(lib/tls/tls-server.c:128 起)
  • 可选回退与混用:支持 LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT 等选项在同端口区分非 TLS 访问并执行重定向或回退(tls-server.c:190 起)

vhost 与 wsi 关系与多路复用

  • struct lws_vhost 管理监听、协议集与 TLS 上下文(private-lib-core-net.h:425
  • 每个新连接为一个 struct lws(wsi),绑定到 vhost 并根据角色(HTTP/WS/H2)驱动状态机
  • HTTP/2 与 WS 在 wsi 上具备子流/扩展能力,靠 mux 与角色 ops 管理(同头文件)

性能优化措施

  • TLS 会话缓存:
    • 复用与缓存管理:lib/tls/openssl/openssl-session.c:77 lws_tls_reuse_session210 lws_tls_session_new_cb
    • 基于 vhost 的 LRU 缓存与 TTL 过期回调,控制条目上限(178152
  • OCSP Stapling:未检索到直接集成入口(代码库中未匹配 OCSP/stapling API),可通过 OpenSSL 回调在用户层拓展;当前以会话缓存与 SNI/ALPN 优化为主。

最小 HTTPS 服务器示例

c 复制代码
#include <libwebsockets.h>

static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
    switch (reason) {
    case LWS_CALLBACK_HTTP: {
        unsigned char buf[LWS_PRE + 512], *p = &buf[LWS_PRE], *end = &buf[sizeof(buf) - 1];
        const char *body = "hello";
        if (lws_add_http_common_headers(wsi, 200, "text/plain", (lws_filepos_t)5, &p, end)) return 1;
        if (lws_finalize_write_http_header(wsi, &buf[LWS_PRE], &p, end)) return 1;
        if (lws_write(wsi, (unsigned char *)body, 5, LWS_WRITE_HTTP_FINAL) < 0) return 1;
        return 1;
    }
    default:
        break;
    }
    return 0;
}

static const struct lws_protocols protocols[] = {
    { "http", callback_http, 0, 0, 0, NULL, 0 },
    LWS_PROTOCOL_LIST_TERM
};

int main(void) {
    struct lws_context_creation_info info;
    memset(&info, 0, sizeof(info));
    info.port = 8443;
    info.protocols = protocols;
    info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
    info.ssl_cert_filepath = "localhost-100y.cert";
    info.ssl_private_key_filepath = "localhost-100y.key";
    struct lws_context *cx = lws_create_context(&info);
    if (!cx) return 1;
    while (lws_service(cx, 0) >= 0) { }
    lws_context_destroy(cx);
    return 0;
}
  • 运行前准备:在工作目录放置 localhost-100y.certlocalhost-100y.key(库自带多个 minimal 示例使用同名证书,可参照 minimal-examples-lowlevel/http-server/minimal-http-server-tls/README.md 的生成命令)。
  • 编译示例命令(根据环境调整):
    • gcc -o minimal-https minimal-https.c -lwebsockets
    • 或使用 CMake 将该文件加入目标并链接 libwebsockets

代码参考索引

  • lib/core/context.c:393 lws_create_context
  • lib/core-net/vhost.c:1030 lws_create_vhost 内部触发 _lws_vhost_init_server
  • lib/roles/http/server/server.c:449 _lws_vhost_init_server
  • lib/roles/http/server/server.c:89 _lws_vhost_init_server_af
  • lib/core-net/network.c:274 lws_socket_bind
  • lib/roles/http/server/server.c:403 listen() 调用处
  • lib/roles/listen/ops-listen.c:28 rops_handle_POLLIN_listen 接受连接
  • lib/roles/http/parsers.c:1018 lws_parse HTTP 状态机
  • lib/roles/h1/ops-h1.c:41 lws_read_h1 读入驱动
  • include/libwebsockets/lws-protocols-plugins.h:44 struct lws_protocols
  • include/libwebsockets/lws-callbacks.h:217 回调枚举
  • lib/tls/openssl/openssl-server.c:483 lws_tls_server_vhost_backend_init
  • lib/tls/openssl/openssl-server.c:585 lws_tls_server_certs_load 调用点
  • lib/tls/openssl/openssl-server.c:594 lws_tls_server_new_nonblocking
  • lib/tls/openssl/openssl-server.c:655 lws_tls_server_accept
  • lib/tls/openssl/openssl-session.c:77 lws_tls_reuse_session
  • libwebsockets.dox:857 INPUT 包含 READMEs

TLS1.2 与 TLS1.3差异要点

  • TLS1.3:握手消息经过加密阶段分层下发(如EncryptedExtensions),秘钥派生流程简化;套件仅定义对称算法组合(AEAD),密钥交换由组参数控制(如X25519)。
  • TLS1.2:握手明文阶段较长,套件同时定义密钥交换与对称算法;服务器可能下发ServerKeyExchange等独立消息。
  • 在libwebsockets中,具体差异由OpenSSL后端管理,lws侧通过SSL_CTX配置套件、ALPN与回调,握手推进统一由SSL_accept与事件循环驱动。

TLS 握手时序(细粒度)

Client libwebsockets OpenSSL HTTP/WS TCP connect (accept) 1 SSL_new + set_fd + set_nbio 2 WANT_READ / WANT_WRITE 3 resume SSL_accept 4 loop [SSL_accept non-blocking] ClientHello parsed 5 SNI select (servername) 6 ALPN select (h2 / http/1.1) 7 ServerHello 8 EncryptedExtensions 9 Certificate / CertificateVerify 10 Finished 11 opt [TLS1.3] ServerHello / Certificate / ServerKeyExchange / ServerHelloDone 12 ClientKeyExchange / ChangeCipherSpec / Finished 13 opt [TLS1.2] handshake done 14 route to HTTP/WS (lws_handshake_server) 15 application data over TLS 16 Client libwebsockets OpenSSL HTTP/WS

对应代码实现关键位置

  • 监听与接受:lib/roles/listen/ops-listen.c:28 接受连接并调用 lws_adopt_descriptor_vhost
  • 创建SSL与非阻塞BIO:lib/tls/openssl/openssl-server.c:594 lws_tls_server_new_nonblocking
  • 推进握手:lib/tls/openssl/openssl-server.c:655 lws_tls_server_accept(WANT_READ/WRITE 驱动)
  • 客户端证书验证回调:lib/tls/openssl/openssl-server.c:39 OpenSSL_verify_callback
  • SNI选择:lib/tls/openssl/openssl-server.c:100 lws_ssl_server_name_cb
  • ALPN选择注册:lib/tls/tls.c:228 SSL_CTX_set_alpn_select_cb;协商后通知:lib/core-net/vhost.c:113 lws_role_call_alpn_negotiated
  • 握手后进入HTTP解析:lib/roles/http/server/server.c:2285 lws_handshake_server;H1解析状态机:lib/roles/http/parsers.c:1018 lws_parse
相关推荐
大白的编程日记.1 小时前
【计算网络学习笔记】MySql的多版本控制MVCC和Read View
网络·笔记·学习·mysql
optimistic_chen1 小时前
【Redis 系列】Redis详解
linux·数据库·redis·缓存·xsheel
低客的黑调1 小时前
了解JVM 结构和运行机制,从小白编程Java 大佬
java·linux·开发语言
想唱rap1 小时前
C++ map和set
linux·运维·服务器·开发语言·c++·算法
CodeByV2 小时前
【Linux】Ext 系列文件系统深度解析:从磁盘到软硬链接
linux·服务器
运维-大白同学2 小时前
2025最全面开源devops运维平台功能介绍
linux·运维·kubernetes·开源·运维开发·devops
梦在深巷、3 小时前
linux系统防火墙之iptables
linux·运维·服务器
布朗克1683 小时前
HTTP 与 HTTPS 的工作原理及其区别
http·https
shmexon3 小时前
上海兆越亮相无锡新能源盛会,以硬核通信科技赋能“能碳未来”
网络·人工智能