/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_common.h"
#include "ngx_http_lua_directive.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_cache.h"
#include "ngx_http_lua_contentby.h"
#include "ngx_http_lua_accessby.h"
#include "ngx_http_lua_rewriteby.h"
#include "ngx_http_lua_logby.h"
#include "ngx_http_lua_headerfilterby.h"
#include "ngx_http_lua_bodyfilterby.h"
#include "ngx_http_lua_initby.h"
#include "ngx_http_lua_initworkerby.h"
#include "ngx_http_lua_shdict.h"
#include "ngx_http_lua_ssl_certby.h"
#include "ngx_http_lua_lex.h"
#include "api/ngx_http_lua_api.h"
#include "ngx_http_lua_log_ringbuf.h"
#include "ngx_http_lua_log.h"
typedef struct ngx_http_lua_block_parser_ctx_s
ngx_http_lua_block_parser_ctx_t;
#if defined(NDK) && NDK
#include "ngx_http_lua_setby.h"
static ngx_int_t ngx_http_lua_set_by_lua_init(ngx_http_request_t *r);
#endif
static u_char *ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag,
size_t tag_len, size_t *chunkname_len);
static ngx_int_t ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf,
ngx_http_lua_block_parser_ctx_t *ctx);
static u_char *ngx_http_lua_strlstrn(u_char *s1, u_char *last, u_char *s2,
size_t n);
struct ngx_http_lua_block_parser_ctx_s {
ngx_uint_t start_line;
int token_len;
};
enum {
FOUND_LEFT_CURLY = 0,
FOUND_RIGHT_CURLY,
FOUND_LEFT_LBRACKET_STR,
FOUND_LBRACKET_STR = FOUND_LEFT_LBRACKET_STR,
FOUND_LEFT_LBRACKET_CMT,
FOUND_LBRACKET_CMT = FOUND_LEFT_LBRACKET_CMT,
FOUND_RIGHT_LBRACKET,
FOUND_COMMENT_LINE,
FOUND_DOUBLE_QUOTED,
FOUND_SINGLE_QUOTED
};
char *
ngx_http_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_lua_main_conf_t *lmcf = conf;
ngx_str_t *value, name;
ngx_shm_zone_t *zone;
ngx_shm_zone_t **zp;
ngx_http_lua_shdict_ctx_t *ctx;
ssize_t size;
if (lmcf->shdict_zones == NULL) {
lmcf->shdict_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t));
if (lmcf->shdict_zones == NULL) {
return NGX_CONF_ERROR;
}
if (ngx_array_init(lmcf->shdict_zones, cf->pool, 2,
sizeof(ngx_shm_zone_t *))
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
value = cf->args->elts;
ctx = NULL;
if (value[1].len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid lua shared dict name \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
name = value[1];
size = ngx_parse_size(&value[2]);
if (size <= 8191) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid lua shared dict size \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_shdict_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
ctx->name = name;
ctx->main_conf = lmcf;
ctx->log = &cf->cycle->new_log;
zone = ngx_http_lua_shared_memory_add(cf, &name, (size_t) size,
&ngx_http_lua_module);
if (zone == NULL) {
return NGX_CONF_ERROR;
}
if (zone->data) {
ctx = zone->data;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"lua_shared_dict \"%V\" is already defined as "
"\"%V\"", &name, &ctx->name);
return NGX_CONF_ERROR;
}
zone->init = ngx_http_lua_shdict_init_zone;
zone->data = ctx;
zp = ngx_array_push(lmcf->shdict_zones);
if (zp == NULL) {
return NGX_CONF_ERROR;
}
*zp = zone;
lmcf->requires_shm = 1;
return NGX_CONF_OK;
}
char *
ngx_http_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *p = conf;
ngx_flag_t *fp;
char *ret;
ret = ngx_conf_set_flag_slot(cf, cmd, conf);
if (ret != NGX_CONF_OK) {
return ret;
}
fp = (ngx_flag_t *) (p + cmd->offset);
if (!*fp) {
ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,
"lua_code_cache is off; this will hurt "
"performance");
}
return NGX_CONF_OK;
}
char *
ngx_http_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_lua_main_conf_t *lmcf = conf;
ngx_str_t *value;
if (lmcf->lua_cpath.len != 0) {
return "is duplicate";
}
dd("enter");
value = cf->args->elts;
lmcf->lua_cpath.len = value[1].len;
lmcf->lua_cpath.data = value[1].data;
return NGX_CONF_OK;
}
char *
ngx_http_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_lua_main_conf_t *lmcf = conf;
ngx_str_t *value;
if (lmcf->lua_path.len != 0) {
return "is duplicate";
}
dd("enter");
value = cf->args->elts;
lmcf->lua_path.len = value[1].len;
lmcf->lua_path.data = value[1].data;
return NGX_CONF_OK;
}
#if defined(NDK) && NDK
char *
ngx_http_lua_set_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_set_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
u_char *p;
ngx_str_t *value;
ngx_str_t target;
ndk_set_var_t filter;
ngx_http_lua_set_var_data_t *filter_data;
/*
* value[0] = "set_by_lua"
* value[1] = target variable name
* value[2] = lua script source to be executed
* value[3..] = real params
* */
value = cf->args->elts;
target = value[1];
filter.type = NDK_SET_VAR_MULTI_VALUE_DATA;
filter.func = cmd->post;
filter.size = cf->args->nelts - 3; /* get number of real params */
filter_data = ngx_palloc(cf->pool, sizeof(ngx_http_lua_set_var_data_t));
if (filter_data == NULL) {
return NGX_CONF_ERROR;
}
filter_data->size = filter.size;
p = ngx_palloc(cf->pool,
sizeof("set_by_lua") + NGX_HTTP_LUA_INLINE_KEY_LEN);
if (p == NULL) {
return NGX_CONF_ERROR;
}
filter_data->key = p;
p = ngx_copy(p, "set_by_lua", sizeof("set_by_lua") - 1);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[2].data, value[2].len);
*p = '\0';
filter_data->script = value[2];
filter.data = filter_data;
return ndk_set_var_multi_value_core(cf, &target, &value[3], &filter);
}
char *
ngx_http_lua_set_by_lua_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
u_char *p;
ngx_str_t *value;
ngx_str_t target;
ndk_set_var_t filter;
ngx_http_lua_set_var_data_t *filter_data;
/*
* value[0] = "set_by_lua_file"
* value[1] = target variable name
* value[2] = lua script file path to be executed
* value[3..] = real params
* */
value = cf->args->elts;
target = value[1];
filter.type = NDK_SET_VAR_MULTI_VALUE_DATA;
filter.func = cmd->post;
filter.size = cf->args->nelts - 2; /* get number of real params and
lua script */
filter_data = ngx_palloc(cf->pool, sizeof(ngx_http_lua_set_var_data_t));
if (filter_data == NULL) {
return NGX_CONF_ERROR;
}
filter_data->size = filter.size;
p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
filter_data->key = p;
p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[2].data, value[2].len);
*p = '\0';
ngx_str_null(&filter_data->script);
filter.data = filter_data;
return ndk_set_var_multi_value_core(cf, &target, &value[2], &filter);
}
ngx_int_t
ngx_http_lua_filter_set_by_lua_inline(ngx_http_request_t *r, ngx_str_t *val,
ngx_http_variable_value_t *v, void *data)
{
lua_State *L;
ngx_int_t rc;
ngx_http_lua_set_var_data_t *filter_data = data;
if (ngx_http_lua_set_by_lua_init(r) != NGX_OK) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
filter_data->script.data,
filter_data->script.len,
filter_data->key, "=set_by_lua");
if (rc != NGX_OK) {
return NGX_ERROR;
}
rc = ngx_http_lua_set_by_chunk(L, r, val, v, filter_data->size,
&filter_data->script);
if (rc != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_http_lua_filter_set_by_lua_file(ngx_http_request_t *r, ngx_str_t *val,
ngx_http_variable_value_t *v, void *data)
{
lua_State *L;
ngx_int_t rc;
u_char *script_path;
size_t nargs;
ngx_http_lua_set_var_data_t *filter_data = data;
dd("set by lua file");
if (ngx_http_lua_set_by_lua_init(r) != NGX_OK) {
return NGX_ERROR;
}
filter_data->script.data = v[0].data;
filter_data->script.len = v[0].len;
/* skip the lua file path argument */
v++;
nargs = filter_data->size - 1;
dd("script: %.*s", (int) filter_data->script.len, filter_data->script.data);
dd("nargs: %d", (int) nargs);
script_path = ngx_http_lua_rebase_path(r->pool, filter_data->script.data,
filter_data->script.len);
if (script_path == NULL) {
return NGX_ERROR;
}
L = ngx_http_lua_get_lua_vm(r, NULL);
/* load Lua script file (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
filter_data->key);
if (rc != NGX_OK) {
return NGX_ERROR;
}
rc = ngx_http_lua_set_by_chunk(L, r, val, v, nargs, &filter_data->script);
if (rc != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
#endif /* defined(NDK) && NDK */
char *
ngx_http_lua_rewrite_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_rewrite_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
size_t chunkname_len;
u_char *p, *chunkname;
ngx_str_t *value;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_loc_conf_t *llcf = conf;
ngx_http_compile_complex_value_t ccv;
dd("enter");
#if defined(nginx_version) && nginx_version >= 8042 && nginx_version <= 8053
return "does not work with " NGINX_VER;
#endif
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (llcf->rewrite_handler) {
return "is duplicate";
}
value = cf->args->elts;
if (value[1].len == 0) {
/* Oops...Invalid location conf */
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid location config: no runnable Lua code");
return NGX_CONF_ERROR;
}
if (cmd->post == ngx_http_lua_rewrite_handler_inline) {
chunkname = ngx_http_lua_gen_chunk_name(cf, "rewrite_by_lua",
sizeof("rewrite_by_lua") - 1,
&chunkname_len);
if (chunkname == NULL) {
return NGX_CONF_ERROR;
}
llcf->rewrite_chunkname = chunkname;
/* Don't eval nginx variables for inline lua code */
llcf->rewrite_src.value = value[1];
p = ngx_palloc(cf->pool,
chunkname_len + NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->rewrite_src_key = p;
p = ngx_copy(p, chunkname, chunkname_len);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &llcf->rewrite_src;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (llcf->rewrite_src.lengths == NULL) {
/* no variable found */
p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->rewrite_src_key = p;
p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
}
}
llcf->rewrite_handler = (ngx_http_handler_pt) cmd->post;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
lmcf->requires_rewrite = 1;
lmcf->requires_capture_filter = 1;
return NGX_CONF_OK;
}
char *
ngx_http_lua_access_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_access_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
size_t chunkname_len;
u_char *p, *chunkname;
ngx_str_t *value;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_loc_conf_t *llcf = conf;
ngx_http_compile_complex_value_t ccv;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (llcf->access_handler) {
return "is duplicate";
}
value = cf->args->elts;
if (value[1].len == 0) {
/* Oops...Invalid location conf */
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid location config: no runnable Lua code");
return NGX_CONF_ERROR;
}
if (cmd->post == ngx_http_lua_access_handler_inline) {
chunkname = ngx_http_lua_gen_chunk_name(cf, "access_by_lua",
sizeof("access_by_lua") - 1,
&chunkname_len);
if (chunkname == NULL) {
return NGX_CONF_ERROR;
}
llcf->access_chunkname = chunkname;
/* Don't eval nginx variables for inline lua code */
llcf->access_src.value = value[1];
p = ngx_palloc(cf->pool,
chunkname_len + NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->access_src_key = p;
p = ngx_copy(p, chunkname, chunkname_len);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &llcf->access_src;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (llcf->access_src.lengths == NULL) {
/* no variable found */
p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->access_src_key = p;
p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
}
}
llcf->access_handler = (ngx_http_handler_pt) cmd->post;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
lmcf->requires_access = 1;
lmcf->requires_capture_filter = 1;
return NGX_CONF_OK;
}
char *
ngx_http_lua_content_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_content_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
size_t chunkname_len;
u_char *p;
u_char *chunkname;
ngx_str_t *value;
ngx_http_core_loc_conf_t *clcf;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_loc_conf_t *llcf = conf;
ngx_http_compile_complex_value_t ccv;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (llcf->content_handler) {
return "is duplicate";
}
value = cf->args->elts;
dd("value[0]: %.*s", (int) value[0].len, value[0].data);
dd("value[1]: %.*s", (int) value[1].len, value[1].data);
if (value[1].len == 0) {
/* Oops...Invalid location conf */
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid location config: no runnable Lua code");
return NGX_CONF_ERROR;
}
if (cmd->post == ngx_http_lua_content_handler_inline) {
chunkname = ngx_http_lua_gen_chunk_name(cf, "content_by_lua",
sizeof("content_by_lua") - 1,
&chunkname_len);
if (chunkname == NULL) {
return NGX_CONF_ERROR;
}
llcf->content_chunkname = chunkname;
dd("chunkname: %s", chunkname);
/* Don't eval nginx variables for inline lua code */
llcf->content_src.value = value[1];
p = ngx_palloc(cf->pool,
chunkname_len + NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->content_src_key = p;
p = ngx_copy(p, chunkname, chunkname_len);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &llcf->content_src;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (llcf->content_src.lengths == NULL) {
/* no variable found */
p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->content_src_key = p;
p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
}
}
llcf->content_handler = (ngx_http_handler_pt) cmd->post;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
lmcf->requires_capture_filter = 1;
/* register location content handler */
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
if (clcf == NULL) {
return NGX_CONF_ERROR;
}
clcf->handler = ngx_http_lua_content_handler;
return NGX_CONF_OK;
}
char *
ngx_http_lua_log_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_log_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
size_t chunkname_len;
u_char *p, *chunkname;
ngx_str_t *value;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_loc_conf_t *llcf = conf;
ngx_http_compile_complex_value_t ccv;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (llcf->log_handler) {
return "is duplicate";
}
value = cf->args->elts;
if (value[1].len == 0) {
/* Oops...Invalid location conf */
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid location config: no runnable Lua code");
return NGX_CONF_ERROR;
}
if (cmd->post == ngx_http_lua_log_handler_inline) {
chunkname = ngx_http_lua_gen_chunk_name(cf, "log_by_lua",
sizeof("log_by_lua") - 1,
&chunkname_len);
if (chunkname == NULL) {
return NGX_CONF_ERROR;
}
llcf->log_chunkname = chunkname;
/* Don't eval nginx variables for inline lua code */
llcf->log_src.value = value[1];
p = ngx_palloc(cf->pool,
chunkname_len + NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->log_src_key = p;
p = ngx_copy(p, chunkname, chunkname_len);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &llcf->log_src;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (llcf->log_src.lengths == NULL) {
/* no variable found */
p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->log_src_key = p;
p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
}
}
llcf->log_handler = (ngx_http_handler_pt) cmd->post;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
lmcf->requires_log = 1;
return NGX_CONF_OK;
}
char *
ngx_http_lua_header_filter_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_header_filter_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
u_char *p;
ngx_str_t *value;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_loc_conf_t *llcf = conf;
ngx_http_compile_complex_value_t ccv;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (llcf->header_filter_handler) {
return "is duplicate";
}
value = cf->args->elts;
if (value[1].len == 0) {
/* Oops...Invalid location conf */
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid location config: no runnable Lua code");
return NGX_CONF_ERROR;
}
if (cmd->post == ngx_http_lua_header_filter_inline) {
/* Don't eval nginx variables for inline lua code */
llcf->header_filter_src.value = value[1];
p = ngx_palloc(cf->pool,
sizeof("header_filter_by_lua") +
NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->header_filter_src_key = p;
p = ngx_copy(p, "header_filter_by_lua",
sizeof("header_filter_by_lua") - 1);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &llcf->header_filter_src;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (llcf->header_filter_src.lengths == NULL) {
/* no variable found */
p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->header_filter_src_key = p;
p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
}
}
llcf->header_filter_handler = (ngx_http_handler_pt) cmd->post;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
lmcf->requires_header_filter = 1;
return NGX_CONF_OK;
}
char *
ngx_http_lua_body_filter_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_body_filter_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
u_char *p;
ngx_str_t *value;
ngx_http_lua_main_conf_t *lmcf;
ngx_http_lua_loc_conf_t *llcf = conf;
ngx_http_compile_complex_value_t ccv;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (llcf->body_filter_handler) {
return "is duplicate";
}
value = cf->args->elts;
if (value[1].len == 0) {
/* Oops...Invalid location conf */
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid location config: no runnable Lua code");
return NGX_CONF_ERROR;
}
if (cmd->post == ngx_http_lua_body_filter_inline) {
/* Don't eval nginx variables for inline lua code */
llcf->body_filter_src.value = value[1];
p = ngx_palloc(cf->pool,
sizeof("body_filter_by_lua") +
NGX_HTTP_LUA_INLINE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->body_filter_src_key = p;
p = ngx_copy(p, "body_filter_by_lua", sizeof("body_filter_by_lua") - 1);
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &llcf->body_filter_src;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (llcf->body_filter_src.lengths == NULL) {
/* no variable found */
p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1);
if (p == NULL) {
return NGX_CONF_ERROR;
}
llcf->body_filter_src_key = p;
p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
}
}
llcf->body_filter_handler = (ngx_http_output_body_filter_pt) cmd->post;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module);
lmcf->requires_body_filter = 1;
lmcf->requires_header_filter = 1;
return NGX_CONF_OK;
}
char *
ngx_http_lua_init_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_init_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
u_char *name;
ngx_str_t *value;
ngx_http_lua_main_conf_t *lmcf = conf;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (lmcf->init_handler) {
return "is duplicate";
}
value = cf->args->elts;
if (value[1].len == 0) {
/* Oops...Invalid location conf */
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid location config: no runnable Lua code");
return NGX_CONF_ERROR;
}
lmcf->init_handler = (ngx_http_lua_main_conf_handler_pt) cmd->post;
if (cmd->post == ngx_http_lua_init_by_file) {
name = ngx_http_lua_rebase_path(cf->pool, value[1].data,
value[1].len);
if (name == NULL) {
return NGX_CONF_ERROR;
}
lmcf->init_src.data = name;
lmcf->init_src.len = ngx_strlen(name);
} else {
lmcf->init_src = value[1];
}
return NGX_CONF_OK;
}
char *
ngx_http_lua_init_worker_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *rv;
ngx_conf_t save;
save = *cf;
cf->handler = ngx_http_lua_init_worker_by_lua;
cf->handler_conf = conf;
rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
*cf = save;
return rv;
}
char *
ngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
u_char *name;
ngx_str_t *value;
ngx_http_lua_main_conf_t *lmcf = conf;
dd("enter");
/* must specify a content handler */
if (cmd->post == NULL) {
return NGX_CONF_ERROR;
}
if (lmcf->init_worker_handler) {
return "is duplicate";
}
value = cf->args->elts;
lmcf->init_worker_handler = (ngx_http_lua_main_conf_handler_pt) cmd->post;
if (cmd->post == ngx_http_lua_init_worker_by_file) {
name = ngx_http_lua_rebase_path(cf->pool, value[1].data,
value[1].len);
if (name == NULL) {
return NGX_CONF_ERROR;
}
lmcf->init_worker_src.data = name;
lmcf->init_worker_src.len = ngx_strlen(name);
} else {
lmcf->init_worker_src = value[1];
}
return NGX_CONF_OK;
}
#if defined(NDK) && NDK
static ngx_int_t
ngx_http_lua_set_by_lua_init(ngx_http_request_t *r)
{
lua_State *L;
ngx_http_lua_ctx_t *ctx;
ngx_http_cleanup_t *cln;
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
if (ctx == NULL) {
ctx = ngx_http_lua_create_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
} else {
L = ngx_http_lua_get_lua_vm(r, ctx);
ngx_http_lua_reset_ctx(r, L, ctx);
}
if (ctx->cleanup == NULL) {
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_lua_request_cleanup_handler;
cln->data = ctx;
ctx->cleanup = &cln->handler;
}
ctx->context = NGX_HTTP_LUA_CONTEXT_SET;
return NGX_OK;
}
#endif
static u_char *
ngx_http_lua_gen_chunk_name(ngx_conf_t *cf, const char *tag, size_t tag_len,
size_t *chunkname_len)
{
u_char *p, *out;
size_t len;
len = sizeof("=(:)") - 1 + tag_len + cf->conf_file->file.name.len
+ NGX_INT64_LEN + 1;
out = ngx_palloc(cf->pool, len);
if (out == NULL) {
return NULL;
}
if (cf->conf_file->file.name.len) {
p = cf->conf_file->file.name.data + cf->conf_file->file.name.len;
while (--p >= cf->conf_file->file.name.data) {
if (*p == '/' || *p == '\\') {
p++;
goto found;
}
}
p++;
} else {
p = cf->conf_file->file.name.data;
}
found:
ngx_snprintf(out, len, "=%*s(%*s:%d)%Z",
tag_len, tag, cf->conf_file->file.name.data
+ cf->conf_file->file.name.len - p,
p, cf->conf_file->line);
*chunkname_len = len;
return out;
}
/* a specialized version of the standard ngx_conf_parse() function */
char *
ngx_http_lua_conf_lua_block_parse(ngx_conf_t *cf, ngx_command_t *cmd)
{
ngx_http_lua_block_parser_ctx_t ctx;
int level = 1;
char *rv;
u_char *p;
size_t len;
ngx_str_t *src, *dst;
ngx_int_t rc;
ngx_uint_t i, start_line;
ngx_array_t *saved;
enum {
parse_block = 0,
parse_param
} type;
if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
type = parse_block;
} else {
type = parse_param;
}
saved = cf->args;
cf->args = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));
if (cf->args == NULL) {
return NGX_CONF_ERROR;
}
ctx.token_len = 0;
start_line = cf->conf_file->line;
dd("init start line: %d", (int) start_line);
ctx.start_line = start_line;
for ( ;; ) {
rc = ngx_http_lua_conf_read_lua_token(cf, &ctx);
dd("parser start line: %d", (int) start_line);
switch (rc) {
case NGX_ERROR:
goto done;
case FOUND_LEFT_CURLY:
ctx.start_line = cf->conf_file->line;
if (type == parse_param) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"block directives are not supported "
"in -g option");
goto failed;
}
level++;
dd("seen block start: level=%d", (int) level);
break;
case FOUND_RIGHT_CURLY:
level--;
dd("seen block done: level=%d", (int) level);
if (type != parse_block || level < 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"}\": level %d, "
"starting at line %ui", level,
start_line);
goto failed;
}
if (level == 0) {
ngx_http_lua_assert(cf->handler);
src = cf->args->elts;
for (len = 0, i = 0; i < cf->args->nelts; i++) {
len += src[i].len;
}
dd("saved nelts: %d", (int) saved->nelts);
dd("temp nelts: %d", (int) cf->args->nelts);
#if 0
ngx_http_lua_assert(saved->nelts == 1);
#endif
dst = ngx_array_push(saved);
if (dst == NULL) {
return NGX_CONF_ERROR;
}
dst->len = len;
dst->len--; /* skip the trailing '}' block terminator */
p = ngx_palloc(cf->pool, len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
dst->data = p;
for (i = 0; i < cf->args->nelts; i++) {
p = ngx_copy(p, src[i].data, src[i].len);
}
p[-1] = '\0'; /* override the last '}' char to null */
cf->args = saved;
rv = (*cf->handler)(cf, cmd, cf->handler_conf);
if (rv == NGX_CONF_OK) {
goto done;
}
if (rv == NGX_CONF_ERROR) {
goto failed;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);
goto failed;
}
break;
case FOUND_LBRACKET_STR:
case FOUND_LBRACKET_CMT:
case FOUND_RIGHT_LBRACKET:
case FOUND_COMMENT_LINE:
case FOUND_DOUBLE_QUOTED:
case FOUND_SINGLE_QUOTED:
break;
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unknown return value from the lexer: %i", rc);
goto failed;
}
}
failed:
rc = NGX_ERROR;
done:
if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_lua_conf_read_lua_token(ngx_conf_t *cf,
ngx_http_lua_block_parser_ctx_t *ctx)
{
enum {
OVEC_SIZE = 2
};
int i, rc;
int ovec[OVEC_SIZE];
u_char *start, *p, *q, ch;
off_t file_size;
size_t len, buf_size;
ssize_t n, size;
ngx_uint_t start_line;
ngx_str_t *word;
ngx_buf_t *b;
#if nginx_version >= 1009002
ngx_buf_t *dump;
#endif
b = cf->conf_file->buffer;
#if nginx_version >= 1009002
dump = cf->conf_file->dump;
#endif
start = b->pos;
start_line = cf->conf_file->line;
buf_size = b->end - b->start;
dd("lexer start line: %d", (int) start_line);
file_size = ngx_file_size(&cf->conf_file->file.info);
for ( ;; ) {
if (b->pos >= b->last
|| (b->last - b->pos < (b->end - b->start) / 3
&& cf->conf_file->file.offset < file_size))
{
if (cf->conf_file->file.offset >= file_size) {
cf->conf_file->line = ctx->start_line;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, expecting "
"terminating characters for lua code "
"block");
return NGX_ERROR;
}
len = b->last - start;
if (len == buf_size) {
cf->conf_file->line = start_line;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long lua code block, probably "
"missing terminating characters");
return NGX_ERROR;
}
if (len) {
ngx_memmove(b->start, start, len);
}
size = (ssize_t) (file_size - cf->conf_file->file.offset);
if (size > b->end - (b->start + len)) {
size = b->end - (b->start + len);
}
n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
cf->conf_file->file.offset);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
if (n != size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
ngx_read_file_n " returned "
"only %z bytes instead of %z",
n, size);
return NGX_ERROR;
}
b->pos = b->start + (b->pos - start);
b->last = b->start + len + n;
start = b->start;
#if nginx_version >= 1009002
if (dump) {
dump->last = ngx_cpymem(dump->last, b->start + len, size);
}
#endif
}
rc = ngx_http_lua_lex(b->pos, b->last - b->pos, ovec);
if (rc < 0) { /* no match */
/* alas. the lexer does not yet support streaming processing. need
* more work below */
if (cf->conf_file->file.offset >= file_size) {
cf->conf_file->line = ctx->start_line;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected end of file, expecting "
"terminating characters for lua code "
"block");
return NGX_ERROR;
}
len = b->last - b->pos;
if (len == buf_size) {
cf->conf_file->line = start_line;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long lua code block, probably "
"missing terminating characters");
return NGX_ERROR;
}
if (len) {
ngx_memcpy(b->start, b->pos, len);
}
size = (ssize_t) (file_size - cf->conf_file->file.offset);
if (size > b->end - (b->start + len)) {
size = b->end - (b->start + len);
}
n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
cf->conf_file->file.offset);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
if (n != size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
ngx_read_file_n " returned "
"only %z bytes instead of %z",
n, size);
return NGX_ERROR;
}
b->pos = b->start + len;
b->last = b->pos + n;
start = b->start;
continue;
}
if (rc == FOUND_LEFT_LBRACKET_STR || rc == FOUND_LEFT_LBRACKET_CMT) {
/* we update the line numbers for best error messages when the
* closing long bracket is missing */
for (i = 0; i < ovec[0]; i++) {
ch = b->pos[i];
if (ch == LF) {
cf->conf_file->line++;
}
}
b->pos += ovec[0];
ovec[1] -= ovec[0];
ovec[0] = 0;
if (rc == FOUND_LEFT_LBRACKET_CMT) {
p = &b->pos[2]; /* we skip the leading "--" prefix */
rc = FOUND_LBRACKET_CMT;
} else {
p = b->pos;
rc = FOUND_LBRACKET_STR;
}
/* we temporarily rewrite [=*[ in the input buffer to ]=*] to
* construct the pattern for the corresponding closing long
* bracket without additional buffers. */
ngx_http_lua_assert(p[0] == '[');
p[0] = ']';
ngx_http_lua_assert(b->pos[ovec[1] - 1] == '[');
b->pos[ovec[1] - 1] = ']';
/* search for the corresponding closing bracket */
dd("search pattern for the closing long bracket: \"%.*s\" (len=%d)",
(int) (b->pos + ovec[1] - p), p, (int) (b->pos + ovec[1] - p));
q = ngx_http_lua_strlstrn(b->pos + ovec[1], b->last, p,
b->pos + ovec[1] - p - 1);
if (q == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"Lua code block missing the closing "
"long bracket \"%*s\"",
b->pos + ovec[1] - p, p);
return NGX_ERROR;
}
/* restore the original opening long bracket */
p[0] = '[';
b->pos[ovec[1] - 1] = '[';
ovec[1] = q - b->pos + b->pos + ovec[1] - p;
dd("found long bracket token: \"%.*s\"",
(int) (ovec[1] - ovec[0]), b->pos + ovec[0]);
}
for (i = 0; i < ovec[1]; i++) {
ch = b->pos[i];
if (ch == LF) {
cf->conf_file->line++;
}
}
b->pos += ovec[1];
ctx->token_len = ovec[1] - ovec[0];
break;
}
word = ngx_array_push(cf->args);
if (word == NULL) {
return NGX_ERROR;
}
word->data = ngx_pnalloc(cf->temp_pool, b->pos - start);
if (word->data == NULL) {
return NGX_ERROR;
}
len = b->pos - start;
ngx_memcpy(word->data, start, len);
word->len = len;
return rc;
}
char *
ngx_http_lua_capture_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
#ifndef HAVE_INTERCEPT_ERROR_LOG_PATCH
return "not found: missing the capture error log patch for nginx";
#else
ngx_str_t *value;
ssize_t size;
u_char *data;
ngx_cycle_t *cycle;
ngx_http_lua_main_conf_t *lmcf = conf;
ngx_http_lua_log_ringbuf_t *ringbuf;
value = cf->args->elts;
cycle = cf->cycle;
if (lmcf->requires_capture_log) {
return "is duplicate";
}
if (value[1].len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid capture error log size \"%V\"",
&value[1]);
return NGX_CONF_ERROR;
}
size = ngx_parse_size(&value[1]);
if (size < NGX_MAX_ERROR_STR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid capture error log size \"%V\", "
"minimum size is %d", &value[1],
NGX_MAX_ERROR_STR);
return NGX_CONF_ERROR;
}
if (cycle->intercept_error_log_handler) {
return "capture error log handler has been hooked";
}
ringbuf = (ngx_http_lua_log_ringbuf_t *)
ngx_palloc(cf->pool, sizeof(ngx_http_lua_log_ringbuf_t));
if (ringbuf == NULL) {
return NGX_CONF_ERROR;
}
data = ngx_palloc(cf->pool, size);
if (data == NULL) {
return NGX_CONF_ERROR;
}
ngx_http_lua_log_ringbuf_init(ringbuf, data, size);
lmcf->requires_capture_log = 1;
cycle->intercept_error_log_handler = (ngx_log_intercept_pt)
ngx_http_lua_capture_log_handler;
cycle->intercept_error_log_data = ringbuf;
return NGX_CONF_OK;
#endif
}
/*
* ngx_http_lua_strlstrn() is intended to search for static substring
* with known length in string until the argument last. The argument n
* must be length of the second substring - 1.
*/
static u_char *
ngx_http_lua_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n)
{
ngx_uint_t c1, c2;
c2 = (ngx_uint_t) *s2++;
last -= n;
do {
do {
if (s1 >= last) {
return NULL;
}
c1 = (ngx_uint_t) *s1++;
dd("testing char '%c' vs '%c'", (int) c1, (int) c2);
} while (c1 != c2);
dd("testing against pattern \"%.*s\"", (int) n, s2);
} while (ngx_strncmp(s1, s2, n) != 0);
return --s1;
}
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */