zoukankan      html  css  js  c++  java
  • NGINX(四)配置解析

    前言

    nginx配置解析是在初始化ngx_cycle_t数据结构时,首先解析core模块,然后core模块依次解析自己的子模块。

    配置解析过程

    nginx调用ngx_conf_parse函数进行配置文件解析,下面是核心代码,函数首先打开配置文件,然后循环调用ngx_conf_read_token读取一行配置,解析出来保存在cf->args中,如果遇到";" 或者 "{", 在文件没有结束和错误情况下,则会回到ngx_conf_parse中,继续执行ngx_conf_handler函数。

    char *
    ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename )
    {
        char             *rv;
        ngx_fd_t          fd ;
        ngx_int_t         rc ;
        ngx_buf_t         buf ;
        ngx_conf_file_t  *prev, conf_file;
        enum {
            parse_file = 0 ,
            parse_block ,
            parse_param
        } type ;
    
        if (filename) {
            /* 打开配置文件 */
            fd = ngx_open_file(filename->data , NGX_FILE_RDONLY, NGX_FILE_OPEN, 0 );
            if ( fd == NGX_INVALID_FILE ) {
                ngx_conf_log_error (NGX_LOG_EMERG, cf, ngx_errno ,
                                   ngx_open_file_n " "%s" failed",
                                   filename ->data);
                return NGX_CONF_ERROR;
            }
    
            prev = cf-> conf_file;
    
            cf ->conf_file = &conf_file ;
    
            if ( ngx_fd_info(fd , & cf->conf_file->file .info) == NGX_FILE_ERROR) {
                ngx_log_error (NGX_LOG_EMERG, cf->log , ngx_errno,
                              ngx_fd_info_n " "%s" failed", filename->data);
            }
    
            cf ->conf_file->buffer = &buf;
    
            buf .start = ngx_alloc(NGX_CONF_BUFFER , cf-> log);
            if ( buf.start == NULL ) {
                goto failed;
            }
    
            buf .pos = buf.start ;
            buf .last = buf.start ;
            buf .end = buf.last + NGX_CONF_BUFFER;
            buf .temporary = 1;
    
            cf ->conf_file->file.fd = fd;
            cf ->conf_file->file.name .len = filename->len ;
            cf ->conf_file->file.name .data = filename->data ;
            cf ->conf_file->file.offset = 0 ;
            cf ->conf_file->file.log = cf-> log;
            cf ->conf_file->line = 1;
    
            type = parse_file;
    
        } else if (cf->conf_file ->file. fd != NGX_INVALID_FILE ) {
    
            type = parse_block;
    
        } else {
            type = parse_param;
        }
    
    
    for ( ;; ) {
        rc = ngx_conf_read_token (cf);
    
        /*
         * ngx_conf_read_token() may return
         *
         *    NGX_ERROR             there is error
         *    NGX_OK                the token terminated by ";" was found
         *    NGX_CONF_BLOCK_START  the token terminated by "{" was found
         *    NGX_CONF_BLOCK_DONE   the "}" was found
         *    NGX_CONF_FILE_DONE    the configuration file is done
         */
    
        if (rc == NGX_ERROR) {
             goto done;
        }
    
        if (rc == NGX_CONF_BLOCK_DONE) {
    
             if ( type != parse_block ) {
                ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "unexpected "}"");
                 goto failed;
             }
    
             goto done;
        }
    
        if (rc == NGX_CONF_FILE_DONE) {
    
             if ( type == parse_block ) {
                ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                                   "unexpected end of file, expecting "}"");
                 goto failed;
             }
    
             goto done;
        }
    
        if (rc == NGX_CONF_BLOCK_START) {
    
             if ( type == parse_param ) {
                ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                                   "block directives are not supported "
                                   "in -g option");
                 goto failed;
             }
        }
    
        /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */
    
        if (cf-> handler) {
    
             /*
             * the custom handler, i.e., that is used in the http's
             * "types { ... }" directive
             */
    
             if ( rc == NGX_CONF_BLOCK_START ) {
                ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "unexpected "{"");
                 goto failed;
             }
    
            rv = (* cf->handler)(cf , NULL , cf-> handler_conf);
             if ( rv == NGX_CONF_OK ) {
                 continue;
             }
    
             if ( rv == NGX_CONF_ERROR ) {
                 goto failed;
             }
    
            ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, rv );
    
             goto failed;
        }
    
    
        rc = ngx_conf_handler (cf, rc);
    
        if (rc == NGX_ERROR) {
             goto failed;
        }
    }
    
    failed:
    
        rc = NGX_ERROR ;
    
    done:
    
        if (filename) {
            if ( cf->conf_file->buffer ->start) {
                ngx_free (cf-> conf_file->buffer ->start);
            }
    
            if ( ngx_close_file(fd ) == NGX_FILE_ERROR) {
                ngx_log_error (NGX_LOG_ALERT, cf->log , ngx_errno,
                              ngx_close_file_n " %s failed",
                              filename ->data);
                return NGX_CONF_ERROR;
            }
    
            cf ->conf_file = prev;
        }
    
        if (rc == NGX_ERROR) {
            return NGX_CONF_ERROR;
        }
    
        return NGX_CONF_OK ;
    }
    

    ngx_conf_handler函数如下,函数会遍历所有模块,并匹配配置命令key,取出配置上下文指针,执行相应的set函数。

    static ngx_int_t
    ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
    {
        char           *rv;
        void           *conf, **confp;
        ngx_uint_t      i , found;
        ngx_str_t      *name;
        ngx_command_t  *cmd;
    
        name = cf ->args-> elts;
    
        found = 0;
    
        /*遍历所有模块,并比较模块中所有配置的key值*/
        for (i = 0 ; ngx_modules[i]; i ++) {
    
            cmd = ngx_modules[i]->commands ;
            if ( cmd == NULL) {
                continue;
            }
    
            for ( /* void */ ; cmd->name .len; cmd++) {
    
                if ( name->len != cmd-> name.len ) {
                    continue;
                }
                 /*比较命令key值是否和配置的一致*/
                if ( ngx_strcmp(name ->data, cmd->name .data) != 0) {
                    continue;
                }
    
                found = 1 ;
                
                 /*过滤掉非指定模块,进入ngx_conf_parse前,会指定module_type值,解析指定模块*/
                if ( ngx_modules[i ]->type != NGX_CONF_MODULE
                    && ngx_modules[i]->type != cf-> module_type)
                {
                    continue;
                }
    
                /*判断解析的配置命令类型是否和指定的类型一致,cmd_type在进入ngx_conf_parse前指定*/
    
                if (!( cmd->type & cf-> cmd_type)) {
                    continue;
                }
    
                if (!( cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {
                    ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                                      "directive "%s" is not terminated by ";"",
                                      name ->data);
                    return NGX_ERROR;
                }
    
                if (( cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {
                    ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                                       "directive "%s" has no opening "{"",
                                       name ->data);
                    return NGX_ERROR;
                }
    
                /*判断配置参数个数是否正确*/
    
                if (!( cmd->type & NGX_CONF_ANY)) {
    
                    if ( cmd->type & NGX_CONF_FLAG) {
    
                        if ( cf->args->nelts != 2 ) {
                            goto invalid;
                        }
    
                    } else if (cmd ->type & NGX_CONF_1MORE) {
    
                        if ( cf->args->nelts < 2 ) {
                            goto invalid;
                        }
    
                    } else if (cmd ->type & NGX_CONF_2MORE) {
    
                        if ( cf->args->nelts < 3 ) {
                            goto invalid;
                        }
    
                    } else if (cf ->args-> nelts > NGX_CONF_MAX_ARGS ) {
    
                        goto invalid;
    
                    } else if (!(cmd ->type & argument_number[cf ->args-> nelts - 1 ]))
                    {
                        goto invalid;
                    }
                }
    
                /* 获取指定配置上下文的指针,*/
    
                conf = NULL ;
    
                if ( cmd->type & NGX_DIRECT_CONF) {
                     /*
                     *取ngx_core_module模块配置ngx_core_conf_t指针,由于该模块结构中直接配置了create_conf回调函数,已经分配过内存,直接使用
                     */
                    conf = (( void **) cf->ctx )[ngx_modules[i]->index ];
    
                } else if (cmd ->type & NGX_MAIN_CONF) {
                     /*
                     *假设cmd->name值是http,即正在解析http{}块,(((void **) cf->ctx)[ngx_modules[i]->index]取出的是ngx_http_module模块配置ngx_http_conf_ctx_t的指针,
                     *由于配置还未进行内存分配,因此这里取了指针的地址,以备后面ngx_http_block对其进行内存分配
                     */
                    conf = &((( void **) cf->ctx )[ngx_modules[i]->index ]);
    
                } else if (cf ->ctx) {
                     /*
                     *假设cmd->name值是location,并出现在server{}第一层中,即解析server { location{} }块,首先confp取出的是ngx_http_conf_ctx_t中srv_conf指针值,
                     *conf则取出当前模块对应的http配置指针
                     */
                    confp = *( void **) ((char *) cf->ctx + cmd-> conf);
    
                    if ( confp) {
                        conf = confp[ ngx_modules[i ]->ctx_index];
                    }
                }
                
                 /*配置的set函数*/
                rv = cmd-> set(cf , cmd, conf);
    
                if ( rv == NGX_CONF_OK ) {
                    return NGX_OK;
                }
    
                if ( rv == NGX_CONF_ERROR ) {
                    return NGX_ERROR;
                }
    
                ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                                   ""%s" directive %s" , name-> data, rv );
    
                return NGX_ERROR;
            }
        }
    
        if (found) {
            ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                               ""%s" directive is not allowed here" , name->data);
    
            return NGX_ERROR;
        }
    
        ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                           "unknown directive "%s"" , name-> data);
    
        return NGX_ERROR ;
    
    invalid:
    
        ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
                           "invalid number of arguments in "%s" directive",
                           name ->data);
    
        return NGX_ERROR ;
    }
    

    下面我们假设配置读取的是http{}块,则set函数即为ngx_http_block,函数首先给ngx_http_conf_ctx_t配置分配内存,然后递归调用ngx_conf_parse开始解析所有NGX_HTTP_MODULE模块中main_conf字段,解析中如果遇到location{}或者server{}块,则调用相应的set函数进行解析,整个http{}块共享同一个main_conf。如果一个字段可以同时出现在main_conf,srv_conf中,则这个字段会保存在srv_conf中,如果一个字段可以同时出现在main_conf,srv_conf,loc_conf中,则此配置必定保存在loc_conf中。配置的合并其实就是对那些可以出现在多个层的字段,如果内层没有赋值,则由外层向内层赋值过程。

    static char *
    ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd , void *conf)
    {
        char                        *rv;
        ngx_uint_t                   mi , m, s;
        ngx_conf_t                   pcf ;
        ngx_http_module_t           *module;
        ngx_http_conf_ctx_t         *ctx;
        ngx_http_core_loc_conf_t    *clcf;
        ngx_http_core_srv_conf_t   **cscfp;
        ngx_http_core_main_conf_t   *cmcf;
        
        /*ngx_http_ctx_t分配内存*/
        ctx = ngx_pcalloc (cf-> pool, sizeof(ngx_http_conf_ctx_t ));
        if (ctx == NULL) {
            return NGX_CONF_ERROR;
        }
        
        /*conf指针即ngx_conf_handler中取出的ngx_http_ctx_t指针的地址,将分配的内存赋值,相当于(((void **) cf->ctx)[ngx_modules[i]->index]) = ctx*/
        *(ngx_http_conf_ctx_t **) conf = ctx;
    
    
        /*设置HTTP模块相关配置的索引地址*/
        ngx_http_max_module = 0 ;
        for (m = 0 ; ngx_modules[m]; m ++) {
            if ( ngx_modules[m ]->type != NGX_HTTP_MODULE) {
                continue;
            }
    
            ngx_modules [m]-> ctx_index = ngx_http_max_module ++;
        }
    
    
        /* main_conf整个http{}块共享同一个main_conf */
        ctx->main_conf = ngx_pcalloc(cf->pool ,
                                     sizeof(void *) * ngx_http_max_module );
        if (ctx-> main_conf == NULL) {
            return NGX_CONF_ERROR;
        }
    
        /*
         * http{}块没有srv_conf,合并时使用
         */
        ctx->srv_conf = ngx_pcalloc(cf->pool , sizeof (void *) * ngx_http_max_module);
        if (ctx-> srv_conf == NULL) {
            return NGX_CONF_ERROR;
        }
    
        /*
         * http{}没有loc_conf,合并时使用
         */
    
        ctx->loc_conf = ngx_pcalloc(cf->pool , sizeof (void *) * ngx_http_max_module);
        if (ctx-> loc_conf == NULL) {
            return NGX_CONF_ERROR;
        }
    
        /*
         * create the main_conf's, the null srv_conf's, and the null loc_conf's
         * of the all http modules
         */
    
        for (m = 0 ; ngx_modules[m]; m ++) {
            if ( ngx_modules[m ]->type != NGX_HTTP_MODULE) {
                continue;
            }
    
            module = ngx_modules[m]->ctx ;
            mi = ngx_modules[m]->ctx_index ;
    
            if ( module->create_main_conf ) {
                ctx ->main_conf[mi] = module->create_main_conf(cf );
                if ( ctx->main_conf [mi] == NULL) {
                    return NGX_CONF_ERROR;
                }
            }
    
            if ( module->create_srv_conf ) {
                ctx ->srv_conf[mi] = module->create_srv_conf(cf );
                if ( ctx->srv_conf [mi] == NULL) {
                    return NGX_CONF_ERROR;
                }
            }
    
            if ( module->create_loc_conf ) {
                ctx ->loc_conf[mi] = module->create_loc_conf(cf );
                if ( ctx->loc_conf [mi] == NULL) {
                    return NGX_CONF_ERROR;
                }
            }
        }
    
        /*
         *保存原来上下文配置,cf->ctx赋值为ngx_http_ctx_t指针,后面要开始递归解析http{}块,在后面的我们会看到很多这样的操作
         */
        pcf = *cf;
        cf->ctx = ctx;
    
        for (m = 0 ; ngx_modules[m]; m ++) {
            if ( ngx_modules[m ]->type != NGX_HTTP_MODULE) {
                continue;
            }
    
            module = ngx_modules[m]->ctx ;
    
            if ( module->preconfiguration ) {
                if ( module->preconfiguration (cf) != NGX_OK) {
                    return NGX_CONF_ERROR;
                }
            }
        }
    
        /*
         *module_type和cmd_type赋值,进入ngx_conf_parse后解析NGX_HTTP_MODULE模块,并且NGX_HTTP_MAIN_CONF类型的配置
         */
    
        cf->module_type = NGX_HTTP_MODULE;
        cf->cmd_type = NGX_HTTP_MAIN_CONF;
        rv = ngx_conf_parse (cf, NULL);
    
        if (rv != NGX_CONF_OK) {
            goto failed;
        }
    
        /*
         * 上面解析完成后,所有的server{}列表保存在cmcf->servers中,然后开始合并server{}块,合并server{}块时,同时会开始合并location{}块
         */
        cmcf = ctx ->main_conf[ngx_http_core_module.ctx_index ];
        cscfp = cmcf ->servers.elts;
    
        for (m = 0 ; ngx_modules[m]; m ++) {
            if ( ngx_modules[m ]->type != NGX_HTTP_MODULE) {
                continue;
            }
    
            module = ngx_modules[m]->ctx ;
            mi = ngx_modules[m]->ctx_index ;
    
            /* init http{} main_conf's */
    
            if ( module->init_main_conf ) {
                rv = module->init_main_conf(cf , ctx-> main_conf[mi ]);
                if ( rv != NGX_CONF_OK ) {
                    goto failed;
                }
            }
    
            rv = ngx_http_merge_servers(cf, cmcf , module, mi);
            if ( rv != NGX_CONF_OK ) {
                goto failed;
            }
        }
    
    
        /* 创建location节点树, 把location链表转换成三叉树, 利于用户根据请求url取出相应的配置 */
    
        for (s = 0 ; s < cmcf->servers .nelts; s++) {
    
            clcf = cscfp[ s]->ctx->loc_conf [ngx_http_core_module.ctx_index];
    
            if ( ngx_http_init_locations(cf , cscfp[ s], clcf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
    
            if ( ngx_http_init_static_location_trees(cf , clcf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    
    
        if (ngx_http_init_phases(cf, cmcf ) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    
        if (ngx_http_init_headers_in_hash(cf, cmcf ) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    
    
        for (m = 0 ; ngx_modules[m]; m ++) {
            if ( ngx_modules[m ]->type != NGX_HTTP_MODULE) {
                continue;
            }
    
            module = ngx_modules[m]->ctx ;
    
            if ( module->postconfiguration ) {
                if ( module->postconfiguration (cf) != NGX_OK) {
                    return NGX_CONF_ERROR;
                }
            }
        }
    
        if (ngx_http_variables_init_vars(cf) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    
        /*
         * http{}'s cf->ctx was needed while the configuration merging
         * and in postconfiguration process
         */
    
        *cf = pcf;
    
    
        if (ngx_http_init_phase_handlers(cf, cmcf ) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    
    
        /* optimize the lists of ports, addresses and server names */
    
        if (ngx_http_optimize_servers(cf, cmcf , cmcf-> ports) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    
        return NGX_CONF_OK ;
    
    failed:
    
        *cf = pcf;
    
        return rv ;
    }
    

    http模块解析完成后, 所有的server配置都会保存在cmcf->servers数组中, 每个server下的location会组成一个三叉树, 保存在相对应的server中ngx_http_core_loc_conf_t中static_locations. 服务器接收一个url请求后, 通过相应的地址和端口找到server配置, 通过三叉树找到相匹配的ngx_http_core_loc_conf_t配置, 进而获取到正确的配置信息.

  • 相关阅读:
    计算机相关单位换算关系的积累
    谈编程资料
    杂记toCSV
    [转载]Windows 8][Metro Style Apps]淺談Metro Style Apps的瀏覽模式及螢幕解析度
    [转载][Windows 8][Metro Style Apps]Windows 8 開發/執行環境概觀
    【转载】[Windows 8]Hello Windows 8 Windows 8 Developer Preview搶先預覽
    台湾dotnet程序员之家
    [转载]实现Application Tile 更新
    [转载]在.NET中实现OAuth身份认证
    杂记
  • 原文地址:https://www.cnblogs.com/ourroad/p/4861096.html
Copyright © 2011-2022 走看看