-------- 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

Reply via email to