syntax: ngx.var.VAR_NAME context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*
讀或者寫 Nginx 變量值:nginx
value = ngx.var.some_nginx_variable_name ngx.var.some_nginx_variable_name = value
注:僅僅是已經定義了的 Nginx 變量能夠被寫入:api
location /foo { set $my_var ''; # this line is required to create $my_var at config time content_by_lua_block { ngx.var.my_var = 123 ... } }
也就是說,沒法動態建立 Nginx 變量。數組
一些特殊的變量(如 $args 和 $limit_rate)能夠被分配一個值,其餘許多變量不是,如 $query_string, $arg_PARAMETER, $http_NAME。緩存
Nginx 正則組捕獲變量 $1, $2, $3 等等,也能夠經過編寫 ngx.var[1], ngx.var[2], ngx.var[3] 等來讀取此接口。socket
設置 ngx.var.Foo 爲 nil 值將會刪除 $Foo Nginx 變量:函數
ngx.var.args = nil
警告:當讀取 Nginx 變量時,Nginx 將在每一個請求的內存池中分配內存,在請求結束時才釋放。所以當須要在 Lua 代碼中重複讀取 Nginx 變量時,緩存該 Nginx 變量到你本身的 Lua 變量中,以下:性能
local val = ngx.var.some_var --- use the val repeatedly later
爲了防止在當前請求的生命週期內發生內存泄露,緩存結果的另外一種方法是使用 ngx.ctx 表。fetch
未定義的 Nginx 變量值爲 nil,而定義了但沒初始化的 Nginx 變量值爲 Lua 的空字符串。ui
該 API 須要相對昂貴的元方法調用,建議避免在 hot 代碼路徑中使用它。this
在 OpenResty 中,經過該函數將 ngx.var 相關函數註冊進來。
void ngx_http_lua_inject_variable_api(lua_State *L) { /* {{{ regoster reference maps */ /* 建立一張空表,並將其壓棧。等價於 lua_createtable(L, 0, 0) */ lua_newtable(L); /* ngx.var */ /* 建立一張新的空表並壓棧。 * 第二個參數建議了這張表做爲數組使用時會有多少個元素; * 第三個參數建議了這張表可能擁有多少數組以外的元素,即元方法。 * Lua 會使用這些建議來分配這張表。若是你知道這張表用途的更多信息,預分配 * 能夠提升性能。不然,你可使用函數 lua_newtable */ lua_createtable(L, 0, 2 /* nrec */); /* metatable for .var */ /* 將一個 C 函數壓棧。這個函數接收一個 C 函數指針,並將一個類型爲 function 的 Lua 值 * 壓棧。當這個棧頂的值被調用時,將觸發對應的 C 函數。 * 註冊到 Lua 中的任何函數都必須遵循正確的協議來接收參數和返回值(參見 lua_CFunction) */ lua_pushcfunction(L, ngx_http_lua_var_get); /* 作一個等價於 t[k] = v 的操做,這裏的 t 是給出的索引處的值,而 v 是棧頂的那個值。 * 這個函數將把這個值彈出棧。跟在 lua 中同樣,這個函數可能觸發一個 "newindex" 事件的元方法 */ lua_setfield(L, -2, "__index"); lua_pushcfunction(L, ngx_http_lua_var_set); lua_setfield(L, -2, "__newindex"); /* 把一張表彈出棧,並將其設爲給定索引處(這個爲-2)的值的元表 */ lua_setmetatable(L, -2); lua_setfield(L, -2, "var"); }
當 ngx.var 表中不存在 key 對應的值時,則會調用該函數。
/** * Get nginx internal variables content * * @retval Always return a string or nil on Lua stack. Return nil when failed * to get content, and actual content string when found the specified variable. * @seealso ngx_http_lua_var_set */ static int ngx_http_lua_var_get(lua_State *L) { ngx_http_request_t *r; u_char *p, *lowcase; size_t len; ngx_uint_t hash; ngx_str_t name; ngx_http_variable_value_t *vv; #if (NGX_PCRE) u_char *val; ngx_uint_t n; LUA_NUMBER index; int *cap; #endif /* 獲取當前請求上下文結構體 */ r = ngx_http_lua_get_req(L); if (r == NULL) { /* 拋出一個錯誤。錯誤消息的格式由 fmt 給出。後面需提供若干參數,這些參數 * 遵循 lua_pushfstring 中的規則。若是能得到相關信息,它還會在消息前面 * 加上錯誤發生時的文件名及行號。 * 這個函數永遠不會返回。可是在 C 函數中一般遵循慣用法: * return luaL_error(args) */ return luaL_error(L, "no request object found"); } /* 檢測當前請求的 socket 套接字是否有效 */ ngx_http_lua_check_fake_request(L, r); #if (NGX_PCRE) /* 返回給定有效索引處值的類型,當索引無效(或沒法訪問)時返回 LUA_TNONE。 * lua_type 返回的類型被編碼爲一些在 lua.h 中定義的常量: LUA_TNIL, LUA_TNUMBER, * LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, * LUA_TTHREAD, LUA_TLIGHTUSERDATA */ if (lua_type(L, -1) == LUA_TNUMBER) { /* it is a regex capturing variable */ /* 把給定索引處的 Lua 值轉換爲 lua_Number 這樣一個 C 類型。這個 Lua 值 * 必須是一個數字或是一個可轉換爲數字的字符串;不然,lua_tonumber 返回 0 */ index = lua_tonumber(L, -1); if (index <= 0) { /* 將空值壓棧 */ lua_pushnil(L); return 1; } n = (ngx_uint_t) index * 2; dd("n = %d, ncaptures = %d", (int) n, (int) r->ncaptures); if (r->captures == NULL || r->captures_data == NULL || n >= r->ncaptures) { lua_pushnil(L); return 1; } /* n >= 0 && n < r->ncaptures */ cap = r->captures; p = r->captures_data; val = &p[cap[n]]; /* 把指針 val 指向的長度爲 len 的字符串壓棧。Lua 對這個字符串作一個內部副本 * (或是複用一個副本),所以 val 處的內存在函數返回後,能夠釋放掉或是馬上 * 重用於其餘用途。字符串內能夠是任意二進制數據,包括零字符。 * 返回內部副本的指針 */ lua_pushlstring(L, (const char *) val, (size_t) (cap[n + 1] - cap[n])); return 1; } #endif if (lua_type(L, -1) != LUA_TSTRING) { return luaL_error(L, "bad variable name"); } /* 把給定索引處的 Lua 值轉換爲一個 C 字符串。若是 len 不爲 NULL,它還把字符串 * 長度設到 *len 中。這個 Lua 值必須是一個字符串或是一個數字;不然返回 NULL。 * 若是值是一個數字,lua_tolstring 還會把堆棧中的那個值的實際類型轉換爲一個 * 字符串。(當遍歷一張表的時候,若把 lua_tolstring 做用在鍵上,這個轉換有可能 * 致使 lua_next 弄錯) * lua_tolstring 返回一個已對齊指針指向 Lua 狀態機中的字符串。這個字符串總能 * 保證(C 要求的)最後一個字符爲零('\0'),並且它容許在字符串內包含多個這樣的零 * 由於 Lua 中可能發生垃圾收集,因此不保證 lua_tolstring 返回的指針,在對應的值 * 從堆棧中移除後依然有效 */ p = (u_char *) lua_tolstring(L, -1, &len); /* 這個函數分配一塊指定大小的內存塊,把內存塊地址做爲一個徹底用戶數據壓棧, * 並返回這個地址。宿主程序能夠隨意使用這塊內存 */ lowcase = lua_newuserdata(L, len); /* 將 p 指向 len 長度字符串拷貝到 lowcase,同時轉爲小寫,並返回一個hash值 */ hash = ngx_hash_strlow(lowcase, p, len); /* 設置該 Nginx 內部變量的長度和名稱 */ name.len = len; name.data = lowcase; /* 根據 name 獲取對應的值 */ vv = ngx_http_get_variable(r, &name, hash); if (vv == NULL || vv->not_found) { lua_pushnil(L); return 1; } lua_pushlstring(L, (const char *) vv->data, (size_t) vv->len); return 1; }
獲取當前請求上下文結構體。
#define ngx_http_lua_req_key "__ngx_req" static ngx_inline ngx_http_request_t * ngx_http_lua_get_req(lua_State *L) { ngx_http_request_t *r; /* 把全局變量 name(這裏爲 ngx_http_lua_req_key) 裏的值壓棧,返回該值的類型 */ lua_getglobal(L, ngx_http_lua_req_key); /* 若是給定索引處的值是一個徹底用戶數據,函數返回其內存塊的地址。若是隻是一個 * 輕量用戶數據,那麼就返回它表示的指針。不然,返回 NULL */ r = lua_touserdata(L, -1); /* 從棧中彈出 n 個元素(這裏爲 1 個) */ lua_pop(L, 1); return r; }
檢測當前鏈接的 socket 套接字是否有效。
#define ngx_http_lua_check_fake_request(L, r) \ if ((r)->connection->fd == (ngx_socket_t) -1) { \ return luaL_error(L, "API disabled in the current context"); \ }
根據變量名獲取該變量對應的值。
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->variable_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)); 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 v; } return NULL; } vv->not_found = 1; return vv; }
/** * Set nginx internal variable content * * @retval Always return a boolean on Lua stack. Return true when variable * content was modified successfully, false otherwise. * @seealso ngx_http_lua_var_get */ static int ngx_http_lua_var_set(lua_State *L) { ngx_http_variable_t *v; ngx_http_variable_value_t *vv; ngx_http_core_main_conf_t *cmcf; u_char *p, *lowcase, *val; size_t len; ngx_str_t name; ngx_uint_t hash; ngx_http_request_t *r; int value_type; const char *msg; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); /* we skip the first argument that is the table */ /* we read the variable name */ if (lua_type(L, 2) != LUA_TSTRING) { /* 拋出一個錯誤,該函數永不返回,如下是一般寫法 */ return luaL_error(L, "bad variable name") } /* 將指定索引處的 Lua 值轉換爲一個 C 字符串,轉換後的長度經過 len 返回 * 該 Lua 值必須是一個數字或字符串,不然返回 NULL */ p = (u_char *) lua_tolstring(L, 2, &len); /* 分配一塊指定大小的內存塊,並把內存地址做爲一個徹底用戶數據壓棧,並返回這個地址 */ lowcase = lua_newuserdata(L, len + 1); hash = ngx_hash_strlow(lowcase, p, len); lowcase[len] = '\0'; name.len = len; name.data = lowcase; /* we read the variable new value */ /* 返回指定有效索引處的 Lua 值的類型,當索引無效(或沒法訪問)時返回 LUA_TNONE */ value_type = lua_type(L, 3); switch (value_type) { case LUA_TNUMBER: case LUA_TSTRING: /* 檢查指定索引處的 Lua 值是不是一個字符串,並返回該字符串,長度值經過 len 返回 */ p = (u_char *) luaL_checklstring(L, 3, &len); val = ngx_palloc(r->pool, len); if (val == NULL) { return luaL_error(L, "memory allocation error"); } ngx_memcpy(val, p, len); break; case LUA_TNIL: /* undef the variable */ val = NULL; len = 0; break; default: /* 將一個格式化後的字符串壓棧,而後返回該字符串的指針。它和 C 函數 printf 比較像, * 但有一些重要的區別: * - 不須要爲結果分配空間:其結果是一個 Lua 字符串,由 Lua 來關心其內存分配 * (經過 gc 釋放內存) * - 該轉換很是的受限。不支持符號、寬度、精度。轉換符只支持 '%%'(插入一個字符'%'), * '%s'(插入一個帶零終止符的字符串,沒有長度限制),'%f'(插入一個 lua_Nubmer), * '%L'(插入一個 lua_Integer),'%p'(插入一個指針或一個十六進制數), * '%d'(插入一個 int),'%c'(插入一個用 int 表示的單字節字符), * '%U'(插入一個用 long int 表示的 UTF-8 字) */ msg = lua_pushfstring(L, "string, number, or nil expected, " "but got %s", lua_typename(L, value_type)); /* 拋出上一個函數壓棧的錯誤報告。使用下列標準信息幷包含了一段 extramsg 做爲註解: * bad argument #arg to 'funcname' (extramsg) * 該函數永不返回 */ return luaL_argerror(L, 1, msg); } /* we fetch the variable itself */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); v = ngx_hash_find(&cmcf->variable_hash, hash, name.data, name.len); if (v) { if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { return luaL_error(L, "variable \"%s\" not changeable", lowcase); } if (v->set_handler) { dd("set variable with set_handler"); vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { return luaL_error(L, "no memory"); } if (value_type == LUA_TNIL) { vv->valid = 0; vv->not_found = 1; vv->no_cacheable = 0; vv->data = NULL; vv->len = 0; } else { vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = val; vv->len = len; } v->set_handler(r, vv, v->data); return 0; } if (v->flags & NGX_HTTP_VAR_INDEXED) { vv = &r->variables[v->index]; dd("set indexed variable"); if (value_type == LUA_TNIL) { vv->valid = 0; vv->not_found = 1; vv->no_cacheable = 0; vv->data = NULL; vv->len = 0; } else { vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = val; vv->len = len; } return 0; } return luaL_error(L, "variable \"%s\" cannot be assigned a value", lowcase); } /* variable not found */ return luaL_error(L, "variable \"%s\" not found for writing; " "maybe it is a built-in variable that is not changeable " "or you forgot to use \"set $%s '';\" " "in the config file to define it first", lowcase, lowcase); }