/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/** * error reporting if lua has an error. * Extracts the error from lua stack and prints
*/ staticvoid report_lua_error(lua_State *L, request_rec *r)
{ constchar *lua_response;
r->status = HTTP_INTERNAL_SERVER_ERROR;
r->content_type = "text/html";
ap_rputs("
switch (spec->scope) { case AP_LUA_SCOPE_ONCE: case AP_LUA_SCOPE_UNSET:
apr_pool_create(&pool, r->pool);
apr_pool_tag(pool, "mod_lua-vm"); break; case AP_LUA_SCOPE_REQUEST:
pool = r->pool; break; case AP_LUA_SCOPE_CONN:
pool = r->connection->pool; break; #if APR_HAS_THREADS case AP_LUA_SCOPE_THREAD:
pool = apr_thread_pool_get(r->connection->current_thread); break; case AP_LUA_SCOPE_SERVER:
pool = r->server->process->pool; break; #endif default:
ap_assert(0);
}
*lifecycle_pool = pool; return spec;
}
staticconstchar* ap_lua_interpolate_string(apr_pool_t* pool, constchar* string, constchar** values)
{ char *stringBetween; constchar* ret; int srclen,x,y;
srclen = strlen(string);
ret = "";
y = 0; for (x=0; x < srclen; x++) { if (string[x] == '$' && x != srclen-1 && string[x+1] >= '0' && string[x+1] <= '9') { int v = *(string+x+1) - '0'; if (x-y > 0) {
stringBetween = apr_pstrndup(pool, string+y, x-y);
} else {
stringBetween = "";
}
ret = apr_pstrcat(pool, ret, stringBetween, values[v], NULL);
y = ++x+1;
}
}
if (x-y > 0 && y > 0) {
stringBetween = apr_pstrndup(pool, string+y, x-y);
ret = apr_pstrcat(pool, ret, stringBetween, NULL);
} /* If no replacement was made, just return the original string */ elseif (y == 0) { return string;
} return ret;
}
/** * "main"
*/ staticint lua_handler(request_rec *r)
{ int rc = OK; if (strcmp(r->handler, "lua-script")) { return DECLINED;
} /* Decline the request if the script does not exist (or is a directory),
* rather than just returning internal server error */ if (
(r->finfo.filetype == APR_NOFILE)
|| (r->finfo.filetype & APR_DIR)
) { return DECLINED;
}
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "handling [%s] in mod_lua", r->filename);
/* XXX: This seems wrong because it may generate wrong headers for HEAD requests */ if (!r->header_only) {
lua_State *L;
apr_pool_t *pool; const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
&lua_module);
ap_lua_vm_spec *spec = create_vm_spec(&pool, r, cfg, NULL, NULL, NULL,
0, "handle", "request handler");
L = ap_lua_get_lua_state(pool, spec, r); if (!L) { /* TODO annotate spec with failure reason */
r->status = HTTP_INTERNAL_SERVER_ERROR;
ap_rputs("Unable to compile VM, see logs", r);
ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR;
}
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "got a vm!");
lua_getglobal(L, "handle"); if (!lua_isfunction(L, -1)) {
ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01475) "lua: Unable to find entry function '%s' in %s (not a valid function)", "handle",
spec->file);
ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR;
}
ap_lua_run_lua_request(L, r); if (lua_pcall(L, 1, 1, 0)) {
report_lua_error(L, r);
} if (lua_isnumber(L, -1)) {
rc = lua_tointeger(L, -1);
}
ap_lua_release_state(L, spec, r);
} return rc;
}
ctx = apr_pcalloc(r->pool, sizeof(lua_filter_ctx));
ctx->broken = 0;
*c = ctx; /* Find the filter that was called. * XXX: If we were wired with mod_filter, the filter (mod_filters name) * and the provider (our underlying filters name) need to have matched.
*/ 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(02328) "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(02329) "lua: Unable to find entry function '%s' in %s (not a valid function)",
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);
/* 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, &nres); if (rc == LUA_YIELD) { if (f->frec->providers == NULL) { /* Not wired by mod_filter */
apr_table_unset(r->headers_out, "Content-Length");
apr_table_unset(r->headers_out, "Content-MD5");
apr_table_unset(r->headers_out, "ETAG");
} return OK;
} else {
ap_lua_release_state(L, spec, r); return APR_ENOENT;
}
}
} return APR_ENOENT;
}
/* 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 */
ap_remove_output_filter(f); return ap_pass_brigade(f->next,pbbIn);
} else { /* We've got a willing lua filter, setup and check for a prefix */
size_t olen;
apr_bucket *pbktOut; constchar* output = lua_tolstring(ctx->L, 1, &olen);
if (olen > 0) {
pbktOut = apr_bucket_heap_create(output, olen, NULL, c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
rv = ap_pass_brigade(f->next, ctx->tmpBucket);
apr_brigade_cleanup(ctx->tmpBucket); if (rv != APR_SUCCESS) { return rv;
}
}
}
}
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) { while (!APR_BRIGADE_EMPTY(pbbIn)) { constchar *data;
apr_size_t len;
apr_bucket *pbktOut;
pbktIn = APR_BRIGADE_FIRST(pbbIn); if (APR_BUCKET_IS_EOS(pbktIn)) { break;
}
/* 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, &nres) == LUA_YIELD && nres == 1) {
size_t olen; constchar* output = lua_tolstring(L, 1, &olen); if (olen > 0) {
pbktOut = apr_bucket_heap_create(output, olen, NULL,
c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
rv = ap_pass_brigade(f->next, ctx->tmpBucket);
apr_brigade_cleanup(ctx->tmpBucket); if (rv != APR_SUCCESS) { return rv;
}
}
} else {
ctx->broken = 1;
ap_lua_release_state(L, ctx->spec, r);
ap_remove_output_filter(f);
apr_brigade_cleanup(pbbIn);
apr_brigade_cleanup(ctx->tmpBucket);
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02663) "lua: Error while executing filter: %s",
lua_tostring(L, -1)); return HTTP_INTERNAL_SERVER_ERROR;
}
apr_bucket_delete(pbktIn);
} /* 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_BRIGADE_EMPTY(pbbIn) && APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(pbbIn))) {
apr_bucket *pbktEOS;
lua_pushnil(L);
lua_setglobal(L, "bucket"); if (lua_resume(L, 0, &nres) == LUA_YIELD && nres == 1) {
apr_bucket *pbktOut;
size_t olen; constchar* output = lua_tolstring(L, 1, &olen); if (olen > 0) {
pbktOut = apr_bucket_heap_create(output, olen, NULL,
c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktOut);
}
}
pbktEOS = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(ctx->tmpBucket, pbktEOS);
ap_lua_release_state(L, ctx->spec, r);
rv = ap_pass_brigade(f->next, ctx->tmpBucket);
apr_brigade_cleanup(ctx->tmpBucket); if (rv != APR_SUCCESS) { return rv;
}
}
} /* Clean up */
apr_brigade_cleanup(pbbIn); return APR_SUCCESS;
}
/* 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; if (rc == APR_EGENERAL) {
ctx->broken = 1;
ap_remove_input_filter(f); return HTTP_INTERNAL_SERVER_ERROR;
} if (rc == APR_ENOENT ) {
ap_remove_input_filter(f);
ctx->broken = 1;
} if (rc == APR_SUCCESS) {
ctx->tmpBucket = apr_brigade_create(r->pool, c->bucket_alloc);
}
}
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; constchar *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, &nres) == LUA_YIELD && nres == 1) {
size_t olen; constchar* 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); return APR_SUCCESS;
} else {
ctx->broken = 1;
ap_lua_release_state(L, ctx->spec, r);
ap_remove_input_filter(f);
apr_bucket_delete(pbktIn); 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, &nres) == LUA_YIELD && nres == 1) {
apr_bucket *pbktOut;
size_t olen; constchar* 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);
}
} return APR_SUCCESS;
}
if (!L) {
ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01477) "lua: Failed to obtain lua interpreter for entry function '%s' in %s",
hook_spec->function_name, hook_spec->file_name); return HTTP_INTERNAL_SERVER_ERROR;
}
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 entry function '%s' in %s (not a valid function)",
hook_spec->function_name,
hook_spec->file_name);
ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR;
}
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);
}
if (lua_pcall(L, 1, 1, 0)) {
report_lua_error(L, r);
ap_lua_release_state(L, spec, r); return HTTP_INTERNAL_SERVER_ERROR;
}
rc = DECLINED; if (lua_isnumber(L, -1)) {
rc = lua_tointeger(L, -1);
ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "Lua hook %s:%s for phase %s returned %d",
hook_spec->file_name, hook_spec->function_name, name, rc);
} else {
ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(03017) "Lua hook %s:%s for phase %s did not return a numeric value",
hook_spec->file_name, hook_spec->function_name, name); return HTTP_INTERNAL_SERVER_ERROR;
} if (rc != DECLINED) {
ap_lua_release_state(L, spec, r); return rc;
}
ap_lua_release_state(L, spec, r);
}
} return DECLINED;
}
/* Fix for making sure that LuaMapHandler works when FallbackResource is set */ staticint lua_map_handler_fixups(request_rec *r)
{ /* If there is no handler set yet, this might be a LuaMapHandler request */ if (r->handler == NULL) { int n = 0;
ap_regmatch_t match[10]; const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
&lua_module); for (n = 0; n < cfg->mapped_handlers->nelts; n++) {
ap_lua_mapped_handler_spec *hook_spec =
((ap_lua_mapped_handler_spec **) cfg->mapped_handlers->elts)[n];
/* Okay, this deserves a little explanation -- in order for the errors that lua * generates to be 'accuarate', including line numbers, we basically inject * N line number new lines into the 'top' of the chunk reader..... * * be happy. this is cool. *
*/ staticconstchar *lf = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; #define N_LF 32
/* You can be unhappy now. * * This is uncool. * * When you create a <Section handler in httpd, the only 'easy' way to create * a directory context is to parse the section, and convert it into a 'normal' * Configureation option, and then collapse the entire section, in memory, * back into the parent section -- from which you can then get the new directive * invoked.... anyways. evil. Rici taught me how to do this hack :-)
*/ staticconstchar *hack_section_handler(cmd_parms *cmd, void *_cfg, constchar *arg)
{
ap_lua_dir_cfg *cfg = (ap_lua_dir_cfg *) _cfg;
ap_directive_t *directive = cmd->directive;
hack_section_baton *baton = directive->data; constchar *key = apr_psprintf(cmd->pool, "%s_%d", baton->name, baton->apr_hook_when);
line = apr_pstrndup(cmd->temp_pool, line, endp - line);
if (line[0]) { constchar *word;
word = ap_getword_conf(cmd->temp_pool, &line); if (*word) {
function = apr_pstrdup(cmd->pool, word);
}
word = ap_getword_conf(cmd->temp_pool, &line); if (*word) { if (!strcasecmp("early", word)) {
when = AP_LUA_HOOK_FIRST;
} elseif (!strcasecmp("late", word)) {
when = AP_LUA_HOOK_LAST;
} else { return apr_pstrcat(cmd->pool, cmd->cmd->name, "> 2nd argument must be 'early' or 'late'", NULL);
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.