专栏首页原创分享nginx0.1.0之http模块初始化源码分析(2)

nginx0.1.0之http模块初始化源码分析(2)

本文讲解http各个模块create_srv_conf和create_loc_conf钩子,还有指令的解析。 各模块的create_srv_conf和create_loc_conf函数逻辑都类似,不一一列举,执行完后内存视图是。

这里没有描述 下面是指令的解析。

1 access模块

1 allow、deny指令

// 每次遇到allow或者deny命令的时候执行的回调
static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
                                  void *conf)
{
    ngx_http_access_loc_conf_t *alcf = conf;

    ngx_str_t               *value;
    ngx_inet_cidr_t          in_cidr;
    ngx_http_access_rule_t  *rule;
    // 存储配置的结构体
    if (alcf->rules == NULL) {
        alcf->rules = ngx_create_array(cf->pool, 5,
                                       sizeof(ngx_http_access_rule_t));
        if (alcf->rules == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    if (!(rule = ngx_push_array(alcf->rules))) {
        return NGX_CONF_ERROR;
    }

    value = cf->args->elts;
    // 第一个字符是d说明是deny,否则是allow
    rule->deny = (value[0].data[0] == 'd') ? 1 : 0;
    // all
    if (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0) {
        rule->mask = 0;
        rule->addr = 0;

        return NGX_CONF_OK;
    }
    // 配置了具体的值,转成二进制形式的ip
    rule->addr = inet_addr((char *) value[1].data);
    // ip有效
    if (rule->addr != INADDR_NONE) {
        rule->mask = 0xffffffff;

        return NGX_CONF_OK;
    }
    // 无效则判断值的格式为cidr
    if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid paramter \"%s\"",
                           value[1].data);
        return NGX_CONF_ERROR;
    }

    rule->mask = in_cidr.mask;
    rule->addr = in_cidr.addr;

    return NGX_CONF_OK;
}    

2 gzip

1 enable指令 gzip模块的配置对应的结构体是

struct {
    ngx_flag_t           enable;
    ngx_flag_t           no_buffer;

    ngx_bufs_t           bufs;

    ngx_uint_t           http_version;
    ngx_uint_t           proxied;

    int                  level;
    size_t               wbits;
    size_t               memlevel;
    ssize_t              min_length;
} ngx_http_gzip_conf_t

使用ngx_conf_set_flag_slot设置enable为0或1。

2 gzip_buffers 设置bufs的num和size字段。即分配num个size大小的buffer用于压缩响应。默认是一页内存的size。

3 gzip_comp_level 设置level字段为压缩等级。并配置了

ngx_conf_num_bounds_t  ngx_http_gzip_comp_level_bounds = {
    ngx_conf_check_num_bounds, 1, 9
};

ngx_conf_check_num_bounds函数用于检查设置的值是否合法。

char *ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data)
{
    ngx_conf_num_bounds_t  *bounds = post;
    ngx_int_t  *np = data;
    // 如果设置了hign是-1则只需要校验值是否大于low
    if (bounds->high == -1) {
        if (*np >= bounds->low) {
            return NGX_CONF_OK;
        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "value must be equal or more than %d", bounds->low);

        return NGX_CONF_ERROR;
    }
    // 校验值
    if (*np >= bounds->low && *np <= bounds->high) {
        return NGX_CONF_OK;
    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "value must be between %d and %d",
                       bounds->low, bounds->high);

    return NGX_CONF_ERROR;
}

4 gzip_window 设置wbits字段大小。并配置了校验函数。

static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data)
{
    int *np = data;

    int  wbits, wsize;

    wbits = 15;
    // 32 * 1024 = 2的15次方,256等于2的8次方
    for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
        // 如果用户传的和当前的大小一样,把绝对大小转成2的次方数存储
        if (wsize == *np) {
            // 用户传的是1k,2k,nginx存的是2的几次方
            *np = wbits;

            return NGX_CONF_OK;
        }
        // 减一即除以2,右移一位
        wbits--;
    }

    return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
}

5 gzip_hash 设置memlevel字段的值,设置了ngx_http_gzip_set_hash_p函数进行校验。类似ngx_http_gzip_set_window_p函数。

6 gzip_no_buffer 设置no_buffer字段

7 gzip_http_version 设置http_version字段。设置校验函数。

char *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;

    ngx_uint_t       *np, i;
    ngx_str_t        *value;
    ngx_conf_enum_t  *e;

    np = (ngx_uint_t *) (p + cmd->offset);

    if (*np != NGX_CONF_UNSET_UINT) {
        return "is duplicate";
    }

    value = cf->args->elts;
    /*
           数组,每个元素一个值有效值
           ngx_conf_enum_t  ngx_http_gzip_http_version[] = {
            { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
            { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
            { ngx_null_string, 0 }
        };
    */
    e = cmd->post;

    for (i = 0; e[i].name.len != 0; i++) {
        // 长度不一样或不相等
        if (e[i].name.len != value[1].len
            || ngx_strcasecmp(e[i].name.data, value[1].data) != 0)
        {
            continue;
        }
        // 转成内部表示的数值
        *np = e[i].value;

        return NGX_CONF_OK;
    }

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                       "invalid value \"%s\"", value[1].data);

    return NGX_CONF_ERROR;
}    

8 gzip_proxied 设置proxied字段。nginx作为中间代理的时候,哪些条件下需要会对响应进行压缩。

ngx_conf_bitmask_t  ngx_http_gzip_proxied_mask[] = {
    { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
    { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
    { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
    { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
    { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
    { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
    { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
    { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
    { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
    { ngx_null_string, 0 }
};
char *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;

    ngx_uint_t          *np, i, m;
    ngx_str_t           *value;
    ngx_conf_bitmask_t  *mask;


    np = (ngx_uint_t *) (p + cmd->offset);
    value = cf->args->elts;
    mask = cmd->post;

    for (i = 1; i < cf->args->nelts; i++) {
        for (m = 0; mask[m].name.len != 0; m++) {
            // 比较字符串,不一样则跳过,一样则判断是否已经存在,否则获取对应的mask设置对应的位
            if (mask[m].name.len != value[i].len
                || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)
            {
                continue;
            }
            // 为true说明np已经存在该mask对应的值
            if (*np & mask[m].mask) {
                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                                   "duplicate value \"%s\"", value[i].data);

            } else {
                // 设置对应的位
                *np |= mask[m].mask;
            }

            break;
        }

        if (mask[m].name.len == 0) {
            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                               "invalid value \"%s\"", value[i].data);

            return NGX_CONF_ERROR;
        }
    }

    return NGX_CONF_OK;
}

9 gzip_min_length 设置min_length字段,代表响应的包多大时才进行压缩。响应包根据centent-length进行判断。

3 index模块

1 index 处理请求路径以/结尾的的url

// 把配置的路径放到indices字段。
static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
                                      void *conf)
{
    ngx_http_index_loc_conf_t *ilcf = conf;

    ngx_uint_t  i;
    ngx_str_t  *index, *value;

    value = cf->args->elts;
    // 第一个值不能是绝对路径
    if (value[1].data[0] == '/' && ilcf->indices.nelts == 0) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "first index \"%s\" in \"%s\" directive "
                           "must not be absolute",
                           value[1].data, cmd->name.data);
        return NGX_CONF_ERROR;
    }

    for (i = 1; i < cf->args->nelts; i++) {
        if (value[i].len == 0) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "index \"%s\" in \"%s\" directive is invalid",
                               value[1].data, cmd->name.data);
            return NGX_CONF_ERROR;
        }
        // push一个字符串到indices数组里
        ngx_test_null(index, ngx_push_array(&ilcf->indices), NGX_CONF_ERROR);
        index->len = value[i].len;
        index->data = value[i].data;
        // 更新值index指令后面的路径最大字符数
        if (ilcf->max_index_len < index->len + 1) {
            ilcf->max_index_len = index->len + 1;
        }
    }

    return NGX_CONF_OK;
}

4 ssl模块

ssl模块主要是设置该结构体的值。

 struct {
    ngx_flag_t      enable;
    ngx_str_t       certificate;
    ngx_str_t       certificate_key;

    ngx_ssl_ctx_t  *ssl_ctx;
} ngx_http_ssl_srv_conf_t

5 static_handler模块

1 redirect_cache c char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *p = conf; ¨K19K }

6 user_id模块

user_id模块是跟设置cookie相关的

1 userid 是否开启设置cookie的功能。处理函数是ngx_conf_set_enum_slot,可用值是

ngx_http_userid_state[] = {
    { ngx_string("off"), NGX_HTTP_USERID_OFF },
    { ngx_string("log"), NGX_HTTP_USERID_LOG },
    { ngx_string("v1"), NGX_HTTP_USERID_V1 },
    { ngx_string("on"), NGX_HTTP_USERID_ON },
    { ngx_null_string, 0 }
};

2 userid_service 处理函数是ngx_conf_set_num_slot

3 userid_name,userid_domain,userid_path 这三个是cookie相关的,名字,域名。路径。处理函数是ngx_conf_set_str_slot。其中域名指令配置了校验函数。

char *ngx_conf_check_domain(ngx_conf_t *cf, void *post, void *data)
{
    ngx_str_t  *domain = data;

    if (domain->len == 4 && ngx_strcmp(domain->data, "none") == 0) {
        domain->len = 1;
        domain->data = (u_char *) ".";
    }

    return NGX_CONF_OK;
}

4 userid_expires 该指令设置cookie的过期时间。

char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_userid_conf_t *ucf = conf;

    ngx_str_t   *value;

    if (ucf->expires != NGX_CONF_UNSET) {
        return "is duplicate";
    }

    value = cf->args->elts;
    // 值是max代表cookie永不过期
    if (ngx_strcmp(value[1].data, "max") == 0) {
        ucf->expires = NGX_HTTP_USERID_MAX_EXPIRES;
        return NGX_CONF_OK;
    }
    // off代表会话cookie
    if (ngx_strcmp(value[1].data, "off") == 0) {
        ucf->expires = 0;
        return NGX_CONF_OK;
    }
    // 设置一个固定的时间
    ucf->expires = ngx_parse_time(&value[1], 1);
    if (ucf->expires == NGX_ERROR) {
        return "invalid value";
    }

    if (ucf->expires == NGX_PARSE_LARGE_TIME) {
        return "value must be less than 68 years";
    }

    return NGX_CONF_OK;
}

因为http子模块很多,就先分析到这。后面再继续分析其他的子模块。

本文分享自微信公众号 - 编程杂技(theanarkh)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • nginx1.17.9源码分析之管理配置的结构体(1)

    之前对nginx0.1.0版本进行了部分代码的分析,接下来的时间,打算以最新版的源码为基础,重新开始分析nginx的实现。这是第一篇。

    theanarkh
  • 从nginx1.17.9源码理解nginx -s reload

    使用nginx的时候,我们经常会使用nginx -s reload命令重启。下面我们就分析一下,执行这个命令的时候,nginx里发生了什么?我们从nginx的m...

    theanarkh
  • nginx0.1.0之event模块初始化源码分析(3)

    前面已经分析了event初始化的整体流程和第一步create_conf,接下来看一下第二步ngx_conf_parse。这里不分析该函数的代码,该函数主要是遍历...

    theanarkh
  • 高并发服务器的设计--架构与瓶颈的设计

    做架构设计,难免有时候被人问及系统的瓶颈在哪,那首先来了解下什么是瓶颈? 打个形象的比方,人的嘴巴可以吞下一整个面包,但是却咽不下去,因为食管不给力,它比较细,...

    李海彬
  • nginx0.1.0之event模块初始化源码分析(3)

    前面已经分析了event初始化的整体流程和第一步create_conf,接下来看一下第二步ngx_conf_parse。这里不分析该函数的代码,该函数主要是遍历...

    theanarkh
  • Netty+MUI从零打造一个仿微信的高性能聊天项目,兼容iPhone/iPad/安卓

    要说到微信,我相信是个人都应该知道,几乎人人都会安装这款社交APP吧,它已经成为了我们生活中不可缺少的一份子。

    风间影月
  • 腾讯云Serverless架构安装Python依赖的小工具(包括对外的API,基于SCF)

    很久很久之前,做了一个在线下载依赖包的工具,但是由于是放在了CVM上,收费比较高昂,而自己比较清贫,所以没能坚持多久,那个工具就被我下掉了,后来有小伙伴就给我留...

    Dfounderliu
  • 在 Vue 中使用 TypeScript 的一些思考(实践)

    两种形式输出结果一致,同是创建一个 Vue 子类,但在书写组件选项如 props,mixin 时,有些不同。特别是当你使用 Vue.extend() 时,为了让...

    三毛
  • 基于Django的电子商务网站开发(连载24)

    在这里r'^view_goods/(?P<good_id>[0-9]+)/$'表示view_goods/后面跟着一个由数字组成的字符串,这个字符串定义为变量go...

    小老鼠
  • Ionic3 开发流程

    简单介绍自己使用Ionic3开发的过程,涉及到的知识点如下: Angular Ionic Cordova ES6 TypeScript Scss ...

    spilledyear

扫码关注云+社区

领取腾讯云代金券