开设购物网站的方案,企业软件定制开发报价,wordpress 默认分页,怎么制作游戏 需要什么软件目录 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")