#include #include #include #include #include #define LUA_FILTER "liFilter*" typedef int (*lua_Filter_Attrib)(liFilter *f, lua_State *L); static int lua_filter_attr_read_in(liFilter *f, lua_State *L) { li_lua_push_chunkqueue(L, f->in); return 1; } static int lua_filter_attr_read_out(liFilter *f, lua_State *L) { li_lua_push_chunkqueue(L, f->out); return 1; } #define AR(m) { #m, lua_filter_attr_read_##m, NULL } #define AW(m) { #m, NULL, lua_filter_attr_write_##m } #define ARW(m) { #m, lua_filter_attr_read_##m, lua_filter_attr_write_##m } static const struct { const char* key; lua_Filter_Attrib read_attr, write_attr; } filter_attribs[] = { AR(in), AR(out), { NULL, NULL, NULL } }; static int lua_filter_index(lua_State *L) { liFilter *f; 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; f = li_lua_get_filter(L, 1); if (!f) return 0; if (lua_isnumber(L, 2)) return 0; if (!lua_isstring(L, 2)) return 0; key = lua_tostring(L, 2); for (i = 0; filter_attribs[i].key ; i++) { if (0 == strcmp(key, filter_attribs[i].key)) { if (filter_attribs[i].read_attr) return filter_attribs[i].read_attr(f, L); break; } } lua_pushstring(L, "cannot read attribute "); lua_pushstring(L, key); lua_pushstring(L, " in filter"); lua_concat(L, 3); lua_error(L); return 0; } static int lua_filter_newindex(lua_State *L) { liFilter *f; const char *key; int i; if (lua_gettop(L) != 3) { lua_pushstring(L, "incorrect number of arguments"); lua_error(L); } f = li_lua_get_filter(L, 1); if (!f) return 0; if (lua_isnumber(L, 2)) return 0; if (!lua_isstring(L, 2)) return 0; key = lua_tostring(L, 2); for (i = 0; filter_attribs[i].key ; i++) { if (0 == strcmp(key, filter_attribs[i].key)) { if (filter_attribs[i].write_attr) return filter_attribs[i].write_attr(f, L); break; } } lua_pushstring(L, "cannot write attribute "); lua_pushstring(L, key); lua_pushstring(L, "in filter"); lua_concat(L, 3); lua_error(L); return 0; } static const luaL_Reg filter_mt[] = { { "__index", lua_filter_index }, { "__newindex", lua_filter_newindex }, { NULL, NULL } }; static void init_filter_mt(lua_State *L) { luaL_register(L, NULL, filter_mt); } void li_lua_init_filter_mt(lua_State *L) { if (luaL_newmetatable(L, LUA_FILTER)) { init_filter_mt(L); } lua_pop(L, 1); } liFilter* li_lua_get_filter(lua_State *L, int ndx) { if (!lua_isuserdata(L, ndx)) return NULL; if (!lua_getmetatable(L, ndx)) return NULL; luaL_getmetatable(L, LUA_FILTER); 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 *(liFilter**) lua_touserdata(L, ndx); } int li_lua_push_filter(lua_State *L, liFilter *f) { liFilter **pf; pf = (liFilter**) lua_newuserdata(L, sizeof(liFilter*)); *pf = f; if (luaL_newmetatable(L, LUA_FILTER)) { init_filter_mt(L); } lua_setmetatable(L, -2); return 1; } typedef struct filter_lua_config filter_lua_config; struct filter_lua_config { lua_State *L; int class_ref; }; typedef struct filter_lua_state filter_lua_state; struct filter_lua_state { lua_State *L; int object_ref; }; static filter_lua_state* filter_lua_state_new(liVRequest *vr, filter_lua_config *config) { int object_ref = LUA_NOREF; liServer *srv = vr->wrk->srv; lua_State *L = config->L; gboolean dolock = (L == srv->L); if (dolock) li_lua_lock(srv); lua_rawgeti(L, LUA_REGISTRYINDEX, config->class_ref); /* +1 */ li_lua_push_vrequest(L, vr); /* +1 */ if (li_lua_call_object(srv, vr, L, "new", 2, 1, FALSE)) { /* -2, +1 on success */ if (!lua_isnil(L, -1)) { object_ref = luaL_ref(L, LUA_REGISTRYINDEX); /* -1 */ } else { /* no error; nil is interpreted as "don't need this filter for this request" */ lua_pop(L, 1); /* -1 */ } } else { VR_ERROR(vr, "%s", "li_lua_call_object failed"); li_vrequest_error(vr); } if (dolock) li_lua_unlock(srv); if (LUA_NOREF != object_ref) { filter_lua_state *state = g_slice_new0(filter_lua_state); state->L = L; state->object_ref = object_ref; return state; } else { return NULL; } } static void filter_lua_state_free(liVRequest *vr, filter_lua_state *state) { liServer *srv = vr->wrk->srv; lua_State *L = state->L; gboolean dolock = (L == srv->L); if (dolock) li_lua_lock(srv); lua_rawgeti(L, LUA_REGISTRYINDEX, state->object_ref); /* +1 */ li_lua_push_vrequest(L, vr); /* +1 */ li_lua_call_object(srv, vr, L, "finished", 2, 0, TRUE); /* -2 */ luaL_unref(L, LUA_REGISTRYINDEX, state->object_ref); if (dolock) li_lua_unlock(srv); g_slice_free(filter_lua_state, state); } static void filter_lua_free(liVRequest *vr, liFilter *f) { filter_lua_state *state = (filter_lua_state*) f->param; filter_lua_state_free(vr, state); } static liHandlerResult filter_lua_handle(liVRequest *vr, liFilter *f) { filter_lua_state *state = (filter_lua_state*) f->param; liServer *srv = vr->wrk->srv; lua_State *L = state->L; gboolean dolock = (L == srv->L); liHandlerResult res; if (dolock) li_lua_lock(srv); lua_rawgeti(L, LUA_REGISTRYINDEX, state->object_ref); /* +1 */ li_lua_push_vrequest(L, vr); /* +1 */ li_lua_push_chunkqueue(L, f->out); /* +1 */ li_lua_push_chunkqueue(L, f->in); /* +1 */ if (li_lua_call_object(NULL, vr, L, "handle", 4, 1, FALSE)) { /* -4, +1 on success */ res = LI_HANDLER_GO_ON; if (!lua_isnil(L, -1)) { int rc = lua_tointeger(L, -1); switch (rc) { case LI_HANDLER_GO_ON: case LI_HANDLER_COMEBACK: case LI_HANDLER_WAIT_FOR_EVENT: case LI_HANDLER_ERROR: res = rc; break; default: VR_ERROR(vr, "lua filter returned an error or an unknown value (%i)", rc); res = LI_HANDLER_ERROR; } } lua_pop(L, 1); } else { res = LI_HANDLER_ERROR; } if (dolock) li_lua_unlock(srv); return res; } static liHandlerResult filter_lua_in(liVRequest *vr, gpointer param, gpointer *context) { filter_lua_config *config = param; filter_lua_state *state = filter_lua_state_new(vr, config); UNUSED(context); if (state) { li_vrequest_add_filter_in(vr, filter_lua_handle, filter_lua_free, state); } return LI_HANDLER_GO_ON; } static liHandlerResult filter_lua_out(liVRequest *vr, gpointer param, gpointer *context) { filter_lua_config *config = param; filter_lua_state *state = filter_lua_state_new(vr, config); UNUSED(context); if (state) { li_vrequest_add_filter_out(vr, filter_lua_handle, filter_lua_free, state); } return LI_HANDLER_GO_ON; } static void filter_lua_action_free(liServer *srv, gpointer param) { filter_lua_config *config = param; lua_State *L = config->L; gboolean dolock = (L == srv->L); if (dolock) li_lua_lock(srv); luaL_unref(L, LUA_REGISTRYINDEX, config->class_ref); if (dolock) li_lua_unlock(srv); g_slice_free(filter_lua_config, config); } static int filter_lua_action_create(lua_State *L, liActionFuncCB act_cb) { liServer *srv = lua_touserdata(L, lua_upvalueindex(1)); liAction *act; filter_lua_config *config; if (lua_gettop(L) != 1 || lua_isnil(L, 1)) { int n = lua_gettop(L); lua_pushstring(L, "expected exactly one parameter for lighty.filter_[in/out], got "); lua_pushinteger(L, n); lua_concat(L, 2); return lua_error(L); } config = g_slice_new0(filter_lua_config); config->L = L; config->class_ref = luaL_ref(L, LUA_REGISTRYINDEX); act = li_action_new_function(act_cb, NULL, filter_lua_action_free, config); return li_lua_push_action(srv, L, act); } static int filter_lua_in_create(lua_State *L) { return filter_lua_action_create(L, filter_lua_in); } static int filter_lua_out_create(lua_State *L) { return filter_lua_action_create(L, filter_lua_out); } void li_lua_init_filters(lua_State *L, liServer* srv) { lua_pushlightuserdata(L, srv); lua_pushcclosure(L, filter_lua_in_create, 1); lua_setfield(L, -2, "filter_in"); lua_pushlightuserdata(L, srv); lua_pushcclosure(L, filter_lua_out_create, 1); lua_setfield(L, -2, "filter_out"); } liFilter* li_lua_vrequest_add_filter_in(lua_State *L, liVRequest *vr, int state_ndx) { filter_lua_state *state; lua_pushvalue(L, state_ndx); state = g_slice_new0(filter_lua_state); state->L = L; state->object_ref = luaL_ref(L, LUA_REGISTRYINDEX); return li_vrequest_add_filter_in(vr, filter_lua_handle, filter_lua_free, state); } liFilter* li_lua_vrequest_add_filter_out(lua_State *L, liVRequest *vr, int state_ndx) { filter_lua_state *state; lua_pushvalue(L, state_ndx); state = g_slice_new0(filter_lua_state); state->L = L; state->object_ref = luaL_ref(L, LUA_REGISTRYINDEX); return li_vrequest_add_filter_out(vr, filter_lua_handle, filter_lua_free, state); }