绍兴网站建设方案推广,外贸流程询盘,数字博物馆网站建设,wordpress跳转链接404目录 1. 源起2. 分析验证环境的配置3. 源码分析3.1 代理模块的请求入口点分析3.2 发起与上游服务器的连接3.3 连接回调3.4 TCP连接建立成功后为上下游数据透传做准备3.5 TCP连接的ssl上下文初始化3.6 ssl握手成功后的处理3.7 连接数据的收与发1. 源起 我一直来对ssl建立连接的过… 目录 1. 源起2. 分析验证环境的配置3. 源码分析3.1 代理模块的请求入口点分析3.2 发起与上游服务器的连接3.3 连接回调3.4 TCP连接建立成功后为上下游数据透传做准备3.5 TCP连接的ssl上下文初始化3.6 ssl握手成功后的处理3.7 连接数据的收与发 1. 源起 我一直来对ssl建立连接的过程一知半解,以前分析nginx代码的时候一旦碰到ssl连接部分的代码都是直接跳过,前面在分析ngx_http_upstream_dynamic_module的时候正好想到了是不是可以给它添加一个能够支持https健康检查的功能,所以今天决定沉下心来仔细分析一下nginx本身的与上游服务器建立连接的实现逻辑。 为了简单起见,我决定选用ngx_stream_proxy_module模块作为分析学习的目标,因为相对于ngx_http_stream_proxy_module来说,前者逻辑上更加纯粹,少了七层的业务逻辑,让我能够更加专注地去分析关于ssl连接处理逻辑部分。 希望这次通过分析,能够对ssl连接的建立以及其后续的读写交互的实现逻辑有个整体的把握,在此基础上,将来为ngx_http_upstream_dynamic_module实现一个能够支持https主动健康检测的功能。 这次,我准备采用官方原生的最新稳定版nginx 1.24.0作为分析对象。为什么不用tengine呢?因为现在的tengine还夹杂了国密openssl的支持逻辑,采用官方原生版本更加纯粹,让分析的目标更加聚焦。
2. 分析验证环境的配置 配置一个分析验证环境来进行测试分析还是非常简单的,过程如下: 1. 从官网下载好nginx 1.24.0版本后进行解压,然后用以下命令进行配置: ./configure --prefix=`pwd` --with-stream --with-stream_ssl_module这样nginx从源码层面就开启了支持ssl的TCP代理能力。 2. 对nginx.conf文件进行配置,配置内容如下:
#user nobody;
worker_processes 1;#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024;
}stream {server {listen 9000;proxy_ssl on; /* 连接上游服务器采用ssl协议 */proxy_pass 104.193.88.77:443;}
} 3. 启动nginx,进行curl测试: curl "http://127.0.0.1:9000/test/" -v* Trying 127.0.0.1:9000...
* Connected to 127.0.0.1 (127.0.0.1) port 9000 (#0)GET /test/ HTTP/1.1Host: 127.0.0.1:9000User-Agent: curl/7.81.0Accept: */** Mark bundle as not supporting multiuseHTTP/1.1 500 Internal Server ErrorContent-Length: 0Content-Type: text/plain; charset=utf-8Date: Wed, 07 Feb 2024 02:57:01 GMTServer: bfe* Connection #0 to host 127.0.0.1 left intact 从上面的信息中已经可以看到上游服务器已经响应了,只不过它响应是500错误,这个无所谓,至少表明https透明代理的功能已经正常工作了。
3. 源码分析 本文主要聚焦在ssl连接逻辑的分析,所以中间会跳过和ssl逻辑不太相关的代码,虽然可能这部分对nginx本身的功能逻辑非常重要。另外,本文也假设只是TCP代理,不对UDP代理进行分析。 下面直接进入主题,从代理模块的请求入口点开始分析。
3.1 代理模块的请求入口点分析 代理模块的请求入口点是ngx_stream_proxy_handler函数,一旦客户端和nginx建立了TCP连接后,nginx就会调用代理模块的这个函数,开始与上游服务器建立连接。 该函数源码主要就是以下列出的三个步骤:
static void
ngx_stream_proxy_handler(ngx_stream_session_t *s)
{/* 创建ngx_stream_upstream_t上下文,对它进行必要的初始化,并关联到ngx_stream_session_t中 *//* 如果上游服务器的地址已经解析好就调用ngx_stream_proxy_connect开始连接上游服务器 *//* 如果上游服务器的地址需要域名解析则开启异步解析流程 */
}因此,我们需要重点关注的是ngx_stream_proxy_connect,它负责与上游服务器建立TCP连接。
3.2 发起与上游服务器的连接
static void
ngx_stream_proxy_connect(ngx_stream_session_t *s)
{ngx_int_t rc;ngx_connection_t *c, *pc;ngx_stream_upstream_t *u;ngx_stream_proxy_srv_conf_t *pscf;c = s-connection;c-log-action = "connecting to upstream";pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);u = s-upstream;u-connected = 0;u-proxy_protocol = pscf-proxy_protocol;if (u-state) {u-state-response_time = ngx_current_msec - u-start_time;}u-state = ngx_array_push(s-upstream_states);if (u-state == NULL) {ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);return;}ngx_memzero(u-state, sizeof(ngx_stream_upstream_state_t));u-start_time = ngx_current_msec;u-state-connect_time = (ngx_msec_t) -1;u-state-first_byte_time = (ngx_msec_t) -1;u-state-response_time = (ngx_msec_t) -1;/* 创建SOCKET,并发起异步连接请求*/rc = ngx_event_connect_peer(u-peer);ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c-log, 0, "proxy connect: %i", rc);if (rc == NGX_ERROR) {ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);return;}u-state-peer = u-peer.name;if (rc == NGX_BUSY) {ngx_log_error(NGX_LOG_ERR, c-log, 0, "no live upstreams")