Open vislee opened 7 years ago
在nginx网站上有一篇帖子《If Is Evil》 该帖子描述了在if使用的过程中,如果你不是非常熟悉nginx的流程或者没有充分的测试,可能会发生一些你预想不到的情况。并给出了一些“邪恶的”实例。
下面,会通过代码结合实例分析一下if为什么是“邪恶的”。
location /only-one-if { set $true 1; if ($true) { add_header X-First 1; } if ($true) { add_header X-Second 2; } return 204; }
首先set指令在ngx_http_rewrite_loc_conf_t的codes数组中添加ngx_http_script_value_code_t元素,回调函数code为ngx_http_script_value_code,text_data 为变量预赋值。 接着向codes添加一个元素ngx_http_script_var_code_t,其回调函数code为ngx_http_script_set_var_code,回调函数参数index为变量在cmcf->variables数组的下标。
第一个if指令添加元素,先添加ngx_http_script_var_code_t元素,其回调函数为ngx_http_script_var_code,回调函数参数为变量在cmcf->variables数组的下标。接着添加ngx_http_script_if_code_t元素,其回调函数code为ngx_http_script_if_code,回调函数参数loc_conf指向该if对应的location的ctx,回调参数next指向本if结构添加在codes中元素的结尾(该赋值是在解析完location以后进行的,为的就是跳过该location所添加到code数组中的元素)。然后解析该location下的add_header配置。
第二个if指令添加元素,同上。
return指令添加元素。添加ngx_http_script_return_code_t元素,回调函数code是ngx_http_script_return_code,回调参数status为204.
配置文件解析完后,最终会调用ngx_http_rewrite_merge_loc_conf函数合并配置,而在rewrite模块并没有合并codes数组,而是在该数组中添加了个空指针作为结束标志。
rewrite模块的handler函数被添加到server rewrite阶段和rewrite阶段。其handler的checker函数是ngx_http_core_rewrite_phase。
在该ngx_http_rewrite_handler函数中,如果codes为空,则立即返回。checker函数会执行该阶段的其他handler。而上述set、if、return等指令是配置在location中的,所以server rewrite阶段直接就返回了。
在rewrite阶段,会分配一个ngx_http_script_engine_t结构执行codes数组中添加的回调。
执行set添加的回调ngx_http_script_value_code回调函数,把变量付给sp数组的第一个元素, 该数组类型为ngx_variable_value_t。接着执行ngx_http_script_set_var_code回调函数,把添加到sp中的元素的值赋值到r->variables数组index下标的元素。
执行if添加的回调ngx_http_script_var_code,把变量赋值到sp数组的第一个元素(set执行两个回调函数执行完毕后,sp指针没有向后移动)。接着执行ngx_http_script_if_code回调,判断添加到sp的元素是否不为0。不为0则会把请求的location上下文数组赋值为对应if的location上下文数组,并调用ngx_http_update_location_config函数更新。
同上,执行ngx_http_script_var_code回调,再执行ngx_http_script_if_code回调,赋值location的上下文数组并调用ngx_http_update_location_config函数更新,覆盖了上一个if的配置信息。
执行return添加的ngx_http_script_return_code回调函数,赋值status为204。
至此,ngx_http_rewrite_handler回调返回204,rewrite阶段的checker函数调用ngx_http_finalize_request函数提前结束请求。
总结: return会跳过rewrite阶段后面log阶段前面的所有阶段的执行。 if执行如果没有break,则不会退出。后面的if会覆盖前面的if。
概述
在nginx网站上有一篇帖子《If Is Evil》 该帖子描述了在if使用的过程中,如果你不是非常熟悉nginx的流程或者没有充分的测试,可能会发生一些你预想不到的情况。并给出了一些“邪恶的”实例。
下面,会通过代码结合实例分析一下if为什么是“邪恶的”。
代码分析
解析配置文件
首先set指令在ngx_http_rewrite_loc_conf_t的codes数组中添加ngx_http_script_value_code_t元素,回调函数code为ngx_http_script_value_code,text_data 为变量预赋值。 接着向codes添加一个元素ngx_http_script_var_code_t,其回调函数code为ngx_http_script_set_var_code,回调函数参数index为变量在cmcf->variables数组的下标。
第一个if指令添加元素,先添加ngx_http_script_var_code_t元素,其回调函数为ngx_http_script_var_code,回调函数参数为变量在cmcf->variables数组的下标。接着添加ngx_http_script_if_code_t元素,其回调函数code为ngx_http_script_if_code,回调函数参数loc_conf指向该if对应的location的ctx,回调参数next指向本if结构添加在codes中元素的结尾(该赋值是在解析完location以后进行的,为的就是跳过该location所添加到code数组中的元素)。然后解析该location下的add_header配置。
第二个if指令添加元素,同上。
return指令添加元素。添加ngx_http_script_return_code_t元素,回调函数code是ngx_http_script_return_code,回调参数status为204.
配置文件解析完后,最终会调用ngx_http_rewrite_merge_loc_conf函数合并配置,而在rewrite模块并没有合并codes数组,而是在该数组中添加了个空指针作为结束标志。
请求处理
rewrite模块的handler函数被添加到server rewrite阶段和rewrite阶段。其handler的checker函数是ngx_http_core_rewrite_phase。
在该ngx_http_rewrite_handler函数中,如果codes为空,则立即返回。checker函数会执行该阶段的其他handler。而上述set、if、return等指令是配置在location中的,所以server rewrite阶段直接就返回了。
在rewrite阶段,会分配一个ngx_http_script_engine_t结构执行codes数组中添加的回调。
执行set添加的回调ngx_http_script_value_code回调函数,把变量付给sp数组的第一个元素, 该数组类型为ngx_variable_value_t。接着执行ngx_http_script_set_var_code回调函数,把添加到sp中的元素的值赋值到r->variables数组index下标的元素。
执行if添加的回调ngx_http_script_var_code,把变量赋值到sp数组的第一个元素(set执行两个回调函数执行完毕后,sp指针没有向后移动)。接着执行ngx_http_script_if_code回调,判断添加到sp的元素是否不为0。不为0则会把请求的location上下文数组赋值为对应if的location上下文数组,并调用ngx_http_update_location_config函数更新。
同上,执行ngx_http_script_var_code回调,再执行ngx_http_script_if_code回调,赋值location的上下文数组并调用ngx_http_update_location_config函数更新,覆盖了上一个if的配置信息。
执行return添加的ngx_http_script_return_code回调函数,赋值status为204。
至此,ngx_http_rewrite_handler回调返回204,rewrite阶段的checker函数调用ngx_http_finalize_request函数提前结束请求。
总结: return会跳过rewrite阶段后面log阶段前面的所有阶段的执行。 if执行如果没有break,则不会退出。后面的if会覆盖前面的if。