15.Nginx解析配置文件之ngx_conf_read_token
来源:互联网 发布:外网映射端口 编辑:程序博客网 时间:2024/05/22 08:12
Nginx的可配置选项非常的多, 意味着配置文件可以相当复杂,那么解析起来也是困难重重,今天学习一下与配置解析有关的函数ngx_conf_read_token;
顾名思义,可以看出这是一个用于从配置文件中提取token的函数, 什么是token呢?
举栗子,下面这个配置片段中,user是一个token,www-data是一个token,worker_processes、auto、pid、/run/nginx.pid、events、worker_connections、768都是token;
multi_accept、on就不是token了,因为它们在#后面,是行注释的一部分
user www-data;worker_processes auto;pid /run/nginx.pid;events { worker_connections 768; # multi_accept on;}
从下面的代码分析中,我们也可以看出ngx_conf_read_token并不能一次提取所有token,在解析完一个配置行(以分号结束)或者遇到配置块的开始(以左花括号开始)时就会返回;因此我们想要解析完整个配置文件, 可能需要进行多次调用
/* core/ngx_conf_file.c *//* 从配置文件解析token*/static int ngx_conf_read_token(ngx_conf_t *cf){ u_char *start, ch, *src, *dst; int len; int found, need_space, last_space, sharp_comment; int quoted, s_quoted, d_quoted; ssize_t n; ngx_str_t *word; ngx_buf_t *b; found = 0; // 用来表示是否找到一个完整的token need_space = 0; // 当接下来扫描到的字符需要是空格/水平制表符/回车符/换行符时置为1 last_space = 1; // 当扫描到空格/水平制表符/回车符/换行符时置为1 sharp_comment = 0; // 用来表示当前扫描的字符是否在一个#开头的行注释中 quoted = s_quoted = d_quoted = 0; // quoted: 当扫描到一个转义序列的\时置为1 // s_quoted: 当扫描到一个单引号时置为1 // d_quoted: 当扫描到一个双引号时置为1 cf->args->nelts = 0; // cf->args数组用来存放解析到的token b = cf->conf_file->buffer; // 缓冲区b, 里面存放配置内容 start = b->pos; // b->pos用来标记当前将要扫描的字符位置 // start用来标记一个token的起始字符#if 0ngx_log_debug(cf->log, "TOKEN START");#endif for ( ;; ) { if (b->pos >= b->last) { // b->last用来标记缓冲区内有效内容的结束位置, // 这里意味着已经读取的配置内容解析完毕 if (cf->conf_file->file.offset >= ngx_file_size(&cf->conf_file->file.info)) { // 当配置文件的当前偏移量等于配置文件大小时, 说明配置文件已经读取完毕, // 也就意味着配置文件全部解析完毕 return NGX_CONF_FILE_DONE; } if (b->pos - start) { // 意味着当前token还没解析完毕, 只扫描了它的部分字符, // 此时将这部分字符拷贝到缓冲区头 ngx_memcpy(b->start, start, b->pos - start); } // 继续读取配置文件到缓冲区 n = ngx_read_file(&cf->conf_file->file, b->start + (b->pos - start), b->end - (b->start + (b->pos - start)), cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } // 更新b->pos, 使之指向刚读取内容的起始位置 b->pos = b->start + (b->pos - start); // start指向缓冲区起始地址, 也就是指向当前要解析的token的起始字符 start = b->start; // 更新b->last, 使之指向刚读取内容的结束位置 b->last = b->pos + n; } // 读取当前扫描的字符, 并让b->pos后移 ch = *b->pos++;#if 0ngx_log_debug(cf->log, "%d:%d:%d:%d:%d '%c'" _ last_space _ need_space _ quoted _ s_quoted _ d_quoted _ ch);#endif if (ch == LF) { // 如果当前扫描的字符为换行符 // cf->conf_file的line成员加1, // line成员用来记录已扫描的配置文件行数 cf->conf_file->line++; if (sharp_comment) { // 如果sharp_comment为1, 那么这里的换行符意味着#行注释的结束, // 置sharp_comment为0 sharp_comment = 0; } } if (sharp_comment) { // 如果sharp_comment为1, 那么这里的字符就是行注释的一部分, 直接跳过 continue; } if (quoted) { // 如果quoted为1, 意味着上一个扫描的字符为\, 当前字符就是转义序列的一部分, // 置quoted为0, 表示当前扫描到的转义序列结束, 然后直接跳过 quoted = 0; continue; } if (need_space) { // 当need_space为1时, 意味着当前扫描到的字符需要是空格 if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { // 如果当前扫描到的字符为空格/水平制表符/回车符/换行符时, // 置need_space为0, 因为已经满足了要求; // 置last_space为1, 标记当前扫描到了这些字符; // 然后直接跳过 last_space = 1; need_space = 0; continue; } if (ch == ';' || ch == '{') { return NGX_OK; } ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "unexpected '%c' in %s:%d", ch, cf->conf_file->file.name.data, cf->conf_file->line); return NGX_ERROR; } if (last_space) { // 如果上一个扫描的字符为空格/水平制表符/回车符/换行符 if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { // 当前扫描的字符依旧为空格/水平制表符/回车符/换行符, 那么直接跳过 continue; } // 如果当前扫描的字符不为空格/水平制表符/回车符/换行符, 意味着当前扫描的字符为下一个token // 的起始字符, start指向当前扫描的字符 start = b->pos - 1; switch (ch) { case ';': case '{': // 如果当前扫描到的字符为分号或者左花括号 if (cf->args->nelts == 0) { // 如果此次解析到的token数量为0, 返回NGX_ERROR; // 因为分号或左花括号之前必须出现至少一个token, 否则说明配置有误 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "unexpected '%c' in %s:%d", ch, cf->conf_file->file.name.data, cf->conf_file->line); return NGX_ERROR; } // 意味着配置行的结束或者配置块的开始, 返回NGX_OK return NGX_OK; case '}': // 如果当前扫描到的字符为右花括号 if (cf->args->nelts > 0) { // 如果此次解析到的token数量大于0, 返回NGX_ERROR; // 因为右花括号必须独占一行, 否则说明配置有误 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "unexpected '}' in %s:%d", cf->conf_file->file.name.data, cf->conf_file->line); return NGX_ERROR; } // 意味着配置块的结束, 返回NGX_CONF_BLOCK_DONE return NGX_CONF_BLOCK_DONE; case '#': // 如果当前扫描到的字符为井号, // 意味着遇到行注释的开始, 置sharp_comment为1; // 然后直接跳过 sharp_comment = 1; continue; case '\\': // 如果当前扫描到的字符为反斜杠, // 意味着遇到转义序列的开始, 置quoted为1; // 置last_space为0; 然后直接跳过 quoted = 1; last_space = 0; continue; case '"': // 如果当前扫描到的字符为双引号, 加上前一个扫描的字符为空格, // 那么我们可以推断该双引号为左双引号; // 那么下一个token的开始字符必定为当前扫描字符的下一个字符, start向后移; // 置d_quoted为1; 置last_space为0; 然后直接跳过 start++; d_quoted = 1; last_space = 0; continue; case '\'': // 如果当前扫描到的字符为单引号, 与上面的双引号同理, 只是需要置s_quoted为1 start++; s_quoted = 1; last_space = 0; continue; default: // 如果当前扫描到的字符为其他字符, 说明是token中的字符, 置last_space为0即可 last_space = 0; } } else { // 如果上一个扫描的字符不为空格/水平制表符/回车符/换行符 if (ch == '\\') { // 如果当前扫描到的字符为反斜杠, 意味着遇到转义序列的开始, 置quoted为1; // 然后直接跳过 quoted = 1; continue; } // 如果上一个扫描的字符不为空格/水平制表符/回车符/换行符, 那么当前扫描字符为单引号或双引号时, // 我们需要结合s_quoted或d_quoted来判断是左还是右 if (d_quoted) { // 如果d_quoted为1, 说明之前遇到一个双引号 if (ch == '"') { // 如果当前扫描的字符为双引号, 那么必为右双引号; // 置d_quoted为0; // 右双引号之后需要出现空格, 置need_space为1; // 右双引号意味着一个token的结束, 置found为1 d_quoted = 0; need_space = 1; found = 1; } } else if (s_quoted) { // 如果s_quoted为1, 说明之前遇到一个单引号 if (ch == '\'') { // 如果当前扫描的字符为单引号, 那么必为右单引号; // 置s_quoted为0; // 右单引号之后需要出现空格, 置need_space为1; // 右单引号意味着一个token的结束, 置found为1 s_quoted = 0; need_space = 1; found = 1; } } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF || ch == ';' || ch == '{') { // 如果当前扫描的字符为空格/水平制表符/回车符/换行符/分号/左花括号, // 意味着一个token的结束, 置found为1; 置last_space为1 last_space = 1; found = 1; } if (found) { // 如果found为1, 说明解析到一个token // 从cf->args数组中申请存放一个元素的内存空间 if (!(word = ngx_push_array(cf->args))) { return NGX_ERROR; } // 从cf->pool内存池中申请一块大小为b->pos - start + 1的内存空间用来存放token字符串(包含结束符); // 其实我觉得这里应该b->pos - start就足矣 // 举栗子, "ab"_ 此时,start指向a, b->pos指向下划线模拟的空格, 只需要b->pos - start = 3个字节即可存放token if (!(word->data = ngx_palloc(cf->pool, b->pos - start + 1))) { return NGX_ERROR; } // 遍历缓冲区中扫描到的token字符串, 将其拷贝到刚才申请到的内存中; // 当然这里也需要验证该token的合法性和过滤其中的无用字符 for (dst = word->data, src = start, len = 0; src < b->pos - 1; len++) { if (*src == '\\') { // 如果token的当前字符为反斜杠 switch (src[1]) { case '"': case '\'': case '\\': // 如果token的下一个字符为双引号/单引号/反斜杠, // 那么直接退出循环 src++; break; case 't': // 如果token的下一个字符为t, 意味着与当前字符组成一个水平制表符, // 那么直接拷贝一个水平制表符 *dst++ = '\t'; src += 2; continue; case 'r': // 如果token的下一个字符为r, 意味着与当前字符组成一个回车符, // 那么直接拷贝一个回车符 *dst++ = '\r'; src += 2; continue; case 'n': // 如果token的下一个字符为n, 意味着与当前字符组成一个换行符, // 那么直接拷贝一个换行符 *dst++ = '\n'; src += 2; continue; } } // 将缓冲区中token的当前字符拷贝至申请的内存中 *dst++ = *src++; } // 在申请的内存中加上结束符; // 记录token长度为len *dst = '\0'; word->len = len;#if 0ngx_log_debug(cf->log, "FOUND %d:'%s'" _ word->len _ word->data);#endif if (ch == ';' || ch == '{') { // 如果当前扫描到的字符为分号/左花括号, 那么意味着配置行的结束或配置块的开始, // 返回NGX_OK return NGX_OK; } // 置found为0, 以查找下一个token found = 0; } } }}
阅读全文
0 0
- 15.Nginx解析配置文件之ngx_conf_read_token
- nginx 配置文件解析函数------------ngx_conf_read_token
- nginx的配置文件解析:ngx_conf_read_token函数
- nginx ngx_conf_read_token详细分析
- Nginx配置文件解析之二
- Nginx配置文件解析之三
- nginx配置文件解析过程之神图
- Nginx情景分析之配置文件解析
- 16.Nginx解析配置文件之ngx_conf_parse
- 【Nginx】初识nginx---配置文件解析
- nginx 配置文件解析
- Nginx配置文件解析
- Nginx配置文件解析之一
- nginx配置文件解析
- nginx 配置文件解析--二
- nginx配置文件解析
- Nginx的配置文件解析
- nginx 配置文件解析
- 在其他数都出现K次的数组中找到只出现一次的数
- TensorFlow 初体验 Windows安装tensorflow
- 广度/宽度优先搜索(BFS)
- 画正方体
- Ubuntu安装google chrome
- 15.Nginx解析配置文件之ngx_conf_read_token
- Git的使用以及相关命令
- python文件与异常
- hello world
- Activity执行两次问题
- UTF-8和GBK等中文字符编码格式介绍及相互转换
- 解决:scripts/kconfig/zconf.tab.c:199:24: fatal error: zconf.hash.c: No such file or directory
- 注册登录测试用例—祭我所踩过的那些坑
- Leetcode 算法题04