正文
复杂值提供了一种简单的方法来计算一个包含有文本、变量以及文本变量组合等情况的表达式的值。
由ngx_http_compile_complex_value所表示的复杂值在配置阶段被编译到ngx_http_complex_value_t类型中,该编译的结果在运行阶段可以被用来计算表达式的值。
这里,ccv里包含了全部初始化复杂值cv所需的参数:
-
cf — 配置指针
-
value — 待解析的字符串 (输入)
-
complex_value — 编译后的值 (输出)
-
zero — 是否对结果进行0结尾处理
-
conf_prefix — 是否将结果带上配置前缀(nginx当前查找配置的目录)
-
root_prefix — 是否将结果带上根前缀(通常是nginx的安装目录)
zero标记位在需要把结果传递给要求0结尾字符串的库时,非常有用,而前缀相关的标记位在处理文件名时很方便。
对于正确的编译,可以从cv.lengths成员获取到表达式中是否存在变量的情况。如果为NULL,则表示表达式中只是纯文本,所以没有必要将其保存成一个复杂值,使用简单的字符串就可以了。
ngx_http_set_complex_value_slot()可以在声明指令的时候对复杂值进行初始化。
在运行阶段,复杂值可以使用ngx_http_complex_value()函数来计算:
给定请求r和之前编译的cv,该函数会对表达式的值进行急计算并将结果存放在res变量中。
HTTP请求总是通过ngx_http_request_t结构体的loc_conf成员来绑定到某个location上。这意味着在任意时刻,任何模块都可以通过调用ngx_http_get_module_loc_conf(r, module)来获取到location的配置。
在HTTP请求的生命周期内,其location可能会改变多次。初始时,default server的default location会被分配给HTTP请求。一旦这个请求切换到了另外一个不同的server(比如通过HTTP的"Host"头,或者通过SSL的SNI扩展),该server的default location也同样会分配给这个请求。
接下来在NGX_HTTP_FIND_CONFIG_PHASE阶段中会重新为请求选择location。在这个阶段里,location的选择是基于请求的URI,在此server中全部的非命名location中查找得来的。
ngx_http_rewrite_module模块也可能在NGX_HTTP_REWRITE_PHASE阶段对请求的URI进行修改,这样的话请求会重新发送回NGX_HTTP_FIND_CONFIG_PHASE阶段使用新的URI进行location匹配。
也可以在任意时候通过对ngx_http_internal_redirect(r, uri, args)和ngx_http_named_location(r, name)函数进行调用来实现将请求重定向到一个新的location。
ngx_http_internal_redirect(r, uri, args)函数修改请求的URI并且将请求发送回NGX_HTTP_SERVER_REWRITE_PHASE阶段。之后请求被分配到server默认的location上,然后在NGX_HTTP_FIND_CONFIG_PHASE阶段根据请求新的URI来选择location。
下面是一个同时带有新的请求参数的内部重定向的例子。
ngx_http_named_location(r, name)函数将请求重定向到一个命名location。目标location的名称通过参数传递,并在当前server中的全部命名location中查找,接着请求会被发送到NGX_HTTP_REWRITE_PHASE阶段。
下面是一个将请求重定向到命名location @foo的例子:
当ngx_http_internal_redirect(r, uri, args)和ngx_http_named_location(r, name)这两个函数被调用时,nginx模块可能已经向HTTP请求的ctx成员中存储了一些上下文。这些上下文在请求发生location切换之后可能会变得不一致。为了避免这种不一致性,所有的请求上下文会被这两个函数清除。
被重定向以及被重写的请求成为了内部请求进而可以访问内部location。内部请求的internal标记位被设置为真。
子请求主要用来将一个请求的输出合并到另外一个请求中,很可能和其他数据混合。一个子请求看起来就像是一个普通的请求,但是和其父请求共享某些数据。
具体来说,所有和客户端输入相关的数据都是共享的,因为子请求不从客户端接收任何额外的数据。子请求的请求结构中的parent成员保存了指向其父请求的指针,如果是main request则此成员为空。成员main存储了指向一组请求中main请求的指针。
子请求从NGX_HTTP_SERVER_REWRITE_PHASE阶段开始。它经历的其他阶段和普通请求相同,并基于其URI来分配location。
子请求的输出头总是被忽略。子请求的输出体通过ngx_http_postpone_filter插入到父请求产生的数据中的合适位置。
子请求和活动请求的概念相关。一个请求r被认为是活动的,如果c->data == r,c是表示nginx和客户端连接的对象。在任意时候,只有一组请求中的活动请求才允许将其输出缓冲发送给客户端。
一个非活动请求仍然可以将其数据发送到过滤链中,但是这些数据不会通过ngx_http_postpone_filter过滤并且数据会一直保留在这个过滤器中,直到请求变成活动状态。下面是一些关于请求活动性的规则:
一个子请求是用过调用ngx_http_subrequest(r, uri, args, psr, ps, flags)函数来创建的,其中r是父请求,uri和args分别是子请求的URI和请求参数,psr是一个输出参数,含有新创建的子请求的引用,ps是一个回调函数,用来在子请求结束的时候通知父请求,flags是子请求的创建标记位。有如下标记位可以使用:
-
NGX_HTTP_SUBREQUEST_IN_MEMORY - 子请求的输出不需要发送给客户端,而是在内存中保留。此标记位只对代理子请求有效。在子请求结束后,它的输出会以ngx_buf_t类型存放在r->upstream->buffer中。
-
NGX_HTTP_SUBREQUEST_WAITED - 子请求的done标记位会被设置,即使当其结束时处于非活动状态。这个标记位被SSI过滤器使用。
-
NGX_HTTP_SUBREQUEST_CLONE - 子请求作为父请求的克隆来创建。如此创建的子请求将继承父请求的location并从父请求所在的阶段继续执行。
下面的例子中创建了一个URI为"/foo"的子请求。
这个例子是将当前请求进行克隆并为子请求设置了一个结束回调函数。
子请求通常在body过滤器中创建。在这种情况下,子请求的输出可以被当成任意的显式请求输出处理。这意味着子请求的输出会在其他全部先于子请求创建的显式缓冲之后,以及在除此之外的任何缓冲之前,发送给客户端。
这个顺序对于大型的子请求层次结构也同样有效。下面演示了将一个子请求插入到所有请求数据缓冲之后,但是在拥有last_buf的最后一个缓冲之前的例子。
一个子请求也可以为了输出数据之外的目的而创建。例如,ngx_http_auth_request_module在NGX_HTTP_ACCESS_PHASE阶段创建了一个子请求。
为了在这个阶段禁止任何输出,子请求的header_only标志被设置。这可以避免子请求的body被发送到客户端。子请求的header无论如何都是被忽略的。子请求的结果可以通过回调handler来分析处理。