You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Rüdiger Plüm <ru...@vodafone.com> on 2012/08/27 09:41:33 UTC

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


-------- 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: 	humbedooh@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


RE: Fwd: 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

Posted by Plüm, Rüdiger, Vodafone Group <ru...@vodafone.com>.

> -----Original Message-----
> From: Daniel Gruno [mailto:rumble@cord.dk]
> Sent: Montag, 27. August 2012 14:06
> To: dev@httpd.apache.org
> Subject: Re: Fwd: 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
> 
> On 08/27/2012 11:59 AM, Plüm, Rüdiger, Vodafone Group wrote:
> >
> > Thanks. I guess you noticed that I found more important issues with
> the code than this comment :-)
> >
> > Regards
> >
> > Rüdiger
> >
> I've tried my best to correct the errors you mentioned, but I'm having

You should try to leave the loop if ap_pass_brigade does not return APR_SUCCESS and should return this (!= APR_SUCCESS) to
the caller. It is always a bad sign if the return code of ap_pass_brigade gets lost unhandled or unpassed.

> some trouble figuring out how the input filter should pass along data to
> the next in the chain. What I have currently is this:
> 
> /* Get the output from Lua's yield() as a string */
> const char* output = lua_tolstring(L, 1, &olen);
> /* Make a bucket for it */
> pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc);
> /* Add it to the brigade */
> APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
> /* Do something more here involving cleanups or..?? */
> 
> But then what? Am I supposed to make a call to some ap_*_brigade
> function? I've checked some of the example filter modules, but I can't
> seem to find any suitable way of doing this other than finishing up the
> brigade and returning APR_SUCCESS in the end.

Keep the input brigade you fetched in the context and return to the caller.
You will be called again if more data is needed. Then you can continue processing the
data in your context input brigade first and fetch further data on your own if you run
out of data.

Regards

Rüdiger

Re: Fwd: 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

Posted by Daniel Gruno <ru...@cord.dk>.
On 08/27/2012 11:59 AM, Plüm, Rüdiger, Vodafone Group wrote:
> 
> Thanks. I guess you noticed that I found more important issues with the code than this comment :-)
> 
> Regards
> 
> Rüdiger
> 
I've tried my best to correct the errors you mentioned, but I'm having
some trouble figuring out how the input filter should pass along data to
the next in the chain. What I have currently is this:

/* Get the output from Lua's yield() as a string */
const char* output = lua_tolstring(L, 1, &olen);
/* Make a bucket for it */
pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc);
/* Add it to the brigade */
APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
/* Do something more here involving cleanups or..?? */

But then what? Am I supposed to make a call to some ap_*_brigade
function? I've checked some of the example filter modules, but I can't
seem to find any suitable way of doing this other than finishing up the
brigade and returning APR_SUCCESS in the end.

Any pointers (or some code) would be much appreciated. I have plenty
imagination, but my httpd C API knowledge can be somewhat lacking at
certain points.

With regards,
Daniel.

Re: Fwd: 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

Posted by Daniel Gruno <ru...@cord.dk>.
On 08/27/2012 11:59 AM, Plüm, Rüdiger, Vodafone Group wrote:
> 
> Thanks. I guess you noticed that I found more important issues with the code than this comment :-)
> 
> Regards
> 
> Rüdiger
> 
Haha, yeah, but that's a big mess to clean up (I'm not a programmer by
trade, so I'm a bit slow in that regard ;) ), so it will take me a while
to get through all that, but I will get through it...eventually.

With regards,
Daniel.

RE: Fwd: 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

Posted by Plüm, Rüdiger, Vodafone Group <ru...@vodafone.com>.

> -----Original Message-----
> From: Daniel Gruno [mailto:rumble@cord.dk]
> Sent: Montag, 27. August 2012 10:45
> To: dev@httpd.apache.org
> Subject: Re: Fwd: 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
> 
> On 08/27/2012 09:41 AM, Rüdiger Plüm wrote:
> 
> > +    /* Clean up and pass on the brigade to the next filter in the
> chain */
> >
> >
> > Where do we do a cleanup here?
> >
> > +    return APR_SUCCESS;
> > +}
> > +
> <snip>
> >
> > Regards
> >
> > Rüdiger
> >
> Heh, I believe that's what is called a copypasto. Both cleanups and
> passing on content is done earlier in the chain, the specific comment is
> simply a remnant of creating the input filter based on the code used for
> the output filter (you'll see the same comment in the output filter
> code, where it actually makes sense). I'll remove the comment.

Thanks. I guess you noticed that I found more important issues with the code than this comment :-)

Regards

Rüdiger


Re: Fwd: 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

Posted by Daniel Gruno <ru...@cord.dk>.
On 08/27/2012 09:41 AM, Rüdiger Plüm wrote:

> +    /* Clean up and pass on the brigade to the next filter in the chain */
> 
> 
> Where do we do a cleanup here?
> 
> +    return APR_SUCCESS;
> +}
> +
<snip>
> 
> Regards
> 
> Rüdiger
> 
Heh, I believe that's what is called a copypasto. Both cleanups and
passing on content is done earlier in the chain, the specific comment is
simply a remnant of creating the input filter based on the code used for
the output filter (you'll see the same comment in the output filter
code, where it actually makes sense). I'll remove the comment.

With regards and thanks,
Daniel.