diff options
author | kaiwu <kaiwu2004@gmail.com> | 2025-03-01 12:42:23 +0800 |
---|---|---|
committer | kaiwu <kaiwu2004@gmail.com> | 2025-03-01 12:42:23 +0800 |
commit | 3f33461e4948bf05e60bdff35ec6c57a649c7860 (patch) | |
tree | 284c2ba95a41536ae1bff6bea710db0709a64739 /ngx_lua-0.10.28/src/ngx_http_lua_util.c | |
download | openresty-3f33461e4948bf05e60bdff35ec6c57a649c7860.tar.gz openresty-3f33461e4948bf05e60bdff35ec6c57a649c7860.zip |
openresty bundle
Diffstat (limited to 'ngx_lua-0.10.28/src/ngx_http_lua_util.c')
-rw-r--r-- | ngx_lua-0.10.28/src/ngx_http_lua_util.c | 4558 |
1 files changed, 4558 insertions, 0 deletions
diff --git a/ngx_lua-0.10.28/src/ngx_http_lua_util.c b/ngx_lua-0.10.28/src/ngx_http_lua_util.c new file mode 100644 index 0000000..c73bddc --- /dev/null +++ b/ngx_lua-0.10.28/src/ngx_http_lua_util.c @@ -0,0 +1,4558 @@ + +/* + * Copyright (C) Xiaozhe Wang (chaoslawful) + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "nginx.h" +#include "ngx_http_lua_directive.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_lua_exception.h" +#include "ngx_http_lua_pcrefix.h" +#include "ngx_http_lua_args.h" +#include "ngx_http_lua_uri.h" +#include "ngx_http_lua_req_body.h" +#include "ngx_http_lua_headers.h" +#include "ngx_http_lua_output.h" +#include "ngx_http_lua_control.h" +#include "ngx_http_lua_ndk.h" +#include "ngx_http_lua_subrequest.h" +#include "ngx_http_lua_log.h" +#include "ngx_http_lua_string.h" +#include "ngx_http_lua_misc.h" +#include "ngx_http_lua_consts.h" +#include "ngx_http_lua_shdict.h" +#include "ngx_http_lua_coroutine.h" +#include "ngx_http_lua_socket_tcp.h" +#include "ngx_http_lua_socket_udp.h" +#include "ngx_http_lua_sleep.h" +#include "ngx_http_lua_setby.h" +#include "ngx_http_lua_headerfilterby.h" +#include "ngx_http_lua_bodyfilterby.h" +#include "ngx_http_lua_logby.h" +#include "ngx_http_lua_probe.h" +#include "ngx_http_lua_uthread.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_timer.h" +#include "ngx_http_lua_config.h" +#include "ngx_http_lua_socket_tcp.h" +#include "ngx_http_lua_ssl_certby.h" +#include "ngx_http_lua_ssl.h" +#include "ngx_http_lua_log_ringbuf.h" +#if (NGX_THREADS) +#include "ngx_http_lua_worker_thread.h" +#endif + + +#if 1 +#undef ngx_http_lua_probe_info +#define ngx_http_lua_probe_info(msg) +#endif + + +#ifndef NGX_HTTP_LUA_BT_DEPTH +#define NGX_HTTP_LUA_BT_DEPTH 22 +#endif + + +#ifndef NGX_HTTP_LUA_BT_MAX_COROS +#define NGX_HTTP_LUA_BT_MAX_COROS 5 +#endif + + +#if (NGX_HTTP_LUA_HAVE_SA_RESTART) +#define NGX_HTTP_LUA_SA_RESTART_SIGS { \ + ngx_signal_value(NGX_RECONFIGURE_SIGNAL), \ + ngx_signal_value(NGX_REOPEN_SIGNAL), \ + ngx_signal_value(NGX_NOACCEPT_SIGNAL), \ + ngx_signal_value(NGX_TERMINATE_SIGNAL), \ + ngx_signal_value(NGX_SHUTDOWN_SIGNAL), \ + ngx_signal_value(NGX_CHANGEBIN_SIGNAL), \ + SIGALRM, \ + SIGINT, \ + SIGIO, \ + SIGCHLD, \ + SIGSYS, \ + SIGPIPE, \ + 0 \ +}; +#endif + + +char ngx_http_lua_code_cache_key; +char ngx_http_lua_socket_pool_key; +char ngx_http_lua_coroutines_key; +char ngx_http_lua_headers_metatable_key; + + +ngx_uint_t ngx_http_lua_location_hash = 0; +ngx_uint_t ngx_http_lua_content_length_hash = 0; + + +static ngx_int_t ngx_http_lua_send_http10_headers(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx); +static void ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log); +static void ngx_http_lua_init_globals(lua_State *L, ngx_cycle_t *cycle, + ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log); +#ifdef OPENRESTY_LUAJIT +static void ngx_http_lua_inject_global_write_guard(lua_State *L, + ngx_log_t *log); +#endif +static void ngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx, + const char *fieldname, const char *path, const char *default_path, + ngx_log_t *log); +static ngx_int_t ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx); +static ngx_int_t ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx); +static ngx_int_t ngx_http_lua_handle_rewrite_jump(lua_State *L, + ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); +static int ngx_http_lua_thread_traceback(lua_State *L, lua_State *co, + ngx_http_lua_co_ctx_t *coctx); +static void ngx_http_lua_inject_ngx_api(lua_State *L, + ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log); +static void ngx_http_lua_inject_arg_api(lua_State *L); +static int ngx_http_lua_param_set(lua_State *L); +static ngx_int_t ngx_http_lua_output_filter(ngx_http_request_t *r, + ngx_chain_t *in); +static ngx_int_t ngx_http_lua_send_special(ngx_http_request_t *r, + ngx_uint_t flags); +static void ngx_http_lua_finalize_threads(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, lua_State *L); +static ngx_int_t ngx_http_lua_post_zombie_thread(ngx_http_request_t *r, + ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread); +static void ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r, + lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx); +static ngx_int_t ngx_http_lua_on_abort_resume(ngx_http_request_t *r); +static void ngx_http_lua_close_fake_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_lua_flush_pending_output(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx); +static ngx_int_t + ngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx); +static lua_State *ngx_http_lua_new_state(lua_State *parent_vm, + ngx_cycle_t *cycle, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log); +static int ngx_http_lua_get_raw_phase_context(lua_State *L); + + +#ifndef LUA_PATH_SEP +#define LUA_PATH_SEP ";" +#endif + + +#if !defined(LUA_DEFAULT_PATH) && (NGX_DEBUG) +#define LUA_DEFAULT_PATH "../lua-resty-core/lib/?.lua;" \ + "../lua-resty-lrucache/lib/?.lua" +#endif + + +#define AUX_MARK "\1" + + +static void +ngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx, + const char *fieldname, const char *path, const char *default_path, + ngx_log_t *log) +{ + const char *tmp_path; + const char *prefix; + + /* XXX here we use some hack to simplify string manipulation */ + tmp_path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP, + LUA_PATH_SEP AUX_MARK LUA_PATH_SEP); + + lua_pushlstring(L, (char *) cycle->prefix.data, cycle->prefix.len); + prefix = lua_tostring(L, -1); + tmp_path = luaL_gsub(L, tmp_path, "$prefix", prefix); + tmp_path = luaL_gsub(L, tmp_path, "${prefix}", prefix); + lua_pop(L, 3); + + dd("tmp_path path: %s", tmp_path); + +#if (NGX_DEBUG) + tmp_path = +#else + (void) +#endif + luaL_gsub(L, tmp_path, AUX_MARK, default_path); + +#if (NGX_DEBUG) + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, + "lua setting lua package.%s to \"%s\"", fieldname, tmp_path); +#endif + + lua_remove(L, -2); + + /* fix negative index as there's new data on stack */ + tab_idx = (tab_idx < 0) ? (tab_idx - 1) : tab_idx; + lua_setfield(L, tab_idx, fieldname); +} + + +#ifndef OPENRESTY_LUAJIT +/** + * Create new table and set _G field to itself. + * + * After: + * | new table | <- top + * | ... | + * */ +void +ngx_http_lua_create_new_globals_table(lua_State *L, int narr, int nrec) +{ + lua_createtable(L, narr, nrec + 1); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_G"); +} +#endif /* OPENRESTY_LUAJIT */ + + +static lua_State * +ngx_http_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle, + ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log) +{ + lua_State *L; + const char *old_path; + const char *new_path; + size_t old_path_len; + const char *old_cpath; + const char *new_cpath; + size_t old_cpath_len; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "lua creating new vm state"); + + L = luaL_newstate(); + if (L == NULL) { + return NULL; + } + + luaL_openlibs(L); + + lua_getglobal(L, "package"); + + if (!lua_istable(L, -1)) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "the \"package\" table does not exist"); + return NULL; + } + + if (parent_vm) { + lua_getglobal(parent_vm, "package"); + lua_getfield(parent_vm, -1, "path"); + old_path = lua_tolstring(parent_vm, -1, &old_path_len); + lua_pop(parent_vm, 1); + + lua_pushlstring(L, old_path, old_path_len); + lua_setfield(L, -2, "path"); + + lua_getfield(parent_vm, -1, "cpath"); + old_path = lua_tolstring(parent_vm, -1, &old_path_len); + lua_pop(parent_vm, 2); + + lua_pushlstring(L, old_path, old_path_len); + lua_setfield(L, -2, "cpath"); + + } else { +#ifdef LUA_DEFAULT_PATH +# define LUA_DEFAULT_PATH_LEN (sizeof(LUA_DEFAULT_PATH) - 1) + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "lua prepending default package.path with %s", + LUA_DEFAULT_PATH); + + lua_pushliteral(L, LUA_DEFAULT_PATH ";"); /* package default */ + lua_getfield(L, -2, "path"); /* package default old */ + lua_concat(L, 2); /* package new */ + lua_setfield(L, -2, "path"); /* package */ +#endif + +#ifdef LUA_DEFAULT_CPATH +# define LUA_DEFAULT_CPATH_LEN (sizeof(LUA_DEFAULT_CPATH) - 1) + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "lua prepending default package.cpath with %s", + LUA_DEFAULT_CPATH); + + lua_pushliteral(L, LUA_DEFAULT_CPATH ";"); /* package default */ + lua_getfield(L, -2, "cpath"); /* package default old */ + lua_concat(L, 2); /* package new */ + lua_setfield(L, -2, "cpath"); /* package */ +#endif + + if (lmcf->lua_path.len != 0) { + lua_getfield(L, -1, "path"); /* get original package.path */ + old_path = lua_tolstring(L, -1, &old_path_len); + + dd("old path: %s", old_path); + + lua_pushlstring(L, (char *) lmcf->lua_path.data, + lmcf->lua_path.len); + new_path = lua_tostring(L, -1); + + ngx_http_lua_set_path(cycle, L, -3, "path", new_path, old_path, + log); + + lua_pop(L, 2); + } + + if (lmcf->lua_cpath.len != 0) { + lua_getfield(L, -1, "cpath"); /* get original package.cpath */ + old_cpath = lua_tolstring(L, -1, &old_cpath_len); + + dd("old cpath: %s", old_cpath); + + lua_pushlstring(L, (char *) lmcf->lua_cpath.data, + lmcf->lua_cpath.len); + new_cpath = lua_tostring(L, -1); + + ngx_http_lua_set_path(cycle, L, -3, "cpath", new_cpath, old_cpath, + log); + + + lua_pop(L, 2); + } + } + + lua_pop(L, 1); /* remove the "package" table */ + + ngx_http_lua_init_registry(L, log); + ngx_http_lua_init_globals(L, cycle, lmcf, log); + + return L; +} + + +lua_State * +ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *L, int *ref) +{ + int base; + lua_State *co; + +#ifdef HAVE_LUA_RESETTHREAD + ngx_queue_t *q; + + ngx_http_lua_main_conf_t *lmcf; + ngx_http_lua_thread_ref_t *tref; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua creating new thread"); + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + + if (L == lmcf->lua && !ngx_queue_empty(&lmcf->cached_lua_threads)) { + q = ngx_queue_head(&lmcf->cached_lua_threads); + tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue); + + ngx_http_lua_assert(tref->ref != LUA_NOREF); + ngx_http_lua_assert(tref->co != NULL); + + co = tref->co; + *ref = tref->ref; + + tref->co = NULL; + tref->ref = LUA_NOREF; + + ngx_queue_remove(q); + ngx_queue_insert_head(&lmcf->free_lua_threads, q); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua reusing cached lua thread %p (ref %d)", co, *ref); + +#if 0 + { + int n = 0; + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + if (!lua_isnil(L, -1) && !lua_isnil(L, -2)) { + n++; + } + + lua_pop(L, 1); + } + + lua_pop(L, 1); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "! lua reusing cached lua thread %p (ref %d, n %d)", + co, *ref, n); + } +#endif + + } else +#endif + { + base = lua_gettop(L); + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + + co = lua_newthread(L); + +#ifndef OPENRESTY_LUAJIT + /* {{{ inherit coroutine's globals to main thread's globals table + * for print() function will try to find tostring() in current + * globals table. + */ + /* new globals table for coroutine */ + ngx_http_lua_create_new_globals_table(co, 0, 0); + + lua_createtable(co, 0, 1); + ngx_http_lua_get_globals_table(co); + lua_setfield(co, -2, "__index"); + lua_setmetatable(co, -2); + + ngx_http_lua_set_globals_table(co); + /* }}} */ +#endif /* OPENRESTY_LUAJIT */ + + *ref = luaL_ref(L, -2); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, + ngx_cycle->log, 0, "lua ref lua thread %p (ref %d)", co, + *ref); + + if (*ref == LUA_NOREF) { + lua_settop(L, base); /* restore main thread stack */ + return NULL; + } + + lua_settop(L, base); + } + + return co; +} + + +void +ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L, + ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx) +{ +#ifdef HAVE_LUA_RESETTHREAD + ngx_queue_t *q; + ngx_http_lua_main_conf_t *lmcf; + ngx_http_lua_thread_ref_t *tref; +#endif + + if (coctx->co_ref == LUA_NOREF) { + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua deleting light thread %p (ref %d)", coctx->co, + coctx->co_ref); + + ngx_http_lua_probe_thread_delete(r, coctx->co, ctx); + +#ifdef HAVE_LUA_RESETTHREAD + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + + if (ctx != NULL + && coctx->co == ctx->entry_co_ctx.co + && L == lmcf->lua && !ngx_queue_empty(&lmcf->free_lua_threads)) + { + lua_resetthread(L, coctx->co); + q = ngx_queue_head(&lmcf->free_lua_threads); + tref = ngx_queue_data(q, ngx_http_lua_thread_ref_t, queue); + + ngx_http_lua_assert(tref->ref == LUA_NOREF); + ngx_http_lua_assert(tref->co == NULL); + + tref->ref = coctx->co_ref; + tref->co = coctx->co; + + ngx_queue_remove(q); + ngx_queue_insert_head(&lmcf->cached_lua_threads, q); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua caching unused lua thread %p (ref %d)", coctx->co, + coctx->co_ref); + + } else { +#endif + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http lua unref thread %p: %d", coctx->co, + coctx->co_ref); + + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + coroutines_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_unref(L, -1, coctx->co_ref); + lua_pop(L, 1); +#ifdef HAVE_LUA_RESETTHREAD + } +#endif + + coctx->co_ref = LUA_NOREF; + coctx->co_status = NGX_HTTP_LUA_CO_DEAD; +} + + +u_char * +ngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len) +{ + u_char *p; + ngx_str_t dst; + + dst.data = ngx_palloc(pool, len + 1); + if (dst.data == NULL) { + return NULL; + } + + dst.len = len; + + p = ngx_copy(dst.data, src, len); + *p = '\0'; + + if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->prefix, &dst) + != NGX_OK) + { + return NULL; + } + + return dst.data; +} + + +ngx_int_t +ngx_http_lua_send_header_if_needed(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx) +{ + ngx_int_t rc; + + dd("send header if needed: %d", r->header_sent || ctx->header_sent); + + if (!r->header_sent && !ctx->header_sent) { + if (r->headers_out.status == 0) { + r->headers_out.status = NGX_HTTP_OK; + } + + if (!ctx->mime_set + && ngx_http_lua_set_content_type(r, ctx) != NGX_OK) + { + return NGX_ERROR; + } + + if (!ctx->headers_set) { + ngx_http_clear_content_length(r); + ngx_http_clear_accept_ranges(r); + } + + if (!ctx->buffering) { + dd("sending headers"); + rc = ngx_http_send_header(r); + if (r->filter_finalize) { + ngx_http_set_ctx(r, ctx, ngx_http_lua_module); + } + + ctx->header_sent = 1; + return rc; + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, + ngx_chain_t *in) +{ + ngx_int_t rc; + ngx_chain_t *cl; + ngx_chain_t **ll; + ngx_http_lua_loc_conf_t *llcf; + +#if 1 + if (ctx->acquired_raw_req_socket || ctx->eof) { + dd("ctx->eof already set or raw req socket already acquired"); + return NGX_OK; + } +#endif + + if ((r->method & NGX_HTTP_HEAD) && !r->header_only) { + r->header_only = 1; + } + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf->http10_buffering + && !ctx->buffering + && !r->header_sent + && !ctx->header_sent + && r->http_version < NGX_HTTP_VERSION_11 + && r->headers_out.content_length_n < 0) + { + ctx->buffering = 1; + } + + rc = ngx_http_lua_send_header_if_needed(r, ctx); + + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + + if (r->header_only) { + ctx->eof = 1; + + if (!r->request_body && r == r->main) { + if (ngx_http_discard_request_body(r) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ctx->buffering) { + return ngx_http_lua_send_http10_headers(r, ctx); + } + + return rc; + } + + if (in == NULL) { + dd("last buf to be sent"); + +#if 1 + if (!r->request_body && r == r->main) { + if (ngx_http_discard_request_body(r) != NGX_OK) { + return NGX_ERROR; + } + } +#endif + + if (ctx->buffering) { + rc = ngx_http_lua_send_http10_headers(r, ctx); + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + if (ctx->out) { + + rc = ngx_http_lua_output_filter(r, ctx->out); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + ctx->out = NULL; + } + } + + ctx->eof = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua sending last buf of the response body"); + + rc = ngx_http_lua_send_special(r, NGX_HTTP_LAST); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_OK; + } + + /* in != NULL */ + + if (ctx->buffering) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua buffering output bufs for the HTTP 1.0 request"); + + for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { + ll = &cl->next; + } + + *ll = in; + + return NGX_OK; + } + + return ngx_http_lua_output_filter(r, in); +} + + +static ngx_int_t +ngx_http_lua_send_special(ngx_http_request_t *r, ngx_uint_t flags) +{ + ngx_int_t rc; + ngx_http_request_t *ar; /* active request */ + + ar = r->connection->data; + + if (ar != r) { + + /* bypass ngx_http_postpone_filter_module */ + + r->connection->data = r; + rc = ngx_http_send_special(r, flags); + r->connection->data = ar; + return rc; + } + + return ngx_http_send_special(r, flags); +} + + +static ngx_int_t +ngx_http_lua_output_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_int_t rc; + ngx_http_lua_ctx_t *ctx; + ngx_http_request_t *ar; /* active request */ + + ar = r->connection->data; + + if (ar != r) { + + /* bypass ngx_http_postpone_filter_module */ + + r->connection->data = r; + rc = ngx_http_output_filter(r, in); + r->connection->data = ar; + return rc; + } + + rc = ngx_http_output_filter(r, in); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + if (ctx == NULL) { + return rc; + } + + ngx_chain_update_chains(r->pool, + &ctx->free_bufs, &ctx->busy_bufs, &in, + (ngx_buf_tag_t) &ngx_http_lua_module); + + return rc; +} + + +static ngx_int_t +ngx_http_lua_send_http10_headers(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx) +{ + off_t size; + ngx_chain_t *cl; + ngx_int_t rc; + + if (r->header_sent || ctx->header_sent) { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua sending HTTP 1.0 response headers"); + + if (r->header_only) { + goto send; + } + + if (r->headers_out.content_length == NULL) { + for (size = 0, cl = ctx->out; cl; cl = cl->next) { + size += ngx_buf_size(cl->buf); + } + + r->headers_out.content_length_n = size; + } + +send: + + rc = ngx_http_send_header(r); + ctx->header_sent = 1; + return rc; +} + + +static void +ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, + "lua initializing lua registry"); + + /* {{{ register a table to anchor lua coroutines reliably: + * {([int]ref) = [cort]} */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + coroutines_key)); + lua_createtable(L, 0, 32 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ + + /* + * the the Lua request ctx data table will create in resty.core.ctx, + * just equivalent to the following code: + * lua_pushliteral(L, ngx_http_lua_ctx_tables_key); + * lua_createtable(L, 0, 0); + * lua_rawset(L, LUA_REGISTRYINDEX); + */ + + /* create the registry entry for the Lua socket connection pool table */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + socket_pool_key)); + lua_createtable(L, 0, 8 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); + + /* {{{ register table to cache user code: + * { [(string)cache_key] = <code closure> } */ + lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( + code_cache_key)); + lua_createtable(L, 0, 8 /* nrec */); + lua_rawset(L, LUA_REGISTRYINDEX); + /* }}} */ +} + + +static void +ngx_http_lua_init_globals(lua_State *L, ngx_cycle_t *cycle, + ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, + "lua initializing lua globals"); + +#if defined(NDK) && NDK + ngx_http_lua_inject_ndk_api(L); +#endif /* defined(NDK) && NDK */ + + ngx_http_lua_inject_ngx_api(L, lmcf, log); +} + + +static void +ngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf, + ngx_log_t *log) +{ + lua_createtable(L, 0 /* narr */, 115 /* nrec */); /* ngx.* */ + + lua_pushcfunction(L, ngx_http_lua_get_raw_phase_context); + lua_setfield(L, -2, "_phase_ctx"); + + ngx_http_lua_inject_arg_api(L); + + ngx_http_lua_inject_http_consts(L); + ngx_http_lua_inject_core_consts(L); + + ngx_http_lua_inject_log_api(L); + ngx_http_lua_inject_output_api(L); + ngx_http_lua_inject_string_api(L); + ngx_http_lua_inject_control_api(log, L); + ngx_http_lua_inject_subrequest_api(L); + ngx_http_lua_inject_sleep_api(L); + + ngx_http_lua_inject_req_api(log, L); + ngx_http_lua_inject_resp_header_api(L); + ngx_http_lua_create_headers_metatable(log, L); + ngx_http_lua_inject_shdict_api(lmcf, L); + ngx_http_lua_inject_socket_tcp_api(log, L); + ngx_http_lua_inject_socket_udp_api(log, L); + ngx_http_lua_inject_uthread_api(log, L); + ngx_http_lua_inject_timer_api(L); + ngx_http_lua_inject_config_api(L); +#if (NGX_THREADS) + ngx_http_lua_inject_worker_thread_api(log, L); +#endif + + lua_getglobal(L, "package"); /* ngx package */ + lua_getfield(L, -1, "loaded"); /* ngx package loaded */ + lua_pushvalue(L, -3); /* ngx package loaded ngx */ + lua_setfield(L, -2, "ngx"); /* ngx package loaded */ + lua_pop(L, 2); + + lua_setglobal(L, "ngx"); + + ngx_http_lua_inject_coroutine_api(log, L); +} + + +#ifdef OPENRESTY_LUAJIT +static void +ngx_http_lua_inject_global_write_guard(lua_State *L, ngx_log_t *log) +{ + int rc; + + const char buf[] = + "local ngx_log = ngx.log\n" + "local ngx_WARN = ngx.WARN\n" + "local tostring = tostring\n" + "local ngx_get_phase = ngx.get_phase\n" + "local traceback = require 'debug'.traceback\n" + "local function newindex(table, key, value)\n" + "rawset(table, key, value)\n" + "local phase = ngx_get_phase()\n" + "if phase == 'init_worker' or phase == 'init' then\n" + "return\n" + "end\n" + "ngx_log(ngx_WARN, 'writing a global Lua variable " + "(\\'', tostring(key), '\\') which may lead to " + "race conditions between concurrent requests, so " + "prefer the use of \\'local\\' variables', " + "traceback('', 2))\n" + "end\n" + "setmetatable(_G, { __newindex = newindex })\n" + ; + + rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "=_G write guard"); + + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to load Lua code (%i): %s", + rc, lua_tostring(L, -1)); + + lua_pop(L, 1); + return; + } + + rc = lua_pcall(L, 0, 0, 0); + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, log, 0, + "failed to run Lua code (%i): %s", + rc, lua_tostring(L, -1)); + lua_pop(L, 1); + } +} +#endif + + +void +ngx_http_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in) +{ + ngx_chain_t *cl; + + for (cl = in; cl; cl = cl->next) { + cl->buf->pos = cl->buf->last; + cl->buf->file_pos = cl->buf->file_last; + } +} + + +ngx_int_t +ngx_http_lua_add_copy_chain(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, + ngx_chain_t ***plast, ngx_chain_t *in, ngx_int_t *eof) +{ + ngx_chain_t *cl; + size_t len; + ngx_buf_t *b; + + len = 0; + *eof = 0; + + for (cl = in; cl; cl = cl->next) { + if (ngx_buf_in_memory(cl->buf)) { + len += cl->buf->last - cl->buf->pos; + } + + if (cl->buf->last_in_chain || cl->buf->last_buf) { + *eof = 1; + } + } + + if (len == 0) { + return NGX_OK; + } + + cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, + &ctx->free_bufs, len); + if (cl == NULL) { + return NGX_ERROR; + } + + dd("chains get free buf: %d == %d", (int) (cl->buf->end - cl->buf->start), + (int) len); + + b = cl->buf; + + while (in) { + if (ngx_buf_in_memory(in->buf)) { + b->last = ngx_copy(b->last, in->buf->pos, + in->buf->last - in->buf->pos); + } + + in = in->next; + } + + **plast = cl; + *plast = &cl->next; + + return NGX_OK; +} + + +void +ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, + ngx_http_lua_ctx_t *ctx) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua reset ctx"); + + ngx_http_lua_finalize_threads(r, ctx, L); + +#if 0 + if (ctx->user_co_ctx) { + /* no way to destroy a list but clean up the whole pool */ + ctx->user_co_ctx = NULL; + } +#endif + + ngx_memzero(&ctx->entry_co_ctx, sizeof(ngx_http_lua_co_ctx_t)); + + ctx->entry_co_ctx.next_zombie_child_thread = + &ctx->entry_co_ctx.zombie_child_threads; + + ctx->entry_co_ctx.co_ref = LUA_NOREF; + + ctx->entered_server_rewrite_phase = 0; + ctx->entered_rewrite_phase = 0; + ctx->entered_access_phase = 0; + ctx->entered_content_phase = 0; + + ctx->exit_code = 0; + ctx->exited = 0; + ctx->resume_handler = ngx_http_lua_wev_handler; + + ngx_str_null(&ctx->exec_uri); + ngx_str_null(&ctx->exec_args); + + ctx->co_op = 0; +} + + +/* post read callback for rewrite and access phases */ +void +ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r) +{ + ngx_http_lua_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua post read for rewrite/access phases"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + r->main->count--; + + if (ctx == NULL) { + return; + } + + ctx->read_body_done = 1; + + if (ctx->waiting_more_body) { + ctx->waiting_more_body = 0; + ngx_http_core_run_phases(r); + } +} + + +void +ngx_http_lua_request_cleanup_handler(void *data) +{ + ngx_http_lua_ctx_t *ctx = data; + + ngx_http_lua_request_cleanup(ctx, 0 /* forcible */); +} + + +void +ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int forcible) +{ + lua_State *L; + ngx_http_request_t *r; + ngx_http_lua_main_conf_t *lmcf; + + /* force coroutine handling the request quit */ + if (ctx == NULL) { + dd("ctx is NULL"); + return; + } + + r = ctx->request; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua request cleanup: forcible=%d", forcible); + + if (ctx->cleanup) { + *ctx->cleanup = NULL; + ctx->cleanup = NULL; + } + + lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); + +#if 1 + if (r->connection->fd == (ngx_socket_t) -1) { + /* being a fake request */ + + if (ctx->context == NGX_HTTP_LUA_CONTEXT_TIMER) { + /* being a timer handler */ + lmcf->running_timers--; + } + } +#endif + + L = ngx_http_lua_get_lua_vm(r, ctx); + + ngx_http_lua_finalize_threads(r, ctx, L); +} + + +/* + * description: + * run a Lua coroutine specified by ctx->cur_co_ctx->co + * return value: + * NGX_AGAIN: I/O interruption: r->main->count intact + * NGX_DONE: I/O interruption: r->main->count already incremented by 1 + * NGX_ERROR: error + * >= 200 HTTP status code + */ +ngx_int_t +ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, volatile int nrets) +{ + ngx_http_lua_co_ctx_t *next_coctx, *parent_coctx, *orig_coctx; + int rv, success = 1; + lua_State *next_co; + lua_State *old_co; + const char *err, *msg, *trace; + ngx_int_t rc; +#if (NGX_PCRE) + ngx_pool_t *old_pool = NULL; +#endif + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run thread, top:%d c:%ud", lua_gettop(L), + r->main->count); + + /* set Lua VM panic handler */ + lua_atpanic(L, ngx_http_lua_atpanic); + + NGX_LUA_EXCEPTION_TRY { + + /* + * silence a -Werror=clobbered warning with gcc 5.4 + * due to above setjmp + */ + err = NULL; + msg = NULL; + trace = NULL; + + if (ctx->cur_co_ctx->thread_spawn_yielded) { + ngx_http_lua_probe_info("thread spawn yielded"); + + ctx->cur_co_ctx->thread_spawn_yielded = 0; + nrets = 1; + } + + for ( ;; ) { + + dd("ctx: %p, co: %p, co status: %d, co is_wrap: %d", + ctx, ctx->cur_co_ctx->co, ctx->cur_co_ctx->co_status, + ctx->cur_co_ctx->is_wrap); + +#if (NGX_PCRE) + /* XXX: work-around to nginx regex subsystem */ + old_pool = ngx_http_lua_pcre_malloc_init(r->pool); +#endif + + orig_coctx = ctx->cur_co_ctx; + +#ifdef NGX_LUA_USE_ASSERT + dd("%p: saved co top: %d, nrets: %d, true top: %d", + orig_coctx->co, + (int) orig_coctx->co_top, (int) nrets, + (int) lua_gettop(orig_coctx->co)); +#endif + +#if DDEBUG + if (lua_gettop(orig_coctx->co) > 0) { + dd("co top elem: %s", luaL_typename(orig_coctx->co, -1)); + } + + if (orig_coctx->propagate_error) { + dd("co propagate_error: %d", orig_coctx->propagate_error); + } +#endif + + if (orig_coctx->propagate_error) { + orig_coctx->propagate_error = 0; + goto propagate_error; + } + + ngx_http_lua_assert(orig_coctx->co_top + nrets + == lua_gettop(orig_coctx->co)); + + rv = lua_resume(orig_coctx->co, nrets); + +#if (NGX_PCRE) + /* XXX: work-around to nginx regex subsystem */ + ngx_http_lua_pcre_malloc_done(old_pool); +#endif + +#if 0 + /* test the longjmp thing */ + if (rand() % 2 == 0) { + NGX_LUA_EXCEPTION_THROW(1); + } +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua resume returned %d", rv); + + switch (rv) { + case LUA_YIELD: + /* yielded, let event handler do the rest job */ + /* FIXME: add io cmd dispatcher here */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua thread yielded"); + +#ifdef NGX_LUA_USE_ASSERT + dd("%p: saving curr top after yield: %d (co-op: %d)", + orig_coctx->co, + (int) lua_gettop(orig_coctx->co), (int) ctx->co_op); + orig_coctx->co_top = lua_gettop(orig_coctx->co); +#endif + + if (r->uri_changed) { + return ngx_http_lua_handle_rewrite_jump(L, r, ctx); + } + + if (ctx->exited) { + return ngx_http_lua_handle_exit(L, r, ctx); + } + + if (ctx->exec_uri.len) { + return ngx_http_lua_handle_exec(L, r, ctx); + } + + /* + * check if coroutine.resume or coroutine.yield called + * lua_yield() + */ + switch (ctx->co_op) { + + case NGX_HTTP_LUA_USER_CORO_NOP: + dd("hit! it is the API yield"); + + ngx_http_lua_assert(lua_gettop(ctx->cur_co_ctx->co) == 0); + + ctx->cur_co_ctx = NULL; + + return NGX_AGAIN; + + case NGX_HTTP_LUA_USER_THREAD_RESUME: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua user thread resume"); + + ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP; + nrets = lua_gettop(ctx->cur_co_ctx->co) - 1; + dd("nrets = %d", nrets); + +#ifdef NGX_LUA_USE_ASSERT + /* ignore the return value (the thread) already pushed */ + orig_coctx->co_top--; +#endif + + break; + + case NGX_HTTP_LUA_USER_CORO_RESUME: + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua coroutine: resume"); + + /* + * the target coroutine lies at the base of the + * parent's stack + */ + ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP; + + old_co = ctx->cur_co_ctx->parent_co_ctx->co; + + nrets = lua_gettop(old_co); + if (nrets) { + dd("moving %d return values to parent", nrets); + lua_xmove(old_co, ctx->cur_co_ctx->co, nrets); + +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->parent_co_ctx->co_top -= nrets; +#endif + } + + break; + + default: + /* ctx->co_op == NGX_HTTP_LUA_USER_CORO_YIELD */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua coroutine: yield"); + + ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP; + + if (ngx_http_lua_is_thread(ctx)) { + ngx_http_lua_probe_thread_yield(r, ctx->cur_co_ctx->co); + + /* discard any return values from user + * coroutine.yield()'s arguments */ + lua_settop(ctx->cur_co_ctx->co, 0); + +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top = 0; +#endif + + ngx_http_lua_probe_info("set co running"); + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; + + if (ctx->posted_threads) { + ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx); + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* no pending threads, so resume the thread + * immediately */ + + nrets = 0; + continue; + } + + /* being a user coroutine that has a parent */ + + nrets = lua_gettop(ctx->cur_co_ctx->co); + + next_coctx = ctx->cur_co_ctx->parent_co_ctx; + next_co = next_coctx->co; + + if (nrets) { + dd("moving %d return values to next co", nrets); + lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); +#ifdef NGX_LUA_USE_ASSERT + ctx->cur_co_ctx->co_top -= nrets; +#endif + } + + if (!ctx->cur_co_ctx->is_wrap) { + /* + * prepare return values for coroutine.resume + * (true plus any retvals) + */ + lua_pushboolean(next_co, 1); + lua_insert(next_co, 1); + nrets++; /* add the true boolean value */ + } + + ctx->cur_co_ctx = next_coctx; + + break; + } + + /* try resuming on the new coroutine again */ + continue; + + case 0: + + ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; + + if (ctx->cur_co_ctx->zombie_child_threads) { + ngx_http_lua_cleanup_zombie_child_uthreads(r, L, ctx, + ctx->cur_co_ctx); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua light thread ended normally"); + + if (ngx_http_lua_is_entry_thread(ctx)) { + + lua_settop(L, 0); + + ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + + dd("uthreads: %d", (int) ctx->uthreads); + + if (ctx->uthreads) { + + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* all user threads terminated already */ + goto done; + } + + if (ctx->cur_co_ctx->is_uthread) { + /* being a user thread */ + + lua_settop(L, 0); + + parent_coctx = ctx->cur_co_ctx->parent_co_ctx; + + if (ngx_http_lua_coroutine_alive(parent_coctx)) { + if (ctx->cur_co_ctx->waited_by_parent) { + ngx_http_lua_probe_info("parent already waiting"); + ctx->cur_co_ctx->waited_by_parent = 0; + success = 1; + goto user_co_done; + } + + ngx_http_lua_probe_info("parent still alive"); + + if (ngx_http_lua_post_zombie_thread(r, parent_coctx, + ctx->cur_co_ctx) + != NGX_OK) + { + return NGX_ERROR; + } + + lua_pushboolean(ctx->cur_co_ctx->co, 1); + lua_insert(ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE; + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + ctx->uthreads--; + + if (ctx->uthreads == 0) { + if (ngx_http_lua_entry_thread_alive(ctx)) { + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* all threads terminated already */ + goto done; + } + + /* some other user threads still running */ + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* being a user coroutine that has a parent */ + + success = 1; + +user_co_done: + + nrets = lua_gettop(ctx->cur_co_ctx->co); + + next_coctx = ctx->cur_co_ctx->parent_co_ctx; + + if (next_coctx == NULL) { + /* being a light thread */ + goto no_parent; + } + + next_co = next_coctx->co; + + if (nrets) { + lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); + } + + if (ctx->cur_co_ctx->is_uthread) { + ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + ctx->uthreads--; + } + + if (!ctx->cur_co_ctx->is_wrap) { + /* + * ended successfully, coroutine.resume returns true plus + * any return values + */ + lua_pushboolean(next_co, success); + lua_insert(next_co, 1); + nrets++; + } + + ctx->cur_co_ctx = next_coctx; + + ngx_http_lua_probe_info("set parent running"); + + next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua coroutine: lua user thread ended normally"); + + continue; + + case LUA_ERRRUN: + err = "runtime error"; + break; + + case LUA_ERRSYNTAX: + err = "syntax error"; + break; + + case LUA_ERRMEM: + err = "[lua] memory allocation error"; + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, err); + abort(); + break; + + case LUA_ERRERR: + err = "error handler error"; + break; + + default: + err = "unknown error"; + break; + } + + if (ctx->cur_co_ctx != orig_coctx) { + ctx->cur_co_ctx = orig_coctx; + } + + ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 0); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; + + if (orig_coctx->is_uthread + || orig_coctx->is_wrap + || ngx_http_lua_is_entry_thread(ctx)) + { + ngx_http_lua_thread_traceback(L, orig_coctx->co, orig_coctx); + trace = lua_tostring(L, -1); + + if (lua_isstring(orig_coctx->co, -1)) { + msg = lua_tostring(orig_coctx->co, -1); + dd("user custom error msg: %s", msg); + + } else { + msg = "unknown reason"; + } + } + +propagate_error: + + if (ctx->cur_co_ctx->is_uthread) { + ngx_http_lua_assert(err != NULL && msg != NULL + && trace != NULL); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua user thread aborted: %s: %s\n%s", + err, msg, trace); + + lua_settop(L, 0); + + parent_coctx = ctx->cur_co_ctx->parent_co_ctx; + + if (ngx_http_lua_coroutine_alive(parent_coctx)) { + if (ctx->cur_co_ctx->waited_by_parent) { + ctx->cur_co_ctx->waited_by_parent = 0; + success = 0; + goto user_co_done; + } + + if (ngx_http_lua_post_zombie_thread(r, parent_coctx, + ctx->cur_co_ctx) + != NGX_OK) + { + return NGX_ERROR; + } + + lua_pushboolean(ctx->cur_co_ctx->co, 0); + lua_insert(ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE; + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); + ctx->uthreads--; + + if (ctx->uthreads == 0) { + if (ngx_http_lua_entry_thread_alive(ctx)) { + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + /* all threads terminated already */ + goto done; + } + + /* some other user threads still running */ + ctx->cur_co_ctx = NULL; + return NGX_AGAIN; + } + + if (ngx_http_lua_is_entry_thread(ctx)) { + ngx_http_lua_assert(err != NULL && msg != NULL + && trace != NULL); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "lua entry thread aborted: %s: %s\n%s", + err, msg, trace); + + lua_settop(L, 0); + + /* being the entry thread aborted */ + + if (r->filter_finalize) { + ngx_http_set_ctx(r, ctx, ngx_http_lua_module); + } + + ngx_http_lua_request_cleanup(ctx, 0); + + dd("headers sent? %d", r->header_sent || ctx->header_sent); + + if (ctx->no_abort) { + ctx->no_abort = 0; + return NGX_ERROR; + } + + return (r->header_sent || ctx->header_sent) ? NGX_ERROR : + NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* being a user coroutine that has a parent */ + + next_coctx = ctx->cur_co_ctx->parent_co_ctx; + if (next_coctx == NULL) { + goto no_parent; + } + + next_co = next_coctx->co; + + ngx_http_lua_probe_info("set parent running"); + + next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING; + + ctx->cur_co_ctx = next_coctx; + + if (orig_coctx->is_wrap) { + /* + * coroutine.wrap propagates errors + * to its parent coroutine + */ + next_coctx->propagate_error = 1; + continue; + } + + /* + * ended with error, coroutine.resume returns false plus + * err msg + */ + lua_pushboolean(next_co, 0); + lua_xmove(orig_coctx->co, next_co, 1); + nrets = 2; + + /* try resuming on the new coroutine again */ + continue; + } + + } NGX_LUA_EXCEPTION_CATCH { + dd("nginx execution restored"); + } + + return NGX_ERROR; + +no_parent: + + lua_settop(L, 0); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; + + if (r->filter_finalize) { + ngx_http_set_ctx(r, ctx, ngx_http_lua_module); + } + + ngx_http_lua_request_cleanup(ctx, 0); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua handler aborted: " + "user coroutine has no parent"); + + return (r->header_sent || ctx->header_sent) ? + NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; + +done: + + if (ctx->entered_content_phase + && r->connection->fd != (ngx_socket_t) -1) + { + rc = ngx_http_lua_send_chain_link(r, ctx, + NULL /* last_buf */); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_lua_wev_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + + ngx_http_lua_socket_tcp_upstream_t *u; + + c = r->connection; + wev = c->write; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua run write event handler: timedout:%ud, ready:%ud, " + "writing_raw_req_socket:%ud", + wev->timedout, wev->ready, ctx->writing_raw_req_socket); + + clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + + if (wev->timedout && !ctx->writing_raw_req_socket) { + if (!wev->delayed) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + c->timedout = 1; + + goto flush_coros; + } + + wev->timedout = 0; + wev->delayed = 0; + + if (!wev->ready) { + ngx_add_timer(wev, clcf->send_timeout); + + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, NGX_ERROR); + } + return NGX_ERROR; + } + } + } + + if (!wev->ready && !wev->timedout) { + goto useless; + } + + if (ctx->writing_raw_req_socket) { + ctx->writing_raw_req_socket = 0; + + u = ctx->downstream; + if (u == NULL) { + return NGX_ERROR; + } + + u->write_event_handler(r, u); + return NGX_DONE; + } + + if (c->buffered & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)) { + rc = ngx_http_lua_flush_pending_output(r, ctx); + + dd("flush pending output returned %d, c->error: %d", (int) rc, + c->error); + + if (rc != NGX_ERROR && rc != NGX_OK) { + goto useless; + } + + /* when rc == NGX_ERROR, c->error must be set */ + } + +flush_coros: + + dd("ctx->flushing_coros: %d", (int) ctx->flushing_coros); + + if (ctx->flushing_coros) { + return ngx_http_lua_process_flushing_coroutines(r, ctx); + } + + /* ctx->flushing_coros == 0 */ + +useless: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "useless lua write event handler"); + + if (ctx->entered_content_phase) { + return NGX_OK; + } + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx) +{ + ngx_int_t rc, n; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_http_lua_co_ctx_t *coctx; + + dd("processing flushing coroutines"); + + coctx = &ctx->entry_co_ctx; + n = ctx->flushing_coros; + + if (coctx->flushing) { + coctx->flushing = 0; + + ctx->flushing_coros--; + n--; + ctx->cur_co_ctx = coctx; + + rc = ngx_http_lua_flush_resume_helper(r, ctx); + if (rc == NGX_ERROR || rc >= NGX_OK) { + return rc; + } + + /* rc == NGX_DONE */ + } + + if (n) { + + if (ctx->user_co_ctx == NULL) { + return NGX_ERROR; + } + + part = &ctx->user_co_ctx->part; + coctx = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + coctx = part->elts; + i = 0; + } + + if (coctx[i].flushing) { + coctx[i].flushing = 0; + ctx->flushing_coros--; + n--; + ctx->cur_co_ctx = &coctx[i]; + + rc = ngx_http_lua_flush_resume_helper(r, ctx); + if (rc == NGX_ERROR || rc >= NGX_OK) { + return rc; + } + + /* rc == NGX_DONE */ + + if (n == 0) { + return NGX_DONE; + } + } + } + } + + if (n) { + return NGX_ERROR; + } + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_lua_flush_pending_output(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx) +{ + ngx_int_t rc; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_connection_t *c; + + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + wev = c->write; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua flushing output: buffered 0x%uxd", + c->buffered); + + if (ctx->busy_bufs) { + /* FIXME since cosockets also share this busy_bufs chain, this condition + * might not be strong enough. better use separate busy_bufs chains. */ + rc = ngx_http_lua_output_filter(r, NULL); + + } else { + cl = ngx_http_lua_get_flush_chain(r, ctx); + if (cl == NULL) { + return NGX_ERROR; + } + + rc = ngx_http_lua_output_filter(r, cl); + } + + dd("output filter returned %d", (int) rc); + + if (rc == NGX_ERROR || rc > NGX_OK) { + return rc; + } + + if (c->buffered & (NGX_HTTP_LOWLEVEL_BUFFERED | NGX_LOWLEVEL_BUFFERED)) { + + clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + + if (!wev->delayed) { + ngx_add_timer(wev, clcf->send_timeout); + } + + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, NGX_ERROR); + } + + return NGX_ERROR; + } + + if (ctx->flushing_coros) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua flush still waiting: buffered 0x%uxd", + c->buffered); + + return NGX_DONE; + } + + } else { +#if 1 + if (wev->timer_set && !wev->delayed) { + ngx_del_timer(wev); + } +#endif + } + + return NGX_OK; +} + + +u_char * +ngx_http_lua_digest_hex(u_char *dest, const u_char *buf, int buf_len) +{ + ngx_md5_t md5; + u_char md5_buf[MD5_DIGEST_LENGTH]; + + ngx_md5_init(&md5); + ngx_md5_update(&md5, buf, buf_len); + ngx_md5_final(md5_buf, &md5); + + return ngx_hex_dump(dest, md5_buf, sizeof(md5_buf)); +} + + +void +ngx_http_lua_set_multi_value_table(lua_State *L, int index) +{ + if (index < 0) { + index = lua_gettop(L) + index + 1; + } + + lua_pushvalue(L, -2); /* stack: table key value key */ + lua_rawget(L, index); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* stack: table key value */ + lua_rawset(L, index); /* stack: table */ + + } else { + if (!lua_istable(L, -1)) { + /* just inserted one value */ + lua_createtable(L, 4, 0); + /* stack: table key value value table */ + lua_insert(L, -2); + /* stack: table key value table value */ + lua_rawseti(L, -2, 1); + /* stack: table key value table */ + lua_insert(L, -2); + /* stack: table key table value */ + + lua_rawseti(L, -2, 2); /* stack: table key table */ + + lua_rawset(L, index); /* stack: table */ + + } else { + /* stack: table key value table */ + lua_insert(L, -2); /* stack: table key table value */ + + lua_rawseti(L, -2, lua_objlen(L, -2) + 1); + /* stack: table key table */ + lua_pop(L, 2); /* stack: table */ + } + } +} + + +uintptr_t +ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) +{ + ngx_uint_t n; + uint32_t *escape; + static u_char hex[] = "0123456789ABCDEF"; + + /* " ", "#", "%", "?", %00-%1F, %7F-%FF */ + + static uint32_t uri[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", "%", "+", "?", %00-%1F, %7F-%FF */ + + static uint32_t args[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x80000829, /* 1000 0000 0000 0000 0000 1000 0010 1001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* not ALPHA, DIGIT, "-", ".", "_", "~" */ + + static uint32_t uri_component[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xfc00987d, /* 1111 1100 0000 0000 1001 1000 0111 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */ + + static uint32_t html[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x000000ad, /* 0000 0000 0000 0000 0000 0000 1010 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", """, "%", "'", %00-%1F, %7F-%FF */ + + static uint32_t refresh[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "%", %00-%1F */ + + static uint32_t memcached[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + /* mail_auth is the same as memcached */ + + /* " ", """, "(", ")", ",", "/", ":", ";", "?", + * "<", "=", ">", "?", "@", "[", "]", "\", "{", + * "}", %00-%1F, %7F-%FF + */ + + static uint32_t header_name[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xfc009305, /* 1111 1100 0000 0000 1001 0011 0000 0101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x38000001, /* 0011 1000 0000 0000 0000 0000 0000 0001 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xa8000000, /* 1010 1000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* "%00-%08, %0A-%0F, %7F */ + + static uint32_t header_value[] = { + 0xfffffdff, /* 1111 1111 1111 1111 1111 1101 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + static uint32_t *map[] = + { uri, args, uri_component, html, refresh, memcached, memcached, + header_name, header_value }; + + escape = map[type]; + + if (dst == NULL) { + + /* find the number of the characters to be escaped */ + + n = 0; + + while (size) { + if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + n++; + } + + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + *dst++ = '%'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + + size--; + } + + return (uintptr_t) dst; +} + + +static int +ngx_http_lua_util_hex2int(char xdigit) +{ + if (isdigit(xdigit)) { + return xdigit - '0'; + } + + xdigit = tolower(xdigit); + if (xdigit <= 'f' && xdigit >= 'a') { + return xdigit - 'a' + 10; + } + + return -1; +} + + +/* XXX we also decode '+' to ' ' */ +void +ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size, + ngx_uint_t type) +{ + u_char *d = *dst, *s = *src, *de = (*dst + size); + int isuri = type & NGX_UNESCAPE_URI; + int isredirect = type & NGX_UNESCAPE_REDIRECT; + + while (size--) { + u_char curr = *s++; + + if (curr == '?' && + (type & (NGX_UNESCAPE_URI | NGX_UNESCAPE_REDIRECT))) + { + *d++ = '?'; + break; + + } else if (curr == '%') { + u_char ch; + if (size < 2 || !(isxdigit(s[0]) && isxdigit(s[1]))) { + *d++ = '%'; + continue; + } + /* we can be sure here they must be hex digits */ + ch = ngx_http_lua_util_hex2int(s[0]) * 16 + + ngx_http_lua_util_hex2int(s[1]); + + if ((isuri || isredirect) && ch == '?') { + *d++ = ch; + break; + + } else if (isredirect && (ch <= '%' || ch >= 0x7f)) { + *d++ = '%'; + continue; + } + + *d++ = ch; + s += 2; + size -= 2; + + } else if (curr == '+') { + *d++ = ' '; + continue; + + } else { + *d++ = curr; + } + } + + /* a safe guard if dst need to be null-terminated */ + if (d != de) { + *d = '\0'; + } + + *dst = d; + *src = s; +} + + +void +ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L) +{ + /* ngx.req table */ + + lua_createtable(L, 0 /* narr */, 23 /* nrec */); /* .req */ + + ngx_http_lua_inject_req_header_api(L); + ngx_http_lua_inject_req_uri_api(log, L); + ngx_http_lua_inject_req_args_api(L); + ngx_http_lua_inject_req_body_api(L); + ngx_http_lua_inject_req_socket_api(L); + ngx_http_lua_inject_req_misc_api(L); + + lua_setfield(L, -2, "req"); +} + + +static ngx_int_t +ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx) +{ + ngx_int_t rc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua thread initiated internal redirect to %V", + &ctx->exec_uri); + + ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; + + if (r->filter_finalize) { + ngx_http_set_ctx(r, ctx, ngx_http_lua_module); + } + + ngx_http_lua_request_cleanup(ctx, 1 /* forcible */); + + if (ctx->exec_uri.data[0] == '@') { + if (ctx->exec_args.len > 0) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "query strings %V ignored when exec'ing " + "named location %V", + &ctx->exec_args, &ctx->exec_uri); + } + + r->write_event_handler = ngx_http_request_empty_handler; + +#if 1 + if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) { + /* resume the read event handler */ + + r->read_event_handler = ngx_http_block_reading; + } +#endif + +#if 1 + /* clear the modules contexts */ + ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); +#endif + + rc = ngx_http_named_location(r, &ctx->exec_uri); + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + +#if 0 + if (!ctx->entered_content_phase) { + /* XXX ensure the main request ref count + * is decreased because the current + * request will be quit */ + r->main->count--; + dd("XXX decrement main count: c:%d", (int) r->main->count); + } +#endif + + return NGX_DONE; + } + + dd("internal redirect to %.*s", (int) ctx->exec_uri.len, + ctx->exec_uri.data); + + r->write_event_handler = ngx_http_request_empty_handler; + + if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) { + /* resume the read event handler */ + + r->read_event_handler = ngx_http_block_reading; + } + + rc = ngx_http_internal_redirect(r, &ctx->exec_uri, &ctx->exec_args); + + dd("internal redirect returned %d when in content phase? " + "%d", (int) rc, ctx->entered_content_phase); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + +#if 0 + if (!ctx->entered_content_phase) { + /* XXX ensure the main request ref count + * is decreased because the current + * request will be quit */ + dd("XXX decrement main count"); + r->main->count--; + } +#endif + + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx) +{ + ngx_int_t rc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua thread aborting request with status %d", + ctx->exit_code); + + ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; + + if (r->filter_finalize) { + ngx_http_set_ctx(r, ctx, ngx_http_lua_module); + } + + ngx_http_lua_request_cleanup(ctx, 0); + + if (r->connection->fd == (ngx_socket_t) -1) { /* fake request */ + return ctx->exit_code; + } + +#if 1 + if (!r->header_sent + && !ctx->header_sent + && r->headers_out.status == 0 + && ctx->exit_code >= NGX_HTTP_OK) + { + r->headers_out.status = ctx->exit_code; + } +#endif + + if (ctx->buffering + && r->headers_out.status + && ctx->exit_code != NGX_ERROR + && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT + && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST + && ctx->exit_code != NGX_HTTP_CLOSE) + { + rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + if (ctx->exit_code >= NGX_HTTP_OK) { + return NGX_HTTP_OK; + } + + return ctx->exit_code; + } + + if ((ctx->exit_code == NGX_OK + && ctx->entered_content_phase) + || (ctx->exit_code >= NGX_HTTP_OK + && ctx->exit_code < NGX_HTTP_SPECIAL_RESPONSE + && ctx->exit_code != NGX_HTTP_NO_CONTENT)) + { + rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + } + +#if 1 + if ((r->header_sent || ctx->header_sent) + && ctx->exit_code > NGX_OK + && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT + && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST + && ctx->exit_code != NGX_HTTP_CLOSE) + { + if (ctx->entered_content_phase) { + return NGX_OK; + } + + return NGX_HTTP_OK; + } +#endif + + return ctx->exit_code; +} + + +void +ngx_http_lua_process_args_option(ngx_http_request_t *r, lua_State *L, + int table, ngx_str_t *args) +{ + u_char *key; + size_t key_len; + u_char *value; + size_t value_len; + size_t len = 0; + size_t key_escape = 0; + uintptr_t total_escape = 0; + int n; + int i; + u_char *p; + + if (table < 0) { + table = lua_gettop(L) + table + 1; + } + + n = 0; + lua_pushnil(L); + while (lua_next(L, table) != 0) { + if (lua_type(L, -2) != LUA_TSTRING) { + luaL_error(L, "attempt to use a non-string key in the " + "\"args\" option table"); + return; + } + + key = (u_char *) lua_tolstring(L, -2, &key_len); + + key_escape = 2 * ngx_http_lua_escape_uri(NULL, key, key_len, + NGX_ESCAPE_URI_COMPONENT); + total_escape += key_escape; + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + case LUA_TSTRING: + value = (u_char *) lua_tolstring(L, -1, &value_len); + + total_escape += 2 * ngx_http_lua_escape_uri(NULL, value, value_len, + NGX_ESCAPE_URI_COMPONENT); + + len += key_len + value_len + (sizeof("=") - 1); + n++; + + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) { + len += key_len; + n++; + } + + break; + + case LUA_TTABLE: + + i = 0; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_isboolean(L, -1)) { + if (lua_toboolean(L, -1)) { + len += key_len; + + } else { + lua_pop(L, 1); + continue; + } + + } else { + value = (u_char *) lua_tolstring(L, -1, &value_len); + + if (value == NULL) { + luaL_error(L, "attempt to use %s as query arg value", + luaL_typename(L, -1)); + return; + } + + total_escape += + 2 * ngx_http_lua_escape_uri(NULL, value, + value_len, + NGX_ESCAPE_URI_COMPONENT); + + len += key_len + value_len + (sizeof("=") - 1); + } + + if (i++ > 0) { + total_escape += key_escape; + } + + n++; + lua_pop(L, 1); + } + + break; + + default: + luaL_error(L, "attempt to use %s as query arg value", + luaL_typename(L, -1)); + return; + } + + lua_pop(L, 1); + } + + len += (size_t) total_escape; + + if (n > 1) { + len += (n - 1) * (sizeof("&") - 1); + } + + dd("len 1: %d", (int) len); + + if (r) { + p = ngx_palloc(r->pool, len); + if (p == NULL) { + luaL_error(L, "no memory"); + return; + } + + } else { + p = lua_newuserdata(L, len); + } + + args->data = p; + args->len = len; + + i = 0; + lua_pushnil(L); + while (lua_next(L, table) != 0) { + key = (u_char *) lua_tolstring(L, -2, &key_len); + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + case LUA_TSTRING: + + if (total_escape) { + p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len, + NGX_ESCAPE_URI_COMPONENT + ); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + *p++ = '='; + + value = (u_char *) lua_tolstring(L, -1, &value_len); + + if (total_escape) { + p = (u_char *) ngx_http_lua_escape_uri(p, value, value_len, + NGX_ESCAPE_URI_COMPONENT + ); + + } else { + p = ngx_copy(p, value, value_len); + } + + if (i != n - 1) { + /* not the last pair */ + *p++ = '&'; + } + + i++; + + break; + + case LUA_TBOOLEAN: + if (lua_toboolean(L, -1)) { + if (total_escape) { + p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + if (i != n - 1) { + /* not the last pair */ + *p++ = '&'; + } + + i++; + } + + break; + + case LUA_TTABLE: + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + + if (lua_isboolean(L, -1)) { + if (lua_toboolean(L, -1)) { + if (total_escape) { + p = (u_char *) + ngx_http_lua_escape_uri(p, key, key_len, + NGX_ESCAPE_URI_COMPONENT); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + } else { + lua_pop(L, 1); + continue; + } + + } else { + + if (total_escape) { + p = (u_char *) + ngx_http_lua_escape_uri(p, key, + key_len, + NGX_ESCAPE_URI_COMPONENT + ); + + } else { + dd("shortcut: no escape required"); + + p = ngx_copy(p, key, key_len); + } + + *p++ = '='; + + value = (u_char *) lua_tolstring(L, -1, &value_len); + + if (total_escape) { + p = (u_char *) + ngx_http_lua_escape_uri(p, value, + value_len, + NGX_ESCAPE_URI_COMPONENT + ); + + } else { + p = ngx_copy(p, value, value_len); + } + } + + if (i != n - 1) { + /* not the last pair */ + *p++ = '&'; + } + + i++; + lua_pop(L, 1); + } + + break; + + default: + luaL_error(L, "should not reach here"); + return; + } + + lua_pop(L, 1); + } + + if (p - args->data != (ssize_t) len) { + luaL_error(L, "buffer error: %d != %d", + (int) (p - args->data), (int) len); + return; + } +} + + +static ngx_int_t +ngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx) +{ + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua thread aborting request with URI rewrite jump: " + "\"%V?%V\"", &r->uri, &r->args); + + ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); + + ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); + + ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; + + if (r->filter_finalize) { + ngx_http_set_ctx(r, ctx, ngx_http_lua_module); + } + + ngx_http_lua_request_cleanup(ctx, 1 /* forcible */); + ngx_http_lua_init_ctx(r, ctx); + + return NGX_OK; +} + + +/* XXX ngx_open_and_stat_file is static in the core. sigh. */ +ngx_int_t +ngx_http_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, + ngx_log_t *log) +{ + ngx_fd_t fd; + ngx_file_info_t fi; + + if (of->fd != NGX_INVALID_FILE) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (of->uniq == ngx_file_uniq(&fi)) { + goto done; + } + + } else if (of->test_dir) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (ngx_is_dir(&fi)) { + goto done; + } + } + + if (!of->log) { + + /* + * Use non-blocking open() not to hang on FIFO files, etc. + * This flag has no effect on a regular files. + */ + + fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + } else { + fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + } + + if (fd == NGX_INVALID_FILE) { + of->failed = ngx_open_file_n; + goto failed; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + return NGX_ERROR; + } + + if (ngx_is_dir(&fi)) { + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + } else { + of->fd = fd; + + if (of->directio <= ngx_file_size(&fi)) { + if (ngx_directio_on(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_directio_on_n " \"%s\" failed", name); + + } else { + of->is_directio = 1; + } + } + } + +done: + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->fs_size = ngx_file_fs_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; + +failed: + + of->fd = NGX_INVALID_FILE; + of->err = ngx_errno; + + return NGX_ERROR; +} + + +ngx_chain_t * +ngx_http_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p, + ngx_chain_t **free, size_t len) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + u_char *start, *end; + + const ngx_buf_tag_t tag = (ngx_buf_tag_t) &ngx_http_lua_module; + + if (*free) { + cl = *free; + *free = cl->next; + cl->next = NULL; + + b = cl->buf; + start = b->start; + end = b->end; + if (start && (size_t) (end - start) >= len) { + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0, + "lua reuse free buf memory %O >= %uz, cl:%p, p:%p", + (off_t) (end - start), len, cl, start); + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->start = start; + b->pos = start; + b->last = start; + b->end = end; + b->tag = tag; + + if (len) { + b->temporary = 1; + } + + return cl; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0, + "lua reuse free buf chain, but reallocate memory " + "because %uz >= %O, cl:%p, p:%p", len, + (off_t) (b->end - b->start), cl, b->start); + + if (ngx_buf_in_memory(b) && b->start) { + ngx_pfree(p, b->start); + } + + ngx_memzero(b, sizeof(ngx_buf_t)); + + if (len == 0) { + return cl; + } + + b->start = ngx_palloc(p, len); + if (b->start == NULL) { + return NULL; + } + + b->end = b->start + len; + + dd("buf start: %p", cl->buf->start); + + b->pos = b->start; + b->last = b->start; + b->tag = tag; + b->temporary = 1; + + return cl; + } + + cl = ngx_alloc_chain_link(p); + if (cl == NULL) { + return NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, + "lua allocate new chainlink and new buf of size %uz, cl:%p", + len, cl); + + cl->buf = len ? ngx_create_temp_buf(p, len) : ngx_calloc_buf(p); + if (cl->buf == NULL) { + return NULL; + } + + dd("buf start: %p", cl->buf->start); + + cl->buf->tag = tag; + cl->next = NULL; + + return cl; +} + + +static int +ngx_http_lua_thread_traceback(lua_State *L, lua_State *co, + ngx_http_lua_co_ctx_t *coctx) +{ + int base; + int level, coid; + lua_Debug ar; + + base = lua_gettop(L); + lua_checkstack(L, 3); + lua_pushliteral(L, "stack traceback:"); + coid = 0; + + while (co) { + + if (coid >= NGX_HTTP_LUA_BT_MAX_COROS) { + break; + } + + lua_checkstack(L, 2); + lua_pushfstring(L, "\ncoroutine %d:", coid++); + + level = 0; + + while (lua_getstack(co, level++, &ar)) { + + lua_checkstack(L, 5); + + if (level > NGX_HTTP_LUA_BT_DEPTH) { + lua_pushliteral(L, "\n\t..."); + break; + } + + lua_pushliteral(L, "\n\t"); + lua_getinfo(co, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + + if (ar.currentline > 0) { + lua_pushfstring(L, "%d:", ar.currentline); + } + + if (*ar.namewhat != '\0') { /* is there a name? */ + lua_pushfstring(L, " in function " LUA_QS, ar.name); + + } else { + if (*ar.what == 'm') { /* main? */ + lua_pushliteral(L, " in main chunk"); + + } else if (*ar.what == 'C' || *ar.what == 't') { + lua_pushliteral(L, " ?"); /* C function or tail call */ + + } else { + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + } + } + + if (lua_gettop(L) - base >= 15) { + lua_concat(L, lua_gettop(L) - base); + } + + /* check if the coroutine has a parent coroutine*/ + coctx = coctx->parent_co_ctx; + if (!coctx || coctx->co_status == NGX_HTTP_LUA_CO_DEAD) { + break; + } + + co = coctx->co; + } + + lua_concat(L, lua_gettop(L) - base); + return 1; +} + + +int +ngx_http_lua_traceback(lua_State *L) +{ + if (!lua_isstring(L, 1)) { /* 'message' not a string? */ + return 1; /* keep it intact */ + } + + lua_getglobal(L, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + + +static void +ngx_http_lua_inject_arg_api(lua_State *L) +{ + lua_pushliteral(L, "arg"); + lua_newtable(L); /* .arg table aka {} */ + + lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* the metatable */ + + lua_pushcfunction(L, ngx_http_lua_param_set); + lua_setfield(L, -2, "__newindex"); + + lua_setmetatable(L, -2); /* tie the metatable to param table */ + + dd("top: %d, type -1: %s", lua_gettop(L), luaL_typename(L, -1)); + + lua_rawset(L, -3); /* set ngx.arg table */ +} + + +static int +ngx_http_lua_param_set(lua_State *L) +{ + ngx_http_lua_ctx_t *ctx; + ngx_http_request_t *r; + + r = ngx_http_lua_get_req(L); + if (r == NULL) { + return 0; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return luaL_error(L, "ctx not found"); + } + + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_BODY_FILTER); + + return ngx_http_lua_body_filter_param_set(L, r, ctx); +} + + +ngx_http_lua_co_ctx_t * +ngx_http_lua_get_co_ctx(lua_State *L, ngx_http_lua_ctx_t *ctx) +{ +#ifdef HAVE_LUA_EXDATA2 + return (ngx_http_lua_co_ctx_t *) lua_getexdata2(L); +#else + ngx_uint_t i; + ngx_list_part_t *part; + ngx_http_lua_co_ctx_t *coctx; + + if (L == ctx->entry_co_ctx.co) { + return &ctx->entry_co_ctx; + } + + if (ctx->user_co_ctx == NULL) { + return NULL; + } + + part = &ctx->user_co_ctx->part; + coctx = part->elts; + + /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */ + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + coctx = part->elts; + i = 0; + } + + if (coctx[i].co == L) { + return &coctx[i]; + } + } + + return NULL; +#endif +} + + +ngx_http_lua_co_ctx_t * +ngx_http_lua_create_co_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) +{ + ngx_http_lua_co_ctx_t *coctx; + + if (ctx->user_co_ctx == NULL) { + ctx->user_co_ctx = ngx_list_create(r->pool, 4, + sizeof(ngx_http_lua_co_ctx_t)); + if (ctx->user_co_ctx == NULL) { + return NULL; + } + } + + coctx = ngx_list_push(ctx->user_co_ctx); + if (coctx == NULL) { + return NULL; + } + + ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t)); + + coctx->next_zombie_child_thread = &coctx->zombie_child_threads; + coctx->co_ref = LUA_NOREF; + + return coctx; +} + + +/* this is for callers other than the content handler */ +ngx_int_t +ngx_http_lua_run_posted_threads(ngx_connection_t *c, lua_State *L, + ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_uint_t nreqs) +{ + ngx_int_t rc; + ngx_http_lua_posted_thread_t *pt; + + for ( ;; ) { + if (c->destroyed || c->requests != nreqs) { + return NGX_DONE; + } + + pt = ctx->posted_threads; + if (pt == NULL) { + return NGX_DONE; + } + + ctx->posted_threads = pt->next; + + ngx_http_lua_probe_run_posted_thread(r, pt->co_ctx->co, + (int) pt->co_ctx->co_status); + + if (pt->co_ctx->co_status != NGX_HTTP_LUA_CO_RUNNING) { + continue; + } + + ctx->cur_co_ctx = pt->co_ctx; + + rc = ngx_http_lua_run_thread(L, r, ctx, 0); + + if (rc == NGX_AGAIN) { + continue; + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + continue; + } + + /* rc == NGX_ERROR || rc >= NGX_OK */ + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + } + + return rc; + } + + /* impossible to reach here */ +} + + +ngx_int_t +ngx_http_lua_post_thread(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, + ngx_http_lua_co_ctx_t *coctx) +{ + ngx_http_lua_posted_thread_t **p; + ngx_http_lua_posted_thread_t *pt; + + pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t)); + if (pt == NULL) { + return NGX_ERROR; + } + + pt->co_ctx = coctx; + pt->next = NULL; + + for (p = &ctx->posted_threads; *p; p = &(*p)->next) { /* void */ } + + *p = pt; + + return NGX_OK; +} + + +static void +ngx_http_lua_finalize_threads(ngx_http_request_t *r, + ngx_http_lua_ctx_t *ctx, lua_State *L) +{ + int ref; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_http_lua_co_ctx_t *cc, *coctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http lua finalize threads"); + +#if 1 + coctx = ctx->on_abort_co_ctx; + if (coctx && coctx->co_ref != LUA_NOREF) { + if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) { + /* the on_abort thread contributes to the coctx->uthreads + * counter only when it actually starts running */ + ngx_http_lua_cleanup_pending_operation(coctx); + ctx->uthreads--; + } + + ngx_http_lua_del_thread(r, L, ctx, coctx); + ctx->on_abort_co_ctx = NULL; + } +#endif + + if (ctx->user_co_ctx) { + part = &ctx->user_co_ctx->part; + cc = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + cc = part->elts; + i = 0; + } + + coctx = &cc[i]; + + ref = coctx->co_ref; + + if (ref != LUA_NOREF) { + ngx_http_lua_cleanup_pending_operation(coctx); + + ngx_http_lua_del_thread(r, L, ctx, coctx); + + ctx->uthreads--; + } + } + + ctx->user_co_ctx = NULL; + } + + ngx_http_lua_assert(ctx->uthreads == 0); + + coctx = &ctx->entry_co_ctx; + + ref = coctx->co_ref; + if (ref != LUA_NOREF) { + ngx_http_lua_cleanup_pending_operation(coctx); + ngx_http_lua_del_thread(r, L, ctx, coctx); + } +} + + +static ngx_int_t +ngx_http_lua_post_zombie_thread(ngx_http_request_t *r, + ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread) +{ + ngx_http_lua_posted_thread_t *pt; + + pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t)); + if (pt == NULL) { + return NGX_ERROR; + } + + pt->co_ctx = thread; + pt->next = NULL; + + ngx_http_lua_assert(parent->next_zombie_child_thread != NULL); + + *parent->next_zombie_child_thread = pt; + parent->next_zombie_child_thread = &pt->next; + + return NGX_OK; +} + + +static void +ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r, + lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx) +{ + ngx_http_lua_posted_thread_t *pt; + + for (pt = coctx->zombie_child_threads; pt; pt = pt->next) { + if (pt->co_ctx->co_ref != LUA_NOREF) { + ngx_http_lua_del_thread(r, L, ctx, pt->co_ctx); + ctx->uthreads--; + } + } + + coctx->zombie_child_threads = NULL; + coctx->next_zombie_child_thread = &coctx->zombie_child_threads; +} + + +ngx_int_t +ngx_http_lua_check_broken_connection(ngx_http_request_t *r, ngx_event_t *ev) +{ + int n; + char buf[1]; + ngx_err_t err; + ngx_int_t event; + ngx_connection_t *c; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http lua check client, write event:%d, \"%V\"", + ev->write, &r->uri); + + c = r->connection; + + if (c->error) { + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + + event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; + + if (ngx_del_event(ev, event, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + return NGX_HTTP_CLIENT_CLOSED_REQUEST; + } + +#if (NGX_HTTP_V2) + if (r->stream) { + return NGX_OK; + } +#endif + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + + if (!ev->pending_eof) { + return NGX_OK; + } + + ev->eof = 1; + + if (ev->kq_errno) { + ev->error = 1; + } + + ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, + "kevent() reported that client prematurely closed " + "connection"); + + return NGX_HTTP_CLIENT_CLOSED_REQUEST; + } + +#endif + + n = recv(c->fd, buf, 1, MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err, + "http lua recv(): %d", n); + + if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { + return NGX_OK; + } + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + dd("event is active"); + + event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; + +#if 1 + if (ngx_del_event(ev, event, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } +#endif + } + + dd("HERE %d", (int) n); + + if (n > 0) { + return NGX_OK; + } + + if (n == -1) { + if (err == NGX_EAGAIN) { + dd("HERE"); + return NGX_OK; + } + + ev->error = 1; + + } else { /* n == 0 */ + err = 0; + } + + ev->eof = 1; + + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "client prematurely closed connection"); + + return NGX_HTTP_CLIENT_CLOSED_REQUEST; +} + + +void +ngx_http_lua_rd_check_broken_connection(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_event_t *rev; + ngx_http_lua_ctx_t *ctx; + + if (r->done) { + return; + } + + rc = ngx_http_lua_check_broken_connection(r, r->connection->read); + + if (rc == NGX_OK) { + return; + } + + /* rc == NGX_ERROR || rc > NGX_OK */ + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + if (ctx->on_abort_co_ctx == NULL) { + r->connection->error = 1; + ngx_http_lua_request_cleanup(ctx, 0); + ngx_http_lua_finalize_request(r, rc); + return; + } + + if (ctx->on_abort_co_ctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) { + + /* on_abort already run for the current request handler */ + + rev = r->connection->read; + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { + ngx_http_lua_request_cleanup(ctx, 0); + ngx_http_lua_finalize_request(r, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + return; + } + + ctx->uthreads++; + ctx->resume_handler = ngx_http_lua_on_abort_resume; + ctx->on_abort_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; + ctx->cur_co_ctx = ctx->on_abort_co_ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua waking up the on_abort callback thread"); + + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; + + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + r->write_event_handler(r); +} + + +static ngx_int_t +ngx_http_lua_on_abort_resume(ngx_http_request_t *r) +{ + lua_State *vm; + ngx_int_t rc; + ngx_uint_t nreqs; + ngx_connection_t *c; + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->resume_handler = ngx_http_lua_wev_handler; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua resuming the on_abort callback thread"); + +#if 0 + ngx_http_lua_probe_info("tcp resume"); +#endif + + c = r->connection; + vm = ngx_http_lua_get_lua_vm(r, ctx); + nreqs = c->requests; + + rc = ngx_http_lua_run_thread(vm, r, ctx, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua run thread returned %d", rc); + + if (rc == NGX_AGAIN) { + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (rc == NGX_DONE) { + ngx_http_lua_finalize_request(r, NGX_DONE); + return ngx_http_lua_run_posted_threads(c, vm, r, ctx, nreqs); + } + + if (ctx->entered_content_phase) { + ngx_http_lua_finalize_request(r, rc); + return NGX_DONE; + } + + return rc; +} + + +ngx_int_t +ngx_http_lua_test_expect(ngx_http_request_t *r) +{ + ngx_int_t n; + ngx_str_t *expect; + + if (r->expect_tested + || r->headers_in.expect == NULL + || r->http_version < NGX_HTTP_VERSION_11) + { + return NGX_OK; + } + + r->expect_tested = 1; + + expect = &r->headers_in.expect->value; + + if (expect->len != sizeof("100-continue") - 1 + || ngx_strncasecmp(expect->data, (u_char *) "100-continue", + sizeof("100-continue") - 1) + != 0) + { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "send 100 Continue"); + + n = r->connection->send(r->connection, + (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF, + sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1); + + if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) { + return NGX_OK; + } + + /* we assume that such small packet should be send successfully */ + + return NGX_ERROR; +} + + +void +ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx && ctx->cur_co_ctx) { + ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); + } + + if (r->connection->fd != (ngx_socket_t) -1) { + ngx_http_finalize_request(r, rc); + return; + } + + ngx_http_lua_finalize_fake_request(r, rc); +} + + +void +ngx_http_lua_finalize_fake_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_connection_t *c; +#if (NGX_HTTP_SSL) + ngx_ssl_conn_t *ssl_conn; + ngx_http_lua_ssl_ctx_t *cctx; +#endif + + c = r->connection; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http lua finalize fake request: %d, a:%d, c:%d", + rc, r == c->data, r->main->count); + + if (rc == NGX_DONE) { + ngx_http_lua_close_fake_request(r); + return; + } + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + +#if (NGX_HTTP_SSL) + + if (r->connection->ssl) { + ssl_conn = r->connection->ssl->connection; + if (ssl_conn) { + c = ngx_ssl_get_connection(ssl_conn); + + if (c && c->ssl) { + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + if (cctx != NULL) { + cctx->exit_code = 0; + } + } + } + } + +#endif + + ngx_http_lua_close_fake_request(r); + return; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + c->write->delayed = 0; + ngx_del_timer(c->write); + } + + ngx_http_lua_close_fake_request(r); +} + + +static void +ngx_http_lua_close_fake_request(ngx_http_request_t *r) +{ + ngx_connection_t *c; + + r = r->main; + c = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http lua fake request count:%d", r->count); + + if (r->count == 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http lua fake request " + "count is zero"); + } + + r->count--; + + if (r->count) { + return; + } + + ngx_http_lua_free_fake_request(r); + ngx_http_lua_close_fake_connection(c); +} + + +void +ngx_http_lua_free_fake_request(ngx_http_request_t *r) +{ + ngx_log_t *log; + ngx_http_cleanup_t *cln; + + log = r->connection->log; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http lua close fake " + "request"); + + if (r->pool == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "http lua fake request " + "already closed"); + return; + } + + cln = r->cleanup; + r->cleanup = NULL; + + while (cln) { + if (cln->handler) { + cln->handler(cln->data); + } + + cln = cln->next; + } + + r->request_line.len = 0; + + r->connection->destroyed = 1; +} + + +void +ngx_http_lua_close_fake_connection(ngx_connection_t *c) +{ + ngx_pool_t *pool; + ngx_connection_t *saved_c = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http lua close fake http connection %p", c); + + c->destroyed = 1; + + pool = c->pool; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + c->read->closed = 1; + c->write->closed = 1; + + /* we temporarily use a valid fd (0) to make ngx_free_connection happy */ + + c->fd = 0; + + if (ngx_cycle->files) { + saved_c = ngx_cycle->files[0]; + } + + ngx_free_connection(c); + + c->fd = (ngx_socket_t) -1; + + if (ngx_cycle->files) { + ngx_cycle->files[0] = saved_c; + } + + if (pool) { + ngx_destroy_pool(pool); + } +} + + +ngx_int_t +ngx_http_lua_init_vm(lua_State **new_vm, lua_State *parent_vm, + ngx_cycle_t *cycle, ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf, + ngx_log_t *log, ngx_pool_cleanup_t **pcln) +{ + int rc; + lua_State *L; + ngx_uint_t i; + ngx_pool_cleanup_t *cln; + ngx_http_lua_preload_hook_t *hook; + ngx_http_lua_vm_state_t *state; + + cln = ngx_pool_cleanup_add(pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + /* create new Lua VM instance */ + L = ngx_http_lua_new_state(parent_vm, cycle, lmcf, log); + if (L == NULL) { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "lua initialize the " + "global Lua VM %p", L); + + /* register cleanup handler for Lua VM */ + cln->handler = ngx_http_lua_cleanup_vm; + + state = ngx_alloc(sizeof(ngx_http_lua_vm_state_t), log); + if (state == NULL) { + return NGX_ERROR; + } + + state->vm = L; + state->count = 1; + + cln->data = state; + + if (lmcf->vm_cleanup == NULL) { + /* this assignment will happen only once, + * and also only for the main Lua VM */ + lmcf->vm_cleanup = cln; + } + + if (pcln) { + *pcln = cln; + } + +#ifdef OPENRESTY_LUAJIT + /* load FFI library first since cdata needs it */ + luaopen_ffi(L); +#endif + + if (lmcf->preload_hooks) { + + /* register the 3rd-party module's preload hooks */ + + lua_getglobal(L, "package"); + lua_getfield(L, -1, "preload"); + + hook = lmcf->preload_hooks->elts; + + for (i = 0; i < lmcf->preload_hooks->nelts; i++) { + + ngx_http_lua_probe_register_preload_package(L, + hook[i].package); + + lua_pushcfunction(L, hook[i].loader); + lua_setfield(L, -2, (char *) hook[i].package); + } + + lua_pop(L, 2); + } + + *new_vm = L; + + lua_getglobal(L, "require"); + lua_pushstring(L, "resty.core"); + + rc = lua_pcall(L, 1, 1, 0); + if (rc != 0) { + return NGX_DECLINED; + } + +#ifdef OPENRESTY_LUAJIT + ngx_http_lua_inject_global_write_guard(L, log); +#endif + + return NGX_OK; +} + + +void +ngx_http_lua_cleanup_vm(void *data) +{ + lua_State *L; + ngx_http_lua_vm_state_t *state = data; + +#if (DDEBUG) + if (state) { + dd("cleanup VM: c:%d, s:%p", (int) state->count, state->vm); + } +#endif + + if (state) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua decrementing the reference count for Lua VM: %i", + state->count); + + if (--state->count == 0) { + L = state->vm; + ngx_http_lua_cleanup_conn_pools(L); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua close the global Lua VM %p", L); + lua_close(L); + ngx_free(state); + } + } +} + + +ngx_connection_t * +ngx_http_lua_create_fake_connection(ngx_pool_t *pool) +{ + ngx_log_t *log; + ngx_connection_t *c; + ngx_connection_t *saved_c = NULL; + + /* (we temporarily use a valid fd (0) to make ngx_get_connection happy) */ + if (ngx_cycle->files) { + saved_c = ngx_cycle->files[0]; + } + + c = ngx_get_connection(0, ngx_cycle->log); + + if (ngx_cycle->files) { + ngx_cycle->files[0] = saved_c; + } + + if (c == NULL) { + return NULL; + } + + c->fd = (ngx_socket_t) -1; + c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + + if (pool) { + c->pool = pool; + + } else { + c->pool = ngx_create_pool(128, c->log); + if (c->pool == NULL) { + goto failed; + } + } + + log = ngx_pcalloc(c->pool, sizeof(ngx_log_t)); + if (log == NULL) { + goto failed; + } + + c->log = log; + c->log->connection = c->number; + c->log->action = NULL; + c->log->data = NULL; + + c->log_error = NGX_ERROR_INFO; + +#if 0 + c->buffer = ngx_create_temp_buf(c->pool, 2); + if (c->buffer == NULL) { + goto failed; + } + + c->buffer->start[0] = CR; + c->buffer->start[1] = LF; +#endif + + c->error = 1; + + dd("created fake connection: %p", c); + + return c; + +failed: + + ngx_http_lua_close_fake_connection(c); + return NULL; +} + + +ngx_http_request_t * +ngx_http_lua_create_fake_request(ngx_connection_t *c) +{ + ngx_http_request_t *r; + + r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)); + if (r == NULL) { + return NULL; + } + + c->requests++; + + r->pool = c->pool; + + dd("r pool allocated: %d", (int) (sizeof(ngx_http_lua_ctx_t) + + sizeof(void *) * ngx_http_max_module + sizeof(ngx_http_cleanup_t))); + +#if 0 + hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); + if (hc == NULL) { + goto failed; + } + + r->header_in = c->buffer; + r->header_end = c->buffer->start; + + if (ngx_list_init(&r->headers_out.headers, r->pool, 0, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + goto failed; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 0, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + goto failed; + } +#endif + + r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); + if (r->ctx == NULL) { + return NULL; + } + +#if 0 + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts + * sizeof(ngx_http_variable_value_t)); + if (r->variables == NULL) { + goto failed; + } +#endif + + r->connection = c; + + r->headers_in.content_length_n = 0; + c->data = r; +#if 0 + hc->request = r; + r->http_connection = hc; +#endif + r->signature = NGX_HTTP_MODULE; + r->main = r; + r->count = 1; + + r->method = NGX_HTTP_UNKNOWN; + + r->headers_in.keep_alive_n = -1; + r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; + r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1; + + r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; + r->discard_body = 1; + + dd("created fake request %p", r); + + return r; +} + + +ngx_int_t +ngx_http_lua_report(ngx_log_t *log, lua_State *L, int status, + const char *prefix) +{ + const char *msg; + + if (status && !lua_isnil(L, -1)) { + msg = lua_tostring(L, -1); + if (msg == NULL) { + msg = "unknown error"; + } + + ngx_log_error(NGX_LOG_ERR, log, 0, "%s error: %s", prefix, msg); + lua_pop(L, 1); + } + + /* force a full garbage-collection cycle */ + lua_gc(L, LUA_GCCOLLECT, 0); + + return status == 0 ? NGX_OK : NGX_ERROR; +} + + +int +ngx_http_lua_do_call(ngx_log_t *log, lua_State *L) +{ + int status, base; +#if (NGX_PCRE) + ngx_pool_t *old_pool; +#endif + + base = lua_gettop(L); /* function index */ + lua_pushcfunction(L, ngx_http_lua_traceback); /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ + +#if (NGX_PCRE) + old_pool = ngx_http_lua_pcre_malloc_init(ngx_cycle->pool); +#endif + + status = lua_pcall(L, 0, 0, base); + +#if (NGX_PCRE) + ngx_http_lua_pcre_malloc_done(old_pool); +#endif + + lua_remove(L, base); + + return status; +} + + +static int +ngx_http_lua_get_raw_phase_context(lua_State *L) +{ + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + +#ifdef OPENRESTY_LUAJIT + r = lua_getexdata(L); +#else + r = lua_touserdata(L, 1); +#endif + + if (r == NULL) { + return 0; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return 0; + } + + lua_pushinteger(L, (int) ctx->context); + return 1; +} + + +ngx_http_cleanup_t * +ngx_http_lua_cleanup_add(ngx_http_request_t *r, size_t size) +{ + ngx_http_cleanup_t *cln; + ngx_http_lua_ctx_t *ctx; + + if (size == 0) { + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + + r = r->main; + + if (ctx != NULL && ctx->free_cleanup) { + cln = ctx->free_cleanup; + ctx->free_cleanup = cln->next; + + dd("reuse cleanup: %p", cln); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua http cleanup reuse: %p", cln); + + cln->handler = NULL; + cln->next = r->cleanup; + + r->cleanup = cln; + + return cln; + } + } + + return ngx_http_cleanup_add(r, size); +} + + +void +ngx_http_lua_cleanup_free(ngx_http_request_t *r, ngx_http_cleanup_pt *cleanup) +{ + ngx_http_cleanup_t **last; + ngx_http_cleanup_t *cln; + ngx_http_lua_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + if (ctx == NULL) { + return; + } + + r = r->main; + + cln = (ngx_http_cleanup_t *) + ((u_char *) cleanup - offsetof(ngx_http_cleanup_t, handler)); + + dd("cln: %p, cln->handler: %p, &cln->handler: %p", + cln, cln->handler, &cln->handler); + + last = &r->cleanup; + + while (*last) { + if (*last == cln) { + *last = cln->next; + + cln->next = ctx->free_cleanup; + ctx->free_cleanup = cln; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua http cleanup free: %p", cln); + + return; + } + + last = &(*last)->next; + } +} + + +#if (NGX_HTTP_LUA_HAVE_SA_RESTART) +void +ngx_http_lua_set_sa_restart(ngx_log_t *log) +{ + int *signo; + int sigs[] = NGX_HTTP_LUA_SA_RESTART_SIGS; + struct sigaction act; + + for (signo = sigs; *signo != 0; signo++) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "setting SA_RESTART for signal %d", *signo); + + if (sigaction(*signo, NULL, &act) != 0) { + ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to get " + "sigaction for signal %d", *signo); + } + + act.sa_flags |= SA_RESTART; + + if (sigaction(*signo, &act, NULL) != 0) { + ngx_log_error(NGX_LOG_WARN, log, ngx_errno, "failed to set " + "sigaction for signal %d", *signo); + } + } +} +#endif + + +size_t +ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size) +{ + size_t n; + u_char c; + static u_char hex[] = "0123456789ABCDEF"; + + static uint32_t escape[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + if (dst == NULL) { + + /* find the number of characters to be escaped */ + + n = 0; + + while (size) { + c = *src; + if (escape[c >> 5] & (1 << (c & 0x1f))) { + n += 4; + + } else { + n++; + } + + src++; + size--; + } + + return n; + } + + while (size) { + c = *src; + if (escape[c >> 5] & (1 << (c & 0x1f))) { + *dst++ = '\\'; + *dst++ = 'x'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + + size--; + } + + return 0; +} + + +ngx_int_t +ngx_http_lua_copy_escaped_header(ngx_http_request_t *r, + ngx_str_t *dst, int is_name) +{ + size_t escape; + size_t len; + u_char *data; + int type; + + type = is_name + ? NGX_HTTP_LUA_ESCAPE_HEADER_NAME : NGX_HTTP_LUA_ESCAPE_HEADER_VALUE; + + data = dst->data; + len = dst->len; + + escape = ngx_http_lua_escape_uri(NULL, data, len, type); + if (escape > 0) { + /* + * we allocate space for the trailing '\0' char here because nginx + * header values must be null-terminated + */ + dst->data = ngx_palloc(r->pool, len + 2 * escape + 1); + if (dst->data == NULL) { + return NGX_ERROR; + } + + ngx_http_lua_escape_uri(dst->data, data, len, type); + dst->len = len + 2 * escape; + dst->data[dst->len] = '\0'; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_lua_decode_base64mime(ngx_str_t *dst, ngx_str_t *src) +{ + size_t i; + u_char *d, *s, ch; + size_t data_len = 0; + u_char buf[4]; + size_t buf_len = 0; + static u_char basis[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + for (i = 0; i < src->len; i++) { + ch = src->data[i]; + if (ch == '=') { + break; + } + + if (basis[ch] == 77) { + continue; + } + + data_len++; + } + + if (data_len % 4 == 1) { + return NGX_ERROR; + } + + s = src->data; + d = dst->data; + + for (i = 0; i < src->len; i++) { + if (s[i] == '=') { + break; + } + + if (basis[s[i]] == 77) { + continue; + } + + buf[buf_len++] = s[i]; + if (buf_len == 4) { + *d++ = (u_char) (basis[buf[0]] << 2 | basis[buf[1]] >> 4); + *d++ = (u_char) (basis[buf[1]] << 4 | basis[buf[2]] >> 2); + *d++ = (u_char) (basis[buf[2]] << 6 | basis[buf[3]]); + buf_len = 0; + } + } + + if (buf_len > 1) { + *d++ = (u_char) (basis[buf[0]] << 2 | basis[buf[1]] >> 4); + } + + if (buf_len > 2) { + *d++ = (u_char) (basis[buf[1]] << 4 | basis[buf[2]] >> 2); + } + + dst->len = d - dst->data; + + return NGX_OK; +} + + +ngx_addr_t * +ngx_http_lua_parse_addr(lua_State *L, u_char *text, size_t len) +{ + ngx_addr_t *addr; + size_t socklen; + in_addr_t inaddr; + ngx_uint_t family; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct in6_addr inaddr6; + struct sockaddr_in6 *sin6; + + /* + * prevent MSVC8 warning: + * potentially uninitialized local variable 'inaddr6' used + */ + ngx_memzero(&inaddr6, sizeof(struct in6_addr)); +#endif + + inaddr = ngx_inet_addr(text, len); + + if (inaddr != INADDR_NONE) { + family = AF_INET; + socklen = sizeof(struct sockaddr_in); + +#if (NGX_HAVE_INET6) + + } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) { + family = AF_INET6; + socklen = sizeof(struct sockaddr_in6); +#endif + + } else { + return NULL; + } + + addr = lua_newuserdata(L, sizeof(ngx_addr_t) + socklen + len); + if (addr == NULL) { + luaL_error(L, "no memory"); + return NULL; + } + + addr->sockaddr = (struct sockaddr *) ((u_char *) addr + sizeof(ngx_addr_t)); + + ngx_memzero(addr->sockaddr, socklen); + + addr->sockaddr->sa_family = (u_char) family; + addr->socklen = socklen; + + switch (family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr->sockaddr; + ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr->sockaddr; + sin->sin_addr.s_addr = inaddr; + break; + } + + addr->name.data = (u_char *) addr->sockaddr + socklen; + addr->name.len = len; + ngx_memcpy(addr->name.data, text, len); + + return addr; +} + + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ |