mod_lua.c revision 8ef30b572f5cee245f1dd27860e9a4c7d5f375cd
842ae4bd224140319ae7feec1872b93dfd491143fielding * Licensed to the Apache Software Foundation (ASF) under one or more
842ae4bd224140319ae7feec1872b93dfd491143fielding * contributor license agreements. See the NOTICE file distributed with
842ae4bd224140319ae7feec1872b93dfd491143fielding * this work for additional information regarding copyright ownership.
842ae4bd224140319ae7feec1872b93dfd491143fielding * The ASF licenses this file to You under the Apache License, Version 2.0
842ae4bd224140319ae7feec1872b93dfd491143fielding * (the "License"); you may not use this file except in compliance with
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * the License. You may obtain a copy of the License at
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * Unless required by applicable law or agreed to in writing, software
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * distributed under the License is distributed on an "AS IS" BASIS,
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * See the License for the specific language governing permissions and
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * limitations under the License.
449efc4dc68e42cc4421d15498a689618aab5dc3coarAPR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap_lua, AP_LUA, int, lua_open,
291eb44b3adaf8247425286615b4f4b69fbea274minfrinAPR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap_lua, AP_LUA, int, lua_request,
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *lua_ssl_val = NULL;
a7ad08f37d876bde1a32f0cf793f4799536ab1a5benstatic APR_OPTIONAL_FN_TYPE(ssl_is_https) *lua_ssl_is_https = NULL;
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingtypedef struct {
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm const char *name;
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm const char *file_name;
291eb44b3adaf8247425286615b4f4b69fbea274minfrintypedef struct
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * error reporting if lua has an error.
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding * Extracts the error from lua stack and prints
1ccd992d37d62c8cb2056126f2234f64ec189bfddougmstatic void report_lua_error(lua_State *L, request_rec *r)
066877f1a045103acfdd376d48cdd473c33f409bdougm ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, r->pool, APLOGNO(01471) "Lua error: %s",
1ccd992d37d62c8cb2056126f2234f64ec189bfddougmstatic void lua_open_callback(lua_State *L, apr_pool_t *p, void *ctx)
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic const char *scope_to_string(unsigned int scope)
066877f1a045103acfdd376d48cdd473c33f409bdougm return "once";
291eb44b3adaf8247425286615b4f4b69fbea274minfrin return "request";
291eb44b3adaf8247425286615b4f4b69fbea274minfrin return "conn";
291eb44b3adaf8247425286615b4f4b69fbea274minfrin return "thread";
291eb44b3adaf8247425286615b4f4b69fbea274minfrin return "server";
a7ad08f37d876bde1a32f0cf793f4799536ab1a5benstatic void ap_lua_release_state(lua_State* L, ap_lua_vm_spec* spec, request_rec* r) {
a6b9ed64fdf548c61de9714e2cfb999ec59d149cgstein lua_getfield(L, LUA_REGISTRYINDEX, "Apache2.Lua.server_spec");
0f081398cf0eef8cc7c66a535d450110a92dc8aefieldingstatic ap_lua_vm_spec *create_vm_spec(apr_pool_t **lifecycle_pool,
36d38d22e0d385db01f5773a579f44b8f02e4b1fsf const char *filename,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding const char *bytecode,
9bf4319b4fc7b31295b945215a55e2a92ba57903wrowe const char *function,
9bf4319b4fc7b31295b945215a55e2a92ba57903wrowe const char *what)
9bf4319b4fc7b31295b945215a55e2a92ba57903wrowe ap_lua_vm_spec *spec = apr_pcalloc(r->pool, sizeof(ap_lua_vm_spec));
4d7e28c869788fb00bffda29a67f1b10e19f159dnd spec->codecache = (cfg->codecache == AP_LUA_CACHE_UNSET) ? AP_LUA_CACHE_STAT : cfg->codecache;
4d7e28c869788fb00bffda29a67f1b10e19f159dnd ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(02313)
4d7e28c869788fb00bffda29a67f1b10e19f159dnd "%s details: scope: %s, file: %s, func: %s",
291eb44b3adaf8247425286615b4f4b69fbea274minfrin pool = apr_thread_pool_get(r->connection->current_thread);
291eb44b3adaf8247425286615b4f4b69fbea274minfrinstatic const char* ap_lua_interpolate_string(apr_pool_t* pool, const char* string, const char** values)
291eb44b3adaf8247425286615b4f4b69fbea274minfrin const char* ret;
291eb44b3adaf8247425286615b4f4b69fbea274minfrin for (x=0; x < srclen; x++) {
291eb44b3adaf8247425286615b4f4b69fbea274minfrin if (string[x] == '$' && x != srclen-1 && string[x+1] >= '0' && string[x+1] <= '9') {
291eb44b3adaf8247425286615b4f4b69fbea274minfrin if (x-y > 0) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding ret = apr_pstrcat(pool, ret, stringBetween, values[v], NULL);
36d38d22e0d385db01f5773a579f44b8f02e4b1fsf y = ++x+1;
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz if (x-y > 0 && y > 0) {
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* If no replacement was made, just return the original string */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding else if (y==0) {
f23a64b4e8a5f213b2aafb2bf6775e883e21f05fjim /* Decline the request if the script does not exist (or is a directory),
f23a64b4e8a5f213b2aafb2bf6775e883e21f05fjim * rather than just returning internal server error */
66a73d4405f9d941672c0343b36f6c494413a6b5rpluem ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(01472)
f23a64b4e8a5f213b2aafb2bf6775e883e21f05fjim /* XXX: This seems wrong because it may generate wrong headers for HEAD requests */
291eb44b3adaf8247425286615b4f4b69fbea274minfrin const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
291eb44b3adaf8247425286615b4f4b69fbea274minfrin ap_lua_vm_spec *spec = create_vm_spec(&pool, r, cfg, NULL, NULL, NULL,
291eb44b3adaf8247425286615b4f4b69fbea274minfrin /* TODO annotate spec with failure reason */
291eb44b3adaf8247425286615b4f4b69fbea274minfrin ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, APLOGNO(01474) "got a vm!");
291eb44b3adaf8247425286615b4f4b69fbea274minfrin ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01475)
291eb44b3adaf8247425286615b4f4b69fbea274minfrin "lua: Unable to find function %s in %s",
291eb44b3adaf8247425286615b4f4b69fbea274minfrin/* ------------------- Input/output content filters ------------------- */
291eb44b3adaf8247425286615b4f4b69fbea274minfrinstatic apr_status_t lua_setup_filter_ctx(ap_filter_t* f, request_rec* r, lua_filter_ctx** c) {
291eb44b3adaf8247425286615b4f4b69fbea274minfrin ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
291eb44b3adaf8247425286615b4f4b69fbea274minfrin const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
f23a64b4e8a5f213b2aafb2bf6775e883e21f05fjim /* Find the filter that was called */
f23a64b4e8a5f213b2aafb2bf6775e883e21f05fjim ((ap_lua_filter_handler_spec **) cfg->mapped_filters->elts)[n];
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz if (!strcasecmp(hook_spec->filter_name, f->frec->name)) {
c7ab5a433d38d5eae5fc0bb76be80ffab6e4f71dniq ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02328)
c7ab5a433d38d5eae5fc0bb76be80ffab6e4f71dniq "lua: Failed to obtain lua interpreter for %s %s",
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02329)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "lua: Unable to find function %s in %s",
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz /* If a Lua filter is interested in filtering a request, it must first do a yield,
a7ad08f37d876bde1a32f0cf793f4799536ab1a5ben * otherwise we'll assume that it's not interested and pretend we didn't find it.
291eb44b3adaf8247425286615b4f4b69fbea274minfrinstatic apr_status_t lua_output_filter_handle(ap_filter_t *f, apr_bucket_brigade *pbbIn) {
e8f95a682820a599fe41b22977010636be5c2717jim /* Set up the initial filter context and acquire the function.
4f9a74ad7e44b0464f7cf56525a205d788becacbtrawick * The corresponding Lua function should yield here.
4f9a74ad7e44b0464f7cf56525a205d788becacbtrawick /* No filter entry found (or the script declined to filter), just pass on the buckets */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz /* While the Lua function is still yielding, pass in buckets to the coroutine */
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz const char *data;
fd7cb2b590294250e5b219512992cd5747289fbbbrianp /* read the bucket */
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ);
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz /* Push the bucket onto the Lua stack as a global var */
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz /* If Lua yielded, it means we have something to pass on */
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding pbktOut = apr_bucket_heap_create(output, olen, NULL,
291eb44b3adaf8247425286615b4f4b69fbea274minfrin /* If we've safely reached the end, do a final call to Lua to allow for any
291eb44b3adaf8247425286615b4f4b69fbea274minfrin finishing moves by the script, such as appending a tail. */
291eb44b3adaf8247425286615b4f4b69fbea274minfrin pbktOut = apr_bucket_heap_create(output, olen, NULL,
291eb44b3adaf8247425286615b4f4b69fbea274minfrin /* Clean up */
291eb44b3adaf8247425286615b4f4b69fbea274minfrinstatic apr_status_t lua_input_filter_handle(ap_filter_t *f,
291eb44b3adaf8247425286615b4f4b69fbea274minfrin /* Set up the initial filter context and acquire the function.
291eb44b3adaf8247425286615b4f4b69fbea274minfrin * The corresponding Lua function should yield here.
291eb44b3adaf8247425286615b4f4b69fbea274minfrin ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
291eb44b3adaf8247425286615b4f4b69fbea274minfrin /* If the Lua script broke or denied serving the request, just pass the buckets through */
291eb44b3adaf8247425286615b4f4b69fbea274minfrin return ap_get_brigade(f->next, pbbOut, eMode, eBlock, nBytes);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding ret = ap_get_brigade(f->next, ctx->tmpBucket, eMode, eBlock, nBytes);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding if (eMode == AP_MODE_EATCRLF || ret != APR_SUCCESS)
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding /* While the Lua function is still yielding, pass buckets to the coroutine */
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz apr_bucket *pbktIn = APR_BRIGADE_FIRST(ctx->tmpBucket);
36d38d22e0d385db01f5773a579f44b8f02e4b1fsf const char *data;
33510984c759eb3da154ceb0db9b75fa0031d3b4sf /* read the bucket */
33510984c759eb3da154ceb0db9b75fa0031d3b4sf /* Push the bucket onto the Lua stack as a global var */
33510984c759eb3da154ceb0db9b75fa0031d3b4sf /* If Lua yielded, it means we have something to pass on */
185aa71728867671e105178b4c66fbc22b65ae26sf pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc);
36d38d22e0d385db01f5773a579f44b8f02e4b1fsf /* If we've safely reached the end, do a final call to Lua to allow for any
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz finishing moves by the script, such as appending a tail. */
33510984c759eb3da154ceb0db9b75fa0031d3b4sf apr_bucket *pbktEOS = apr_bucket_eos_create(c->bucket_alloc);
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz const char* output = lua_tolstring(L, 1, &olen);
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz pbktOut = apr_bucket_heap_create(output, olen, 0, c->bucket_alloc);
9bf4319b4fc7b31295b945215a55e2a92ba57903wrowe/* ---------------- Configury stuff --------------- */
32c4bc04f89b16521718145dc731f750144d7b38wrowe/** harnesses for magic hooks **/
33510984c759eb3da154ceb0db9b75fa0031d3b4sfstatic int lua_request_rec_hook_harness(request_rec *r, const char *name, int apr_hook_when)
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding const char *key = apr_psprintf(r->pool, "%s_%d", name, apr_hook_when);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding apr_array_header_t *hook_specs = apr_hash_get(cfg->hooks, key,
a6b9ed64fdf548c61de9714e2cfb999ec59d149cgstein ((ap_lua_mapped_handler_spec **) hook_specs->elts)[i];
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz spec = create_vm_spec(&pool, r, cfg, server_cfg,
291eb44b3adaf8247425286615b4f4b69fbea274minfrin "request hook");
30e3e760b737f13ce800fa02c5930ade7659ba66niq ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01477)
30e3e760b737f13ce800fa02c5930ade7659ba66niq "lua: Failed to obtain lua interpreter for %s %s",
30e3e760b737f13ce800fa02c5930ade7659ba66niq ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01478)
185aa71728867671e105178b4c66fbc22b65ae26sf "lua: Unable to find function %s in %s",
291eb44b3adaf8247425286615b4f4b69fbea274minfrin int rc, n = 0;
449efc4dc68e42cc4421d15498a689618aab5dc3coar ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
449efc4dc68e42cc4421d15498a689618aab5dc3coar const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
a877b7d5d03f91d6c93076d9ccf14469c70c648dcoar ((ap_lua_mapped_handler_spec **) cfg->mapped_handlers->elts)[n];
a877b7d5d03f91d6c93076d9ccf14469c70c648dcoar if (!ap_regexec(hook_spec->uri_pattern, r->uri, 10, match, 0)) {
a877b7d5d03f91d6c93076d9ccf14469c70c648dcoar for (i=0;i<10;i++) {
affb82a2d7fc07c1a862d800ef47af966b898768nd values[i] = apr_pstrndup(r->pool, r->uri+match[i].rm_so, match[i].rm_eo - match[i].rm_so);
affb82a2d7fc07c1a862d800ef47af966b898768nd filename = ap_lua_interpolate_string(r->pool, hook_spec->file_name, values);
a877b7d5d03f91d6c93076d9ccf14469c70c648dcoar function_name = ap_lua_interpolate_string(r->pool, hook_spec->function_name, values);
0f081398cf0eef8cc7c66a535d450110a92dc8aefielding "mapped handler");
fd0edaa8e3d4dd67d0604ccef2e96b071db96643fielding ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02330)
e8c95302287c86cd1f984eeb25cf3bfa9e2d33bbslive "lua: Failed to obtain lua interpreter for %s %s",
fd0edaa8e3d4dd67d0604ccef2e96b071db96643fielding ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02331)
076ae4ad21f0b3f25e2feabd9886b9500929eb2ejerenkrantz "lua: Unable to find function %s in %s",
ap_lua_run_lua_request(L, r);
ap_lua_run_lua_request(L, r);
t = lua_gettop(L);
lua_settop(L, t);
report_lua_error(L, r);
return HTTP_INTERNAL_SERVER_ERROR;
return rc;
return DECLINED;
apr_size_t i = 0;
while (i < bufsiz) {
char ch;
typedef struct cr_ctx
const char *endstr;
} cr_ctx;
static const char *lf =
return lf;
apr_size_t i = 0;
*plen = 0;
return NULL;
typedef struct hack_section_baton
const char *name;
int apr_hook_when;
const char *arg)
if (!hook_specs) {
sizeof(ap_lua_mapped_handler_spec *));
return NULL;
void *mconfig,
const char *line)
if (line[0]) {
const char *word;
char *tmp;
int rv;
if (function) {
/* This lua State is used only to compile the input strings -> bytecode, so we don't need anything extra. */
if (rv != 0) {
return errstr;
luaL_Buffer b;
luaL_pushresult(&b);
if (!*current) {
return NULL;
void *_cfg,
const char *file,
const char *function,
int apr_hook_when)
if (!hook_specs) {
sizeof(ap_lua_mapped_handler_spec *));
return NULL;
void *_cfg,
const char *file,
const char *function)
return NULL;
void *_cfg,
const char *file,
const char *function,
int direction)
return NULL;
if (lookup) {
return DECLINED;
const char *file,
const char *function,
const char *when)
if (err) {
return err;
if (when) {
const char *line)
line);
const char *file,
const char *function)
const char *line)
const char *file,
const char *function)
const char *line)
line);
const char *file,
const char *function,
const char *when)
if (when) {
const char *line)
line);
const char *file,
const char *function)
const char *line)
line);
const char *file,
const char *function,
const char *when)
if (when) {
const char *line)
line);
const char *file,
const char *function,
const char *when)
if (when) {
const char *line)
line);
const char *file,
const char *function)
if (err) {
return err;
if (err) {
return err;
function);
if (err) {
return err;
if (err) {
return err;
const char *line)
line);
const char *arg,
char *fixed_filename;
arg,
return NULL;
const char *arg)
void *_cfg,
const char *arg)
void *_cfg,
const char *arg)
arg);
return NULL;
void *_cfg,
const char *arg)
arg);
return NULL;
void *_cfg,
const char *scope,
const char *min,
const char *max)
#if !APR_HAS_THREADS
scope);
#if !APR_HAS_THREADS
scope);
if (vmin == 0) {
#if APR_HAS_THREADS
,scope);
return NULL;
const char *root)
&lua_module);
return NULL;
AP_LUA_DECLARE(const char *) ap_lua_ssl_val(apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *var)
if (lua_ssl_val) {
return NULL;
const void **parsed_require_line)
const char *provider_name;
const char *arg;
return NULL;
const void *parsed_require_line)
lua_State *L;
&lua_module);
&lua_module);
int result;
int nargs = 0;
if (L == NULL) {
return AUTHZ_GENERAL_ERROR;
return AUTHZ_GENERAL_ERROR;
ap_lua_run_lua_request(L, r);
return AUTHZ_GENERAL_ERROR;
return AUTHZ_GENERAL_ERROR;
return AUTHZ_GENERAL_ERROR;
switch (result) {
case AUTHZ_DENIED:
case AUTHZ_GRANTED:
case AUTHZ_NEUTRAL:
case AUTHZ_GENERAL_ERROR:
case AUTHZ_DENIED_NO_USER:
return result;
return AUTHZ_GENERAL_ERROR;
const char *function)
if (err)
return err;
return NULL;
"Add a directory to lua's package.cpath"),
NULL,
NULL,
NULL,
NULL,
{NULL}
return cfg;
return OK;
return cfg;
ap_lua_push_request(L, r);
return OK;
return OK;
const void *key,
const void *overlay_val,
const void *base_val,
const void *data)
a->codecache = (overrides->codecache== AP_LUA_CACHE_UNSET) ? base->codecache : overrides->codecache;
/* http_request.h hooks */
#if APR_HAS_THREADS