-------- Original Message --------
Subject: svn commit: r1377475 - in /httpd/httpd/trunk: CHANGES
docs/manual/mod/mod_lua.xml
docs/manual/style/scripts/prettify.js modules/lua/lua_vmprep.h
modules/lua/mod_lua.c modules/lua/mod_lua.h
Date: Sun, 26 Aug 2012 18:39:59 GMT
From: humbed...@apache.org
Author: humbedooh
Date: Sun Aug 26 18:39:58 2012
New Revision: 1377475
URL: http://svn.apache.org/viewvc?rev=1377475&view=rev
Log:
Add new directives, LuaInputFilter/LuaOutputFilter for creating content filters
using Lua.
Modified:
httpd/httpd/trunk/CHANGES
httpd/httpd/trunk/docs/manual/mod/mod_lua.xml
httpd/httpd/trunk/docs/manual/style/scripts/prettify.js
httpd/httpd/trunk/modules/lua/lua_vmprep.h
httpd/httpd/trunk/modules/lua/mod_lua.c
httpd/httpd/trunk/modules/lua/mod_lua.h
Modified: httpd/httpd/trunk/modules/lua/mod_lua.c
URL:
http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/lua/mod_lua.c?rev=1377475&r1=1377474&r2=1377475&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/lua/mod_lua.c (original)
+++ httpd/httpd/trunk/modules/lua/mod_lua.c Sun Aug 26 18:39:58 2012
@@ -55,6 +55,14 @@ typedef struct {
apr_hash_t *lua_authz_providers;
+typedef struct
+{
+ apr_bucket_brigade *tmpBucket;
+ lua_State *L;
+ ap_lua_vm_spec *spec;
+ int broken;
+} lua_filter_ctx;
+
/**
* error reporting if lua has an error.
@@ -290,6 +298,281 @@ static int lua_handler(request_rec *r)
}
+/* ------------------- Input/output content filters ------------------- */
+
+
+static apr_status_t lua_setup_filter_ctx(ap_filter_t* f, request_rec* r,
lua_filter_ctx**
c) {
+ apr_pool_t *pool;
+ ap_lua_vm_spec *spec;
+ int n, rc;
+ lua_State *L;
+ lua_filter_ctx *ctx;
+ ap_lua_server_cfg *server_cfg =
ap_get_module_config(r->server->module_config,
+&lua_module);
+ const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
+&lua_module);
+
+ ctx = apr_pcalloc(r->pool, sizeof(lua_filter_ctx));
+ ctx->broken = 0;
+ *c = ctx;
+ /* Find the filter that was called */
+ for (n = 0; n< cfg->mapped_filters->nelts; n++) {
+ ap_lua_filter_handler_spec *hook_spec =
+ ((ap_lua_filter_handler_spec **) cfg->mapped_filters->elts)[n];
+
+ if (hook_spec == NULL) {
+ continue;
+ }
+ if (!strcasecmp(hook_spec->filter_name, f->frec->name)) {
+ spec = create_vm_spec(&pool, r, cfg, server_cfg,
+ hook_spec->file_name,
+ NULL,
+ 0,
+ hook_spec->function_name,
+ "filter");
+ L = ap_lua_get_lua_state(pool, spec, r);
+ if (L) {
+ L = lua_newthread(L);
+ }
+
+ if (!L) {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01477)
+ "lua: Failed to obtain lua interpreter for %s
%s",
+ hook_spec->function_name,
hook_spec->file_name);
+ ap_lua_release_state(L, spec, r);
+ return APR_EGENERAL;
+ }
+ if (hook_spec->function_name != NULL) {
+ lua_getglobal(L, hook_spec->function_name);
+ if (!lua_isfunction(L, -1)) {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01478)
+ "lua: Unable to find function %s in %s",
+ hook_spec->function_name,
+ hook_spec->file_name);
+ ap_lua_release_state(L, spec, r);
+ return APR_EGENERAL;
+ }
+
+ ap_lua_run_lua_request(L, r);
+ }
+ else {
+ int t;
+ ap_lua_run_lua_request(L, r);
+
+ t = lua_gettop(L);
+ lua_setglobal(L, "r");
+ lua_settop(L, t);
+ }
+ ctx->L = L;
+ ctx->spec = spec;
+
+ /* If a Lua filter is interested in filtering a request, it must
first do a yield,
+ * otherwise we'll assume that it's not interested and pretend we
didn't find
it.
+ */
+ rc = lua_resume(L, 1);
+ if (rc == LUA_YIELD) {
+ return OK;
+ }
+ else {
+ ap_lua_release_state(L, spec, r);
+ return APR_ENOENT;
+ }
+ }
+ }
+ return APR_ENOENT;
+}
+
+static apr_status_t lua_output_filter_handle(ap_filter_t *f,
apr_bucket_brigade *pbbIn) {
+ apr_bucket *e;
+ request_rec *r = f->r;
+ int rc;
+ lua_State *L;
+ lua_filter_ctx* ctx;
+ conn_rec *c = r->connection;
+ apr_bucket *pbktIn;
+ apr_bucket_brigade *pbbOut = NULL;
+
+ /* Set up the initial filter context and acquire the function.
+ * The corresponding Lua function should yield here.
+ */
+ if (!f->ctx) {
+ rc = lua_setup_filter_ctx(f,r,&ctx);
+ if (rc == APR_EGENERAL) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ if (rc == APR_ENOENT) {
+ /* No filter entry found (or the script declined to filter), just
pass on the
buckets */
In this case you should remove yourself from the filter chain via
ap_remove_output_filter(f);
to avoid entering the filter code again.
+ return ap_pass_brigade(f->next,pbbIn);
+ }
+ f->ctx = ctx;
+ }
+ ctx = (lua_filter_ctx*) f->ctx;
+ L = ctx->L;
+ /* While the Lua function is still yielding, pass in buckets to the
coroutine */
+ if (!ctx->broken) {
+ pbbOut=apr_brigade_create(r->pool, c->bucket_alloc);
Create this brigade once and store it in the context (ctx) to avoid memory
leaks. Just clean it up
every time you get here.
+ for (pbktIn = APR_BRIGADE_FIRST(pbbIn);
+ pbktIn != APR_BRIGADE_SENTINEL(pbbIn);
+ pbktIn = APR_BUCKET_NEXT(pbktIn))
+ {
+ const char *data;
+ apr_size_t len;
+ apr_bucket *pbktOut;
+
+ /* read the bucket */
+ apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ);
Style. There are several location in this this patch where spaces are missing
after ',' or around '='.
+
+ /* Push the bucket onto the Lua stack as a global var */
+ lua_pushlstring(L, data, len);
+ lua_setglobal(L, "bucket");
+
+ /* If Lua yielded, it means we have something to pass on */
+ if (lua_resume(L, 0) == LUA_YIELD) {
+ size_t olen;
+ const char* output = lua_tolstring(L, 1,&olen);
+ pbktOut = apr_bucket_heap_create(output, olen, NULL,
+ c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut);
Please have a look at
http://httpd.apache.org/docs/2.4/developer/output-filters.html#filtering
Do not consume the whole brigade before passing it along after the loop. Always
pass it along directly.
Think of a brigade that just contains one 16GB file bucket. That for sure would
let the server explode.
+ }
+ else {
+ ctx->broken = 1;
+ ap_lua_release_state(L, ctx->spec, r);
If you no intend to continue filtering remove yourself from the chain:
ap_remove_output_filter(f);
And do not forget to clean up the input brigade here as well.
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ /* If we've safely reached the end, do a final call to Lua to allow
for any
+ finishing moves by the script, such as appending a tail. */
+ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(pbbIn))) {
+ apr_bucket *pbktEOS;
+ lua_pushnil(L);
+ lua_setglobal(L, "bucket");
+ if (lua_resume(L, 0) == LUA_YIELD) {
+ apr_bucket *pbktOut;
+ size_t olen;
+ const char* output = lua_tolstring(L, 1,&olen);
+ pbktOut = apr_bucket_heap_create(output, olen, NULL,
+ c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut);
+ }
+ pbktEOS=apr_bucket_eos_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pbbOut,pbktEOS);
+ ap_lua_release_state(L, ctx->spec, r);
+ }
+ }
+ /* Clean up and pass on the brigade to the next filter in the chain */
+ apr_brigade_cleanup(pbbIn);
+ if (pbbOut) {
+ return ap_pass_brigade(f->next,pbbOut);
+ }
+ else {
+ return ap_pass_brigade(f->next,pbbIn);
Why cleaning up pbbIn first and then passing it along?
+ }
+}
+
+
+
+static apr_status_t lua_input_filter_handle(ap_filter_t *f,
+ apr_bucket_brigade *pbbOut,
+ ap_input_mode_t eMode,
+ apr_read_type_e eBlock,
+ apr_off_t nBytes)
+{
+ request_rec *r = f->r;
+ int rc, lastCall = 0;
+ lua_State *L;
+ lua_filter_ctx* ctx;
+ conn_rec *c = r->connection;
+ apr_status_t ret;
+
+ /* Set up the initial filter context and acquire the function.
+ * The corresponding Lua function should yield here.
+ */
+ if (!f->ctx) {
+ rc = lua_setup_filter_ctx(f,r,&ctx);
+ f->ctx = ctx;
+ ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
Why creating a brigade if we might error out below.
+ if (rc == APR_EGENERAL) {
+ ctx->broken = 1;
We should remove ourselves here.
ap_remove_input_filter(f);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ if (rc == APR_ENOENT ) {
+ ctx->broken = 1;
same here. We should remove ourselves from the chain if we do not want to do
anything
+ }
+ }
+ ctx = (lua_filter_ctx*) f->ctx;
+ L = ctx->L;
+ /* If the Lua script broke or denied serving the request, just pass the
buckets through
*/
+ if (ctx->broken) {
+ return ap_get_brigade(f->next, pbbOut, eMode, eBlock, nBytes);
+ }
+
+ if (APR_BRIGADE_EMPTY(ctx->tmpBucket)) {
+ ret = ap_get_brigade(f->next, ctx->tmpBucket, eMode, eBlock, nBytes);
+
+ if (eMode == AP_MODE_EATCRLF || ret != APR_SUCCESS)
+ return ret;
+ }
+
+ /* While the Lua function is still yielding, pass buckets to the coroutine
*/
+ if (!ctx->broken) {
+ lastCall = 0;
+ while(!APR_BRIGADE_EMPTY(ctx->tmpBucket)) {
+ apr_bucket *pbktIn = APR_BRIGADE_FIRST(ctx->tmpBucket);
+ apr_bucket *pbktOut;
+ const char *data;
+ apr_size_t len;
+
+ if(APR_BUCKET_IS_EOS(pbktIn)) {
+ APR_BUCKET_REMOVE(pbktIn);
+ break;
+ }
+
+ /* read the bucket */
+ ret=apr_bucket_read(pbktIn,&data,&len, eBlock);
+ if(ret != APR_SUCCESS) {
+ return ret;
+ }
+
+ /* Push the bucket onto the Lua stack as a global var */
+ lastCall++;
+ lua_pushlstring(L, data, len);
+ lua_setglobal(L, "bucket");
+
+ /* If Lua yielded, it means we have something to pass on */
+ if (lua_resume(L, 0) == LUA_YIELD) {
+ size_t olen;
+ const char* output = lua_tolstring(L, 1,&olen);
+ pbktOut = apr_bucket_heap_create(output, olen, 0,
c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
Same issue here: Don't queue up everything before passing it along.
+ apr_bucket_delete(pbktIn);
+ }
+ else {
+ ctx->broken = 1;
+ ap_lua_release_state(L, ctx->spec, r);
We should remove ourselves here.
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ /* If we've safely reached the end, do a final call to Lua to allow
for any
+ finishing moves by the script, such as appending a tail. */
+ if (lastCall == 0) {
+ apr_bucket *pbktEOS = apr_bucket_eos_create(c->bucket_alloc);
+ lua_pushnil(L);
+ lua_setglobal(L, "bucket");
+ if (lua_resume(L, 0) == LUA_YIELD) {
+ apr_bucket *pbktOut;
+ size_t olen;
+ const char* output = lua_tolstring(L, 1,&olen);
+ pbktOut = apr_bucket_heap_create(output, olen, 0,
c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
+ }
+ APR_BRIGADE_INSERT_TAIL(pbbOut,pbktEOS);
+ ap_lua_release_state(L, ctx->spec, r);
+ }
+ }
+ /* Clean up and pass on the brigade to the next filter in the chain */
Where do we do a cleanup here?
+ return APR_SUCCESS;
+}
+
/* ---------------- Configury stuff --------------- */
Regards
Rüdiger