本文讲解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
char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *p = conf;
ngx_int_t i, j, dup, invalid;
ngx_str_t *value, line;
ngx_http_cache_t *c;
ngx_http_cache_hash_t *ch, **chp;
chp = (ngx_http_cache_hash_t **) (p + cmd->offset);
if (*chp) {
return "is duplicate";
}
// ch指向ngx_http_cache_hash_t结构体
if (!(ch = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_hash_t)))) {
return NGX_CONF_ERROR;
}
// 挂载
*chp = ch;
dup = 0;
invalid = 0;
value = cf->args->elts;
// 格式 value[i] => a=1
for (i = 1; i < cf->args->nelts; i++) {
// 第二个字符是=
if (value[i].data[1] != '=') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%s\"", value[i].data);
return NGX_CONF_ERROR;
}
switch (value[i].data[0]) {
case 'h':
// 已经赋值过
if (ch->hash) {
dup = 1;
break;
}
// 把等号后面的值转成数字
ch->hash = ngx_atoi(value[i].data + 2, value[i].len - 2);
if (ch->hash == (size_t) NGX_ERROR || ch->hash == 0) {
invalid = 1;
break;
}
continue;
case 'n':
if (ch->nelts) {
dup = 1;
break;
}
ch->nelts = ngx_atoi(value[i].data + 2, value[i].len - 2);
if (ch->nelts == (size_t) NGX_ERROR || ch->nelts == 0) {
invalid = 1;
break;
}
continue;
case 'l':
if (ch->life) {
dup = 1;
break;
}
line.len = value[i].len - 2;
line.data = value[i].data + 2;
ch->life = ngx_parse_time(&line, 1);
if (ch->life == NGX_ERROR || ch->life == 0) {
invalid = 1;
break;
}
continue;
case 'u':
if (ch->update) {
dup = 1;
break;
}
line.len = value[i].len - 2;
line.data = value[i].data + 2;
ch->update = ngx_parse_time(&line, 1);
if (ch->update == NGX_ERROR || ch->update == 0) {
invalid = 1;
break;
}
continue;
default:
invalid = 1;
}
if (dup) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate value \"%s\"", value[i].data);
return NGX_CONF_ERROR;
}
if (invalid) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%s\"", value[i].data);
return NGX_CONF_ERROR;
}
}
// 分配一个元素是ngx_http_cache_t的二维数组
ch->elts = ngx_pcalloc(cf->pool,
ch->hash * ch->nelts * sizeof(ngx_http_cache_t));
if (ch->elts == NULL) {
return NGX_CONF_ERROR;
}
// 初始化二维数组的字段
for (i = 0; i < (ngx_int_t) ch->hash; i++) {
// ch->elts是一个二维数组,元素是ngx_http_cache_t,c等于每个一维数组的首地址
c = ch->elts + i * ch->nelts;
// 初始化该一维数组的fd字段,长度是ch->nelts
for (j = 0; j < (ngx_int_t) ch->nelts; j++) {
c[j].fd = NGX_INVALID_FILE;
}
}
return NGX_CONF_OK;
}
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子模块很多,就先分析到这。后面再继续分析其他的子模块。
欢迎关注公众号