Add lua filters; fixed some lua-api bugs; enhanced lua chunkqueue api

personal/stbuehler/wip
Stefan Bühler 14 years ago
parent 3d50819d14
commit 881669d96a

@ -21,6 +21,13 @@ LI_API void li_lua_init_environment_mt(lua_State *L);
LI_API liEnvironment* li_lua_get_environment(lua_State *L, int ndx);
LI_API int li_lua_push_environment(lua_State *L, liEnvironment *env);
LI_API void li_lua_init_filter_mt(lua_State *L);
LI_API liFilter* li_lua_get_filter(lua_State *L, int ndx);
LI_API int li_lua_push_filter(lua_State *L, liFilter *f);
LI_API void li_lua_init_filters(lua_State *L, liServer* srv);
LI_API liFilter* li_lua_vrequest_add_filter_in(lua_State *L, liVRequest *vr, int state_ndx);
LI_API liFilter* li_lua_vrequest_add_filter_out(lua_State *L, liVRequest *vr, int state_ndx);
LI_API void li_lua_init_http_headers_mt(lua_State *L);
LI_API liHttpHeaders* li_lua_get_http_headers(lua_State *L, int ndx);
LI_API int li_lua_push_http_headers(lua_State *L, liHttpHeaders *headers);
@ -55,7 +62,15 @@ LI_API int li_lua_metatable_index(lua_State *L);
LI_API void li_lua_init(lua_State* L, liServer* srv, liWorker* wrk);
LI_API int li_lua_push_traceback(lua_State *L, int narg);
LI_API int li_lua_push_traceback(lua_State *L, int nargs);
/* nargs: number of arguments *with* object; object must be the first of the arguments
* returns: FALSE: an error occured. stack balance -nargs (i.e. popped args)
* TRUE: stack balance nresp-narg; popped args, pushed results
* srv/vr are only needed for error logging
* if optional is TRUE don't log an error if the method doesn't exist
*/
LI_API gboolean li_lua_call_object(liServer *srv, liVRequest *vr, lua_State *L, const char* method, int nargs, int nresults, gboolean optional);
LI_API void li_lua_restore_globals(lua_State *L);
LI_API void li_lua_new_globals(lua_State *L);

@ -225,6 +225,7 @@ SET(LIGHTTPD_SHARED_SRC ${LIGHTTPD_SHARED_SRC}
core_lua.c
connection_lua.c
environment_lua.c
filters_lua.c
http_headers_lua.c
physical_lua.c
request_lua.c

@ -50,6 +50,7 @@ lua_src= \
connection_lua.c \
core_lua.c \
environment_lua.c \
filters_lua.c \
http_headers_lua.c \
physical_lua.c \
request_lua.c \

@ -161,21 +161,45 @@ static int lua_chunkqueue_reset(lua_State *L) {
return 0;
}
static int lua_chunkqueue_steal_all(lua_State *L) {
liChunkQueue *cq, *cq_from;
cq = li_lua_get_chunkqueue(L, 1);
cq_from = li_lua_get_chunkqueue(L, 2);
if (!cq_from) {
lua_pushliteral(L, "Expected source chunkqueue to steal from");
return lua_error(L);
}
li_chunkqueue_steal_all(cq, cq_from);
return 0;
}
static int lua_chunkqueue_skip_all(lua_State *L) {
liChunkQueue *cq;
cq = li_lua_get_chunkqueue(L, 1);
li_chunkqueue_skip_all(cq);
return 0;
}
static const luaL_Reg chunkqueue_mt[] = {
{ "__index", lua_chunkqueue_index },
{ "__newindex", lua_chunkqueue_newindex },
{ "add", lua_chunkqueue_add },
{ "reset", lua_chunkqueue_reset },
{ "steal_all", lua_chunkqueue_steal_all },
{ "skip_all", lua_chunkqueue_skip_all },
{ 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 li_lua_init_chunk_mt(lua_State *L) {

@ -23,13 +23,43 @@ static int traceback (lua_State *L) {
return 1;
}
int li_lua_push_traceback(lua_State *L, int narg) {
int base = lua_gettop(L) - narg; /* function index */
int li_lua_push_traceback(lua_State *L, int nargs) {
int base = lua_gettop(L) - nargs; /* function index */
lua_pushcfunction(L, traceback);
lua_insert(L, base);
return base;
}
gboolean li_lua_call_object(liServer *srv, liVRequest *vr, lua_State *L, const char* method, int nargs, int nresults, gboolean optional) {
int errfunc;
gboolean result;
lua_getfield(L, -nargs, method); /* +1 "function" */
if (!lua_isfunction(L, -1)) {
if (!optional) {
_ERROR(srv, vr, "li_lua_call_object: method '%s' not found", method);
}
lua_pop(L, 1 + nargs);
return FALSE;
}
lua_insert(L, lua_gettop(L) - nargs); /* move function before arguments */
errfunc = li_lua_push_traceback(L, nargs); /* +1 "errfunc" */
if (lua_pcall(L, nargs, nresults, errfunc)) { /* pops func and arguments, push result */
_ERROR(srv, vr, "lua_pcall(): %s", lua_tostring(L, -1));
lua_pop(L, 1); /* -1 */
result = FALSE;
} else {
result = TRUE;
}
lua_remove(L, errfunc); /* -1 "errfunc" */
/* "errfunc", "function" and args have been popped - only results remain (and nothing if lua_pcall failed) */
return result;
}
int li_lua_metatable_index(lua_State *L) {
/* search for entry in mt, i.e. functions */
if (!lua_getmetatable(L, 1)) return 0; /* +1 */
@ -141,6 +171,7 @@ void li_lua_init(lua_State *L, liServer *srv, liWorker *wrk) {
li_lua_init_chunk_mt(L);
li_lua_init_connection_mt(L);
li_lua_init_environment_mt(L);
li_lua_init_filter_mt(L);
li_lua_init_physical_mt(L);
li_lua_init_request_mt(L);
li_lua_init_response_mt(L);
@ -157,6 +188,7 @@ void li_lua_init(lua_State *L, liServer *srv, liWorker *wrk) {
}
lua_newtable(L); /* lighty. */
li_lua_init_filters(L, srv);
lua_pushlightuserdata(L, srv);
lua_pushcclosure(L, li_lua_error, 1);

@ -0,0 +1,360 @@
#include <lighttpd/base.h>
#include <lighttpd/core_lua.h>
#include <lighttpd/actions_lua.h>
#include <lualib.h>
#include <lauxlib.h>
#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);
}

@ -53,6 +53,9 @@ static gboolean filters_handle_out_close(liVRequest *vr, liFilters *fs) {
case LI_HANDLER_WAIT_FOR_EVENT:
break; /* ignore - filter has to call li_vrequest_joblist_append(vr); */
case LI_HANDLER_ERROR:
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "filter %i return an error", i);
}
return FALSE;
}
}
@ -78,6 +81,9 @@ static gboolean filters_run(liVRequest *vr, liFilters *fs) {
case LI_HANDLER_WAIT_FOR_EVENT:
break; /* ignore - filter has to call li_vrequest_joblist_append(vr); */
case LI_HANDLER_ERROR:
if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) {
VR_DEBUG(vr, "filter %i return an error", i);
}
return FALSE;
}
f->knows_out_is_closed = f->out->is_closed;
@ -460,12 +466,14 @@ static gboolean vrequest_do_handle_read(liVRequest *vr) {
return TRUE;
}
static gboolean vrequest_do_handle_write(liVRequest *vr) {
static void vrequest_do_handle_write(liVRequest *vr) {
if (!filters_handle_out_close(vr, &vr->filters_out)) {
li_vrequest_error(vr);
return;
}
if (!filters_run(vr, &vr->filters_out)) {
li_vrequest_error(vr);
return;
}
switch (vr->handle_response_body(vr)) {
@ -473,14 +481,14 @@ static gboolean vrequest_do_handle_write(liVRequest *vr) {
break;
case LI_HANDLER_COMEBACK:
li_vrequest_joblist_append(vr); /* come back later */
return FALSE;
return;
case LI_HANDLER_WAIT_FOR_EVENT:
return FALSE;
return;
case LI_HANDLER_ERROR:
li_vrequest_error(vr);
break;
}
return TRUE;
return;
}
void li_vrequest_state_machine(liVRequest *vr) {

@ -287,6 +287,32 @@ static int lua_vrequest_enter_action(lua_State *L) {
return 0;
}
static int lua_vrequest_add_filter_in(lua_State *L) {
liVRequest *vr;
if (lua_gettop(L) != 2) {
lua_pushstring(L, "incorrect number of arguments");
lua_error(L);
}
vr = li_lua_get_vrequest(L, 1);
return li_lua_push_filter(L, li_lua_vrequest_add_filter_in(L, vr, 2));
}
static int lua_vrequest_add_filter_out(lua_State *L) {
liVRequest *vr;
if (lua_gettop(L) != 2) {
lua_pushstring(L, "incorrect number of arguments");
lua_error(L);
}
vr = li_lua_get_vrequest(L, 1);
return li_lua_push_filter(L, li_lua_vrequest_add_filter_out(L, vr, 2));
}
static const luaL_Reg vrequest_mt[] = {
{ "__index", lua_vrequest_index },
{ "__newindex", lua_vrequest_newindex },
@ -302,6 +328,9 @@ static const luaL_Reg vrequest_mt[] = {
{ "enter_action", lua_vrequest_enter_action },
{ "add_filter_in", lua_vrequest_add_filter_in },
{ "add_filter_out", lua_vrequest_add_filter_out },
{ NULL, NULL }
};

@ -69,6 +69,7 @@ def build(bld):
connection_lua.c
core_lua.c
environment_lua.c
filters_lua.c
http_headers_lua.c
physical_lua.c
request_lua.c

Loading…
Cancel
Save