nginx加载var变量
以mtls中的校验结果ngx.var.ssl_client_verify 为例
首先ngx.var变量名结构体定义如下
struct ngx_http_variable_s {
ngx_str_t name; /* must be first to build the hash */
ngx_http_set_variable_pt set_handler;
ngx_http_get_variable_pt get_handler;
uintptr_t data;
ngx_uint_t flags;
ngx_uint_t index;
};
{ ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },
其中get_handler方法为ngx_http_ssl_variable
该方法首先执行data函数("ssl_client_verify"中data函数为ngx_ssl_get_client_verify),并从中获取数据,将返回的数据赋值给var变量,从data的入参可以看出,当获取一个请求的var变量赋值点时,已经建立 了ssl连接
static ngx_int_t
ngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
ngx_str_t s;
// ssl连接存在
if (r->connection->ssl) {
if (handler(r->connection, r->pool, &s) != NGX_OK) {
return NGX_ERROR;
}
v->len = s.len;
v->data = s.data;
if (v->len) {
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
}
v->not_found = 1;
return NGX_OK;
}
ngx_ssl_get_client_verify调用openssl函数,获取对端的证书,服务器对端证书即为客户端证书
ngx_int_t
ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
X509 *cert;
long rc;
const char *str;
cert = SSL_get_peer_certificate(c->ssl->connection);
// 如果证书不存在,ssl_client_verify为NONE
if (cert == NULL) {
ngx_str_set(s, "NONE");
return NGX_OK;
}
X509_free(cert);
rc = SSL_get_verify_result(c->ssl->connection);
if (rc == X509_V_OK) {
if (ngx_ssl_ocsp_get_status(c, &str) == NGX_OK) {
ngx_str_set(s, "SUCCESS");
return NGX_OK;
}
} else {
str = X509_verify_cert_error_string(rc);
}
s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str));
if (s->data == NULL) {
return NGX_ERROR;
}
s->len = ngx_sprintf(s->data, "FAILED:%s", str) - s->data;
return NGX_OK;
}
ssl的var变量由数组ngx_http_ssl_vars定义,在ngx_http_ssl_add_variables中调用,该函数执行时间是读取conf配置前置操作,ngx_http_ssl_add_variables主要操作就是挂载变量的get_handler和data函数,并在调用ngx.var.xxx执行get_handler函数
static ngx_int_t
ngx_http_ssl_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_ssl_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static ngx_http_module_t ngx_http_ssl_module_ctx = {
ngx_http_ssl_add_variables, /* preconfiguration */
ngx_http_ssl_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_http_ssl_create_srv_conf, /* create server configuration */
ngx_http_ssl_merge_srv_conf, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
当调用ngx.var.xxx语句时,会调用挂载的handler函数和data函数,获取变量值,具体实现如下:
lua-resty-core中有var.lua 文件
该文件定义如下,首先定义了ngx.var 变量,同时利用元表定义了获取ngx.var.xxx的函数var_get
ngx.var = new_tab(0, 0)
....
local function var_get(self, name)
...
end
local function var_set(self, name, value)
...
end
do
local mt = new_tab(0, 2)
mt.__index = var_get
mt.__newindex = var_set
setmetatable(ngx.var, mt)
end
var_get 通过核心函数ngx_lua_ffi_var_get,调用上述注册的get handler 函数,获取变量的值
local function var_get(self, name)
local r = get_request()
if not r then
error("no request found")
end
local value_len = get_size_ptr()
local rc
if type(name) == "number" then
rc = ngx_lua_ffi_var_get(r, nil, 0, nil, name, value_ptr, value_len,
errmsg)
else
if type(name) ~= "string" then
error("bad variable name", 2)
end
local name_len = #name
local lowcase_buf = get_string_buf(name_len)
rc = ngx_lua_ffi_var_get(r, name, name_len, lowcase_buf, 0, value_ptr,
value_len, errmsg)
end
-- ngx.log(ngx.WARN, "rc = ", rc)
if rc == 0 then -- NGX_OK
return ffi_str(value_ptr[0], value_len[0])
end
if rc == -5 then -- NGX_DECLINED
return nil
end
if rc == -1 then -- NGX_ERROR
error(ffi_str(errmsg[0]), 2)
end
end
int
ngx_http_lua_ffi_var_get(ngx_http_request_t *r, u_char *name_data,
size_t name_len, u_char *lowcase_buf, int capture_id, u_char **value,
size_t *value_len, char **err)
{
ngx_uint_t hash;
ngx_str_t name;
ngx_http_variable_value_t *vv;
#if (NGX_PCRE)
u_char *p;
ngx_uint_t n;
int *cap;
#endif
...
hash = ngx_hash_strlow(lowcase_buf, name_data, name_len);
name.data = lowcase_buf;
name.len = name_len;
dd("variable name: %.*s", (int) name_len, lowcase_buf);
vv = ngx_http_get_variable(r, &name, hash); //调用注册的handler函数获取变量值
if (vv == NULL || vv->not_found) {
return NGX_DECLINED;
}
*value = vv->data;
*value_len = vv->len;
return NGX_OK;
}
ngx_http_variable_value_t *
ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)
{
size_t len;
ngx_uint_t i, n;
ngx_http_variable_t *v;
ngx_http_variable_value_t *vv;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
if (v) {
if (v->flags & NGX_HTTP_VAR_INDEXED) {
return ngx_http_get_flushed_variable(r, v->index);
}
if (ngx_http_variable_depth == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"cycle while evaluating variable \"%V\"", name);
return NULL;
}
ngx_http_variable_depth--;
vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
// 根据name获取挂载的get_handler函数,
if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
ngx_http_variable_depth++;
return vv;
}
ngx_http_variable_depth++;
return NULL;
}
vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
if (vv == NULL) {
return NULL;
}
len = 0;
v = cmcf->prefix_variables.elts;
n = cmcf->prefix_variables.nelts;
for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
if (name->len >= v[i].name.len && name->len > len
&& ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0)
{
len = v[i].name.len;
n = i;
}
}
if (n != cmcf->prefix_variables.nelts) {
if (v[n].get_handler(r, vv, (uintptr_t) name) == NGX_OK) {
return vv;
}
return NULL;
}
vv->not_found = 1;
return vv;
}