在命令行中調用lua執行一條輸出語句, 以下所示。html
$ luajit -e "print('Hello, Lua')" Hello, Lua
在C語言中建立Lua運行環境,執行一樣的Lua語句也至關簡單。nginx
#include <stdio.h> #include <lua.h> #include <lualib.h> #include <lauxlib.h> const char script[] = "print('Hello, Lua!')"; int main(void) { /* 建立一個全局的global_State結構和表明一個協程的lua_State結構,lua_State做爲主協程返回 */ lua_State *L = luaL_newstate(); if (!L) return -1; /* 將print, math,string,table等Lua內置的函數庫註冊到協程中 */ luaL_openlibs(L); /* 加載一段Lua代碼,將其編譯成Lua虛擬機的字節碼 */ int ret = luaL_loadstring(L, script); if (ret != 0) { return -1; } /* 在Lua虛擬機中執行前面加載的Lua代碼 */ //ret = lua_pcall(L, 0, LUA_MULTRET, 0); ret = lua_resume(L, 0); if (ret != 0) { return -1; } lua_close(L); return 0; }
編寫一個簡單的Nginx模塊,在ACCESS階段執行配置中指定的Lua腳本, 主要工做有瀏覽器
模塊名爲ngx_http_test_module,源文件爲ngx_http_test_module.c, 文件內容以下。bash
#include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> #include <lua.h> #include <lualib.h> #include <lauxlib.h> typedef struct { lua_State *vm; ngx_str_t script; } ngx_http_test_loc_conf_t; static ngx_int_t ngx_http_test_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_test_init(ngx_conf_t *cf); static void * ngx_http_test_create_loc_conf(ngx_conf_t *cf); static ngx_command_t ngx_http_test_commands[] = { { ngx_string("access_by_lua"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_test_loc_conf_t, script), NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_test_module_ctx = { NULL, ngx_http_test_init, NULL, NULL, NULL, NULL, ngx_http_test_create_loc_conf, NULL }; ngx_module_t ngx_http_test_module = { NGX_MODULE_V1, &ngx_http_test_module_ctx, ngx_http_test_commands, NGX_HTTP_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; static ngx_int_t ngx_http_test_handler(ngx_http_request_t *r) { ngx_http_test_loc_conf_t *tlcf = ngx_http_get_module_loc_conf(r, ngx_http_test_module); if (tlcf->script.len == 0) { return NGX_DECLINED; } /* 加載一段Lua代碼,將其編譯成Lua虛擬機的字節碼 */ int ret = luaL_loadstring(tlcf->vm, (const char *)tlcf->script.data); if (ret != 0) { return -1; } /* 調用前面加載的Lua代碼 */ ret = lua_resume(tlcf->vm, 0); if (ret != 0) { return -1; } return NGX_DECLINED; } static void * ngx_http_test_create_loc_conf(ngx_conf_t *cf) { ngx_http_test_loc_conf_t *conf = NULL; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_test_loc_conf_t)); if (conf == NULL) return NULL; ngx_str_null(&conf->script); /* 初始化Lua環境 */ /* 建立一個全局的global_State結構和表明一個協程的lua_State結構,lua_State做爲主協程返回 */ lua_State *L = luaL_newstate(); if (!L) return NULL; /* 將print, math,string,table等Lua內置的函數庫註冊到協程中 */ luaL_openlibs(L); conf->vm = L; return conf; } static ngx_int_t ngx_http_test_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; /* 在ACCESS階段掛在回調函數 */ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); *h = ngx_http_test_handler; return NGX_OK; }
ngx_addon_name=ngx_http_test_module HTTP_MODULES="$HTTP_MODULES ngx_http_test_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_test_module.c"
主要是鏈接Lua庫和頭文件,這裏用的是LuaJIT-2.1.0, 若是用的是其餘版本或者lua5.1須要根據須要更改。curl
export LUAJIT_INC=/usr/local/include/luajit-2.1 export LUAJIT_LIB=/usr/local/lib ./configure --with-debug \ --with-cc-opt='-O0 -I /usr/local/include/luajit-2.1' \ --with-ld-opt='-Wl,-rpath,/usr/local/lib -lluajit-5.1' \ --add-module=$HOME/ngx_http_test_module
在location中增長access_by_lua指定執行的Lua代碼。函數
daemon off; events { worker_connections 1024; } http { server { listen 80; server_name localhost; location / { access_by_lua "print('Hello, Lua!')"; root html; index index.html index.htm; } } }
編譯成功後啓動Nginx,用curl或瀏覽器訪問, Nginx會在終端輸出lua
$ ./sbin/nginx Hello, Lua!
若是以daemon方式運行Nginx,可能沒法輸出內容。url
Nginx的ACCESS階段用來控制是否容許訪問,這裏爲Lua增長一個功能,返回403禁止訪問。spa
爲模塊增長一個ngx_http_test_ctx_t結構,保存執行過程當中須要的一些信息。命令行
typedef struct { int status; } ngx_http_test_ctx_t;
有一個statu的成員,執行Lua腳本後檢查status的值, 若是是403的話就返回NGX_HTTP_FORBIDDEN結束請求。
這裏仿照lua-nginx-module的作法,增長一個方法ngx.exit, 在Lua中調用ngx.exit(403)時將status值設置爲403。
那麼如何在Lua中修改status的值呢?Nginx中的ngx_http_request_t結構體保存了請求的全部信息,包括各個模塊的上下文,將這個結構體的指針以lightuserdata的方式保存到lua_State的全局變量中獲取便可。
ngx_http_test_handler中保存ngx_http_request_t指針
/* 將r保存到全局變量中,key爲ngx_http_test_req_key */ lua_pushlightuserdata(tlcf->vm, r); lua_setglobal(tlcf->vm, ngx_http_test_req_key);
ngx.exit API的實現
static int ngx_http_test_ngx_exit(lua_State *L) { int status; status = luaL_checkint(L, 1); ngx_http_request_t *r; lua_getglobal(L, ngx_http_test_req_key); r = lua_touserdata(L, -1); ngx_http_test_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_test_module); ctx->status = status; lua_pushboolean(L, 1); return 1; }
在函數ngx_http_test_create_loc_conf中,建立全局變量ngx, 類型爲table,將ngx["exit"]值設置爲函數ngx_http_test_ngx_exit。完成ngx.exit 的註冊,在Lua腳本中就能夠經過ngx.exit()的方式調用。
static void * ngx_http_test_create_loc_conf(ngx_conf_t *cf) { ngx_http_test_loc_conf_t *conf = NULL; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_test_loc_conf_t)); if (conf == NULL) return NULL; ngx_str_null(&conf->script); /* 初始化Lua環境 */ /* 建立一個全局的global_State結構和表明一個協程的lua_State結構,lua_State做爲主協程返回 */ lua_State *L = luaL_newstate(); if (!L) return NULL; /* 將print, math,string,table等Lua內置的函數庫註冊到協程中 */ luaL_openlibs(L); /* 註冊ngx API */ lua_createtable(L, 0, 0); lua_pushcfunction(L, ngx_http_test_ngx_exit); lua_setfield(L, -2, "exit"); lua_setglobal(L, "ngx"); conf->vm = L; return conf; }
#include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> #include <lua.h> #include <lualib.h> #include <lauxlib.h> typedef struct { lua_State *vm; ngx_str_t script; } ngx_http_test_loc_conf_t; typedef struct { int status; } ngx_http_test_ctx_t; static ngx_int_t ngx_http_test_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_test_init(ngx_conf_t *cf); static void * ngx_http_test_create_loc_conf(ngx_conf_t *cf); static ngx_command_t ngx_http_test_commands[] = { { ngx_string("access_by_lua"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_test_loc_conf_t, script), NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_test_module_ctx = { NULL, ngx_http_test_init, NULL, NULL, NULL, NULL, ngx_http_test_create_loc_conf, NULL }; ngx_module_t ngx_http_test_module = { NGX_MODULE_V1, &ngx_http_test_module_ctx, ngx_http_test_commands, NGX_HTTP_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; #define ngx_http_test_req_key "__ngx_req" static ngx_int_t ngx_http_test_handler(ngx_http_request_t *r) { ngx_http_test_loc_conf_t *tlcf = ngx_http_get_module_loc_conf(r, ngx_http_test_module); if (tlcf->script.len == 0) { return NGX_DECLINED; } ngx_http_test_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_test_module); if (ctx == NULL) { ctx = ngx_pcalloc(r->pool, sizeof(*ctx)); ngx_http_set_ctx(r, ctx, ngx_http_test_module); } /* 將r保存到全局變量中,key爲ngx_http_test_req_key */ lua_pushlightuserdata(tlcf->vm, r); lua_setglobal(tlcf->vm, ngx_http_test_req_key); /* 加載一段Lua代碼,將其編譯成Lua虛擬機的字節碼 */ int ret = luaL_loadstring(tlcf->vm, (const char *)tlcf->script.data); if (ret != 0) { return NGX_ERROR; } /* 調用前面加載的Lua代碼 */ ret = lua_resume(tlcf->vm, 0); if (ret != 0) { return NGX_ERROR; } if (ctx->status == 403) { return NGX_HTTP_FORBIDDEN; } return NGX_DECLINED; } static int ngx_http_test_ngx_exit(lua_State *L) { int status; status = luaL_checkint(L, 1); ngx_http_request_t *r; lua_getglobal(L, ngx_http_test_req_key); r = lua_touserdata(L, -1); ngx_http_test_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_test_module); ctx->status = status; lua_pushboolean(L, 1); return 1; } static void * ngx_http_test_create_loc_conf(ngx_conf_t *cf) { ngx_http_test_loc_conf_t *conf = NULL; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_test_loc_conf_t)); if (conf == NULL) return NULL; ngx_str_null(&conf->script); /* 初始化Lua環境 */ /* 建立一個全局的global_State結構和表明一個協程的lua_State結構,lua_State做爲主協程返回 */ lua_State *L = luaL_newstate(); if (!L) return NULL; /* 將print, math,string,table等Lua內置的函數庫註冊到協程中 */ luaL_openlibs(L); /* 註冊ngx API */ lua_createtable(L, 0, 0); lua_pushcfunction(L, ngx_http_test_ngx_exit); lua_setfield(L, -2, "exit"); lua_setglobal(L, "ngx"); conf->vm = L; return conf; } static ngx_int_t ngx_http_test_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; /* 在ACCESS階段掛在回調函數 */ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); *h = ngx_http_test_handler; return NGX_OK; }
location / { access_by_lua "ngx.exit(403)"; root html; index index.html index.htm; }
訪問時直接返回403 Forbidden.
$ curl 127.0.0.1 <html> <head><title>403 Forbidden</title></head> <body bgcolor="white"> <center><h1>403 Forbidden</h1></center> <hr><center>nginx/1.10.1</center> </body> </html>
一樣的方法也能夠用於獲取請求的URL, 頭部等信息。如獲取請求方法的實現
static int ngx_http_test_req_get_method(lua_State *L) { int status; status = luaL_checkint(L, 1); ngx_http_request_t *r; lua_getglobal(L, ngx_http_test_req_key); r = lua_touserdata(L, -1); lua_pushlstring(L, (char *) r->method_name.data, r->method_name.len); return 1; }