ngx_conf_read_token
定义在src\core\ngx_conf_file.c
static ngx_int_t
ngx_conf_read_token(ngx_conf_t *cf)
{
u_char *start, ch, *src, *dst;
off_t file_size;
size_t len;
ssize_t n, size;
ngx_uint_t found, need_space, last_space, sharp_comment, variable;
ngx_uint_t quoted, s_quoted, d_quoted, start_line;
ngx_str_t *word;
ngx_buf_t *b, *dump;
found = 0;
need_space = 0;
last_space = 1;
sharp_comment = 0;
variable = 0;
quoted = 0;
s_quoted = 0;
d_quoted = 0;
cf->args->nelts = 0;
b = cf->conf_file->buffer;
dump = cf->conf_file->dump;
start = b->pos;
start_line = cf->conf_file->line;
file_size = ngx_file_size(&cf->conf_file->file.info);
for ( ;; ) {
if (b->pos >= b->last) {
if (cf->conf_file->file.offset >= file_size) {
if (cf->args->nelts > 0 || !last_space) {
if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of parameter, "
"expecting \";\"");
return NGX_ERROR;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, "
"expecting \";\" or \"}\"");
return NGX_ERROR;
}
return NGX_CONF_FILE_DONE;
}
len = b->pos - start;
if (len == NGX_CONF_BUFFER) {
cf->conf_file->line = start_line;
if (d_quoted) {
ch = '"';
} else if (s_quoted) {
ch = '\'';
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long parameter \"%*s...\" started",
10, start);
return NGX_ERROR;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long parameter, probably "
"missing terminating \"%c\" character", ch);
return NGX_ERROR;
}
if (len) {
ngx_memmove(b->start, start, len);
}
size = (ssize_t) (file_size - cf->conf_file->file.offset);
if (size > b->end - (b->start + len)) {
size = b->end - (b->start + len);
}
n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
cf->conf_file->file.offset);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
if (n != size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
ngx_read_file_n " returned "
"only %z bytes instead of %z",
n, size);
return NGX_ERROR;
}
b->pos = b->start + len;
b->last = b->pos + n;
start = b->start;
if (dump) {
dump->last = ngx_cpymem(dump->last, b->pos, size);
}
}
ch = *b->pos++;
if (ch == LF) {
cf->conf_file->line++;
if (sharp_comment) {
sharp_comment = 0;
}
}
if (sharp_comment) {
continue;
}
if (quoted) {
quoted = 0;
continue;
}
if (need_space) {
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
last_space = 1;
need_space = 0;
continue;
}
if (ch == ';') {
return NGX_OK;
}
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
if (ch == ')') {
last_space = 1;
need_space = 0;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%c\"", ch);
return NGX_ERROR;
}
}
if (last_space) {
start = b->pos - 1;
start_line = cf->conf_file->line;
if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
continue;
}
switch (ch) {
case ';':
case '{':
if (cf->args->nelts == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%c\"", ch);
return NGX_ERROR;
}
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
return NGX_OK;
case '}':
if (cf->args->nelts != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"}\"");
return NGX_ERROR;
}
return NGX_CONF_BLOCK_DONE;
case '#':
sharp_comment = 1;
continue;
case '\\':
quoted = 1;
last_space = 0;
continue;
case '"':
start++;
d_quoted = 1;
last_space = 0;
continue;
case '\'':
start++;
s_quoted = 1;
last_space = 0;
continue;
case '$':
variable = 1;
last_space = 0;
continue;
default:
last_space = 0;
}
} else {
if (ch == '{' && variable) {
continue;
}
variable = 0;
if (ch == '\\') {
quoted = 1;
continue;
}
if (ch == '$') {
variable = 1;
continue;
}
if (d_quoted) {
if (ch == '"') {
d_quoted = 0;
need_space = 1;
found = 1;
}
} else if (s_quoted) {
if (ch == '\'') {
s_quoted = 0;
need_space = 1;
found = 1;
}
} else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
|| ch == ';' || ch == '{')
{
last_space = 1;
found = 1;
}
if (found) {
word = ngx_array_push(cf->args);
if (word == NULL) {
return NGX_ERROR;
}
word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1);
if (word->data == NULL) {
return NGX_ERROR;
}
for (dst = word->data, src = start, len = 0;
src < b->pos - 1;
len++)
{
if (*src == '\\') {
switch (src[1]) {
case '"':
case '\'':
case '\\':
src++;
break;
case 't':
*dst++ = '\t';
src += 2;
continue;
case 'r':
*dst++ = '\r';
src += 2;
continue;
case 'n':
*dst++ = '\n';
src += 2;
continue;
}
}
*dst++ = *src++;
}
*dst = '\0';
word->len = len;
if (ch == ';') {
return NGX_OK;
}
if (ch == '{') {
return NGX_CONF_BLOCK_START;
}
found = 0;
}
}
}
}
ngx_conf_read_token
是 Nginx 配置解析的核心函数,负责从配置文件中读取并解析下一个 token(如指令、参数、块等)。
函数签名
static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf);
返回类型:ngx_int_t
- 类型:Nginx 自定义的整型状态码。
- 含义:
NGX_OK
:成功解析一个完整的指令(以;
结尾)。NGX_CONF_BLOCK_START
:遇到{
,表示进入一个新的配置块。NGX_CONF_BLOCK_DONE
:遇到}
,表示当前配置块结束。NGX_CONF_FILE_DONE
:配置文件解析完毕。NGX_ERROR
:解析过程中发生错误(如语法错误、未闭合的引号等)。
参数:ngx_conf_t *cf
- 类型:指向
ngx_conf_t
结构体的指针,包含配置解析的上下文信息。 - 关键字段:
conf_file
:指向当前解析的配置文件对象(类型为ngx_conf_file_t
),包含文件句柄、缓冲区、行号等。args
:动态数组(ngx_array_t
),用于存储当前解析出的参数(token)。pool
:内存池,用于分配参数存储空间。
详解
详解(1)
详解(2)
详解(3)
主要逻辑
1. 初始化
初始化函数运行所需的各种状态变量和上下文。
准备配置文件的缓冲区,并设置初始状态。
-
初始化状态变量:
- 设置标志变量,例如:
found
:是否找到一个完整的 token。need_space
:是否需要空格来分隔 token。last_space
:上一个字符是否是空格。sharp_comment
:是否在注释中(以#
开头)。quoted
:是否遇到转义字符(\
)。d_quoted
和s_quoted
:是否在双引号或单引号字符串中。variable
:是否在解析变量(以$
开头)。
- 这些变量用于管理解析过程中的上下文。
- 设置标志变量,例如:
-
初始化缓冲区:
- 获取配置文件的缓冲区 (
b
) 和当前文件大小 (file_size
)。 - 设置缓冲区的起始位置 (
start
) 和当前行号 (start_line
)。
- 获取配置文件的缓冲区 (
-
清空参数数组:
- 重置
cf->args->nelts
为 0,准备存储解析出的 token。
- 重置
2. 循环部分
作用:
- 逐个字符读取配置文件内容,并根据字符类型解析出 token。
- 管理解析过程中的状态变化,并处理文件结束和错误情况。
-
读取字符:
- 从缓冲区中逐个字符读取配置文件内容。
- 如果缓冲区的内容已经读取完毕,则从文件中读取更多数据到缓冲区。
-
字符处理:
- 根据当前字符的类型,执行不同的逻辑:
- 处理换行符 (
LF
)、注释 (#
)、转义字符 (\
)、引号 ("
和'
)、变量符号 ($
) 等。 - 处理分隔符(如空格、制表符、回车、换行)以及特殊字符(如分号
;
、大括号{
和}
)。
- 处理换行符 (
- 根据当前字符的类型,执行不同的逻辑:
-
提取 token:
- 当遇到分隔符或特殊字符时,提取完整的 token 并存入
cf->args
数组。 - 处理转义字符(例如
\t
、\n
、\r
等)。
- 当遇到分隔符或特殊字符时,提取完整的 token 并存入
-
状态管理:
- 根据字符类型更新状态变量(如
quoted
、d_quoted
、sharp_comment
等)。 - 重置标志变量(如
found
),准备解析下一个 token。
- 根据字符类型更新状态变量(如
-
文件结束处理:
- 如果文件已经读取完毕,检查是否有未结束的 token。
- 如果有未结束的 token,报错并返回
NGX_ERROR
。 - 如果没有未结束的 token,返回
NGX_CONF_FILE_DONE
。
-
错误处理:
- 在解析过程中,如果遇到不合法的字符或语法错误(例如未闭合的引号、过长的 token 等),记录错误日志并返回
NGX_ERROR
。
- 在解析过程中,如果遇到不合法的字符或语法错误(例如未闭合的引号、过长的 token 等),记录错误日志并返回