diff options
author | Krzysztof Krzyżaniak <eloy@debian.org> | 2006-09-25 08:27:05 +0000 |
---|---|---|
committer | Krzysztof Krzyżaniak <eloy@debian.org> | 2006-09-25 08:27:05 +0000 |
commit | cb3017146d5322069fe4003ffcd4c9ada74dacb8 (patch) | |
tree | 5a07eefc6cfd6c668292bfa3471d993dc3b88f1f /src/mod_magnet.c | |
parent | 8e8fd7ec0fc30c1703a192edfceefac6ab5f0f0c (diff) | |
download | lighttpd-cb3017146d5322069fe4003ffcd4c9ada74dacb8.tar.gz |
eloy: new upstream version
Diffstat (limited to 'src/mod_magnet.c')
-rw-r--r-- | src/mod_magnet.c | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/src/mod_magnet.c b/src/mod_magnet.c new file mode 100644 index 0000000..a2f6185 --- /dev/null +++ b/src/mod_magnet.c @@ -0,0 +1,746 @@ +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <setjmp.h> + +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include "mod_magnet_cache.h" +#include "response.h" +#include "stat_cache.h" +#include "status_counter.h" + +#ifdef HAVE_LUA_H +#include <lua.h> +#include <lauxlib.h> + +#define MAGNET_CONFIG_RAW_URL "magnet.attract-raw-url-to" +#define MAGNET_CONFIG_PHYSICAL_PATH "magnet.attract-physical-path-to" +#define MAGNET_RESTART_REQUEST 99 + +/* plugin config for all request/connections */ + +static jmp_buf exceptionjmp; + +typedef struct { + array *url_raw; + array *physical_path; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + script_cache *cache; + + buffer *encode_buf; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_magnet_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->cache = script_cache_init(); + p->encode_buf = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_magnet_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + array_free(s->url_raw); + array_free(s->physical_path); + + free(s); + } + free(p->config_storage); + } + + script_cache_free(p->cache); + buffer_free(p->encode_buf); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_magnet_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { MAGNET_CONFIG_RAW_URL, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { MAGNET_CONFIG_PHYSICAL_PATH, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->url_raw = array_init(); + s->physical_path = array_init(); + + cv[0].destination = s->url_raw; + cv[1].destination = s->physical_path; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_magnet_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(url_raw); + PATCH(physical_path); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_RAW_URL))) { + PATCH(url_raw); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_PHYSICAL_PATH))) { + PATCH(physical_path); + } + } + } + + return 0; +} +#undef PATCH + +static int magnet_print(lua_State *L) { + const char *s = luaL_checkstring(L, 1); + server *srv; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + log_error_write(srv, __FILE__, __LINE__, "ss", + "(lua-print)", s); + + return 0; +} + +static int magnet_atpanic(lua_State *L) { + const char *s = luaL_checkstring(L, 1); + server *srv; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + log_error_write(srv, __FILE__, __LINE__, "ss", + "(lua-atpanic)", s); + + longjmp(exceptionjmp, 1); +} + +static int magnet_reqhdr_get(lua_State *L) { + server *srv; + connection *con; + data_string *ds; + + const char *key = luaL_checkstring(L, 2); + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key))) { + if (ds->value->used) { + lua_pushlstring(L, ds->value->ptr, ds->value->used - 1); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; +} + +static int magnet_status_get(lua_State *L) { + data_integer *di; + server *srv; + size_t key_len = 0; + + const char *key = luaL_checklstring(L, 2, &key_len); + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + di = status_counter_get_counter(srv, key, key_len); + + lua_pushnumber(L, (double)di->value); + + return 1; +} + +static int magnet_status_set(lua_State *L) { + size_t key_len = 0; + server *srv; + + const char *key = luaL_checklstring(L, 2, &key_len); + int counter = luaL_checkint(L, 3); + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + status_counter_set(srv, key, key_len, counter); + + return 0; +} + +typedef struct { + const char *name; + enum { + MAGNET_ENV_UNSET, + + MAGNET_ENV_PHYICAL_PATH, + MAGNET_ENV_PHYICAL_REL_PATH, + MAGNET_ENV_PHYICAL_DOC_ROOT, + + MAGNET_ENV_URI_PATH, + MAGNET_ENV_URI_PATH_RAW, + MAGNET_ENV_URI_SCHEME, + MAGNET_ENV_URI_AUTHORITY, + MAGNET_ENV_URI_QUERY, + + MAGNET_ENV_REQUEST_METHOD, + MAGNET_ENV_REQUEST_URI, + MAGNET_ENV_REQUEST_PROTOCOL + } type; +} magnet_env_t; + +static buffer *magnet_env_get_buffer(server *srv, connection *con, const char *key) { + buffer *dest = NULL; + size_t i; + + const magnet_env_t env[] = { + { "physical.path", MAGNET_ENV_PHYICAL_PATH }, + { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH }, + { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT }, + + { "uri.path", MAGNET_ENV_URI_PATH }, + { "uri.path-raw", MAGNET_ENV_URI_PATH_RAW }, + { "uri.scheme", MAGNET_ENV_URI_SCHEME }, + { "uri.authority", MAGNET_ENV_URI_AUTHORITY }, + { "uri.query", MAGNET_ENV_URI_QUERY }, + + { "request.method", MAGNET_ENV_REQUEST_METHOD }, + { "request.uri", MAGNET_ENV_REQUEST_URI }, + { "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL }, + + { NULL, MAGNET_ENV_UNSET } + }; + + + /** + * map all internal variables to lua + * + */ + + for (i = 0; env[i].name; i++) { + if (0 == strcmp(key, env[i].name)) break; + } + + switch (env[i].type) { + case MAGNET_ENV_PHYICAL_PATH: dest = con->physical.path; break; + case MAGNET_ENV_PHYICAL_REL_PATH: dest = con->physical.rel_path; break; + case MAGNET_ENV_PHYICAL_DOC_ROOT: dest = con->physical.doc_root; break; + + case MAGNET_ENV_URI_PATH: dest = con->uri.path; break; + case MAGNET_ENV_URI_PATH_RAW: dest = con->uri.path_raw; break; + case MAGNET_ENV_URI_SCHEME: dest = con->uri.scheme; break; + case MAGNET_ENV_URI_AUTHORITY: dest = con->uri.authority; break; + case MAGNET_ENV_URI_QUERY: dest = con->uri.query; break; + + case MAGNET_ENV_REQUEST_METHOD: break; + case MAGNET_ENV_REQUEST_URI: dest = con->request.uri; break; + case MAGNET_ENV_REQUEST_PROTOCOL: break; + + case MAGNET_ENV_UNSET: break; + } + + return dest; +} + +static int magnet_env_get(lua_State *L) { + server *srv; + connection *con; + + const char *key = luaL_checkstring(L, 2); + buffer *dest = NULL; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + dest = magnet_env_get_buffer(srv, con, key); + + if (dest && dest->used) { + lua_pushlstring(L, dest->ptr, dest->used - 1); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int magnet_env_set(lua_State *L) { + server *srv; + connection *con; + + const char *key = luaL_checkstring(L, 2); + const char *val = luaL_checkstring(L, 3); + buffer *dest = NULL; + + lua_pushstring(L, "lighty.srv"); + lua_gettable(L, LUA_REGISTRYINDEX); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + lua_pushstring(L, "lighty.con"); + lua_gettable(L, LUA_REGISTRYINDEX); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (NULL != (dest = magnet_env_get_buffer(srv, con, key))) { + buffer_copy_string(dest, val); + } else { + /* couldn't save */ + + return luaL_error(L, "couldn't store '%s' in lighty.env[]", key); + } + + return 0; +} + + +static int magnet_copy_response_header(server *srv, connection *con, plugin_data *p, lua_State *L) { + /** + * get the environment of the function + */ + + lua_getfenv(L, -1); /* -1 is the function */ + + /* lighty.header */ + + lua_getfield(L, -1, "lighty"); /* lighty.* from the env */ + assert(lua_istable(L, -1)); + + lua_getfield(L, -1, "header"); /* lighty.header */ + if (lua_istable(L, -1)) { + /* header is found, and is a table */ + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_isstring(L, -1) && lua_isstring(L, -2)) { + const char *key, *val; + size_t key_len, val_len; + + key = lua_tolstring(L, -2, &key_len); + val = lua_tolstring(L, -1, &val_len); + + response_header_overwrite(srv, con, key, key_len, val, val_len); + } + + lua_pop(L, 1); + } + } + + lua_pop(L, 1); /* pop the header-table */ + lua_pop(L, 1); /* pop the lighty-env */ + lua_pop(L, 1); /* pop the function env */ + + return 0; +} + +/** + * walk through the content array + * + * content = { "<pre>", { file = "/content" } , "</pre>" } + * + * header["Content-Type"] = "text/html" + * + * return 200 + */ +static int magnet_attach_content(server *srv, connection *con, plugin_data *p, lua_State *L) { + /** + * get the environment of the function + */ + + assert(lua_isfunction(L, -1)); + lua_getfenv(L, -1); /* -1 is the function */ + + lua_getfield(L, -1, "lighty"); /* lighty.* from the env */ + assert(lua_istable(L, -1)); + + lua_getfield(L, -1, "content"); /* lighty.content */ + if (lua_istable(L, -1)) { + int i; + /* header is found, and is a table */ + + for (i = 1; ; i++) { + lua_rawgeti(L, -1, i); + + /* -1 is the value and should be the value ... aka a table */ + if (lua_isstring(L, -1)) { + size_t s_len = 0; + const char *s = lua_tolstring(L, -1, &s_len); + + chunkqueue_append_mem(con->write_queue, s, s_len + 1); + } else if (lua_istable(L, -1)) { + lua_getfield(L, -1, "filename"); + lua_getfield(L, -2, "length"); + lua_getfield(L, -3, "offset"); + + if (lua_isstring(L, -3)) { /* filename has to be a string */ + buffer *fn = buffer_init(); + stat_cache_entry *sce; + + buffer_copy_string(fn, lua_tostring(L, -3)); + + if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, fn, &sce)) { + off_t off = 0; + off_t len = 0; + + if (lua_isnumber(L, -1)) { + off = lua_tonumber(L, -1); + } + + if (lua_isnumber(L, -2)) { + len = lua_tonumber(L, -2); + } else { + len = sce->st.st_size; + } + + if (off < 0) { + return luaL_error(L, "offset for '%s' is negative", fn->ptr); + } + + if (len < off) { + return luaL_error(L, "offset > length for '%s'", fn->ptr); + } + + chunkqueue_append_file(con->write_queue, fn, off, len - off); + } + + buffer_free(fn); + } else { + lua_pop(L, 3 + 2); /* correct the stack */ + + return luaL_error(L, "content[%d] is a table and requires the field \"filename\"", i); + } + + lua_pop(L, 3); + } else if (lua_isnil(L, -1)) { + /* oops, end of list */ + + lua_pop(L, 1); + + break; + } else { + lua_pop(L, 4); + + return luaL_error(L, "content[%d] is neither a string nor a table: ", i); + } + + lua_pop(L, 1); /* pop the content[...] table */ + } + } else { + return luaL_error(L, "lighty.content has to be a table"); + } + lua_pop(L, 1); /* pop the header-table */ + lua_pop(L, 1); /* pop the lighty-table */ + lua_pop(L, 1); /* php the function env */ + + return 0; +} + +static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, buffer *name) { + lua_State *L; + int lua_return_value = -1; + /* get the script-context */ + + + L = script_cache_get_script(srv, con, p->cache, name); + + if (lua_isstring(L, -1)) { + log_error_write(srv, __FILE__, __LINE__, + "sbss", + "loading script", + name, + "failed:", + lua_tostring(L, -1)); + + lua_pop(L, 1); + + assert(lua_gettop(L) == 0); /* only the function should be on the stack */ + + con->http_status = 500; + + return HANDLER_FINISHED; + } + + lua_pushstring(L, "lighty.srv"); + lua_pushlightuserdata(L, srv); + lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = srv */ + + lua_pushstring(L, "lighty.con"); + lua_pushlightuserdata(L, con); + lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = con */ + + lua_atpanic(L, magnet_atpanic); + + /** + * we want to create empty environment for our script + * + * setmetatable({}, {__index = _G}) + * + * if a function, symbol is not defined in our env, __index will lookup + * in the global env. + * + * all variables created in the script-env will be thrown + * away at the end of the script run. + */ + lua_newtable(L); /* my empty environment aka {} (sp += 1) */ + + /* we have to overwrite the print function */ + lua_pushcfunction(L, magnet_print); /* (sp += 1) */ + lua_setfield(L, -2, "print"); /* -1 is the env we want to set(sp -= 1) */ + + /** + * lighty.request[] has the HTTP-request headers + * lighty.content[] is a table of string/file + * lighty.header[] is a array to set response headers + */ + + lua_newtable(L); /* lighty.* (sp += 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_reqhdr_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ + lua_setfield(L, -2, "request"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_env_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_env_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ + lua_setfield(L, -2, "env"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_status_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_status_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ + lua_setfield(L, -2, "status"); /* content = {} (sp -= 1) */ + + /* add empty 'content' and 'header' tables */ + lua_newtable(L); /* {} (sp += 1) */ + lua_setfield(L, -2, "content"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_setfield(L, -2, "header"); /* header = {} (sp -= 1) */ + + lua_pushinteger(L, MAGNET_RESTART_REQUEST); + lua_setfield(L, -2, "RESTART_REQUEST"); + + lua_setfield(L, -2, "lighty"); /* lighty.* (sp -= 1) */ + + lua_newtable(L); /* the meta-table for the new env (sp += 1) */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */ + lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */ + + + lua_setfenv(L, -2); /* on the stack should be a modified env (sp -= 1) */ + + if (lua_pcall(L, 0, 1, 0)) { + log_error_write(srv, __FILE__, __LINE__, + "ss", + "lua_pcall():", + lua_tostring(L, -1)); + lua_pop(L, 1); /* remove the error-msg and the function copy from the stack */ + + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + con->http_status = 500; + + return HANDLER_FINISHED; + } + + /* we should have the function-copy and the return value on the stack */ + assert(lua_gettop(L) == 2); + + if (lua_isnumber(L, -1)) { + /* if the ret-value is a number, take it */ + lua_return_value = (int)lua_tonumber(L, -1); + } + lua_pop(L, 1); /* pop the ret-value */ + + magnet_copy_response_header(srv, con, p, L); + + if (lua_return_value > 99) { + con->http_status = lua_return_value; + con->file_finished = 1; + + /* try { ...*/ + if (0 == setjmp(exceptionjmp)) { + magnet_attach_content(srv, con, p, L); + } else { + /* } catch () { */ + con->http_status = 500; + } + + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + /* we are finished */ + return HANDLER_FINISHED; + } else if (MAGNET_RESTART_REQUEST == lua_return_value) { + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + return HANDLER_COMEBACK; + } else { + assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + return HANDLER_GO_ON; + } +} + +static handler_t magnet_attract_array(server *srv, connection *con, plugin_data *p, array *files) { + size_t i; + + /* no filename set */ + if (files->used == 0) return HANDLER_GO_ON; + + /** + * execute all files and jump out on the first !HANDLER_GO_ON + */ + for (i = 0; i < files->used; i++) { + data_string *ds = (data_string *)files->data[i]; + handler_t ret; + + if (buffer_is_empty(ds->value)) continue; + + ret = magnet_attract(srv, con, p, ds->value); + + if (ret != HANDLER_GO_ON) return ret; + } + + return HANDLER_GO_ON; +} + +URIHANDLER_FUNC(mod_magnet_uri_handler) { + plugin_data *p = p_d; + + mod_magnet_patch_connection(srv, con, p); + + return magnet_attract_array(srv, con, p, p->conf.url_raw); +} + +URIHANDLER_FUNC(mod_magnet_physical) { + plugin_data *p = p_d; + + mod_magnet_patch_connection(srv, con, p); + + return magnet_attract_array(srv, con, p, p->conf.physical_path); +} + + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_magnet_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("magnet"); + + p->init = mod_magnet_init; + p->handle_uri_clean = mod_magnet_uri_handler; + p->handle_physical = mod_magnet_physical; + p->set_defaults = mod_magnet_set_defaults; + p->cleanup = mod_magnet_free; + + p->data = NULL; + + return 0; +} + +#else +int mod_magnet_plugin_init(plugin *p) { + return -1; +} +#endif |