什么是代理?代理是作为中间层,作用于上下游两端。将下游的“请求”转移提交到上游,将上游的“响应”提交给下游。代理不仅在设计模式等程序设计中有比较广泛的使用,同时在大型的系统工程中使用也比较广泛,在web服务器领域,代理出现的频率也是很高的。
web服务中的代理有正向代理和反向代理两种,在大型的web服务中,反向代理是一种非常常用的技术,使用反向代理技能起到转发请求的作用,同时也能做到请求的控制及均衡。
正向代理主要的机制是 客户端预先能知道代理服务器的地址及端口,通过客户端的设置来访问代理服务器,继而代理服务器产生作用以完成请求的响应处理,常用的ss 就是使用正向代理的方式来做到访问外部网站的,简单地理解,正向代理的代理服务器是由客户端"发现"的。
反向代理的机制和正向代理在取得代理服务器地址端口的方式是不一样的,反向代理不需要客户端了解代理服务器,无需感知代理服务器的任何信息,通过访问域名通过解析服务得到代理服务器的地址和端口,从而完成访问,反向代理常见的一种功能是cdn(内容分发网络)中的缓存服务的关键功能。
1.下图是正向代理的图解
2.反向代理图解
通过图解应该能容易看出正向代理和反向代理的区别。
以nginx的proxy模块为例来介绍web代理服务的实现的细节。nginx的proxy模块提供了代理的头设置及对后端响应进行解析处理的功能,实际上proxy模块基本也是做了设置和解析处理的功能,具体的上游服务器的连接 接收及到向下游请求端的转发及web内容的缓存主要是nginx upjistream模块来提供的,nginx upstream实现的细节的介绍可以在点击打开链接这个连接中看到。
nginx proxy模块提供了比较多的配置及设置功能,包括变量设置后端的server路径及协议、连接及响应的超时设置、buffer缓冲的设置、向上游获取数据的限速设置、缓存的控制等。会有一篇blog专门用来介绍nginx 代理功能的配置及细节的要点。本篇blog主要详细说明proxy模块的实现。
typedef struct {
ngx_array_t caches; /*http缓存数组 意味着可以配置多个缓存目录*/
} ngx_http_proxy_main_conf_t
struct ngx_http_proxy_rewrite_s { /*代理url重定向处理*/
ngx_http_proxy_rewrite_pt handler; /*重定向处理函数指针*/
union {
ngx_http_complex_value_t complex; /*规则变量*/
#if (NGX_PCRE)
ngx_http_regex_t *regex;
#endif
} pattern;
ngx_http_complex_value_t replacement; /*url内容替换变量*/
}
typedef struct { /*代理使用的http变量*/
ngx_str_t key_start; /**/
ngx_str_t schema; /*协议名*/
ngx_str_t host_header; /*host头*/
ngx_str_t port; /*端口*/
ngx_str_t uri; /*uri*/
} ngx_http_proxy_vars_t
typedef struct { /*代理头结构 可以有多个http头*/
ngx_array_t *flushes;
ngx_array_t *lengths;
ngx_array_t *values;
ngx_hash_t hash;
} ngx_http_proxy_headers_t
typedef struct { /*代理location域配置*/
ngx_http_upstream_conf_t upstream; /*upstream配置*/
ngx_array_t *body_flushes; /*代理包体数据 用于发送到上游服务器*/
ngx_array_t *body_lengths;
ngx_array_t *body_values;
ngx_str_t body_source;
ngx_http_proxy_headers_t headers; /*上面的代理头*/
#if (NGX_HTTP_CACHE)
ngx_http_proxy_headers_t headers_cache; /*包含了缓存控制的代理头*/
#endif
ngx_array_t *headers_source; /*原始头*/
ngx_array_t *proxy_lengths; /*保存了脚本引擎信息 配合proxy_values使用*/
ngx_array_t *proxy_values;
ngx_array_t *redirects; /*url重定向处理数组*/
ngx_array_t *cookie_domains; /*cookie信息配置*/
ngx_array_t *cookie_paths;
ngx_http_complex_value_t *method; /*访问的method变量 如GET HEAD POST...*/
ngx_str_t location; /*重定向的location*/
ngx_str_t url; /*重定向的url*/
#if (NGX_HTTP_CACHE)
ngx_http_complex_value_t cache_key; /*配置的缓存key值变量*/
#endif
ngx_http_proxy_vars_t vars; /*代理所用的变量*/
ngx_flag_t redirect; /*是否重定向*/
ngx_uint_t http_version; /*http协议版本*/
ngx_uint_t headers_hash_max_size; /*hash表最大值*/
ngx_uint_t headers_hash_bucket_size; /*hash表表元素数量*/
#if (NGX_HTTP_SSL)
ngx_uint_t ssl; /*https代理访问*/
ngx_uint_t ssl_protocols;
ngx_str_t ssl_ciphers;
ngx_uint_t ssl_verify_depth;
ngx_str_t ssl_trusted_certificate;
ngx_str_t ssl_crl;
ngx_str_t ssl_certificate;
ngx_str_t ssl_certificate_key;
ngx_array_t *ssl_passwords;
#endif
} ngx_http_proxy_loc_conf_t
typedef struct { /*代理处理的上下文结构*/
ngx_http_status_t status; /*http状态*/
ngx_http_chunked_t chunked; /*chunked传输信息*/
ngx_http_proxy_vars_t vars; /*变量*/
off_t internal_body_length; /*响应包体长度信息*/
ngx_chain_t *free; /*处理chain */
ngx_chain_t *busy;
unsigned head:1; /*HEAD请求*/
unsigned internal_chunked:1; /*包体是chunked方式传输*/
unsigned header_sent:1; /*响应头数据是否已经发送到请求端*/
} ngx_http_proxy_ctx_t
nginx proxy模块处理流程
1.配置content_handler处理函数 由proxy_pass设置来完成
static char *
ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
...
if (plcf->upstream.upstream || plcf->proxy_lengths) { /*已经配置过了*/
return "is duplicate";
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_proxy_handler; /*设置content_handler为proxy_handler处理*/
if (clcf->name.data[clcf->name.len - 1] == '/') { /*路径配置以"/"结束 自动跳转*/
clcf->auto_redirect = 1;
}
value = cf->args->elts;
url = &value[1]; /*取得配置的proxy_pass配置的路径*/
n = ngx_http_script_variables_count(url); /*解析配置的路径 得到变量的数量*/
if (n) { /*代理的路径存在变量*/
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = url;
sc.lengths = &plcf->proxy_lengths;
sc.values = &plcf->proxy_values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) { /*通过脚本引擎 编译变量*/
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_SSL)
plcf->ssl = 1; /*安装了ssl功能 表示ssl可用*/
#endif
return NGX_CONF_OK;
}
if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) { /*配置的路径以http起头 自动设置端口*/
add = 7;
port = 80;
} else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) {/*同上*/
#if (NGX_HTTP_SSL)
plcf->ssl = 1;
add = 8;
port = 443;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"https protocol requires SSL support");
return NGX_CONF_ERROR;
#endif
} else { /*没有配置协议(http或者https)*/
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix");
return NGX_CONF_ERROR;
}
ngx_memzero(&u, sizeof(ngx_url_t));
/*设置upstream的url及端口 no_resolve为真 还未开始解析域名*/
u.url.len = url->len - add;
u.url.data = url->data + add;
u.default_port = port;
u.uri_part = 1;
u.no_resolve = 1;
/*增加一个upstream */
plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
if (plcf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
/*设置proxy location域的schema变量*/
plcf->vars.schema.len = add;
plcf->vars.schema.data = url->data;
plcf->vars.key_start = plcf->vars.schema;
/*将upstream的host及端口设置到变量中*/
ngx_http_proxy_set_vars(&u, &plcf->vars);
/*location是当前location域的名称*/
plcf->location = clcf->name;
if (clcf->named
#if (NGX_PCRE)
|| clcf->regex
#endif
|| clcf->noname)
{ //
if (plcf->vars.uri.len) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_pass\" cannot have URI part in "
"location given by regular expression, "
"or inside named location, "
"or inside \"if\" statement, "
"or inside \"limit_except\" block");
return NGX_CONF_ERROR;
}
plcf->location.len = 0;
}
/*设置proxy的url*/
plcf->url = *url;
return NGX_CONF_OK;
}
2.proxy模块配置的content_handler处理
static ngx_int_t
ngx_http_proxy_handler(ngx_http_request_t *r)
{
...
if (ngx_http_upstream_create(r) != NGX_OK) { /*创建upstream */
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/*为反向代理处理上下文分配空间*/
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
if (ctx == NULL) { /*分配失败 返回500错误*/
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/*设置当前proxy模块的ctx地址*/
ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
/*得到proxy的location域配置信息*/
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
/*r->upstream已经由ngx_http_upstream_create分配过了*/
u = r->upstream;
if (plcf->proxy_lengths == NULL) { /*已经得到了代理的schema 端口及url信息*/
ctx->vars = plcf->vars;
u->schema = plcf->vars.schema;
#if (NGX_HTTP_SSL)
u->ssl = (plcf->upstream.ssl != NULL);
#endif
} else { /*需要使用脚本引擎解析代理的schema 端口和url信息*/
if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
/*设置upstream响应的tag*/
u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;
/*使用upstream的location域配置*/
u->conf = &plcf->upstream;
#if (NGX_HTTP_CACHE)
pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module);
/*得到cache http缓存的配置*/
u->caches = &pmcf->caches; /*这里是缓存的数组*/
u->create_key = ngx_http_proxy_create_key; /*设置create_key生成缓存key值的函数*/
#endif
/*配置各个阶段的处理函数指针*/
u->create_request = ngx_http_proxy_create_request;
u->reinit_request = ngx_http_proxy_reinit_request;
u->process_header = ngx_http_proxy_process_status_line;
u->abort_request = ngx_http_proxy_abort_request;
u->finalize_request = ngx_http_proxy_finalize_request;
r->state = 0;
if (plcf->redirects) { /*proxy的location域配置了url重定向 使用proxy的重定向处理函数来处理重定向*/
u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
}
if (plcf->cookie_domains || plcf->cookie_paths) { /*同上*/
u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
}
u->buffering = plcf->upstream.buffering; //是否进行buffering缓冲
u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); /*为upstream的event_pipe分配空间*/
if (u->pipe == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/*设置event_pipe的处理函数*/
u->pipe->input_filter = ngx_http_proxy_copy_filter;
u->pipe->input_ctx = r;
u->input_filter_init = ngx_http_proxy_input_filter_init;
u->input_filter = ngx_http_proxy_non_buffered_copy_filter;
u->input_filter_ctx = r;
u->accel = 1; /*accel设置为真*/
if (!plcf->upstream.request_buffering
&& plcf->body_values == NULL && plcf->upstream.pass_request_body
&& (!r->headers_in.chunked
|| plcf->http_version == NGX_HTTP_VERSION_11))
{ /*在没有配置request_buffering和没有配置body变量 不是chunked方式传输或者http协议版本是1.1的情况下 不缓存请求包体*/
r->request_body_no_buffering = 1;
}
/*读取请求端的包体和设置upstream初始化函数*/
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { /*处理后的返回值有误 直接返回处理结果 要么重试 要么错误*/
return rc;
}
/*NGX_DONE标志处理完成了*/
return NGX_DONE;
}
nginx proxy代理的流程主要是上面两个,中间处理会使用到的功能在下面作说明。
1.ngx_http_proxy_eval脚本引擎解析配置的proxy代理请求路径
ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
ngx_http_proxy_loc_conf_t *plcf)
{
...
if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,
plcf->proxy_values->elts)
== NULL) /*运行脚本引擎 得到解析出的变量的实际的值*/
{
return NGX_ERROR;
}
if (proxy.len > 7
&& ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0)
{ /*代理路径以http起头 设置默认的端口为80*/
add = 7;
port = 80;
#if (NGX_HTTP_SSL)
} else if (proxy.len > 8
&& ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0)
{ /*代理路径以https起头 设置默认的端口为443*/
add = 8;
port = 443;
r->upstream->ssl = 1;
#endif
} else { /*不满足现有的http协议schema规则 返回错误*/
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"invalid URL prefix in \"%V\"", &proxy);
return NGX_ERROR;
}
u = r->upstream;
/*设置upstream schema信息 (http或者https)*/
u->schema.len = add;
u->schema.data = proxy.data;
ngx_memzero(&url, sizeof(ngx_url_t));
/*得到url数据*/
url.url.len = proxy.len - add;
url.url.data = proxy.data + add;
url.default_port = port;
url.uri_part = 1;
url.no_resolve = 1;
if (ngx_parse_url(r->pool, &url) != NGX_OK) { /*解析url*/
if (url.err) { /*解析url出错 报错并且返回错误*/
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"%s in upstream \"%V\"", url.err, &url.url);
}
return NGX_ERROR;
}
if (url.uri.len) { /*uri是有效的*/
if (url.uri.data[0] == '?') { /*uri以"?"起头 需要对uri进行修正处理*/
p = ngx_pnalloc(r->pool, url.uri.len + 1);
if (p == NULL) {
return NGX_ERROR;
}
*p++ = '/';
ngx_memcpy(p, url.uri.data, url.uri.len);
url.uri.len++;
url.uri.data = p - 1;
}
}
/*设置proxy上下文变量的key_start*/
ctx->vars.key_start = u->schema;
/*将端口信息设置到ctx的变量中*/
ngx_http_proxy_set_vars(&url, &ctx->vars);
/*分配域名解析空间*/
u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
if (u->resolved == NULL) { /*分配失败*/
return NGX_ERROR;
}
if (url.addrs) { /*url的地址是实际的地址(无需解析的)*/
u->resolved->sockaddr = url.addrs[0].sockaddr; /*设置socket地址信息*/
u->resolved->socklen = url.addrs[0].socklen;
u->resolved->name = url.addrs[0].name; /*设置地址名*/
u->resolved->naddrs = 1;
}
/*需要进行域名解析*/
u->resolved->host = url.host; //设置host
u->resolved->port = (in_port_t) (url.no_port ? port : url.port); /*设置端口 url中没有端口就使用默认的端口 有端口就使用解析的端口*/
u->resolved->no_port = url.no_port; /*no_port标记*/
return NGX_OK;
}
2.ngx_http_proxy_create_request转发(其实可以做配置处理)请求到上游服务器
static ngx_int_t
ngx_http_proxy_create_request(ngx_http_request_t *r)
{
...
u = r->upstream; //得到upstream 在handler中已经创建并初始化过了
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); /*得到proxy location域配置*/
#if (NGX_HTTP_CACHE) /*根据upstream是否能进行缓存 使用不同的http头*/
headers = u->cacheable ? &plcf->headers_cache : &plcf->headers;
#else
headers = &plcf->headers; /*没有编译缓存功能 直接使用头*/
#endif
if (u->method.len) { /**/
/* HEAD was changed to GET to cache response */
method = u->method;
} else if (plcf->method) { /*使用proxy配置的方法*/
if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) {
return NGX_ERROR;
}
} else { /*请求的method*/
method = r->method_name;
}
/*取得proxy模块的上下文*/
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (method.len == 4
&& ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0)
{ /*请求方法是HEAD请求*/
ctx->head = 1;
}
/*初始化发送数据的长度*/
len = method.len + 1 + sizeof(ngx_http_proxy_version) - 1
+ sizeof(CRLF) - 1;
escape = 0; /*转义符长度*/
loc_len = 0; /*location路径长度*/
unparsed_uri = 0; /*未解析的uri*/
if (plcf->proxy_lengths && ctx->vars.uri.len) { /*proxy_pass路径配置了 (proxy_pass配置了uri变量*/
uri_len = ctx->vars.uri.len; /*设置uri长度*/
} else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri) { /*proxy_pass没有配置uri变量 并且request保存了未解析的uri*/
unparsed_uri = 1;
uri_len = r->unparsed_uri.len; /*同上*/
} else {
loc_len = (r->valid_location && ctx->vars.uri.len) ?
plcf->location.len : 0; /*request的location为有效并且proxy_pass配置了uri变量会使用配置的location*/
if (r->quoted_uri || r->space_in_uri || r->internal) { /*带有百分号或者空格或者internal内部跳转标志的request*/
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI); /*计算得到转义符的长度*/
}
uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape
+ sizeof("?") - 1 + r->args.len; /*计算得到uri的长度*/
}
if (uri_len == 0) { /*uri的长度为0 意味着是无效的*/
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"zero length URI to proxy");
return NGX_ERROR;
}
len += uri_len; /*添加到需处理的长度*/
ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); /*初始化脚本引擎*/
/*刷新no_cacheable标志的http变量 */
ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes);
ngx_http_script_flush_no_cacheable_variables(r, headers->flushes);
if (plcf->body_lengths) { /*配置有body变量*/
le.ip = plcf->body_lengths->elts; /*得到body_lengths的数组第一个元素指针地址*/
le.request = r; /*设置脚本引擎的请求*/
le.flushed = 1; /*对引擎进行flushed标记*/
body_len = 0;
while (*(uintptr_t *) le.ip) { /*循环处理body_lengths中的变量*/
lcode = *(ngx_http_script_len_code_pt *) le.ip; /*得到保存的处理函数*/
body_len += lcode(&le); /*增加lcode处理后变量的长度即处理完body变量后的长度*/
}
ctx->internal_body_length = body_len; /*设置internal_body_length */
len += body_len; /*增加需发送到上游服务器的数据长度*/
} else if (r->headers_in.chunked && r->reading_body) { /*请求是chunked的方式同时还未完成请求包体的读取*/
ctx->internal_body_length = -1; /*设置internal_body_length -1 表示不使用无效标记*/
ctx->internal_chunked = 1; /*chunked传输标记*/
} else {
ctx->internal_body_length = r->headers_in.content_length_n; /*使用请求头的content_length*/
}
le.ip = headers->lengths->elts; /*proxy设置的http请求头元素指针设置到脚本引擎中*/
le.request = r; /*设置脚本引擎的request*/
le.flushed = 1; /*脚本引擎的flushed标记*/
while (*(uintptr_t *) le.ip) { /*脚本引擎遍历数组中的变量*/
lcode = *(ngx_http_script_len_code_pt *) le.ip; /*得到保存的脚本处理函数*/
key_len = lcode(&le); /*处理完成后返回http头key键长度*/
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { /*遍历得到http头key键值 键值可能由多个变量组合*/
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t); /*这里会跳到下一个http头处理*/
if (val_len == 0) { /*http头部的键值是无效的 直接跳过*/
continue;
}
len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1; /*代表一个http头 CRLF收尾*/
}
if (plcf->upstream.pass_request_headers) { /*upstream允许请求端的头部数据穿透*/
part = &r->headers_in.headers.part;
header = part->elts; /*得到请求头数组第一个元素的地址*/
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
/*请求头数组遍历*/
part = part->next;
header = part->elts;
i = 0;
}
if (ngx_hash_find(&headers->hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{ /*跳过proxy配置的http请求头 以免被request请求头覆盖*/
continue;
}
/*表示一个http请求头行的长度 同样以CRLF收尾*/
len += header[i].key.len + sizeof(": ") - 1
+ header[i].value.len + sizeof(CRLF) - 1;
}
}
/*根据前面计算得到的长度创建buffer*/
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) { /*创建buffer失败 返回错误*/
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool); /*创建chain---发送的时候有用*/
if (cl == NULL) { /*创建chain失败 返回错误*/
return NGX_ERROR;
}
cl->buf = b; /*将创建的buffer加入到chain*/
/*拷贝http请求行 method + uri + http_version + CRLF*/
b->last = ngx_copy(b->last, method.data, method.len);
*b->last++ = ' ';
u->uri.data = b->last;
if (plcf->proxy_lengths && ctx->vars.uri.len) { /*处理逻辑同判断uri长度相同*/
b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
} else if (unparsed_uri) {
b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);
} else {
if (r->valid_location) {
b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
}
if (escape) {
ngx_escape_uri(b->last, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI);
b->last += r->uri.len - loc_len + escape;
} else {
b->last = ngx_copy(b->last, r->uri.data + loc_len,
r->uri.len - loc_len);
}
if (r->args.len > 0) {
*b->last++ = '?';
b->last = ngx_copy(b->last, r->args.data, r->args.len);
}
}
u->uri.len = b->last - u->uri.data;
/*拷贝http version数据*/
if (plcf->http_version == NGX_HTTP_VERSION_11) {
b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11,
sizeof(ngx_http_proxy_version_11) - 1);
} else {
b->last = ngx_cpymem(b->last, ngx_http_proxy_version,
sizeof(ngx_http_proxy_version) - 1);
}
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));//初始化http脚本引擎
e.ip = headers->values->elts;
e.pos = b->last; /*将buffer的指针处理偏移挂载到引擎的pos上 为后面处理做准备*/
e.request = r;
e.flushed = 1;
le.ip = headers->lengths->elts;
while (*(uintptr_t *) le.ip) { /*遍历proxy配置的头部数组*/
lcode = *(ngx_http_script_len_code_pt *) le.ip;/*得到脚本处理函数*/
(void) lcode(&le); /*处理脚本 这里处理的是http头键*/
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { /*处理http头键值*/
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (val_len == 0) {
e.skip = 1;
while (*(uintptr_t *) e.ip) { /*循环处理http头键值*/
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t); /*引擎处理到http头键值的下一个变量 因为键值可能由不止一个变量组成*/
e.skip = 0;
continue;
}
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e); /*头部键处理完成 涉及数据拷贝*/
*e.pos++ = ':'; *e.pos++ = ' '; /*http头键和键值之间增加 冒号和空格*/
while (*(uintptr_t *) e.ip) { /*循环处理http头键值 涉及数据拷贝*/
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t); /*引擎处理到下一个头部行*/
*e.pos++ = CR; *e.pos++ = LF; /*以CRLF作为头处理收尾*/
}
b->last = e.pos; /*更新buffer last位置*/
if (plcf->upstream.pass_request_headers) {/*对upstream允许请求端request头部穿透的处理*/
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
/*遍历request头*/
part = part->next;
header = part->elts;
i = 0;
}
/*跳过在proxy中配置的http头*/
if (ngx_hash_find(&headers->hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{
continue;
}
/*拷贝request的头键 键值*/
b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
*b->last++ = ':'; *b->last++ = ' ';
b->last = ngx_copy(b->last, header[i].value.data,
header[i].value.len);
*b->last++ = CR; *b->last++ = LF;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%V: %V\"",
&header[i].key, &header[i].value);
}
}
/*以CRLF收尾表示http头处理完成*/
*b->last++ = CR; *b->last++ = LF;
if (plcf->body_values) { /*配置了包体变量 处理与http头处理是类似的*/
e.ip = plcf->body_values->elts;
e.pos = b->last;
e.skip = 0;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
b->last = e.pos;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header:%N\"%*s\"",
(size_t) (b->last - b->pos), b->pos);
if (r->request_body_no_buffering) { /*http请求的包体不进行缓冲*/
u->request_bufs = cl; /*将前面处理的buffer chain设置到upstream的request_bufs中*/
if (ctx->internal_chunked) { /*设置包体响应处理filter*/
u->output.output_filter = ngx_http_proxy_body_output_filter;
u->output.filter_ctx = r;
}
} else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) { /*proxy没有配置body变量同时允许request包体穿透到upstream*/
body = u->request_bufs;
u->request_bufs = cl;
while (body) { /*将request_bufs (会有配置buffer数量的限制)挂载到buffer chain*/
b = ngx_alloc_buf(r->pool); /*分配一个buffer*/
if (b == NULL) { /*分配失败 返回错误*/
return NGX_ERROR;
}
/*数据拷贝*/
ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
/*分配一个buffer chain*/
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
/*buffer chain链表更新 buffer挂载到新的chain上*/
cl = cl->next;
cl->buf = b;
body = body->next; /*遍历到下一个reuqest_bufs chain*/
}
} else {
u->request_bufs = cl; /*默认直接使用上面处理好的buffer chain*/
}
b->flush = 1; /*标记flush*/
cl->next = NULL; /*收尾设置*/
return NGX_OK;
}
3.ngx_http_proxy_process_status_line对响应行(包含http状态行和http头)的处理
static ngx_int_t
ngx_http_proxy_process_status_line(ngx_http_request_t *r)
{
...
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) { /*得到proxy模块的上下文*/
return NGX_ERROR;
}
u = r->upstream;
rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status); /*解析上游服务器返回的状态行*/
if (rc == NGX_AGAIN) { /*http状态行还未接收完成 到下次处理*/
return rc;
}
if (rc == NGX_ERROR) { /*http状态行解析错误 必要信息的设置*/
#if (NGX_HTTP_CACHE)
if (r->cache) {
r->http_version = NGX_HTTP_VERSION_9;
return NGX_OK;
}
#endif
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent no valid HTTP/1.0 header");
#if 0
if (u->accel) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
#endif
r->http_version = NGX_HTTP_VERSION_9;
u->state->status = NGX_HTTP_OK;
u->headers_in.connection_close = 1;
return NGX_OK;
}
if (u->state && u->state->status == 0) { /*设置upstream的状态码*/
u->state->status = ctx->status.code;
}
u->headers_in.status_n = ctx->status.code; /*设置upstream headers_in的状态码*/
len = ctx->status.end - ctx->status.start;
u->headers_in.status_line.len = len;
u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);
if (u->headers_in.status_line.data == NULL) {
return NGX_ERROR;
}
/*设置upstream headers_in的状态行信息*/
ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy status %ui \"%V\"",
u->headers_in.status_n, &u->headers_in.status_line);
if (ctx->status.http_version < NGX_HTTP_VERSION_11) { /*小于http1.1版本的keep_alive无用 设置connection_close为真 用一次就关*/
u->headers_in.connection_close = 1;
}
u->process_header = ngx_http_proxy_process_header;
return ngx_http_proxy_process_header(r); /*处理上游服务器的http响应头*/
}
4.解析上游服务器返回的http响应头
static ngx_int_t
ngx_http_proxy_process_header(ngx_http_request_t *r)
{
...
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); /*得到upstream 模块main域的配置*/
for ( ;; ) {
rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); /*一行一行地解析http响应头*/
if (rc == NGX_OK) { /*响应头有效*/
/* a header line has been parsed successfully */
/*加入到upstream的headers_in头数组中*/
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) { /*失败 返回错误*/
return NGX_ERROR;
}
/*得到http头的hash值*/
h->hash = r->header_hash;
/*得到http头的key键长度和key键值长度*/
h->key.len = r->header_name_end - r->header_name_start;
h->value.len = r->header_end - r->header_start;
/*分配http头空间 包含键名和键值*/
h->key.data = ngx_pnalloc(r->pool,
h->key.len + 1 + h->value.len + 1 + h->key.len);
if (h->key.data == NULL) { /*分配失败 返回错误*/
h->hash = 0;
return NGX_ERROR;
}
/*设置key键值的地址 和小写key键的地址 小写的key键名位于键名和键值之后*/
h->value.data = h->key.data + h->key.len + 1;
h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
/*拷贝键名信息和键值信息*/
ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
h->key.data[h->key.len] = '\0';
ngx_memcpy(h->value.data, r->header_start, h->value.len);
h->value.data[h->value.len] = '\0';
if (h->key.len == r->lowcase_index) { /*无需转换 设置小写键名*/
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
} else { /*需要转换 设置小写键名*/
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
/*upstream header的处理函数对头处理 通过handler会把这里的header分配到upstream头的hash表中(默认是这样)*/
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%V: %V\"",
&h->key, &h->value);
/*处理下一个头*/
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) { //
/*所有的响应头处理完成*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header done");
/*如果没有 "Server"头和"Date"头 设置一个默认的"Server"和"Date"头*/
if (r->upstream->headers_in.server == NULL) {
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');
ngx_str_set(&h->key, "Server");
ngx_str_null(&h->value);
h->lowcase_key = (u_char *) "server";
}
if (r->upstream->headers_in.date == NULL) {
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');
ngx_str_set(&h->key, "Date");
ngx_str_null(&h->value);
h->lowcase_key = (u_char *) "date";
}
/* clear content length if response is chunked */
u = r->upstream;
if (u->headers_in.chunked) { /*以chunked方式响应 content_length_n标记为无效*/
u->headers_in.content_length_n = -1;
}
/*
* set u->keepalive if response has no body; this allows to keep
* connections alive in case of r->header_only or X-Accel-Redirect
*/
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|| ctx->head
|| (!u->headers_in.chunked
&& u->headers_in.content_length_n == 0))
{
u->keepalive = !u->headers_in.connection_close; /**/
}
if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) { /*http协议转换*/
u->keepalive = 0; /*keepalive关闭*/
if (r->headers_in.upgrade) {
u->upgrade = 1;
}
}
return NGX_OK;
}
if (rc == NGX_AGAIN) { /*上游服务器返回的http头没有接收完整 返回again 下次处理*/
return NGX_AGAIN;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER; /*错误 默认返回无效头部错误*/
}
}
5.根据响应的头设置event_pipe
static ngx_int_t
ngx_http_proxy_input_filter_init(void *data)
{
...
u = r->upstream;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) { /*得到的proxy模块上下文为空 返回错误 需在上下文产生后才能处理*/
return NGX_ERROR;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy filter init s:%ui h:%d c:%d l:%O",
u->headers_in.status_n, ctx->head, u->headers_in.chunked,
u->headers_in.content_length_n);
/* as per RFC2616, 4.4 Message Length */
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|| ctx->head)
{ /*上游服务器空响应(无内容) 或者304或者是head请求 无需处理包体*/
/* 1xx, 204, and 304 and replies to HEAD requests */
/* no 1xx since we don't send Expect and Upgrade */
u->pipe->length = 0;
u->length = 0;
u->keepalive = !u->headers_in.connection_close; //keep-alive设置
} else if (u->headers_in.chunked) { /*上游的响应是chunked方式*/
/* chunked */
/*设置event_pipe处理的input_filter*/
u->pipe->input_filter = ngx_http_proxy_chunked_filter;
u->pipe->length = 3; /* "0" LF LF */
u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;
u->length = 1;
} else if (u->headers_in.content_length_n == 0) {
/*响应的内容的头部content_length_n为0 意味着空响应*/
u->pipe->length = 0;
u->length = 0;
u->keepalive = !u->headers_in.connection_close;
} else { /*返回了数据包含整体的数据和部分(206)类型的数据*/
/* content length or connection close */
u->pipe->length = u->headers_in.content_length_n; /*设置event_pipe要处理的内容长度*/
u->length = u->headers_in.content_length_n; /*设置upstream的内容长度*/
}
return NGX_OK;
}
6.对chunked传输类型的包体过滤处理
static ngx_int_t
ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
...
if (buf->pos == buf->last) { /*buffer空*/
return NGX_OK;
}
r = p->input_ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) { /*proxy上下文为空 返回错误*/
return NGX_ERROR;
}
b = NULL;
prev = &buf->shadow;
for ( ;; ) {
/*解析chunked传输的包体*/
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
if (rc == NGX_OK) {
/*chunked解析成功 从event_pipe的free chain获取空闲的chain; free chain够用直接返回 不够用分配返回*/
cl = ngx_chain_get_free_buf(p->pool, &p->free);
if (cl == NULL) { /*buffer chain是无效的 返回错误*/
return NGX_ERROR;
}
b = cl->buf; /*初始化chain中的buffer */
ngx_memzero(b, sizeof(ngx_buf_t));
/*将解析的buf设置到buffer中*/
b->pos = buf->pos;
b->start = buf->start;
b->end = buf->end;
b->tag = p->tag;
b->temporary = 1; //临时buffer
b->recycled = 1; //buffer是可以回收的
*prev = b; //shadow buffer设置(影子buffer设置)
prev = &b->shadow;
if (p->in) { /*event_pipe in chain已经存在 将cl chain设置为last_in*/
*p->last_in = cl;
} else { /*event_pipt in chain不存在 设置为cl chain (第一个)*/
p->in = cl;
}
p->last_in = &cl->next; /*更新下 last_in */
/* STUB */ b->num = buf->num;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"input buf #%d %p", b->num, b->pos);
if (buf->last - buf->pos >= ctx->chunked.size) { /*解析的buf的数据大小超过了chunk的大小 对buf进行校正*/
buf->pos += (size_t) ctx->chunked.size;
b->last = buf->pos;
ctx->chunked.size = 0;
continue; //下一个
}
ctx->chunked.size -= buf->last - buf->pos; //调整chunk的大小(已经处理的部分)
buf->pos = buf->last; /*调整buf*/
b->last = buf->last;
continue; /*下一个*/
}
if (rc == NGX_DONE) {
/*响应的包体解析完成*/
p->upstream_done = 1;
r->upstream->keepalive = !r->upstream->headers_in.connection_close;
break;
}
if (rc == NGX_AGAIN) { /*包体接收还未完成 需要继续接收*/
/* set p->length, minimal amount of data we want to see */
/*更新 event_pipe的数据长度*/
p->length = ctx->chunked.length;
break;
}
/*chunked响应是无效的 返回错误*/
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid chunked response");
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy chunked state %ui, length %O",
ctx->chunked.state, p->length);
if (b) { /*event_pipe free chain的buffer存在*/
b->shadow = buf; /*将待处理的buf设置成b的shadow(影子)buffer*/
b->last_shadow = 1; /*last_shaow标记为真*/
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"input buf %p %z", b->pos, b->last - b->pos);
return NGX_OK;
}
/* there is no data record in the buf, add it to free chain */
if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) { /*buf中没有数据 直接把buf加入到event_pipe的free chain*/
return NGX_ERROR;
}
return NGX_OK;
}