Nginx在ngx_ssl_create_connection()中注册OpenSSL回调:SSL_set_info_callback设ngx_ssl_info_handler捕获握手状态,SSL_set_verify设ngx_ssl_verify_callback处理证书验证,并通过SSL_set_tlsext_servername_arg和SSL_set_alpn_select_cb支持SNI与ALPN;握手由ngx_ssl_handshake()循环调用SSL_do_handshake()驱动,遇I/O阻塞时转交事件循环处理。

阅读 Nginx 源码中 SSL/TLS 握手阶段的回调函数处理,关键在于理清 OpenSSL 与 Nginx 的交互边界、定位核心回调注册点、理解事件驱动下的状态流转。不需要通读整个 SSL 模块,而是聚焦在“谁注册了哪些回调”、“何时被调用”、“Nginx 如何响应”。
明确 OpenSSL 回调在 Nginx 中的注册位置
Nginx 并不直接使用 OpenSSL 的高层 API(如 SSL_connect/SSL_accept),而是通过设置底层回调,把控制权交还给自己的事件循环。核心注册发生在 ngx_ssl_create_connection() 中:
- 调用 SSL_set_info_callback() 设置通用状态通知回调(ngx_ssl_info_handler)——用于捕获握手各阶段(如 SSL_ST_OK、SSL_ST_RENEGOTIATE)和警告/错误
- 调用 SSL_set_verify() 设置证书验证回调(ngx_ssl_verify_callback)——当启用
ssl_verify_client on时触发,决定是否接受客户端证书 - 对于自定义 SNI 或 ALPN 处理,Nginx 在 ngx_ssl_handshake_handler() 前通过 SSL_set_tlsext_servername_arg() 和 SSL_set_alpn_select_cb() 注册对应逻辑
跟踪握手主流程中的回调触发时机
SSL 握手不是单次调用完成的,而是在 Nginx 的 ngx_ssl_handshake() 中循环调用 SSL_do_handshake(),每次返回后检查结果并决定是否继续。此时,OpenSSL 内部会按需触发你注册的回调:
- ngx_ssl_info_handler 在每次握手状态变化时被调用(例如:从 SSL_ST_INIT → SSL_ST_BEFORE → SSL_ST_OK),可据此判断是否进入密钥交换、证书验证、Finished 等阶段
- 若握手因 I/O 阻塞(如等待客户端发 Certificate),SSL_do_handshake() 返回 -1,SSL_get_error() 返回
SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE,Nginx 就会重新注册 read/write 事件,下次事件就绪再继续 - 真正执行证书验证(verify callback)发生在 OpenSSL 收到客户端证书后、决定是否信任前,此时 ngx_ssl_verify_callback 被调用,Nginx 可检查
X509_STORE_CTX_get_current_cert()并返回 1(accept)或 0(reject)
重点关注与 Nginx 事件循环协同的关键函数
SSL 握手必须适配 Nginx 的非阻塞 I/O 模型,因此不能只看 OpenSSL 回调,更要关注 Nginx 如何把 OpenSSL 的“想读/想写”翻译成 epoll/kqueue 事件:
- ngx_ssl_handshake_handler() 是 SSL 握手的事件入口,由读/写事件触发;它调用 ngx_ssl_handshake(),后者内部反复调用 SSL_do_handshake()
- 当 SSL_do_handshake() 返回
SSL_ERROR_WANT_READ,Nginx 调用 ngx_add_timer() 并等待 socket 可读;返回SSL_ERROR_WANT_WRITE则等待可写 - 超时控制由 ngx_ssl_handshake() 中的
c->ssl->handshaked和c->read->timer共同维护,一旦超时,直接关闭连接
调试与验证的小技巧
源码阅读配合日志更高效:
- 在 ngx_ssl_info_handler 开头加
ngx_log_debug1(NGX_LOG_DEBUG_SSL, c->log, 0, "SSL info: %s", SSL_state_string_long(ssl));,可实时看到握手所处 OpenSSL 内部状态 - 在 ngx_ssl_verify_callback 中打印
SSL_get_peer_certificate(ssl)是否为 NULL,确认客户端是否发送了证书 - 用
strace -e trace=epoll_wait,sendto,recvfrom -p $(pidof nginx)观察实际系统调用,对照源码中 ngx_ssl_handshake() 的返回分支

















