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 等),记录错误日志并返回