From 659ebfdd16fb7d79c96dbde2075e0af80a2019f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Sun, 13 Sep 2009 17:54:16 +0200 Subject: [PATCH] Add core lua support to handle requests --- include/lighttpd/actions_lua.h | 7 +- include/lighttpd/condition_lua.h | 6 +- include/lighttpd/core_lua.h | 73 +++++++ include/lighttpd/server.h | 11 +- src/CMakeLists.txt | 10 + src/main/Makefile.am | 25 ++- src/main/actions_lua.c | 108 +++++++++- src/main/chunk_lua.c | 108 ++++++++++ src/main/condition_lua.c | 6 +- src/main/config_lua.c | 42 +--- src/main/connection_lua.c | 44 ++++ src/main/core_lua.c | 201 ++++++++++++++++++ src/main/environment_lua.c | 152 ++++++++++++++ src/main/http_headers_lua.c | 240 +++++++++++++++++++++ src/main/physical_lua.c | 165 +++++++++++++++ src/main/request_lua.c | 348 +++++++++++++++++++++++++++++++ src/main/response_lua.c | 160 ++++++++++++++ src/main/server.c | 23 ++ src/main/value_lua.c | 7 +- src/main/virtualrequest_lua.c | 259 +++++++++++++++++++++++ src/modules/mod_accesslog.c | 4 +- 21 files changed, 1947 insertions(+), 52 deletions(-) create mode 100644 include/lighttpd/core_lua.h create mode 100644 src/main/chunk_lua.c create mode 100644 src/main/connection_lua.c create mode 100644 src/main/core_lua.c create mode 100644 src/main/environment_lua.c create mode 100644 src/main/http_headers_lua.c create mode 100644 src/main/physical_lua.c create mode 100644 src/main/request_lua.c create mode 100644 src/main/response_lua.c create mode 100644 src/main/virtualrequest_lua.c diff --git a/include/lighttpd/actions_lua.h b/include/lighttpd/actions_lua.h index d87f0ec..418e8d6 100644 --- a/include/lighttpd/actions_lua.h +++ b/include/lighttpd/actions_lua.h @@ -4,7 +4,10 @@ #include #include -liAction* lua_get_action(lua_State *L, int ndx); -int lua_push_action(liServer *srv, lua_State *L, liAction *a); +LI_API liAction* lua_get_action(lua_State *L, int ndx); +LI_API int lua_push_action(liServer *srv, lua_State *L, liAction *a); + +/* create new action from lua function */ +LI_API liAction* lua_make_action(liServer *srv, lua_State *L, int ndx); #endif diff --git a/include/lighttpd/condition_lua.h b/include/lighttpd/condition_lua.h index cb14da2..1d7caad 100644 --- a/include/lighttpd/condition_lua.h +++ b/include/lighttpd/condition_lua.h @@ -4,9 +4,9 @@ #include #include -liCondition* lua_get_condition(lua_State *L, int ndx); -int lua_push_condition(liServer *srv, lua_State *L, liCondition *c); +LI_API liCondition* lua_get_condition(lua_State *L, int ndx); +LI_API int lua_push_condition(liServer *srv, lua_State *L, liCondition *c); -void lua_push_lvalues_dict(liServer *srv, lua_State *L); +LI_API void lua_push_lvalues_dict(liServer *srv, lua_State *L); #endif diff --git a/include/lighttpd/core_lua.h b/include/lighttpd/core_lua.h new file mode 100644 index 0000000..481d31e --- /dev/null +++ b/include/lighttpd/core_lua.h @@ -0,0 +1,73 @@ +#ifndef _LIGHTTPD_CORE_LUA_H_ +#define _LIGHTTPD_CORE_LUA_H_ + +#include +#include + +#define li_lua_lock(srv) g_mutex_lock((srv)->lualock); +#define li_lua_unlock(srv) g_mutex_unlock((srv)->lualock); + +LI_API void lua_init_chunk_mt(lua_State *L); +LI_API liChunk* lua_get_chunk(lua_State *L, int ndx); +LI_API int lua_push_chunk(lua_State *L, liChunk *c); +LI_API liChunkQueue* lua_get_chunkqueue(lua_State *L, int ndx); +LI_API int lua_push_chunkqueue(lua_State *L, liChunkQueue *cq); + +LI_API void lua_init_connection_mt(lua_State *L); +LI_API liConnection* lua_get_connection(lua_State *L, int ndx); +LI_API int lua_push_connection(lua_State *L, liConnection *con); + +LI_API void lua_init_environment_mt(lua_State *L); +LI_API liEnvironment* lua_get_environment(lua_State *L, int ndx); +LI_API int lua_push_environment(lua_State *L, liEnvironment *env); + +LI_API void lua_init_http_headers_mt(lua_State *L); +LI_API liHttpHeaders* lua_get_http_headers(lua_State *L, int ndx); +LI_API int lua_push_http_headers(lua_State *L, liHttpHeaders *headers); + +LI_API void lua_init_physical_mt(lua_State *L); +LI_API liPhysical* lua_get_physical(lua_State *L, int ndx); +LI_API int lua_push_physical(lua_State *L, liPhysical *phys); + +LI_API void lua_init_request_mt(lua_State *L); +LI_API liRequest* lua_get_request(lua_State *L, int ndx); +LI_API int lua_push_request(lua_State *L, liRequest *req); + +LI_API liRequestUri* lua_get_requesturi(lua_State *L, int ndx); +LI_API int lua_push_requesturi(lua_State *L, liRequestUri *uri); + +LI_API void lua_init_response_mt(lua_State *L); +LI_API liResponse* lua_get_response(lua_State *L, int ndx); +LI_API int lua_push_response(lua_State *L, liResponse *resp); + +LI_API void lua_init_vrequest_mt(lua_State *L); +LI_API liVRequest* lua_get_vrequest(lua_State *L, int ndx); +LI_API int lua_push_vrequest(lua_State *L, liVRequest *vr); + + +/* return 1 if value is found in mt (on top of the stack), 0 if it is not found (stack balance = 0) + * table, key on stack at pos 0 and 1 (i.e. __index metho) + */ +LI_API int li_lua_metatable_index(lua_State *L); + +LI_API void li_lua_init(liServer *srv, lua_State *L); + +LI_API int li_lua_push_traceback(lua_State *L, int narg); + +LI_API void li_lua_restore_globals(lua_State *L); +LI_API void li_lua_new_globals(lua_State *L); + +/* joinWith " " (map tostring parameter[from..to]) */ +LI_API GString* lua_print_get_string(lua_State *L, int from, int to); + +/* pairs() for a GHashTable GString -> GString: + * Don't modify the hastable while iterating: + * - no new keys + * - no delete + * Modifying values is fine; g_hash_table_insert() as long as the key already exists too. + * The returned "next" function has an internal state, it ignores the table/state and previous key parameter. + * returns: , nil, nil on the stack (and 3 as c function) + */ +LI_API int li_lua_ghashtable_gstring_pairs(lua_State *L, GHashTable *ht); + +#endif diff --git a/include/lighttpd/server.h b/include/lighttpd/server.h index c72c994..1975528 100644 --- a/include/lighttpd/server.h +++ b/include/lighttpd/server.h @@ -1,8 +1,14 @@ #ifndef _LIGHTTPD_SERVER_H_ #define _LIGHTTPD_SERVER_H_ +#ifndef _LIGHTTPD_BASE_H_ +#error Please include instead of this file +#endif + +struct lua_State; + #ifndef LIGHTTPD_SERVER_MAGIC -#define LIGHTTPD_SERVER_MAGIC ((guint)0x12AB34CD) +# define LIGHTTPD_SERVER_MAGIC ((guint)0x12AB34CD) #endif typedef gboolean (*liConnectionNewCB)(liConnection *con); @@ -41,6 +47,9 @@ struct liServer { liServerState state, dest_state; /** atomic access */ liAngelConnection *acon; + GMutex *lualock; + struct lua_State *L; /** NULL if compiled without Lua */ + liWorker *main_worker; guint worker_count; GArray *workers; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 80ecd12..6ed07bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -182,6 +182,16 @@ SET(LIGHTTPD_SHARED_SRC ${LIGHTTPD_SHARED_SRC} condition_lua.c config_lua.c value_lua.c + + chunk_lua.c + core_lua.c + connection_lua.c + environment_lua.c + http_headers_lua.c + physical_lua.c + request_lua.c + response_lua.c + virtualrequest_lua.c ) ENDIF(WITH_LUA) diff --git a/src/main/Makefile.am b/src/main/Makefile.am index 36039ca..961143f 100644 --- a/src/main/Makefile.am +++ b/src/main/Makefile.am @@ -38,18 +38,27 @@ lighttpd_shared_src= \ \ plugin_core.c -if USE_LUA -lighttpd_shared_src+= \ +lua_src= \ actions_lua.c \ condition_lua.c \ config_lua.c \ - value_lua.c + value_lua.c \ + \ + chunk_lua.c \ + connection_lua.c \ + core_lua.c \ + environment_lua.c \ + http_headers_lua.c \ + physical_lua.c \ + request_lua.c \ + response_lua.c \ + virtualrequest_lua.c + + +if USE_LUA +lighttpd_shared_src+=$(lua_src) endif -EXTRA_lighttpd_SOURCES=\ - actions_lua.c \ - condition_lua.c \ - config_lua.c \ - value_lua.c +EXTRA_lighttpd_SOURCES=$(lua_src) BUILT_SOURCES=config_parser.c http_request_parser.c http_response_parser.c url_parser.c diff --git a/src/main/actions_lua.c b/src/main/actions_lua.c index b3e4169..bd0c061 100644 --- a/src/main/actions_lua.c +++ b/src/main/actions_lua.c @@ -1,10 +1,11 @@ #include +#include #include #include -#define LUA_ACTION "action*" +#define LUA_ACTION "liAction*" liAction* lua_get_action(lua_State *L, int ndx) { if (!lua_isuserdata(L, ndx)) return NULL; @@ -43,3 +44,108 @@ int lua_push_action(liServer *srv, lua_State *L, liAction *a) { lua_setmetatable(L, -2); return 1; } + +typedef struct lua_action_param lua_action_param; +struct lua_action_param { + int func_ref; +}; + +typedef struct lua_action_ctx lua_action_ctx; +struct lua_action_ctx { + int g_ref; +}; + +static liHandlerResult lua_action_func(liVRequest *vr, gpointer param, gpointer *context) { + lua_action_param *par = param; + lua_action_ctx *ctx = *context; + liServer *srv = vr->wrk->srv; + lua_State *L = srv->L; + liHandlerResult res = LI_HANDLER_GO_ON; + int errfunc; + + li_lua_lock(srv); + + lua_rawgeti(L, LUA_REGISTRYINDEX, par->func_ref); + lua_pushvalue(L, -1); + + lua_getfenv(L, -1); + if (!ctx) { + *context = ctx = g_slice_new0(lua_action_ctx); + lua_newtable(L); + lua_pushvalue(L, -1); + ctx->g_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->g_ref); + } + lua_setfield(L, -2, "_G"); + lua_pop(L, 1); + + lua_push_vrequest(L, vr); + + errfunc = li_lua_push_traceback(L, 1); + if (lua_pcall(L, 1, 1, errfunc)) { + ERROR(srv, "lua_pcall(): %s", lua_tostring(L, -1)); + lua_pop(L, 1); + res = LI_HANDLER_ERROR; + } else { + lua_pop(L, 1); + } + lua_remove(L, errfunc); + + lua_getfenv(L, -1); + lua_pushnil(L); + lua_setfield(L, -2, "_G"); + lua_pop(L, 2); + + li_lua_unlock(srv); + + return res; +} + +static liHandlerResult lua_action_cleanup(liVRequest *vr, gpointer param, gpointer context) { + lua_action_ctx *ctx = context; + liServer *srv = vr->wrk->srv; + lua_State *L = srv->L; + UNUSED(param); + + li_lua_lock(srv); + luaL_unref(L, LUA_REGISTRYINDEX, ctx->g_ref); + li_lua_unlock(srv); + + g_slice_free(lua_action_ctx, ctx); + + return LI_HANDLER_GO_ON; +} + +static void lua_action_free(liServer *srv, gpointer param) { + lua_action_param *par = param; + lua_State *L = srv->L; + + li_lua_lock(srv); + luaL_unref(L, LUA_REGISTRYINDEX, par->func_ref); + li_lua_unlock(srv); + + g_slice_free(lua_action_param, par); +} + +liAction* lua_make_action(liServer *srv, lua_State *L, int ndx) { + lua_action_param *par = g_slice_new0(lua_action_param); + + g_assert(L == srv->L); + + lua_pushvalue(L, ndx); /* +1 */ + par->func_ref = luaL_ref(L, LUA_REGISTRYINDEX); /* -1 */ + + /* new environment for function */ + lua_pushvalue(L, ndx); /* +1 */ + lua_newtable(L); /* +1 */ + /* new mt */ + lua_newtable(L); /* +1 */ + lua_getfield(L, LUA_REGISTRYINDEX, "li_globals"); /* +1 */ + lua_setfield(L, -2, "__index"); /* -1 */ + lua_setmetatable(L, -2); /* -1 */ + lua_setfenv(L, -2); /* -1 */ + lua_pop(L, 1); /* -1 */ + + return li_action_new_function(lua_action_func, lua_action_cleanup, lua_action_free, par); +} diff --git a/src/main/chunk_lua.c b/src/main/chunk_lua.c new file mode 100644 index 0000000..0ee9a74 --- /dev/null +++ b/src/main/chunk_lua.c @@ -0,0 +1,108 @@ + +#include + +#include +#include + +#define LUA_CHUNK "liChunk*" +#define LUA_CHUNKQUEUE "liChunkQueue*" + +static void init_chunk_mt(lua_State *L) { + /* TODO */ +} + +static int lua_chunkqueue_add(lua_State *L) { + liChunkQueue *cq; + const char *s; + size_t len; + + luaL_checkany(L, 2); + cq = lua_get_chunkqueue(L, 1); + if (cq == NULL) return 0; + if (lua_isstring(L, 2)) { + s = lua_tolstring(L, 2, &len); + li_chunkqueue_append_mem(cq, s, len); + } else { + lua_pushliteral(L, "Wrong type for chunkqueue add"); + lua_error(L); + } + + return 0; +} + +static const luaL_Reg chunkqueue_mt[] = { + { "add", lua_chunkqueue_add }, + + { NULL, NULL } +}; + +static void init_chunkqueue_mt(lua_State *L) { + luaL_register(L, NULL, chunkqueue_mt); + + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); +} + +void lua_init_chunk_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_CHUNK)) { + init_chunk_mt(L); + } + lua_pop(L, 1); + + if (luaL_newmetatable(L, LUA_CHUNKQUEUE)) { + init_chunkqueue_mt(L); + } + lua_pop(L, 1); +} + +liChunk* lua_get_chunk(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_CHUNK); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liChunk**) lua_touserdata(L, ndx); +} + +int lua_push_chunk(lua_State *L, liChunk *c) { + liChunk **pc; + + pc = (liChunk**) lua_newuserdata(L, sizeof(liChunk*)); + *pc = c; + + if (luaL_newmetatable(L, LUA_CHUNK)) { + init_chunk_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} + +liChunkQueue* lua_get_chunkqueue(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_CHUNKQUEUE); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liChunkQueue**) lua_touserdata(L, ndx); +} + +int lua_push_chunkqueue(lua_State *L, liChunkQueue *cq) { + liChunkQueue **pcq; + + pcq = (liChunkQueue**) lua_newuserdata(L, sizeof(liChunkQueue*)); + *pcq = cq; + + if (luaL_newmetatable(L, LUA_CHUNKQUEUE)) { + init_chunkqueue_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} diff --git a/src/main/condition_lua.c b/src/main/condition_lua.c index 83e5039..045dcae 100644 --- a/src/main/condition_lua.c +++ b/src/main/condition_lua.c @@ -5,9 +5,9 @@ #include #include -#define LUA_CONDITION "condition*" -#define LUA_COND_LVALUE "cond_lliValue*" -#define LUA_COND_LVALUE_T "cond_lvalue_t" +#define LUA_CONDITION "liCondition*" +#define LUA_COND_LVALUE "liConditionLValue*" +#define LUA_COND_LVALUE_T "liCondLValue" /* helper */ #define lua_mt_register_srv(srv, L, name, func) do {\ diff --git a/src/main/config_lua.c b/src/main/config_lua.c index d307a82..56cd2cc 100644 --- a/src/main/config_lua.c +++ b/src/main/config_lua.c @@ -4,6 +4,8 @@ #include #include +# include + #include #include @@ -170,38 +172,13 @@ static int handle_server_setup(liServer *srv, lua_State *L, gpointer _ss) { return 0; } -static int traceback (lua_State *L) { - if (!lua_isstring(L, 1)) /* 'message' not a string? */ - return 1; /* keep it intact */ - lua_getfield(L, LUA_GLOBALSINDEX, "debug"); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - return 1; - } - lua_getfield(L, -1, "traceback"); - if (!lua_isfunction(L, -1)) { - lua_pop(L, 2); - return 1; - } - lua_pushvalue(L, 1); /* pass error message */ - lua_pushinteger(L, 2); /* skip this function and traceback */ - lua_call(L, 2, 1); /* call debug.traceback */ - return 1; -} - -static int push_traceback(lua_State *L, int narg) { - int base = lua_gettop(L) - narg; /* function index */ - lua_pushcfunction(L, traceback); - lua_insert(L, base); - return base; -} - gboolean li_config_lua_load(liServer *srv, const gchar *filename) { - lua_State *L; + lua_State *L = srv->L; int errfunc; - L = luaL_newstate(); - luaL_openlibs(L); + li_lua_lock(srv); + + li_lua_new_globals(L); if (0 != luaL_loadfile(L, filename)) { ERROR(srv, "Loading script '%s' failed: %s", filename, lua_tostring(L, -1)); @@ -218,7 +195,7 @@ gboolean li_config_lua_load(liServer *srv, const gchar *filename) { lua_push_lvalues_dict(srv, L); - errfunc = push_traceback(L, 0); + errfunc = li_lua_push_traceback(L, 0); if (lua_pcall(L, 0, 1, errfunc)) { ERROR(srv, "lua_pcall(): %s", lua_tostring(L, -1)); return FALSE; @@ -234,6 +211,9 @@ gboolean li_config_lua_load(liServer *srv, const gchar *filename) { assert(lua_gettop(L) == 0); - lua_close(L); + li_lua_restore_globals(L); + + li_lua_unlock(srv); + return TRUE; } diff --git a/src/main/connection_lua.c b/src/main/connection_lua.c new file mode 100644 index 0000000..6726959 --- /dev/null +++ b/src/main/connection_lua.c @@ -0,0 +1,44 @@ + +#include + +#include +#include + +#define LUA_CONNECTION "liConnection*" + +static void init_con_mt(lua_State *L) { + /* TODO */ +} + +void lua_init_connection_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_CONNECTION)) { + init_con_mt(L); + } + lua_pop(L, 1); +} + +liConnection* lua_get_connection(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_CONNECTION); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liConnection**) lua_touserdata(L, ndx); +} + +int lua_push_connection(lua_State *L, liConnection *con) { + liConnection **pcon; + + pcon = (liConnection**) lua_newuserdata(L, sizeof(liConnection*)); + *pcon = con; + + if (luaL_newmetatable(L, LUA_CONNECTION)) { + init_con_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} diff --git a/src/main/core_lua.c b/src/main/core_lua.c new file mode 100644 index 0000000..d3c0c39 --- /dev/null +++ b/src/main/core_lua.c @@ -0,0 +1,201 @@ + +#include + +#include +#include + +static int traceback (lua_State *L) { + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + +int li_lua_push_traceback(lua_State *L, int narg) { + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, traceback); + lua_insert(L, base); + return base; +} + +int li_lua_metatable_index(lua_State *L) { + /* search for entry in mt, i.e. functions */ + if (!lua_getmetatable(L, 1)) return 0; /* +1 */ + + lua_pushvalue(L, 2); /* +1 */ + lua_rawget(L, -2); /* */ + + if (!lua_isnil(L, -1)) return 1; + + lua_pop(L, 2); /* -2 */ + + return 0; +} + +static void li_lua_store_globals(lua_State *L) { + /* backup global table reference */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* +1 */ + lua_setfield(L, LUA_REGISTRYINDEX, "li_globals"); /* -1 */ +} + +GString* lua_print_get_string(lua_State *L, int from, int to) { + int i, n = lua_gettop(L); + GString *buf = g_string_sized_new(0); + + lua_getfield(L, LUA_GLOBALSINDEX, "tostring"); + for (i = from; i <= to; i++) { + const char *s; + size_t len; + + lua_pushvalue(L, n+1); + lua_pushvalue(L, i); + lua_call(L, 1, 1); + s = lua_tolstring(L, -1, &len); + lua_pop(L, 1); + + if (NULL == s) { + g_string_free(buf, TRUE); + lua_pushliteral(L, "lua_print_get_string: Couldn't convert parameter to string"); + lua_error(L); + } + if (0 == len) continue; + if (buf->len > 0) { + g_string_append_c(buf, ' '); + g_string_append_len(buf, s, len); + } else { + g_string_append_len(buf, s, len); + } + } + lua_pop(L, 1); + return buf; +} + +static int li_lua_error(lua_State *L) { + liServer *srv = lua_touserdata(L, lua_upvalueindex(1)); + GString *buf = lua_print_get_string(L, 1, lua_gettop(L)); + + ERROR(srv, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + +static int li_lua_warning(lua_State *L) { + liServer *srv = lua_touserdata(L, lua_upvalueindex(1)); + GString *buf = lua_print_get_string(L, 1, lua_gettop(L)); + + WARNING(srv, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + +static int li_lua_info(lua_State *L) { + liServer *srv = lua_touserdata(L, lua_upvalueindex(1)); + GString *buf = lua_print_get_string(L, 1, lua_gettop(L)); + + INFO(srv, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + +static int li_lua_debug(lua_State *L) { + liServer *srv = lua_touserdata(L, lua_upvalueindex(1)); + GString *buf = lua_print_get_string(L, 1, lua_gettop(L)); + + DEBUG(srv, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + + +void li_lua_init(liServer *srv, lua_State *L) { + lua_init_chunk_mt(L); + lua_init_connection_mt(L); + lua_init_environment_mt(L); + lua_init_physical_mt(L); + lua_init_request_mt(L); + lua_init_response_mt(L); + lua_init_vrequest_mt(L); + + /* prefer closure, but just in case */ + lua_pushlightuserdata(L, srv); + lua_setfield(L, LUA_REGISTRYINDEX, "lighty.srv"); + + lua_pushlightuserdata(L, srv); + lua_pushcclosure(L, li_lua_error, 1); + lua_setfield(L, LUA_GLOBALSINDEX, "print"); + + lua_pushlightuserdata(L, srv); + lua_pushcclosure(L, li_lua_warning, 1); + lua_setfield(L, LUA_GLOBALSINDEX, "warning"); + + lua_pushlightuserdata(L, srv); + lua_pushcclosure(L, li_lua_info, 1); + lua_setfield(L, LUA_GLOBALSINDEX, "info"); + + lua_pushlightuserdata(L, srv); + lua_pushcclosure(L, li_lua_debug, 1); + lua_setfield(L, LUA_GLOBALSINDEX, "debug"); + + li_lua_store_globals(L); +} + +void li_lua_restore_globals(lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "li_globals"); /* +1 */ + lua_replace(L, LUA_GLOBALSINDEX); /* -1 */ +} + +void li_lua_new_globals(lua_State *L) { + lua_newtable(L); /* +1 */ + + /* metatable for new global env, link old globals as readonly */ + lua_newtable(L); /* +1 */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* +1 */ + lua_setfield(L, -2, "__index"); /* -1 */ + lua_setmetatable(L, -2); /* -1 */ + + lua_replace(L, LUA_GLOBALSINDEX); /* -1 */ +} + + +static int ghashtable_gstring_next(lua_State *L) { + GHashTableIter *it = lua_touserdata(L, lua_upvalueindex(1)); + gpointer pkey = NULL, pvalue = NULL; + + /* ignore arguments */ + if (g_hash_table_iter_next(it, &pkey, &pvalue)) { + GString *key = pkey, *value = pvalue; + lua_pushlstring(L, key->str, key->len); + lua_pushlstring(L, value->str, value->len); + return 2; + } + lua_pushnil(L); + return 1; +} + +int li_lua_ghashtable_gstring_pairs(lua_State *L, GHashTable *ht) { + GHashTableIter *it = lua_newuserdata(L, sizeof(GHashTableIter)); /* +1 */ + g_hash_table_iter_init(it, ht); + lua_pushcclosure(L, ghashtable_gstring_next, 1); /* -1, +1 */ + lua_pushnil(L); lua_pushnil(L); /* +2 */ + return 3; +} diff --git a/src/main/environment_lua.c b/src/main/environment_lua.c new file mode 100644 index 0000000..0550475 --- /dev/null +++ b/src/main/environment_lua.c @@ -0,0 +1,152 @@ + +#include + +#include +#include + +#define LUA_ENVIRONMENT "liEnvironment*" + +static int lua_environment_get(lua_State *L) { + liEnvironment *env; + const char *ckey; + size_t keylen; + GString *val; + + luaL_checkany(L, 2); + if (NULL == (env = lua_get_environment(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + + val = li_environment_get(env, ckey, keylen); + if (val) { + lua_pushlstring(L, val->str, val->len); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int lua_environment_index(lua_State *L) { + if (li_lua_metatable_index(L)) return 1; + + return lua_environment_get(L); +} + +static int lua_environment_set(lua_State *L) { + liEnvironment *env; + const char *ckey, *cval; + size_t keylen, vallen; + + luaL_checkany(L, 3); + if (NULL == (env = lua_get_environment(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + if (lua_isnil(L, 3)) { + li_environment_remove(env, ckey, keylen); + return 0; + } + + if (NULL == (cval = lua_tolstring(L, 3, &vallen))) return 0; + + li_environment_set(env, ckey, keylen, cval, vallen); + return 0; +} + +static int lua_environment_unset(lua_State *L) { + liEnvironment *env; + const char *ckey; + size_t keylen; + + luaL_checkany(L, 2); + if (NULL == (env = lua_get_environment(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + li_environment_remove(env, ckey, keylen); + + return 0; +} + +static int lua_environment_weak_set(lua_State *L) { + liEnvironment *env; + const char *ckey, *cval; + size_t keylen, vallen; + + luaL_checkany(L, 3); + if (NULL == (env = lua_get_environment(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + if (NULL == (cval = lua_tolstring(L, 3, &vallen))) return 0; + + li_environment_insert(env, ckey, keylen, cval, vallen); + return 0; +} + +static int lua_environment_clear(lua_State *L) { + liEnvironment *env; + + luaL_checkany(L, 1); + if (NULL == (env = lua_get_environment(L, 1))) return 0; + + li_environment_reset(env); + + return 0; +} + +static int lua_environment_pairs(lua_State *L) { + liEnvironment *env; + + luaL_checkany(L, 1); + env = lua_get_environment(L, 1); + + if (!env) return 0; + return li_lua_ghashtable_gstring_pairs(L, env->table); +} + +static const luaL_Reg environment_mt[] = { + { "__index", lua_environment_index }, + { "get", lua_environment_get }, + { "__newindex", lua_environment_set }, + { "set", lua_environment_set }, + { "unset", lua_environment_unset }, + { "weak_set", lua_environment_weak_set }, + { "__pairs", lua_environment_pairs }, + { "pairs", lua_environment_pairs }, + { "clear", lua_environment_clear }, + + { NULL, NULL } +}; + +static void init_env_mt(lua_State *L) { + luaL_register(L, NULL, environment_mt); +} + +void lua_init_environment_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_ENVIRONMENT)) { + init_env_mt(L); + } + lua_pop(L, 1); +} + +liEnvironment* lua_get_environment(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_ENVIRONMENT); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liEnvironment**) lua_touserdata(L, ndx); +} + +int lua_push_environment(lua_State *L, liEnvironment *env) { + liEnvironment **penv; + + penv = (liEnvironment**) lua_newuserdata(L, sizeof(liEnvironment*)); + *penv = env; + + if (luaL_newmetatable(L, LUA_ENVIRONMENT)) { + init_env_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} + diff --git a/src/main/http_headers_lua.c b/src/main/http_headers_lua.c new file mode 100644 index 0000000..121da43 --- /dev/null +++ b/src/main/http_headers_lua.c @@ -0,0 +1,240 @@ + +#include + +#include +#include + +#define LUA_HTTPHEADERS "liHttpHeaders*" + +static int lua_http_headers_get(lua_State *L) { + liHttpHeaders *headers; + const char *ckey; + size_t keylen; + GString *val; + + luaL_checkany(L, 2); + if (NULL == (headers = lua_get_http_headers(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + + val = g_string_sized_new(0); + li_http_header_get_all(val, headers, ckey, keylen); + lua_pushlstring(L, val->str, val->len); + g_string_free(val, TRUE); + + return 1; +} + +static int lua_http_headers_index(lua_State *L) { + if (li_lua_metatable_index(L)) return 1; + + return lua_http_headers_get(L); +} + +static int lua_http_headers_set(lua_State *L) { + liHttpHeaders *headers; + const char *ckey, *cval; + size_t keylen, vallen; + + luaL_checkany(L, 3); + if (NULL == (headers = lua_get_http_headers(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + if (lua_isnil(L, 3)) { + li_http_header_remove(headers, ckey, keylen); + return 0; + } + + if (NULL == (cval = lua_tolstring(L, 3, &vallen))) return 0; + + li_http_header_remove(headers, ckey, keylen); + li_http_header_insert(headers, ckey, keylen, cval, vallen); + return 0; +} + +static int lua_http_headers_append(lua_State *L) { + liHttpHeaders *headers; + const char *ckey, *cval; + size_t keylen, vallen; + + luaL_checkany(L, 3); + if (NULL == (headers = lua_get_http_headers(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + if (lua_isnil(L, 3)) { + return 0; + } + + if (NULL == (cval = lua_tolstring(L, 3, &vallen))) return 0; + + li_http_header_append(headers, ckey, keylen, cval, vallen); + return 0; +} + +static int lua_http_headers_insert(lua_State *L) { + liHttpHeaders *headers; + const char *ckey, *cval; + size_t keylen, vallen; + + luaL_checkany(L, 3); + if (NULL == (headers = lua_get_http_headers(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + if (lua_isnil(L, 3)) { + return 0; + } + + if (NULL == (cval = lua_tolstring(L, 3, &vallen))) return 0; + + li_http_header_insert(headers, ckey, keylen, cval, vallen); + return 0; +} + +static int lua_http_headers_unset(lua_State *L) { + liHttpHeaders *headers; + const char *ckey; + size_t keylen; + + luaL_checkany(L, 2); + if (NULL == (headers = lua_get_http_headers(L, 1))) return 0; + if (NULL == (ckey = lua_tolstring(L, 2, &keylen))) return 0; + li_http_header_remove(headers, ckey, keylen); + return 0; +} + +static int lua_http_headers_clear(lua_State *L) { + liHttpHeaders *headers; + + luaL_checkany(L, 1); + if (NULL == (headers = lua_get_http_headers(L, 1))) return 0; + li_http_headers_reset(headers); + return 0; +} + +static int lua_http_headers_next(lua_State *L) { + liHttpHeader *h; + liHttpHeaders *headers = lua_touserdata(L, lua_upvalueindex(1)); + GList *l = lua_touserdata(L, lua_upvalueindex(2)); + const char *ckey; + size_t keylen; + ckey = lua_tolstring(L, lua_upvalueindex(3), &keylen); + + if (!headers && !l) goto endoflist; + + if (headers) { + lua_pushnil(L); + lua_replace(L, lua_upvalueindex(1)); + if (ckey) { + l = li_http_header_find_first(headers, ckey, keylen); + } else { + l = headers->entries.head; + } + } else { + if (ckey) { + l = li_http_header_find_next(l, ckey, keylen); + } else { + l = g_list_next(l); + } + } + + if (!l) goto endoflist; + + h = l->data; + lua_pushlstring(L, h->data->str, h->keylen); + if (h->data->len > h->keylen + 2) { + lua_pushlstring(L, h->data->str + (h->keylen + 2), h->data->len - (h->keylen + 2)); + } else { + lua_pushliteral(L, ""); + } + + lua_pushlightuserdata(L, l); + lua_replace(L, lua_upvalueindex(2)); + + return 2; + +endoflist: + lua_pushnil(L); + lua_replace(L, lua_upvalueindex(1)); + lua_pushnil(L); + lua_replace(L, lua_upvalueindex(2)); + lua_pushnil(L); + lua_replace(L, lua_upvalueindex(3)); + lua_pushnil(L); + return 1; +} + +static int lua_http_headers_pairs(lua_State *L) { + liHttpHeaders *headers; + gboolean haskey = FALSE; + + luaL_checkany(L, 1); + headers = lua_get_http_headers(L, 1); + + if (lua_gettop(L) == 2) { + luaL_checkstring(L, 2); + haskey = TRUE; + } + + if (!headers) return 0; + + lua_pushlightuserdata(L, headers); + lua_pushlightuserdata(L, NULL); + if (haskey) { + lua_pushvalue(L, 2); + } else { + lua_pushnil(L); + } + lua_pushcclosure(L, lua_http_headers_next, 3); + + return 1; +// return li_lua_ghashtable_gstring_pairs(L, env->table); +} + +static const luaL_Reg http_headers_mt[] = { + { "__index", lua_http_headers_index }, + { "get", lua_http_headers_get }, + { "__newindex", lua_http_headers_set }, + { "set", lua_http_headers_set }, + { "append", lua_http_headers_append }, + { "insert", lua_http_headers_insert }, + { "unset", lua_http_headers_unset }, + { "__pairs", lua_http_headers_pairs }, + { "pairs", lua_http_headers_pairs }, + { "list", lua_http_headers_pairs }, + { "clear", lua_http_headers_clear }, + + { NULL, NULL } +}; + +static void init_http_headers_mt(lua_State *L) { + luaL_register(L, NULL, http_headers_mt); +} + +void lua_init_http_headers_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_HTTPHEADERS)) { + init_http_headers_mt(L); + } + lua_pop(L, 1); +} + +liHttpHeaders* lua_get_http_headers(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_HTTPHEADERS); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liHttpHeaders**) lua_touserdata(L, ndx); +} + +int lua_push_http_headers(lua_State *L, liHttpHeaders *headers) { + liHttpHeaders **pheaders; + + pheaders = (liHttpHeaders**) lua_newuserdata(L, sizeof(liHttpHeaders*)); + *pheaders = headers; + + if (luaL_newmetatable(L, LUA_HTTPHEADERS)) { + init_http_headers_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} diff --git a/src/main/physical_lua.c b/src/main/physical_lua.c new file mode 100644 index 0000000..c8e1ede --- /dev/null +++ b/src/main/physical_lua.c @@ -0,0 +1,165 @@ + +#include + +#include +#include + +#define LUA_PHYSICAL "liPhysical*" + +typedef int (*lua_Physical_Attrib)(liPhysical *phys, lua_State *L); + +#define DEF_LUA_MODIFY_GSTRING(attr) \ +static int lua_physical_attr_read_##attr(liPhysical *phys, lua_State *L) { \ + lua_pushlstring(L, phys->attr->str, phys->attr->len); \ + return 1; \ +} \ + \ +static int lua_physical_attr_write_##attr(liPhysical *phys, lua_State *L) { \ + const char *s; size_t len; \ + luaL_checkstring(L, 3); \ + s = lua_tolstring(L, 3, &len); \ + g_string_truncate(phys->attr, 0); \ + g_string_append_len(phys->attr, s, len); \ + return 0; \ +} + +DEF_LUA_MODIFY_GSTRING(path) +DEF_LUA_MODIFY_GSTRING(doc_root) +DEF_LUA_MODIFY_GSTRING(pathinfo) + +#undef DEF_LUA_MODIFY_GSTRING + +#define AR(m) { #m, lua_physical_attr_read_##m, NULL } +#define AW(m) { #m, NULL, lua_physical_attr_write_##m } +#define ARW(m) { #m, lua_physical_attr_read_##m, lua_physical_attr_write_##m } + +static const struct { + const char* key; + lua_Physical_Attrib read_attr, write_attr; +} physical_attribs[] = { + ARW(path), + ARW(doc_root), + ARW(pathinfo), + + { NULL, NULL, NULL } +}; + +#undef AR +#undef AW +#undef ARW + + +static int lua_physical_index(lua_State *L) { + liPhysical *phys; + const char *key; + int i; + + if (lua_gettop(L) != 2) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + if (li_lua_metatable_index(L)) return 1; + + phys = lua_get_physical(L, 1); + if (!phys) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; physical_attribs[i].key ; i++) { + if (0 == strcmp(key, physical_attribs[i].key)) { + if (physical_attribs[i].read_attr) + return physical_attribs[i].read_attr(phys, L); + break; + } + } + + lua_pushstring(L, "cannot read attribute "); + lua_pushstring(L, key); + lua_pushstring(L, " in physical"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static int lua_physical_newindex(lua_State *L) { + liPhysical *phys; + const char *key; + int i; + + if (lua_gettop(L) != 3) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + phys = lua_get_physical(L, 1); + if (!phys) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; physical_attribs[i].key ; i++) { + if (0 == strcmp(key, physical_attribs[i].key)) { + if (physical_attribs[i].write_attr) + return physical_attribs[i].write_attr(phys, L); + break; + } + } + + lua_pushstring(L, "cannot write attribute "); + lua_pushstring(L, key); + lua_pushstring(L, "in physical"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + + +static const luaL_Reg physical_mt[] = { + { "__index", lua_physical_index }, + { "__newindex", lua_physical_newindex }, + + { NULL, NULL } +}; + +static void init_physical_mt(lua_State *L) { + luaL_register(L, NULL, physical_mt); +} + +void lua_init_physical_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_PHYSICAL)) { + init_physical_mt(L); + } + lua_pop(L, 1); +} + +liPhysical* lua_get_physical(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_PHYSICAL); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liPhysical**) lua_touserdata(L, ndx); +} + +int lua_push_physical(lua_State *L, liPhysical *phys) { + liPhysical **pphys; + + pphys = (liPhysical**) lua_newuserdata(L, sizeof(liPhysical*)); + *pphys = phys; + + if (luaL_newmetatable(L, LUA_PHYSICAL)) { + init_physical_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} diff --git a/src/main/request_lua.c b/src/main/request_lua.c new file mode 100644 index 0000000..5043753 --- /dev/null +++ b/src/main/request_lua.c @@ -0,0 +1,348 @@ + +#include + +#include +#include + +#define LUA_REQUEST "liRequest*" +#define LUA_REQUESTURI "liRequestUri*" + +typedef int (*lua_Request_Attrib)(liRequest *req, lua_State *L); + +static int lua_request_attr_read_headers(liRequest *req, lua_State *L) { + lua_push_http_headers(L, req->headers); + return 1; +} + +static int lua_request_attr_read_http_method(liRequest *req, lua_State *L) { + lua_pushlstring(L, req->http_method_str->str, req->http_method_str->len); + return 1; +} + +static int lua_request_attr_read_http_version(liRequest *req, lua_State *L) { + switch (req->http_version) { + case LI_HTTP_VERSION_1_0: + lua_pushliteral(L, "HTTP/1.0"); + break; + case LI_HTTP_VERSION_1_1: + lua_pushliteral(L, "HTTP/1.1"); + break; + case LI_HTTP_VERSION_UNSET: + default: + lua_pushnil(L); + } + return 1; +} + +static int lua_request_attr_read_content_length(liRequest *req, lua_State *L) { + lua_pushinteger(L, req->content_length); + return 1; +} + +static int lua_request_attr_read_uri(liRequest *req, lua_State *L) { + lua_push_requesturi(L, &req->uri); + return 1; +} + +#define AR(m) { #m, lua_request_attr_read_##m, NULL } +#define AW(m) { #m, NULL, lua_request_attr_write_##m } +#define ARW(m) { #m, lua_request_attr_read_##m, lua_request_attr_write_##m } + +static const struct { + const char* key; + lua_Request_Attrib read_attr, write_attr; +} request_attribs[] = { + AR(headers), + AR(http_method), + AR(http_version), + AR(content_length), + AR(uri), + + { NULL, NULL, NULL } +}; + +#undef AR +#undef AW +#undef ARW + + +static int lua_request_index(lua_State *L) { + liRequest *req; + const char *key; + int i; + + if (lua_gettop(L) != 2) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + if (li_lua_metatable_index(L)) return 1; + + req = lua_get_request(L, 1); + if (!req) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; request_attribs[i].key ; i++) { + if (0 == strcmp(key, request_attribs[i].key)) { + if (request_attribs[i].read_attr) + return request_attribs[i].read_attr(req, L); + break; + } + } + + lua_pushstring(L, "cannot read attribute "); + lua_pushstring(L, key); + lua_pushstring(L, " in request"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static int lua_request_newindex(lua_State *L) { + liRequest *req; + const char *key; + int i; + + if (lua_gettop(L) != 3) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + req = lua_get_request(L, 1); + if (!req) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; request_attribs[i].key ; i++) { + if (0 == strcmp(key, request_attribs[i].key)) { + if (request_attribs[i].write_attr) + return request_attribs[i].write_attr(req, L); + break; + } + } + + lua_pushstring(L, "cannot write attribute "); + lua_pushstring(L, key); + lua_pushstring(L, "in request"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static const luaL_Reg request_mt[] = { + { "__index", lua_request_index }, + { "__newindex", lua_request_newindex }, + + { NULL, NULL } +}; + +static void init_request_mt(lua_State *L) { + luaL_register(L, NULL, request_mt); +} + +typedef int (*lua_RequestUri_Attrib)(liRequestUri *uri, lua_State *L); + +#define DEF_LUA_MODIFY_GSTRING(attr) \ +static int lua_requesturi_attr_read_##attr(liRequestUri *uri, lua_State *L) { \ + lua_pushlstring(L, uri->attr->str, uri->attr->len); \ + return 1; \ +} \ + \ +static int lua_requesturi_attr_write_##attr(liRequestUri *uri, lua_State *L) { \ + const char *s; size_t len; \ + luaL_checkstring(L, 3); \ + s = lua_tolstring(L, 3, &len); \ + g_string_truncate(uri->attr, 0); \ + g_string_append_len(uri->attr, s, len); \ + return 0; \ +} + +DEF_LUA_MODIFY_GSTRING(raw) +DEF_LUA_MODIFY_GSTRING(raw_path) +DEF_LUA_MODIFY_GSTRING(raw_orig_path) +DEF_LUA_MODIFY_GSTRING(scheme) +DEF_LUA_MODIFY_GSTRING(authority) +DEF_LUA_MODIFY_GSTRING(path) +DEF_LUA_MODIFY_GSTRING(query) +DEF_LUA_MODIFY_GSTRING(host) + +#undef DEF_LUA_MODIFY_GSTRING + +#define AR(m) { #m, lua_requesturi_attr_read_##m, NULL } +#define AW(m) { #m, NULL, lua_requesturi_attr_write_##m } +#define ARW(m) { #m, lua_requesturi_attr_read_##m, lua_requesturi_attr_write_##m } + +static const struct { + const char* key; + lua_RequestUri_Attrib read_attr, write_attr; +} requesturi_attribs[] = { + ARW(raw), + ARW(raw_path), + ARW(raw_orig_path), + ARW(scheme), + ARW(authority), + ARW(path), + ARW(query), + ARW(host), + + { NULL, NULL, NULL } +}; + +#undef AR +#undef AW +#undef ARW + + +static int lua_requesturi_index(lua_State *L) { + liRequestUri *uri; + const char *key; + int i; + + if (lua_gettop(L) != 2) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + if (li_lua_metatable_index(L)) return 1; + + uri = lua_get_requesturi(L, 1); + if (!uri) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; requesturi_attribs[i].key ; i++) { + if (0 == strcmp(key, requesturi_attribs[i].key)) { + if (requesturi_attribs[i].read_attr) + return requesturi_attribs[i].read_attr(uri, L); + break; + } + } + + lua_pushstring(L, "cannot read attribute "); + lua_pushstring(L, key); + lua_pushstring(L, " in request uri"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static int lua_requesturi_newindex(lua_State *L) { + liRequestUri *uri; + const char *key; + int i; + + if (lua_gettop(L) != 3) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + uri = lua_get_requesturi(L, 1); + if (!uri) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; requesturi_attribs[i].key ; i++) { + if (0 == strcmp(key, requesturi_attribs[i].key)) { + if (requesturi_attribs[i].write_attr) + return requesturi_attribs[i].write_attr(uri, L); + break; + } + } + + lua_pushstring(L, "cannot write attribute "); + lua_pushstring(L, key); + lua_pushstring(L, "in request uri"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + + +static const luaL_Reg requesturi_mt[] = { + { "__index", lua_requesturi_index }, + { "__newindex", lua_requesturi_newindex }, + + { NULL, NULL } +}; + +static void init_requesturi_mt(lua_State *L) { + luaL_register(L, NULL, requesturi_mt); +} + +void lua_init_request_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_REQUEST)) { + init_request_mt(L); + } + lua_pop(L, 1); + + if (luaL_newmetatable(L, LUA_REQUESTURI)) { + init_requesturi_mt(L); + } + lua_pop(L, 1); +} + +liRequest* lua_get_request(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_REQUEST); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liRequest**) lua_touserdata(L, ndx); +} + +int lua_push_request(lua_State *L, liRequest *req) { + liRequest **preq; + + preq = (liRequest**) lua_newuserdata(L, sizeof(liRequest*)); + *preq = req; + + if (luaL_newmetatable(L, LUA_REQUEST)) { + init_request_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} + +liRequestUri* lua_get_requesturi(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_REQUESTURI); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liRequestUri**) lua_touserdata(L, ndx); +} + +int lua_push_requesturi(lua_State *L, liRequestUri *uri) { + liRequestUri **puri; + + puri = (liRequestUri**) lua_newuserdata(L, sizeof(liRequestUri*)); + *puri = uri; + + if (luaL_newmetatable(L, LUA_REQUESTURI)) { + init_requesturi_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} + diff --git a/src/main/response_lua.c b/src/main/response_lua.c new file mode 100644 index 0000000..4433724 --- /dev/null +++ b/src/main/response_lua.c @@ -0,0 +1,160 @@ + +#include + +#include +#include + +#define LUA_RESPONSE "liResponse*" + +typedef int (*lua_Response_Attrib)(liResponse *resp, lua_State *L); + +static int lua_response_attr_read_headers(liResponse *resp, lua_State *L) { + lua_push_http_headers(L, resp->headers); + return 1; +} + +static int lua_response_attr_read_status(liResponse *resp, lua_State *L) { + lua_pushinteger(L, resp->http_status); + return 1; +} + +static int lua_response_attr_write_status(liResponse *resp, lua_State *L) { + int status = luaL_checkinteger(L, 3); + if (status < 200 || status > 999) { + lua_pushliteral(L, "Invalid http response status: "); + lua_pushinteger(L, status); + lua_concat(L, 2); + lua_error(L); + } + resp->http_status = status; + return 0; +} + + +#define AR(m) { #m, lua_response_attr_read_##m, NULL } +#define AW(m) { #m, NULL, lua_response_attr_write_##m } +#define ARW(m) { #m, lua_response_attr_read_##m, lua_response_attr_write_##m } + +static const struct { + const char* key; + lua_Response_Attrib read_attr, write_attr; +} response_attribs[] = { + AR(headers), + ARW(status), + + { NULL, NULL, NULL } +}; + +static int lua_response_index(lua_State *L) { + liResponse *resp; + const char *key; + int i; + + if (lua_gettop(L) != 2) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + if (li_lua_metatable_index(L)) return 1; + + resp = lua_get_response(L, 1); + if (!resp) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; response_attribs[i].key ; i++) { + if (0 == strcmp(key, response_attribs[i].key)) { + if (response_attribs[i].read_attr) + return response_attribs[i].read_attr(resp, L); + break; + } + } + + lua_pushstring(L, "cannot read attribute "); + lua_pushstring(L, key); + lua_pushstring(L, " in response"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static int lua_response_newindex(lua_State *L) { + liResponse *resp; + const char *key; + int i; + + if (lua_gettop(L) != 3) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + resp = lua_get_response(L, 1); + if (!resp) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; response_attribs[i].key ; i++) { + if (0 == strcmp(key, response_attribs[i].key)) { + if (response_attribs[i].write_attr) + return response_attribs[i].write_attr(resp, L); + break; + } + } + + lua_pushstring(L, "cannot write attribute "); + lua_pushstring(L, key); + lua_pushstring(L, "in response"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static const luaL_Reg response_mt[] = { + { "__index", lua_response_index }, + { "__newindex", lua_response_newindex }, + + { NULL, NULL } +}; + +static void init_response_mt(lua_State *L) { + luaL_register(L, NULL, response_mt); +} + +void lua_init_response_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_RESPONSE)) { + init_response_mt(L); + } + lua_pop(L, 1); +} + +liResponse* lua_get_response(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_RESPONSE); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liResponse**) lua_touserdata(L, ndx); +} + +int lua_push_response(lua_State *L, liResponse *resp) { + liResponse **presp; + + presp = (liResponse**) lua_newuserdata(L, sizeof(liResponse*)); + *presp = resp; + + if (luaL_newmetatable(L, LUA_RESPONSE)) { + init_response_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} diff --git a/src/main/server.c b/src/main/server.c index e85511c..5927a22 100644 --- a/src/main/server.c +++ b/src/main/server.c @@ -2,6 +2,13 @@ #include #include +#ifdef HAVE_LUA_H +# include +# include +# include +#endif + + static void li_server_listen_cb(struct ev_loop *loop, ev_io *w, int revents); static void li_server_stop(liServer *srv); @@ -80,6 +87,16 @@ liServer* li_server_new(const gchar *module_dir) { srv->state = LI_SERVER_INIT; srv->dest_state = LI_SERVER_RUNNING; +#ifdef HAVE_LUA_H + srv->L = luaL_newstate(); + luaL_openlibs(srv->L); + li_lua_init(srv, srv->L); + + srv->lualock = g_mutex_new(); +#else + srv->L = NULL; +#endif + srv->workers = g_array_new(FALSE, TRUE, sizeof(liWorker*)); srv->worker_count = 0; @@ -135,6 +152,12 @@ void li_server_free(liServer* srv) { li_action_release(srv, srv->mainaction); +#ifdef HAVE_LUA_H + lua_close(srv->L); + srv->L = NULL; + g_mutex_free(srv->lualock); +#endif + /* free throttle pools */ { guint i; diff --git a/src/main/value_lua.c b/src/main/value_lua.c index 53485f7..9eedcf2 100644 --- a/src/main/value_lua.c +++ b/src/main/value_lua.c @@ -129,8 +129,13 @@ liValue* li_value_from_lua(liServer *srv, lua_State *L) { lua_pop(L, 1); return NULL; + case LUA_TFUNCTION: { + liAction *a = lua_make_action(srv, L, -1); + lua_pop(L, 1); + return li_value_new_action(srv, a); + } + case LUA_TLIGHTUSERDATA: - case LUA_TFUNCTION: case LUA_TTHREAD: case LUA_TNONE: default: diff --git a/src/main/virtualrequest_lua.c b/src/main/virtualrequest_lua.c new file mode 100644 index 0000000..5d3da76 --- /dev/null +++ b/src/main/virtualrequest_lua.c @@ -0,0 +1,259 @@ + +#include + +#include +#include + +#define LUA_VREQUEST "liVRequest*" + +typedef int (*lua_VRequest_Attrib)(liVRequest *vr, lua_State *L); + +static int lua_vrequest_attr_read_in(liVRequest *vr, lua_State *L) { + lua_push_chunkqueue(L, vr->in); + return 1; +} + +static int lua_vrequest_attr_read_out(liVRequest *vr, lua_State *L) { + lua_push_chunkqueue(L, vr->out); + return 1; +} + +static int lua_vrequest_attr_read_con(liVRequest *vr, lua_State *L) { + lua_push_connection(L, vr->con); + return 1; +} + +static int lua_vrequest_attr_read_env(liVRequest *vr, lua_State *L) { + lua_push_environment(L, &vr->env); + return 1; +} + +static int lua_vrequest_attr_read_req(liVRequest *vr, lua_State *L) { + lua_push_request(L, &vr->request); + return 1; +} + +static int lua_vrequest_attr_read_resp(liVRequest *vr, lua_State *L) { + lua_push_response(L, &vr->response); + return 1; +} + +static int lua_vrequest_attr_read_phys(liVRequest *vr, lua_State *L) { + lua_push_physical(L, &vr->physical); + return 1; +} + +#define AR(m) { #m, lua_vrequest_attr_read_##m, NULL } +#define AW(m) { #m, NULL, lua_vrequest_attr_write_##m } +#define ARW(m) { #m, lua_vrequest_attr_read_##m, lua_vrequest_attr_write_##m } + +static const struct { + const char* key; + lua_VRequest_Attrib read_attr, write_attr; +} vrequest_attribs[] = { + AR(con), + AR(in), + AR(out), + AR(env), + AR(req), + AR(resp), + AR(phys), + + { NULL, NULL, NULL } +}; + +static int lua_vrequest_index(lua_State *L) { + liVRequest *vr; + const char *key; + int i; + + if (lua_gettop(L) != 2) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + if (li_lua_metatable_index(L)) return 1; + + vr = lua_get_vrequest(L, 1); + if (!vr) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; vrequest_attribs[i].key ; i++) { + if (0 == strcmp(key, vrequest_attribs[i].key)) { + if (vrequest_attribs[i].read_attr) + return vrequest_attribs[i].read_attr(vr, L); + break; + } + } + + lua_pushstring(L, "cannot read attribute "); + lua_pushstring(L, key); + lua_pushstring(L, " in vrequest"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static int lua_vrequest_newindex(lua_State *L) { + liVRequest *vr; + const char *key; + int i; + + if (lua_gettop(L) != 3) { + lua_pushstring(L, "incorrect number of arguments"); + lua_error(L); + } + + vr = lua_get_vrequest(L, 1); + if (!vr) return 0; + + if (lua_isnumber(L, 2)) return 0; + if (!lua_isstring(L, 2)) return 0; + + key = lua_tostring(L, 2); + for (i = 0; vrequest_attribs[i].key ; i++) { + if (0 == strcmp(key, vrequest_attribs[i].key)) { + if (vrequest_attribs[i].write_attr) + return vrequest_attribs[i].write_attr(vr, L); + break; + } + } + + lua_pushstring(L, "cannot write attribute "); + lua_pushstring(L, key); + lua_pushstring(L, "in vrequest"); + lua_concat(L, 3); + lua_error(L); + + return 0; +} + +static int lua_vrequest_error(lua_State *L) { + liVRequest *vr; + GString *buf; + vr = lua_get_vrequest(L, 1); + + buf = lua_print_get_string(L, 2, lua_gettop(L)); + + VR_ERROR(vr, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + +static int lua_vrequest_warning(lua_State *L) { + liVRequest *vr; + GString *buf; + vr = lua_get_vrequest(L, 1); + + buf = lua_print_get_string(L, 2, lua_gettop(L)); + + VR_WARNING(vr, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + +static int lua_vrequest_info(lua_State *L) { + liVRequest *vr; + GString *buf; + vr = lua_get_vrequest(L, 1); + + buf = lua_print_get_string(L, 2, lua_gettop(L)); + + VR_INFO(vr, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + +static int lua_vrequest_debug(lua_State *L) { + liVRequest *vr; + GString *buf; + vr = lua_get_vrequest(L, 1); + + buf = lua_print_get_string(L, 2, lua_gettop(L)); + + VR_DEBUG(vr, "(lua): %s", buf->str); + + g_string_free(buf, TRUE); + + return 0; +} + +static int lua_vrequest_handle_direct(lua_State *L) { + liVRequest *vr; + vr = lua_get_vrequest(L, 1); + + lua_pushboolean(L, li_vrequest_handle_direct(vr)); + + return 1; +} + +static int lua_vrequest_is_handled(lua_State *L) { + liVRequest *vr; + vr = lua_get_vrequest(L, 1); + + lua_pushboolean(L, li_vrequest_is_handled(vr)); + + return 1; +} + +static const luaL_Reg vrequest_mt[] = { + { "__index", lua_vrequest_index }, + { "__newindex", lua_vrequest_newindex }, + + { "print", lua_vrequest_error }, + { "warning", lua_vrequest_warning }, + { "info", lua_vrequest_info }, + { "debug", lua_vrequest_debug }, + + { "handle_direct", lua_vrequest_handle_direct }, + { "is_handled", lua_vrequest_is_handled }, + + { NULL, NULL } +}; + +static void init_vrequest_mt(lua_State *L) { + luaL_register(L, NULL, vrequest_mt); +} + +void lua_init_vrequest_mt(lua_State *L) { + if (luaL_newmetatable(L, LUA_VREQUEST)) { + init_vrequest_mt(L); + } + lua_pop(L, 1); +} + +liVRequest* lua_get_vrequest(lua_State *L, int ndx) { + if (!lua_isuserdata(L, ndx)) return NULL; + if (!lua_getmetatable(L, ndx)) return NULL; + luaL_getmetatable(L, LUA_VREQUEST); + if (lua_isnil(L, -1) || lua_isnil(L, -2) || !lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return NULL; + } + lua_pop(L, 2); + return *(liVRequest**) lua_touserdata(L, ndx); +} + +int lua_push_vrequest(lua_State *L, liVRequest *vr) { + liVRequest **pvr; + + pvr = (liVRequest**) lua_newuserdata(L, sizeof(liVRequest*)); + *pvr = vr; + + if (luaL_newmetatable(L, LUA_VREQUEST)) { + init_vrequest_mt(L); + } + + lua_setmetatable(L, -2); + return 1; +} diff --git a/src/modules/mod_accesslog.c b/src/modules/mod_accesslog.c index 0622a35..06d4811 100644 --- a/src/modules/mod_accesslog.c +++ b/src/modules/mod_accesslog.c @@ -265,7 +265,7 @@ static GString *al_format_log(liConnection *con, al_data *ald, GArray *format) { g_string_append_c(str, '-'); break; case AL_FORMAT_REQUEST_HEADER: - li_http_header_get_fast(tmp_gstr, req->headers, GSTR_LEN(e->key)); + li_http_header_get_all(tmp_gstr, req->headers, GSTR_LEN(e->key)); if (tmp_gstr->len) al_append_escaped(str, tmp_gstr); else @@ -275,7 +275,7 @@ static GString *al_format_log(liConnection *con, al_data *ald, GArray *format) { g_string_append_len(str, GSTR_LEN(req->http_method_str)); break; case AL_FORMAT_RESPONSE_HEADER: - li_http_header_get_fast(tmp_gstr, resp->headers, GSTR_LEN(e->key)); + li_http_header_get_all(tmp_gstr, resp->headers, GSTR_LEN(e->key)); if (tmp_gstr->len) al_append_escaped(str, tmp_gstr); else