Nginx 소스 코드 분석

18789 단어
Nginx 의 변수 체 제 를 이해 하 는 것 은 주로 세 가지 가 있 습 니 다. 첫 번 째 는 변수 가 가 져 온 인터페이스 입 니 다. 두 번 째 는 설정 파일 에서 변 수 를 분석 하 는 것 입 니 다. 세 번 째 는 변 수 를 사용 할 때 호출 하 는 과정 입 니 다. 우 리 는 조금씩 봅 니 다 (여기 서 하나의 변수 만 예 를 들 겠 습 니 다).
모듈 ngxhttp_proxy_module 의 변수 $host 사용 과정
 
우선 첫 번 째, 변수의 성명 과 인터페이스 호출:
파일 보기 ngxhttp_variables. c 에 static ngx 성명 이 있 습 니 다.http_variable_t  ngx_http_core_variables [] 즉 http 처리 와 관련 된 핵심 변 수 는 모든 모듈 에서 자신 이 사용 하 는 변 수 를 정의 합 니 다.먼저 ngxhttp_variable_t 의 정의:
struct ngx_http_variable_s {
    ngx_str_t                     name;   /*      must be first to build the hash */
    ngx_http_set_variable_pt      set_handler; /*           */
    ngx_http_get_variable_pt      get_handler; /*           */
    uintptr_t                     data;/*    */
    ngx_uint_t                    flags;
    ngx_uint_t                    index;/*  ,   0*/
}; 

 
우 리 는 그 중의 하 나 를 보 았 다.
{ ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },
이 항목 은 $host 변수의 정의 입 니 다. name 과 get 만 지정 합 니 다.hander, 그럼 이 handler 를 직접 보 겠 습 니 다.
static ngx_int_t
ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
    uintptr_t data)
{ //     handler     http request  ,  v      ,data           。
    ngx_http_core_srv_conf_t  *cscf;

    //  HTTP      Host   URL   Host  (    http             server   ),
    //      
    if (r->headers_in.server.len) {
        v->len = r->headers_in.server.len;
        v->data = r->headers_in.server.data;

    } else {
        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
     
        //        ,         
        v->len = cscf->server_name.len;
        v->data = cscf->server_name.data;
    }

    //        ,         
    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;

    return NGX_OK;
}

 
 첫 번 째 는 기본적으로 여기까지 입 니 다. 두 번 째 변수의 분석 과정 을 계속 살 펴 보 겠 습 니 다. 즉, 설정 파일 에서 변 수 를 사용 할 때 nginx 가 시작 할 때 이 변 수 를 어떻게 분석 하고 어떤 처 리 를 하면 나중에 사용 하기에 편리 합 니까?여기 서 우 리 는 ngxhttp_proxy_module 모듈 에 proxy 설정pass 명령 을 사용 할 때 변수 $host 를 사용 한 다음 처리 과정 을 봅 니 다.
Nginx 에 다음 설정 이 있다 고 가정 합 니 다.
location / {
           proxy_cache cache_one;
           proxy_cache_valid 200 5d;
           proxy_cache_key $host$uri;

           resolver 8.8.8.8;
           proxy_pass http://$host;
           proxy_set_header Host $host;
}

 
우리 ngxhttp_proxy_module. c 파일 에서 명령 proxy패스 처리 하면 됩 니 다.유사 변수 정의, 많은 모듈 에서 이 모듈 이 사용 하 는 명령 을 정의 합 니 다. 이 명령 들 은 설정 파일 에서 사용 할 수 있 습 니 다. 위 설정 의 proxy 와 유사 합 니 다.패스 는 바로 ngxhttp_proxy_module 모듈 의 명령, ngxhttp_proxy_module 모듈 에서 다음 과 같은 명령 을 정의 합 니 다.
static ngx_command_t  ngx_http_proxy_commands[] = {
    { ngx_string("proxy_pass"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
      ngx_http_proxy_pass,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },
  
      .
      .
      .
}

 여기에 proxy 를 인용 하 였 습 니 다.pass 명령 의 정의 입 니 다.명령 정의 에 대해 자세히 설명 하지 않 습 니 다. 이 명령 을 해석 하 는 handler 즉 ngx 를 보 았 습 니 다.http_proxy_pass, 이 handler 는 nginx 가 설정 파일 을 분석 할 때 이 명령 을 만나면 이 명령 의 설정 을 분석 하고 미리 처리 합 니 다. 이 handler 를 살 펴 보 겠 습 니 다.
static char *
ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_proxy_loc_conf_t *plcf = conf;

    size_t                      add;
    u_short                     port;
    ngx_str_t                  *value, *url;
    ngx_url_t                   u;
    ngx_uint_t                  n;
    ngx_http_core_loc_conf_t   *clcf;
    ngx_http_script_compile_t   sc;

    if (plcf->upstream.upstream || plcf->proxy_lengths) {
        return "is duplicate";
    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    //         http     handler
    clcf->handler = ngx_http_proxy_handler;

    //clcf->name.data   location    path  ,   nginx    “/”,
    //     if    ,auto_redirect     1,           uri  
    //location path “/”             URL  (         )
    if (clcf->name.data[clcf->name.len - 1] == '/') {
        clcf->auto_redirect = 1;
    }

    //nginx  proxy_pass http://$host; 이 줄 은 빈 칸 에 따라 배열 로 해석 하여 elts 에 놓 습 니 다.    value = cf->args->elts;

    // proxy_pass http://$host;  value[1]  http://$host
    url = &value[1];

    //      url       ,       ,       ,   1 
    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; 
        //plcf->proxy_lengths          proxy_pass               
        sc.values = &plcf->proxy_values;
        //proxy_values         proxy_pass              
        sc.variables = n;//   
        sc.complete_lengths = 1;
        sc.complete_values = 1;
        //  proxy_pass   url
        if (ngx_http_script_compile(&sc) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
        
       //    ,  
       return NGX_CONF_OK;
    }

 
변수의 주요 처리 과정 은 ngxhttp_script_copile 방법, 이 방법 을 살 펴 보 자.
ngx_int_t
ngx_http_script_compile(ngx_http_script_compile_t *sc)
{
    u_char       ch;
    ngx_str_t    name;
    ngx_uint_t   i, bracket;

    //             sc.lengths sc.values  ,   proxy_lengths proxy_values;
    if (ngx_http_script_init_arrays(sc) != NGX_OK) {
        return NGX_ERROR;
    }
    //source    url,     
    for (i = 0; i < sc->source->len; /* void */ ) {
        name.len = 0;//name        
        //      
        if (sc->source->data[i] == '$') {
            if (++i == sc->source->len) {
                goto invalid_variable;
            }

            if (sc->source->data[i] == '{') {
            //    $    {
            } else {
                bracket = 0;
                //name     ,  $           ,    ++i
                name.data = &sc->source->data[i];
            }

             //  for         name     
            for ( /* void */ ; i < sc->source->len; i++, name.len++) {
                ch = sc->source->data[i];
                if (ch == '}' && bracket) {
                //   }                    
                }

                if ((ch >= 'A' && ch <= 'Z')
                    || (ch >= 'a' && ch <= 'z')
                    || (ch >= '0' && ch <= '9')
                    || ch == '_')
                {
                    continue;
                }

                break;
            }

            if (name.len == 0) {
                goto invalid_variable;
            }

            //      1
            sc->variables++;
            //              
            if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {
                return NGX_ERROR;
            }

            continue;
        }

        if (sc->source->data[i] == '?' && sc->compile_args) {
            //                      
            continue;
        }

        //                  ,      ,    URL, http://        name.data = &sc->source->data[i];

        //  ,        name     
        while (i < sc->source->len) {
            if (sc->source->data[i] == '$') {
                break;
            }
           if (sc->source->data[i] == '?') {
           }

            i++;
            name.len++;
        }

        sc->size += name.len;
         //          
        if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len))
            != NGX_OK)
        {
            return NGX_ERROR;
        }
    }

    //      
    return ngx_http_script_done(sc);

invalid_variable:

    ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");

    return NGX_ERROR;
}

 
 계속 깊이 들 어가 서 ngx 호출http_script_add_var_code (sc, & name) 는 어떻게 새로운 변 수 를 관련 배열 에 넣 습 니까?
static ngx_int_t
ngx_http_script_add_var_code(ngx_http_script_compile_t *sc, ngx_str_t *name)
{
    ngx_int_t                    index, *p;
    ngx_http_script_var_code_t  *code;

    //      name cmcf->variables         ,     name,              
    index = ngx_http_get_variable_index(sc->cf, name);

    if (index == NGX_ERROR) {
        return NGX_ERROR;
    }
     //flushes   
    if (sc->flushes) {
        p = ngx_array_push(*sc->flushes);
        if (p == NULL) {
            return NGX_ERROR;
        }

        *p = index;
    }
     //    sc->lengths      ,       
    code = ngx_http_script_add_code(*sc->lengths,
                                    sizeof(ngx_http_script_var_code_t), NULL);
    if (code == NULL) {
        return NGX_ERROR;
    }
     //      ,        ,                  
    code->code = (ngx_http_script_code_pt) ngx_http_script_copy_var_len_code;
    code->index = (uintptr_t) index;
     //  sc->values       
    code = ngx_http_script_add_code(*sc->values,
                                    sizeof(ngx_http_script_var_code_t),
                                    &sc->main);
    if (code == NULL) {
        return NGX_ERROR;
    }
     //      ,          ,           
    code->code = ngx_http_script_copy_var_code;
    code->index = (uintptr_t) index;

    return NGX_OK;
}

 
여기 보면 ngxhttp_script_add_var_code 에서 분 석 된 변 수 를 노드 로 만 들 것 입 니 다. 하 나 는 길 이 를 계산 하 는 노드 이 고 하 나 는 결 과 를 계산 하 는 노드 입 니 다. 각각 호출 인터페이스 ngx 를 지정 합 니 다.http_script_copy_var_len_code 는 변수 값 의 길이 와 ngx 를 계산 합 니 다.http_script_copy_var_변 수 를 가 져 오 는 code 입 니 다.변 수 를 사용 할 때 이 인 터 페 이 스 를 호출 하여 변수의 길 이 를 계산 하고 값 을 가 져 옵 니 다. 
 
다음은 제 가 직접 ngx 를 보 겠 습 니 다.http_script_copy_var_len_code 와 ngxhttp_script_copy_var_code 의 실현:
 
size_t
ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
{
    ngx_http_variable_value_t   *value;
    ngx_http_script_var_code_t  *code;

    code = (ngx_http_script_var_code_t *) e->ip;
    e->ip += sizeof(ngx_http_script_var_code_t);
    if (e->flushed) {
        //  index      ,        ,      hander     
        value = ngx_http_get_indexed_variable(e->request, code->index);
    } else {
        //               ,             ,    ,  ngx_http_get_indexed_variable()      
        value = ngx_http_get_flushed_variable(e->request, code->index);
    }
    if (value && !value->not_found) {
        return value->len;
    }
    return 0;
}
 다시 보기 ngxhttp_script_copy_var_code 의 실현:
void
ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
{
    u_char                      *p;
    ngx_http_variable_value_t   *value;
    ngx_http_script_var_code_t  *code;

    code = (ngx_http_script_var_code_t *) e->ip;
    e->ip += sizeof(ngx_http_script_var_code_t);
    if (!e->skip) {
        if (e->flushed) {
            value = ngx_http_get_indexed_variable(e->request, code->index);
        } else {
            value = ngx_http_get_flushed_variable(e->request, code->index);
        }

        if (value && !value->not_found) {
            p = e->pos;
            //               ,             e->pos       ,   e->pos     。
            e->pos = ngx_copy(p, value->data, value->len);
        }
    }
}
 
세 번 째 부분 은 변 수 를 사용 하 는 것 입 니 다. 우리 위의 예 는 바로 설정 입 니 다.
proxy_pass http://$host;
, nginx 가 원본 서버 에 자원 을 요청 할 때 설정 한 proxypass 는 새로운 요청 URL 을 생 성하 고 해당 하 는 자원 에 대한 답장 을 요청 합 니 다. 새로운 URL 을 생 성 하 는 곳 을 살 펴 보 겠 습 니 다.
static ngx_int_t
ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
    ngx_http_proxy_loc_conf_t *plcf)
{
    u_char               *p;
    size_t                add;
    u_short               port;
    ngx_str_t             proxy;
    ngx_url_t             url;
    ngx_http_upstream_t  *u;

    //     proxy_pass    URL,   proxy 
    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)
    {
        add = 7;
        port = 80;

#if (NGX_HTTP_SSL)

    } else if (proxy.len > 8
               && ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0)
    {
        add = 8;
        port = 443;
        r->upstream->ssl = 1;

#endif

    } else {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "invalid URL prefix in \"%V\"", &proxy);
        return NGX_ERROR;
    }

    u = r->upstream;
    u->schema.len = add;
    u->schema.data = proxy.data;

    ngx_memzero(&url, sizeof(ngx_url_t));

    url.url.len = proxy.len - add;
    url.url.data = proxy.data + add;
    url.default_port = port;
    url.uri_part = 1;
    url.no_resolve = 1;

    //½âÎöÇëÇóURL£¿
    if (ngx_parse_url(r->pool, &url) != NGX_OK) {
        if (url.err) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "%s in upstream \"%V\"", url.err, &url.url);
        }

        return NGX_ERROR;
    }

    //Èç¹û°üº¬uri²¿·ÖÇÒÈ«²¿Îª²ÎÊý£¬ÔòÖØп½±´Ò»·Ý£¬²¢ÔÚÆäÇ°Ìí¼ÓÉÏ/
    if (url.uri.len) {
        if (url.uri.data[0] == '?') {
            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;
        }
    }

    ctx->vars.key_start = u->schema;

		//ÉèÖÃctx->varsµÄhost-headerºÍportÒÔ¼°uri
    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;
    }

    //url.addrsÓ¦¸Ã±íʾÒѽâÎöhostµÃµ½µÄIPµØÖ·£¬´Ë´¦Ó¦¸Ãδ½âÎö
    if (url.addrs && url.addrs[0].sockaddr) {
        u->resolved->sockaddr = url.addrs[0].sockaddr;
        u->resolved->socklen = url.addrs[0].socklen;
        u->resolved->naddrs = 1;
        u->resolved->host = url.addrs[0].name;

    } else {
        u->resolved->host = url.host;
        u->resolved->port = (in_port_t) (url.no_port ? port : url.port);
        u->resolved->no_port = url.no_port;
    }

    return NGX_OK;
}

 
저희 가 주로 보 러 왔어요.
ngx_http_script_run 의 실현:
u_char *
ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
    void *code_lengths, size_t len, void *code_values)
{
    ngx_uint_t                    i;
    ngx_http_script_code_pt       code;
    ngx_http_script_len_code_pt   lcode;
    ngx_http_script_engine_t      e;
    ngx_http_core_main_conf_t    *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    for (i = 0; i < cmcf->variables.nelts; i++) {
        if (r->variables[i].no_cacheable) {
            r->variables[i].valid = 0;
            r->variables[i].not_found = 0;
        }
    }

    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

    e.ip = code_lengths;
    e.request = r;
    e.flushed = 1;

    while (*(uintptr_t *) e.ip) {
        lcode = *(ngx_http_script_len_code_pt *) e.ip;
        len += lcode(&e);
    }
    value->len = len;
    value->data = ngx_pnalloc(r->pool, len);
    if (value->data == NULL) {
        return NULL;
    }
    e.ip = code_values;
    e.pos = value->data;
    while (*(uintptr_t *) e.ip) {
        code = *(ngx_http_script_code_pt *) e.ip;
        code((ngx_http_script_engine_t *) &e);
    }

    return e.pos;
}

좋은 웹페이지 즐겨찾기