Nginx “邪恶” rewrite

概述

本文主要针对nginx rewrite指令困惑的地方进行讲解,中间会涉及部分原理部分,我尽量用通俗易懂的语言来形容

功能讲解

大致流程

    The ngx_http_rewrite_module module directives are processed in the following order:

    the directives of this module specified on the server level are executed sequentially;

    repeatedly:

    • a location is searched based on a request URI;

    • the directives of this module specified inside the found location are executed sequentially;

    • the loop is repeated if a request URI was rewritten, but not more than 10 times.

涉及phase
  •     NGX_HTTP_SERVER_REWRITE_PHASE,

  •     NGX_HTTP_FIND_CONFIG_PHASE,

  •     NGX_HTTP_REWRITE_PHASE,

  •     NGX_HTTP_POST_REWRITE_PHASE,

 

环境:
  • /aaa内容aaa

  • /bbb内容bbb

 

Example 1:
server {
     listen    80 ;
     location /aaa {
 
     if  ($http_user_agent ~ Mozilla) {
             rewrite /aaa /bbb; 
 
     }
     return  403 ;
 
     }
 
     location /bbb {
     return  402
     }
 
}

在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb),

然后继续执行rewrite模块其他指令,执行到return 403后,该request在nginx中的处理完毕,返回403;

因此浏览器收到应答码为403

 

Example 2:
     server {
         listen    80 ;
         location /aaa {
 
         if  ($http_user_agent ~ Mozilla) {
                 rewrite /aaa /bbb; 
 
         }
#         return  403 ;
 
         }
 
         location /bbb {
         return  402
         }
 
     }

在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb)

rewrite模块执行完毕。因为所有rewrite模块的指令都执行完毕,进入POST_REWRITE_PHASE所在的checker函数ngx_http_core_post_rewrite_phase

中,由于执行了rewrite指令,在函数ngx_http_script_regex_start_code中

if  (code->uri) {
     r->internal =  1 ;
     r->valid_unparsed_uri =  0 ;
     if  (code->break_cycle) { //rewrite ....break 指令分支
         r->valid_location =  0 ;
         r->uri_changed =  0 ;
     else  {
         r->uri_changed =  1 ;
     }
}

可以得知r->uri_changed=1,于是在函数ngx_http_core_post_rewrite_phase中执行如下流程

r->uri_changes--;
if  (r->uri_changes ==  0 ) {
     ngx_log_error(NGX_LOG_ERR, r->connection->log,  0 ,
                   "rewrite or internal redirection cycle "
                   "while processing \"%V\"" , &r->uri);
     ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
     return  NGX_OK;
}
r->phase_handler = ph->next; //ph->next为find_config_index,在ngx_http_init_phase_handlers中可见
 
 
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
r->loc_conf = cscf->ctx->loc_conf;

由于重新进行server和location匹配,因此在上述例子中,浏览器收到的应答状态吗为402

 

Example 3:
server {
     listen    80 ;
     location /aaa {
 
     if  ($http_user_agent ~ Mozilla) {
             rewrite /aaa /bbb  break
 
     }
     return  403 ;
 
     }
 
     location /bbb {
         return  402
     }
 
}

在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb),

由于rewrite break标记符存在,从rewrite指令的配置解析函数ngx_http_rewrite和rewrite模块的handler函数ngx_http_rewrite_handler中

如下部分可以得知,将停止执行rewrite模块的其他一切指令;

ngx_http_rewrite {
....
....
     if  (cf->args->nelts ==  4 ) {
         if  (ngx_strcmp(value[ 3 ].data,  "last" ) ==  0 ) {
             last =  1 ;
         else  if  (ngx_strcmp(value[ 3 ].data,  "break" ) ==  0 ) {
             regex->break_cycle =  1 ;
             last =  1 ;
         else  if  (ngx_strcmp(value[ 3 ].data,  "redirect" ) ==  0 ) {
             regex->status = NGX_HTTP_MOVED_TEMPORARILY;
             regex->redirect =  1 ;
             last =  1 ;
         else  if  (ngx_strcmp(value[ 3 ].data,  "permanent" ) ==  0 ) {
             regex->status = NGX_HTTP_MOVED_PERMANENTLY;
             regex->redirect =  1 ;
             last =  1 ;
         else  {
             ngx_conf_log_error(NGX_LOG_EMERG, cf,  0 ,
                                "invalid parameter \"%V\"" , &value[ 3 ]);
             return  NGX_CONF_ERROR;
         }
.....
.....
.....
     if  (last) {
         code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), ®ex);
         if  (code == NULL) {
             return  NGX_CONF_ERROR;
         }
         *code = NULL;
     }
}
 
 
ngx_http_rewrite_handler
{
.....
.....
     while  (*(uintptr_t *) e->ip) {
         code = *(ngx_http_script_code_pt *) e->ip;
         code(e);
     }
     if  (e->status < NGX_HTTP_BAD_REQUEST) { //如果rewrite指令后不是break和last标记,而是其他两个重定向标记,则从此处直接结束该request处理流程
         return  e->status;
     }
     if  (r->err_status ==  0 ) {
         return  e->status;
     }
     return  r->err_status;
.....
.....
}

 

进入到POST_REWRITE_PHASE所在的checker函数ngx_http_core_post_rewrite_phase中,由example 2 可知r->uri_changed = 0;

在ngx_http_core_post_rewrite_phase中会执行如下流程,因此会继续执行当前server 和 location 上下文中后续的handler,因此浏览器收到内容为bbb

if  (!r->uri_changed) {
     r->phase_handler++;
     return  NGX_AGAIN;
}

 

Example 4:
server {
     listen    80 ;
     location /aaa {
 
     if  ($http_user_agent ~ Mozilla) {
             rewrite /aaa /bbb last; 
 
     }
     return  403 ;
 
     }
 
     location /bbb {
         return  402
     }
 
}

在上述nginx.conf情况下,使用firefox浏览器访问nginx,if匹配成功,执行rewrite重写r->uri(/aaa转换为/bbb),

由于rewrite last标记符存在,停止执行rewrite模块的其他一切指令,参考example 3。

进入到rewrite模块所在的checker函数ngx_http_core_post_rewrite_phase中,由example 2可知r->uri_changed = 1;

于是重新进行server和location匹配,因此在上述例子中,浏览器收到的内容的状态码为402

 

转载于:https://www.cnblogs.com/scottieyuyang/p/5722604.html

你可能感兴趣的:(Nginx “邪恶” rewrite)