nginx配置解析模块分析
nginx提供配置文件供用户方便的定义nginx的行为,通过修改配置项可以指定nginx进程工作模块,指定log的输出方式,指定如何处理用户请求等等。ngx_conf_module模块提供ngx_conf_parse函数在nginx启动过程中被调用来解析配置文件,它读取配置文件内容并将配置项交由指定的模块处理,如将http配置项交由ngx_http_module处理。配置文件通常使用include配置项从其它文件中加载配置,如nginx.conf文件中使用”include mime.types”配置项加载mime.types文件。当在配置文件中发现include配置项时就调用ngx_conf_module模块提供的ngx_conf_include函数来解析,它首先分析出include配置项指定的文件名,对每一个文件分别调用ngx_conf_parse函数来解析。
ngx_conf_parse模块主要做三件事,第一提供ngx_conf_parse函数在nginx启动时被调用来处理配置文件。第二解析include配置项。第三提供预设的配置项解析回调函数(ngx_conf_module提供了12个回调函数)。
include配置项
include file| mask
在当前配置文件中包含其它配置文件,file用于指定被包含的某一个文件名,mask用于支持包含多个文件,如:
include mime.types;
include vhosts/*.conf;
ngx_conf_include函数分析
/*
*配置文件中找到include配置项时该函数被调用
*/
char *
ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
ngx_int_t n;
ngx_str_t *value, file, name;
ngx_glob_t gl;
value = cf->args->elts;
file = value[1];
ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
/***判断文件路径,当include配置项指定的文件名带绝对路径时不做任何处理,
否则默认在nginx启动时指定的配置文件存放路径中查找该文件***/
if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
/***当文件名中包含"*?[" 字符时表示include配置项需包含多个文件***/
if (strpbrk((char *) file.data, "*?[") == NULL) {
ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
return ngx_conf_parse(cf, &file);
}
ngx_memzero(&gl, sizeof(ngx_glob_t));
gl.pattern = file.data;
gl.log = cf->log;
gl.test = 1;
/***调用glob函数将查找所有匹配模式的文件名***/
if (ngx_open_glob(&gl) != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_glob_n " \"%s\" failed", file.data);
return NGX_CONF_ERROR;
}
rv = NGX_CONF_OK;
for ( ;; ) {
/***获取匹配到的文件名***/
n = ngx_read_glob(&gl, &name);
if (n != NGX_OK) {
break;
}
file.len = name.len++;
file.data = ngx_pstrdup(cf->pool, &name);
ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
/***调用ngx_conf_parse函数解析该文件***/
rv = ngx_conf_parse(cf, &file);
if (rv != NGX_CONF_OK) {
break;
}
}
/***释放glob_t对象***/
ngx_close_glob(&gl);
return rv;
}
ngx_conf_parse函数分析
/*
* ngx_conf_parse函数在如下三种情况下被调用:
* 1、nginx启动过程中。
* 2、解析带{}的配置项时。
* 3、解析include 配置项时。
*/
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 (NGX_SUPPRESS_WARN)
fd = NGX_INVALID_FILE;
prev = NULL;
#endif
/***当filename不为空时表示将要打开一个未被解析的配置文件进行解析***/
if (filename) {
/* open configuration file */
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);
}
/***存放配置文件内容的buf***/
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) {
/***在遇到带{}的配置项时,filename参数常为空,file.fd 描述符为该配置项所在文件被打开时的描述符***/
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;
}
/***已找到一个有效的配置项,调用ngx_conf_handler函数查找该由那个模块来解析***/
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;
}