mirror of
https://github.com/apache/httpd.git
synced 2025-08-08 15:02:10 +03:00
Add new directives, LuaInputFilter/LuaOutputFilter for creating content filters using Lua.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1377475 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
3
CHANGES
3
CHANGES
@@ -1,6 +1,9 @@
|
|||||||
-*- coding: utf-8 -*-
|
-*- coding: utf-8 -*-
|
||||||
Changes with Apache 2.5.0
|
Changes with Apache 2.5.0
|
||||||
|
|
||||||
|
*) mod_lua: Add LuaInputFilter/LuaOutputFilter for creating content
|
||||||
|
filters in Lua [Daniel Gruno]
|
||||||
|
|
||||||
*) core: Apply length limit when logging Status header values.
|
*) core: Apply length limit when logging Status header values.
|
||||||
[Jeff Trawick, Chris Darroch]
|
[Jeff Trawick, Chris Darroch]
|
||||||
|
|
||||||
|
@@ -1186,4 +1186,159 @@ end
|
|||||||
</directivesynopsis>
|
</directivesynopsis>
|
||||||
|
|
||||||
|
|
||||||
|
<directivesynopsis>
|
||||||
|
<name>LuaInputFilter</name>
|
||||||
|
<description>Provide a Lua function for content input filtering</description>
|
||||||
|
<syntax>LuaInputFilter filter_name /path/to/lua/script.lua function_name</syntax>
|
||||||
|
<contextlist><context>server config</context> </contextlist>
|
||||||
|
<compatibility>2.5.0 and later</compatibility>
|
||||||
|
|
||||||
|
<usage>
|
||||||
|
<p>Provides a means of adding a Lua function as an input filter.
|
||||||
|
As with output filters, input filters work as coroutines,
|
||||||
|
first yielding before buffers are sent, then yielding whenever
|
||||||
|
a bucket needs to be passed down the chain, and finally (optionally)
|
||||||
|
yielding anything that needs to be appended to the input data. The
|
||||||
|
global variable <code>bucket</code> holds the buckets as they are passed
|
||||||
|
onto the Lua script:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<highlight language="config">
|
||||||
|
LuaInputFilter myInputFilter /www/filter.lua input_filter
|
||||||
|
<FilesMatch "\.lua>
|
||||||
|
SetInputFilter myInputFilter
|
||||||
|
</FilesMatch>
|
||||||
|
</highlight>
|
||||||
|
<highlight language="lua">
|
||||||
|
--[[
|
||||||
|
Example input filter that converts all POST data to uppercase.
|
||||||
|
]]--
|
||||||
|
function input_filter(r)
|
||||||
|
print("luaInputFilter called") -- debug print
|
||||||
|
coroutine.yield() -- Yield and wait for buckets
|
||||||
|
while bucket do -- For each bucket, do...
|
||||||
|
local output = string.upper(bucket) -- Convert all POST data to uppercase
|
||||||
|
coroutine.yield(output) -- Send converted data down the chain
|
||||||
|
end
|
||||||
|
-- No more buckets available.
|
||||||
|
coroutine.yield("&filterSignature=1234") -- Append signature at the end
|
||||||
|
end
|
||||||
|
</highlight>
|
||||||
|
<p>
|
||||||
|
The input filter supports denying/skipping a filter if it is deemed unwanted:
|
||||||
|
</p>
|
||||||
|
<highlight language="lua">
|
||||||
|
function input_filter(r)
|
||||||
|
if not good then
|
||||||
|
return -- Simply deny filtering, passing on the original content instead
|
||||||
|
end
|
||||||
|
coroutine.yield() -- wait for buckets
|
||||||
|
... -- insert filter stuff here
|
||||||
|
end
|
||||||
|
</highlight>
|
||||||
|
<p>
|
||||||
|
See "<a href="#modifying_buckets">Modifying contents with Lua
|
||||||
|
filters</a>" for more information.
|
||||||
|
</p>
|
||||||
|
</usage>
|
||||||
|
</directivesynopsis>
|
||||||
|
|
||||||
|
<directivesynopsis>
|
||||||
|
<name>LuaOutputFilter</name>
|
||||||
|
<description>Provide a Lua function for content output filtering</description>
|
||||||
|
<syntax>LuaOutputFilter filter_name /path/to/lua/script.lua function_name</syntax>
|
||||||
|
<contextlist><context>server config</context> </contextlist>
|
||||||
|
<compatibility>2.5.0 and later</compatibility>
|
||||||
|
|
||||||
|
<usage>
|
||||||
|
<p>Provides a means of adding a Lua function as an output filter.
|
||||||
|
As with input filters, output filters work as coroutines,
|
||||||
|
first yielding before buffers are sent, then yielding whenever
|
||||||
|
a bucket needs to be passed down the chain, and finally (optionally)
|
||||||
|
yielding anything that needs to be appended to the input data. The
|
||||||
|
global variable <code>bucket</code> holds the buckets as they are passed
|
||||||
|
onto the Lua script:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<highlight language="config">
|
||||||
|
LuaOutputFilter myOutputFilter /www/filter.lua output_filter
|
||||||
|
<FilesMatch "\.lua>
|
||||||
|
SetOutputFilter myOutputFilter
|
||||||
|
</FilesMatch>
|
||||||
|
</highlight>
|
||||||
|
<highlight language="lua">
|
||||||
|
--[[
|
||||||
|
Example output filter that escapes all HTML entities in the output
|
||||||
|
]]--
|
||||||
|
function output_filter(r)
|
||||||
|
coroutine.yield("(Handled by myOutputFilter)<br/>\n") -- Prepend some data to the output,
|
||||||
|
-- yield and wait for buckets.
|
||||||
|
while bucket do -- For each bucket, do...
|
||||||
|
local output = r:escape_html(bucket) -- Escape all output
|
||||||
|
coroutine.yield(output) -- Send converted data down the chain
|
||||||
|
end
|
||||||
|
-- No more buckets available.
|
||||||
|
end
|
||||||
|
</highlight>
|
||||||
|
<p>
|
||||||
|
As with the input filter, the output filter supports denying/skipping a filter
|
||||||
|
if it is deemed unwanted:
|
||||||
|
</p>
|
||||||
|
<highlight language="lua">
|
||||||
|
function output_filter(r)
|
||||||
|
if not r.content_type:match("text/html") then
|
||||||
|
return -- Simply deny filtering, passing on the original content instead
|
||||||
|
end
|
||||||
|
coroutine.yield() -- wait for buckets
|
||||||
|
... -- insert filter stuff here
|
||||||
|
end
|
||||||
|
</highlight>
|
||||||
|
<p>
|
||||||
|
See "<a href="#modifying_buckets">Modifying contents with Lua filters</a>" for more
|
||||||
|
information.
|
||||||
|
</p>
|
||||||
|
</usage>
|
||||||
|
</directivesynopsis>
|
||||||
|
|
||||||
|
<section id="modifying_buckets">
|
||||||
|
<title>Modifying contents with Lua filters</title>
|
||||||
|
<p>
|
||||||
|
Filter functions implemented via <directive module="mod_lua">LuaInputFilter</directive>
|
||||||
|
or <directive module="mod_lua">LuaOutputFilter</directive> are designed as
|
||||||
|
three-stage non-blocking functions using coroutines to suspend and resume a
|
||||||
|
function as buckets are sent down the filter chain. The core structure of
|
||||||
|
such a function is:
|
||||||
|
</p>
|
||||||
|
<highlight language="lua">
|
||||||
|
function filter(r)
|
||||||
|
-- Our first yield is to signal that we are ready to receive buckets.
|
||||||
|
-- Before this yield, we can set up our environment, check for conditions,
|
||||||
|
-- and, if we deem it necessary, decline filtering a request alltogether:
|
||||||
|
if something_bad then
|
||||||
|
return -- This would skip this filter.
|
||||||
|
end
|
||||||
|
-- Regardless of whether we have data to prepend, a yield MUST be called here.
|
||||||
|
-- Note that only output filters can prepend data. Input filters must use the
|
||||||
|
-- final stage to append data to the content.
|
||||||
|
coroutine.yield([optional header to be prepended to the content])
|
||||||
|
|
||||||
|
-- After we have yielded, buckets will be sent to us, one by one, and we can
|
||||||
|
-- do whatever we want with them and then pass on the result.
|
||||||
|
-- Buckets are stored in the global variable 'bucket', so we create a loop
|
||||||
|
-- that checks if 'bucket' is not nil:
|
||||||
|
while bucket ~= nil do
|
||||||
|
local output = mangle(bucket) -- Do some stuff to the content
|
||||||
|
coroutine.yield(output) -- Return our new content to the filter chain
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Once the buckets are gone, 'bucket' is set to nil, which will exit the
|
||||||
|
-- loop and land us here. Anything extra we want to append to the content
|
||||||
|
-- can be done by doing a final yield here. Both input and output filters
|
||||||
|
-- can append data to the content in this phase.
|
||||||
|
coroutine.yield([optional footer to be appended to the content])
|
||||||
|
end
|
||||||
|
</highlight>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
</modulesynopsis>
|
</modulesynopsis>
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -47,6 +47,9 @@
|
|||||||
#define AP_LUA_CACHE_STAT 2
|
#define AP_LUA_CACHE_STAT 2
|
||||||
#define AP_LUA_CACHE_FOREVER 3
|
#define AP_LUA_CACHE_FOREVER 3
|
||||||
|
|
||||||
|
#define AP_LUA_FILTER_INPUT 1
|
||||||
|
#define AP_LUA_FILTER_OUTPUT 2
|
||||||
|
|
||||||
typedef void (*ap_lua_state_open_callback) (lua_State *L, apr_pool_t *p,
|
typedef void (*ap_lua_state_open_callback) (lua_State *L, apr_pool_t *p,
|
||||||
void *ctx);
|
void *ctx);
|
||||||
/**
|
/**
|
||||||
@@ -93,6 +96,14 @@ typedef struct
|
|||||||
int codecache;
|
int codecache;
|
||||||
} ap_lua_mapped_handler_spec;
|
} ap_lua_mapped_handler_spec;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *function_name;
|
||||||
|
const char *file_name;
|
||||||
|
const char* filter_name;
|
||||||
|
int direction; /* AP_LUA_FILTER_INPUT | AP_LUA_FILTER_OUTPUT */
|
||||||
|
} ap_lua_filter_handler_spec;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
apr_size_t runs;
|
apr_size_t runs;
|
||||||
apr_time_t modified;
|
apr_time_t modified;
|
||||||
|
@@ -55,6 +55,14 @@ typedef struct {
|
|||||||
|
|
||||||
apr_hash_t *lua_authz_providers;
|
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.
|
* 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 */
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ctx->broken = 1;
|
||||||
|
ap_lua_release_state(L, ctx->spec, r);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (rc == APR_EGENERAL) {
|
||||||
|
ctx->broken = 1;
|
||||||
|
return HTTP_INTERNAL_SERVER_ERROR;
|
||||||
|
}
|
||||||
|
if (rc == APR_ENOENT ) {
|
||||||
|
ctx->broken = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
apr_bucket_delete(pbktIn);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ctx->broken = 1;
|
||||||
|
ap_lua_release_state(L, ctx->spec, r);
|
||||||
|
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 */
|
||||||
|
return APR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---------------- Configury stuff --------------- */
|
/* ---------------- Configury stuff --------------- */
|
||||||
|
|
||||||
@@ -753,6 +1036,33 @@ static const char *register_mapped_file_function_hook(const char *pattern,
|
|||||||
*(ap_lua_mapped_handler_spec **) apr_array_push(cfg->mapped_handlers) = spec;
|
*(ap_lua_mapped_handler_spec **) apr_array_push(cfg->mapped_handlers) = spec;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
static const char *register_filter_function_hook(const char *filter,
|
||||||
|
cmd_parms *cmd,
|
||||||
|
void *_cfg,
|
||||||
|
const char *file,
|
||||||
|
const char *function,
|
||||||
|
int direction)
|
||||||
|
{
|
||||||
|
ap_lua_filter_handler_spec *spec;
|
||||||
|
ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
|
||||||
|
|
||||||
|
spec = apr_pcalloc(cmd->pool, sizeof(ap_lua_filter_handler_spec));
|
||||||
|
spec->file_name = apr_pstrdup(cmd->pool, file);
|
||||||
|
spec->function_name = apr_pstrdup(cmd->pool, function);
|
||||||
|
spec->filter_name = filter;
|
||||||
|
|
||||||
|
*(ap_lua_filter_handler_spec **) apr_array_push(cfg->mapped_filters) = spec;
|
||||||
|
/* TODO: Make it work on other types than just AP_FTYPE_RESOURCE? */
|
||||||
|
if (direction == AP_LUA_FILTER_OUTPUT) {
|
||||||
|
spec->direction = AP_LUA_FILTER_OUTPUT;
|
||||||
|
ap_register_output_filter(filter, lua_output_filter_handle, NULL, AP_FTYPE_RESOURCE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
spec->direction = AP_LUA_FILTER_INPUT;
|
||||||
|
ap_register_input_filter(filter, lua_input_filter_handle, NULL, AP_FTYPE_RESOURCE);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
static int lua_check_user_id_harness_first(request_rec *r)
|
static int lua_check_user_id_harness_first(request_rec *r)
|
||||||
{
|
{
|
||||||
return lua_request_rec_hook_harness(r, "check_user_id", AP_LUA_HOOK_FIRST);
|
return lua_request_rec_hook_harness(r, "check_user_id", AP_LUA_HOOK_FIRST);
|
||||||
@@ -1027,6 +1337,30 @@ static const char *register_map_handler(cmd_parms *cmd, void *_cfg,
|
|||||||
return register_mapped_file_function_hook(match, cmd, _cfg, file,
|
return register_mapped_file_function_hook(match, cmd, _cfg, file,
|
||||||
function);
|
function);
|
||||||
}
|
}
|
||||||
|
static const char *register_output_filter(cmd_parms *cmd, void *_cfg,
|
||||||
|
const char* filter, const char *file, const char *function)
|
||||||
|
{
|
||||||
|
const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|
|
||||||
|
NOT_IN_HTACCESS);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (!function) function = "handle";
|
||||||
|
return register_filter_function_hook(filter, cmd, _cfg, file,
|
||||||
|
function, AP_LUA_FILTER_OUTPUT);
|
||||||
|
}
|
||||||
|
static const char *register_input_filter(cmd_parms *cmd, void *_cfg,
|
||||||
|
const char* filter, const char *file, const char *function)
|
||||||
|
{
|
||||||
|
const char *err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES|
|
||||||
|
NOT_IN_HTACCESS);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (!function) function = "handle";
|
||||||
|
return register_filter_function_hook(filter, cmd, _cfg, file,
|
||||||
|
function, AP_LUA_FILTER_INPUT);
|
||||||
|
}
|
||||||
static const char *register_quick_block(cmd_parms *cmd, void *_cfg,
|
static const char *register_quick_block(cmd_parms *cmd, void *_cfg,
|
||||||
const char *line)
|
const char *line)
|
||||||
{
|
{
|
||||||
@@ -1448,6 +1782,10 @@ command_rec lua_commands[] = {
|
|||||||
"(internal) Byte code handler"),
|
"(internal) Byte code handler"),
|
||||||
AP_INIT_TAKE23("LuaMapHandler", register_map_handler, NULL, OR_ALL,
|
AP_INIT_TAKE23("LuaMapHandler", register_map_handler, NULL, OR_ALL,
|
||||||
"Maps a path to a lua handler"),
|
"Maps a path to a lua handler"),
|
||||||
|
AP_INIT_TAKE3("LuaOutputFilter", register_output_filter, NULL, OR_ALL,
|
||||||
|
"Registers a Lua function as an output filter"),
|
||||||
|
AP_INIT_TAKE3("LuaInputFilter", register_input_filter, NULL, OR_ALL,
|
||||||
|
"Registers a Lua function as an input filter"),
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1459,6 +1797,8 @@ static void *create_dir_config(apr_pool_t *p, char *dir)
|
|||||||
cfg->package_cpaths = apr_array_make(p, 2, sizeof(char *));
|
cfg->package_cpaths = apr_array_make(p, 2, sizeof(char *));
|
||||||
cfg->mapped_handlers =
|
cfg->mapped_handlers =
|
||||||
apr_array_make(p, 1, sizeof(ap_lua_mapped_handler_spec *));
|
apr_array_make(p, 1, sizeof(ap_lua_mapped_handler_spec *));
|
||||||
|
cfg->mapped_filters =
|
||||||
|
apr_array_make(p, 1, sizeof(ap_lua_filter_handler_spec *));
|
||||||
cfg->pool = p;
|
cfg->pool = p;
|
||||||
cfg->hooks = apr_hash_make(p);
|
cfg->hooks = apr_hash_make(p);
|
||||||
cfg->dir = apr_pstrdup(p, dir);
|
cfg->dir = apr_pstrdup(p, dir);
|
||||||
@@ -1537,18 +1877,21 @@ static void *merge_dir_config(apr_pool_t *p, void *basev, void *overridesv)
|
|||||||
a->package_paths = apr_array_append(p, base->package_paths, overrides->package_paths);
|
a->package_paths = apr_array_append(p, base->package_paths, overrides->package_paths);
|
||||||
a->package_cpaths = apr_array_append(p, base->package_cpaths, overrides->package_cpaths);
|
a->package_cpaths = apr_array_append(p, base->package_cpaths, overrides->package_cpaths);
|
||||||
a->mapped_handlers = apr_array_append(p, base->mapped_handlers, overrides->mapped_handlers);
|
a->mapped_handlers = apr_array_append(p, base->mapped_handlers, overrides->mapped_handlers);
|
||||||
|
a->mapped_filters = apr_array_append(p, base->mapped_filters, overrides->mapped_filters);
|
||||||
a->hooks = apr_hash_merge(p, overrides->hooks, base->hooks, overlay_hook_specs, NULL);
|
a->hooks = apr_hash_merge(p, overrides->hooks, base->hooks, overlay_hook_specs, NULL);
|
||||||
}
|
}
|
||||||
else if (a->inherit == AP_LUA_INHERIT_PARENT_LAST) {
|
else if (a->inherit == AP_LUA_INHERIT_PARENT_LAST) {
|
||||||
a->package_paths = apr_array_append(p, overrides->package_paths, base->package_paths);
|
a->package_paths = apr_array_append(p, overrides->package_paths, base->package_paths);
|
||||||
a->package_cpaths = apr_array_append(p, overrides->package_cpaths, base->package_cpaths);
|
a->package_cpaths = apr_array_append(p, overrides->package_cpaths, base->package_cpaths);
|
||||||
a->mapped_handlers = apr_array_append(p, overrides->mapped_handlers, base->mapped_handlers);
|
a->mapped_handlers = apr_array_append(p, overrides->mapped_handlers, base->mapped_handlers);
|
||||||
|
a->mapped_filters = apr_array_append(p, overrides->mapped_filters, base->mapped_filters);
|
||||||
a->hooks = apr_hash_merge(p, base->hooks, overrides->hooks, overlay_hook_specs, NULL);
|
a->hooks = apr_hash_merge(p, base->hooks, overrides->hooks, overlay_hook_specs, NULL);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
a->package_paths = overrides->package_paths;
|
a->package_paths = overrides->package_paths;
|
||||||
a->package_cpaths = overrides->package_cpaths;
|
a->package_cpaths = overrides->package_cpaths;
|
||||||
a->mapped_handlers= overrides->mapped_handlers;
|
a->mapped_handlers= overrides->mapped_handlers;
|
||||||
|
a->mapped_filters= overrides->mapped_filters;
|
||||||
a->hooks= overrides->hooks;
|
a->hooks= overrides->hooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -51,6 +51,7 @@
|
|||||||
#if LUA_VERSION_NUM > 501
|
#if LUA_VERSION_NUM > 501
|
||||||
/* Load mode for lua_load() */
|
/* Load mode for lua_load() */
|
||||||
#define lua_load(a,b,c,d) lua_load(a,b,c,d,NULL)
|
#define lua_load(a,b,c,d) lua_load(a,b,c,d,NULL)
|
||||||
|
#define lua_resume(a,b) lua_resume(a, NULL, b)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Create a set of AP_LUA_DECLARE(type), AP_LUA_DECLARE_NONSTD(type) and
|
/* Create a set of AP_LUA_DECLARE(type), AP_LUA_DECLARE_NONSTD(type) and
|
||||||
@@ -102,9 +103,10 @@ typedef struct
|
|||||||
apr_array_header_t *package_cpaths;
|
apr_array_header_t *package_cpaths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mapped handlers
|
* mapped handlers/filters
|
||||||
*/
|
*/
|
||||||
apr_array_header_t *mapped_handlers;
|
apr_array_header_t *mapped_handlers;
|
||||||
|
apr_array_header_t *mapped_filters;
|
||||||
|
|
||||||
apr_pool_t *pool;
|
apr_pool_t *pool;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user