/** * Send last_buf, terminate output stream * */ static int ngx_http_lua_ngx_eof(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request object found"); } if (lua_gettop(L) != 0) { return luaL_error(L, "no argument is expected"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); rc = ngx_http_lua_send_chain_link(r, ctx, NULL/*indicate last_buf*/); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to send eof buf"); } return 0; }
/** * Output response body * */ int ngx_http_lua_ngx_echo(lua_State *l) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; lua_getglobal(l, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(l, -1); lua_pop(l, 1); if(r) { ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if(ctx != NULL && ctx->eof == 0) { const char *data; size_t len; ngx_buf_t *buf; ngx_chain_t *cl; // concatenate all args into single string // XXX: easy way to support multiple args, any serious performance penalties? lua_concat(l, lua_gettop(l)); data = lua_tolstring(l, -1, &len); if(data) { buf = ngx_calloc_buf(r->pool); if(buf == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "(lua-ngx-echo) can't allocate memory for output buffer!"); } else { // FIXME: is there any need to copy the content first? as // lua string will be invalid when it's poped out from // stack buf->start = buf->pos = (u_char*)data; buf->last = buf->end = (u_char*)(data + len); buf->memory = 1; cl = ngx_alloc_chain_link(r->pool); if(cl == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "(lua-ngx-echo) can't allocate memory for output chain-link!"); } else { cl->next = NULL; cl->buf = buf; ngx_http_lua_send_chain_link(r, ctx, cl); } } } // clear args lua_settop(l, 0); } } else { dd("(lua-ngx-echo) can't find nginx request object!"); } return 0; }
/** * Send last_buf, terminate output stream * */ static int ngx_http_lua_ngx_eof(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } if (lua_gettop(L) != 0) { return luaL_error(L, "no argument is expected"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua send eof"); rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } lua_pushinteger(L, 1); return 1; }
/** * Force flush out response content * */ static int ngx_http_lua_ngx_flush(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_buf_t *buf; ngx_chain_t *cl; ngx_int_t rc; lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } if ((r->method & NGX_HTTP_HEAD) || r->header_only) { return 0; } if (ctx->eof) { return luaL_error(L, "already seen eof"); } buf = ngx_calloc_buf(r->pool); if (buf == NULL) { return luaL_error(L, "memory allocation error"); } buf->flush = 1; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return luaL_error(L, "out of memory"); } cl->next = NULL; cl->buf = buf; rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to send chain link: %d", (int) rc); } return 0; }
/** * Send last_buf, terminate output stream * */ int ngx_http_lua_ngx_eof(lua_State *l) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_buf_t *buf; ngx_chain_t *cl; lua_getglobal(l, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(l, -1); lua_pop(l, 1); if(r) { ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if(ctx != NULL && ctx->eof == 0) { ctx->eof = 1; // set eof flag to prevent further output buf = ngx_calloc_buf(r->pool); if(buf == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "(lua-ngx-eof) can't allocate memory for output buffer!"); } else { buf->last_buf = 1; cl = ngx_alloc_chain_link(r->pool); if(cl == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "(lua-ngx-eof) can't allocate memory for output chain-link!"); } else { cl->next = NULL; cl->buf = buf; ngx_http_lua_send_chain_link(r, ctx, cl); } } } } else { dd("(lua-ngx-eof) can't find nginx request object!"); } return 0; }
static void ngx_http_lua_request_cleanup(void *data) { ngx_http_request_t *r = data; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_ctx_t *ctx; lua_State *L; dd("(lua-request-cleanup) force request coroutine quit"); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); L = lmcf->lua; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); /* force coroutine handling the request quit */ if (ctx == NULL) { return; } if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } lua_getfield(L, LUA_REGISTRYINDEX, NGX_LUA_CORT_REF); lua_rawgeti(L, -1, ctx->cc_ref); if (lua_isthread(L, -1)) { /* coroutine not finished yet, force quit */ ngx_http_lua_del_thread(r, L, ctx->cc_ref, 1); #if 0 ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); #endif } lua_pop(L, 2); }
/** * Force flush out response content * */ static int ngx_http_lua_ngx_flush(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_chain_t *cl; ngx_int_t rc; int n; unsigned wait = 0; ngx_event_t *wev; ngx_http_core_loc_conf_t *clcf; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n > 1) { return luaL_error(L, "attempt to pass %d arguments, but accepted 0 " "or 1", n); } r = ngx_http_lua_get_req(L); if (n == 1 && r == r->main) { luaL_checktype(L, 1, LUA_TBOOLEAN); wait = lua_toboolean(L, 1); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } if (r->header_only) { lua_pushnil(L); lua_pushliteral(L, "header only"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } if (ctx->buffering) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua http 1.0 buffering makes ngx.flush() a no-op"); lua_pushnil(L); lua_pushliteral(L, "buffering"); return 2; } #if 1 if (!r->header_sent && !ctx->header_sent) { lua_pushnil(L); lua_pushliteral(L, "nothing to flush"); return 2; } #endif cl = ngx_http_lua_get_flush_chain(r, ctx); if (cl == NULL) { return luaL_error(L, "no memory"); } rc = ngx_http_lua_send_chain_link(r, ctx, cl); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } dd("wait:%d, rc:%d, buffered:0x%x", wait, (int) rc, r->connection->buffered); wev = r->connection->write; if (wait && (r->connection->buffered & NGX_HTTP_LOWLEVEL_BUFFERED || wev->delayed)) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua flush requires waiting: buffered 0x%uxd, " "delayed:%d", (unsigned) r->connection->buffered, wev->delayed); coctx->flushing = 1; ctx->flushing_coros++; if (ctx->entered_content_phase) { /* mimic ngx_http_set_write_handler */ r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } clcf = ngx_http_get_module_loc_conf(r, 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 (wev->timer_set) { wev->delayed = 0; ngx_del_timer(wev); } lua_pushnil(L); lua_pushliteral(L, "connection broken"); return 2; } ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx); coctx->cleanup = ngx_http_lua_flush_cleanup; coctx->data = r; return lua_yield(L, 0); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua flush asynchronously"); lua_pushinteger(L, 1); return 1; }
static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; const char *p; size_t len; size_t size; ngx_buf_t *b; ngx_chain_t *cl; ngx_int_t rc; int i; int nargs; int type; const char *msg; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } if (r->header_only) { lua_pushnil(L); lua_pushliteral(L, "header only"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } nargs = lua_gettop(L); size = 0; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TTABLE: size += ngx_http_lua_calc_strlen_in_table(L, i, i, 0 /* strict */); break; case LUA_TLIGHTUSERDATA: dd("userdata: %p", lua_touserdata(L, i)); if (lua_touserdata(L, i) == NULL) { size += sizeof("null") - 1; break; } continue; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "ngx.null, or array table expected, " "but got %s", lua_typename(L, type)); return luaL_argerror(L, i, msg); } } if (newline) { size += sizeof("\n") - 1; } if (size == 0) { /* do nothing for empty strings */ lua_pushinteger(L, 1); return 1; } cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size); if (cl == NULL) { return luaL_error(L, "no memory"); } b = cl->buf; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = lua_tolstring(L, i, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TNIL: *b->last++ = 'n'; *b->last++ = 'i'; *b->last++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *b->last++ = 't'; *b->last++ = 'r'; *b->last++ = 'u'; *b->last++ = 'e'; } else { *b->last++ = 'f'; *b->last++ = 'a'; *b->last++ = 'l'; *b->last++ = 's'; *b->last++ = 'e'; } break; case LUA_TTABLE: b->last = ngx_http_lua_copy_str_in_table(L, i, b->last); break; case LUA_TLIGHTUSERDATA: *b->last++ = 'n'; *b->last++ = 'u'; *b->last++ = 'l'; *b->last++ = 'l'; break; default: return luaL_error(L, "impossible to reach here"); } } if (newline) { *b->last++ = '\n'; } #if 0 if (b->last != b->end) { return luaL_error(L, "buffer error: %p != %p", b->last, b->end); } #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, newline ? "lua say response" : "lua print response"); rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } dd("downstream write: %d, buf len: %d", (int) rc, (int) (b->last - b->pos)); lua_pushinteger(L, 1); return 1; }
ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int nret) { int rv; int cc_ref; lua_State *cc; const char *err, *msg; ngx_int_t rc; /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); dd("ctx = %p", ctx); NGX_LUA_EXCEPTION_TRY { cc = ctx->cc; cc_ref = ctx->cc_ref; /* run code */ rv = lua_resume(cc, nret); dd("lua resume returns %d", (int) rv); switch (rv) { case LUA_YIELD: /* yielded, let event handler do the rest job */ /* FIXME: add io cmd dispatcher here */ dd("lua coroutine yielded"); #if 0 ngx_http_lua_dump_postponed(r); #endif lua_settop(cc, 0); return NGX_AGAIN; case 0: dd("normal end %.*s", (int) r->uri.len, r->uri.data); #if 0 ngx_http_lua_dump_postponed(r); #endif ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { dd("cleaning up cleanup"); *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->entered_content_phase) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } return NGX_OK; case LUA_ERRRUN: err = "runtime error"; break; case LUA_ERRSYNTAX: err = "syntax error"; break; case LUA_ERRMEM: err = "memory allocation error"; break; case LUA_ERRERR: err = "error handler error"; break; default: err = "unknown error"; break; } if (lua_isstring(cc, -1)) { dd("user custom error msg"); msg = lua_tostring(cc, -1); } else { if (lua_isnil(cc, -1)) { if (ctx->exited) { dd("run here...exiting... %d", (int) ctx->exit_code); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->exit_code == NGX_OK) { if (ctx->entered_content_phase) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } } return ctx->exit_code; } if (ctx->exec_uri.len) { ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } 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); } #if defined(nginx_version) && nginx_version >= 8011 /* ngx_http_named_location always increments * r->main->count, which is not we want for * non-content phases */ if (! ctx->entered_content_phase) { r->main->count--; } #endif return ngx_http_named_location(r, &ctx->exec_uri); } dd("internal redirect to %.*s", (int) ctx->exec_uri.len, ctx->exec_uri.data); #if defined(nginx_version) && nginx_version >= 8011 /* ngx_http_internal_redirect always increments * r->main->count, which is not we want for * non-content phases */ if (! ctx->entered_content_phase) { r->main->count--; } #endif return ngx_http_internal_redirect(r, &ctx->exec_uri, &ctx->exec_args); } } msg = "unknown reason"; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "content_by_lua aborted: %s: %s", err, msg); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } dd("headers sent? %d", ctx->headers_sent ? 1 : 0); return ctx->headers_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; } NGX_LUA_EXCEPTION_CATCH { dd("nginx execution restored"); } return NGX_ERROR; }
ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int nret) { int rv; int cc_ref; lua_State *cc; const char *err, *msg; ngx_int_t rc; /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); dd("ctx = %p", ctx); NGX_LUA_EXCEPTION_TRY { cc = ctx->cc; cc_ref = ctx->cc_ref; /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_init(r->pool); /* run code */ rv = lua_resume(cc, nret); /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_done(); dd("lua resume returns %d", (int) rv); switch (rv) { case LUA_YIELD: /* yielded, let event handler do the rest job */ /* FIXME: add io cmd dispatcher here */ dd("lua coroutine yielded"); #if 0 ngx_http_lua_dump_postponed(r); #endif lua_settop(cc, 0); return NGX_AGAIN; case 0: dd("normal end %.*s", (int) r->uri.len, r->uri.data); #if 0 ngx_http_lua_dump_postponed(r); #endif ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { dd("cleaning up cleanup"); *ctx->cleanup = NULL; ctx->cleanup = NULL; } if (ctx->entered_content_phase) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } return NGX_OK; case LUA_ERRRUN: err = "runtime error"; break; case LUA_ERRSYNTAX: err = "syntax error"; break; case LUA_ERRMEM: err = "memory allocation error"; break; case LUA_ERRERR: err = "error handler error"; break; default: err = "unknown error"; break; } if (lua_isstring(cc, -1)) { dd("user custom error msg"); msg = lua_tostring(cc, -1); } else { if (lua_isnil(cc, -1)) { if (ctx->exited) { dd("run here...exiting... %d", (int) ctx->exit_code); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } if ((ctx->exit_code == NGX_OK && ctx->entered_content_phase) || (ctx->exit_code >= NGX_HTTP_OK && ctx->exit_code < NGX_HTTP_SPECIAL_RESPONSE)) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } return ctx->exit_code; } if (ctx->exec_uri.len) { ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } 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; rc = ngx_http_named_location(r, &ctx->exec_uri); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (! ctx->entered_content_phase && r != r->connection->data) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ r->main->count--; } return NGX_DONE; } dd("internal redirect to %.*s", (int) ctx->exec_uri.len, ctx->exec_uri.data); /* resume the write event handler */ r->write_event_handler = ngx_http_request_empty_handler; 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; } dd("XXYY HERE %d\n", (int) r->main->count); if (! ctx->entered_content_phase && r != r->connection->data) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ r->main->count--; } return NGX_DONE; } } msg = "unknown reason"; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua handler aborted: %s: %s", err, msg); ngx_http_lua_del_thread(r, L, cc_ref, 0); ctx->cc_ref = LUA_NOREF; if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } dd("headers sent? %d", ctx->headers_sent ? 1 : 0); return ctx->headers_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; } NGX_LUA_EXCEPTION_CATCH { dd("nginx execution restored"); } return NGX_ERROR; }
static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; const char *p; size_t len; size_t size; ngx_buf_t *b; ngx_chain_t *cl; ngx_int_t rc; int i; int nargs; int type; const char *msg; ngx_buf_tag_t tag; lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } if ((r->method & NGX_HTTP_HEAD) || r->header_only) { return 0; } if (ctx->eof) { return luaL_error(L, "seen eof already"); } nargs = lua_gettop(L); size = 0; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TTABLE: size += ngx_http_lua_calc_strlen_in_table(L, i, 0); break; case LUA_TLIGHTUSERDATA: dd("userdata: %p", lua_touserdata(L, i)); if (lua_touserdata(L, i) == NULL) { size += sizeof("null") - 1; break; } continue; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "ngx.null, or array table expected, " "but got %s", lua_typename(L, type)); return luaL_argerror(L, i, msg); } } if (newline) { size += sizeof("\n") - 1; } if (size == 0) { /* do nothing for empty strings */ return 0; } tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } b = cl->buf; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = lua_tolstring(L, i, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TNIL: *b->last++ = 'n'; *b->last++ = 'i'; *b->last++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *b->last++ = 't'; *b->last++ = 'r'; *b->last++ = 'u'; *b->last++ = 'e'; } else { *b->last++ = 'f'; *b->last++ = 'a'; *b->last++ = 'l'; *b->last++ = 's'; *b->last++ = 'e'; } break; case LUA_TTABLE: b->last = ngx_http_lua_copy_str_in_table(L, b->last); break; case LUA_TLIGHTUSERDATA: *b->last++ = 'n'; *b->last++ = 'u'; *b->last++ = 'l'; *b->last++ = 'l'; break; default: return luaL_error(L, "impossible to reach here"); } } if (newline) { *b->last++ = '\n'; } #if 0 if (b->last != b->end) { return luaL_error(L, "buffer error: %p != %p", b->last, b->end); } #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, newline ? "lua say response" : "lua print response"); rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to send data through the output filters"); } dd("downstream write: %d, buf len: %d", (int) rc, (int) (b->last - b->pos)); if (!ctx->out) { #if nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &ctx->free_bufs, &ctx->busy_bufs, &cl, tag); dd("out lua buf tag: %p, buffered: %x, busy bufs: %p", &ngx_http_lua_module, (int) r->connection->buffered, ctx->busy_bufs); }
static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; const char *p; size_t len; size_t size; ngx_buf_t *b; ngx_chain_t *cl; ngx_int_t rc; int i; int nargs; int type; const char *msg; lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); lua_pop(L, 1); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } if ((r->method & NGX_HTTP_HEAD) || r->header_only) { return 0; } if (ctx->eof) { return luaL_error(L, "seen eof already"); } nargs = lua_gettop(L); size = 0; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TTABLE: size += ngx_http_lua_calc_strlen_in_table(L, i); break; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "or array table expected, got %s", lua_typename(L, type)); return luaL_argerror(L, i, msg); } } if (newline) { size += sizeof("\n") - 1; } if (size == 0) { /* do nothing for empty strings */ return 0; } b = ngx_create_temp_buf(r->pool, size); if (b == NULL) { return luaL_error(L, "out of memory"); } for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = lua_tolstring(L, i, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TNIL: *b->last++ = 'n'; *b->last++ = 'i'; *b->last++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *b->last++ = 't'; *b->last++ = 'r'; *b->last++ = 'u'; *b->last++ = 'e'; } else { *b->last++ = 'f'; *b->last++ = 'a'; *b->last++ = 'l'; *b->last++ = 's'; *b->last++ = 'e'; } break; case LUA_TTABLE: b->last = ngx_http_lua_copy_str_in_table(L, b->last); break; default: return luaL_error(L, "impossible to reach here"); } } if (newline) { *b->last++ = '\n'; } if (b->last != b->end) { return luaL_error(L, "buffer error: %p != %p", b->last, b->end); } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return luaL_error(L, "out of memory"); } cl->next = NULL; cl->buf = b; rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to send data through the output filters"); } return 0; }
/** * Force flush out response content * */ static int ngx_http_lua_ngx_flush(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_buf_t *buf; ngx_chain_t *cl; ngx_int_t rc; int n; unsigned wait = 0; ngx_event_t *wev; ngx_http_core_loc_conf_t *clcf; n = lua_gettop(L); if (n > 1) { return luaL_error(L, "attempt to pass %d arguments, but accepted 0 " "or 1", n); } lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); lua_pop(L, 1); if (n == 1 && r == r->main) { luaL_checktype(L, 1, LUA_TBOOLEAN); wait = lua_toboolean(L, 1); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } if ((r->method & NGX_HTTP_HEAD) || r->header_only) { return 0; } if (ctx->eof) { return luaL_error(L, "already seen eof"); } buf = ngx_calloc_buf(r->pool); if (buf == NULL) { return luaL_error(L, "memory allocation error"); } buf->flush = 1; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return luaL_error(L, "out of memory"); } cl->next = NULL; cl->buf = buf; rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to send chain link: %d", (int) rc); } dd("wait:%d, rc:%d, buffered:%d", wait, (int) rc, r->connection->buffered); if (wait && r->connection->buffered) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua flush requires waiting: buffered 0x%uxd", (int) r->connection->buffered); ctx->waiting_flush = 1; if (ctx->entered_content_phase) { /* mimic ngx_http_set_write_handler */ r->write_event_handler = ngx_http_lua_content_wev_handler; } wev = r->connection->write; if (wev->ready && wev->delayed) { return lua_yield(L, 0); } clcf = ngx_http_get_module_loc_conf(r, 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) { return luaL_error(L, "connection broken"); } return lua_yield(L, 0); } return 0; }
ngx_int_t ngx_http_lua_rewrite_handler(ngx_http_request_t *r) { ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; ngx_http_lua_main_conf_t *lmcf; /* XXX we need to take into account ngx_rewrite's location dump */ if (r->uri_changed) { return NGX_DECLINED; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua rewrite handler, uri:\"%V\" c:%ud", &r->uri, r->main->count); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); if (!lmcf->postponed_to_rewrite_phase_end) { ngx_http_core_main_conf_t *cmcf; ngx_http_phase_handler_t tmp; ngx_http_phase_handler_t *ph; ngx_http_phase_handler_t *cur_ph; ngx_http_phase_handler_t *last_ph; lmcf->postponed_to_rewrite_phase_end = 1; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; cur_ph = &ph[r->phase_handler]; last_ph = &ph[cur_ph->next - 1]; #if 0 if (cur_ph == last_ph) { dd("XXX our handler is already the last rewrite phase handler"); } #endif if (cur_ph < last_ph) { dd("swaping the contents of cur_ph and last_ph..."); tmp = *cur_ph; memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t)); *last_ph = tmp; r->phase_handler--; /* redo the current ph */ return NGX_DECLINED; } } llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->rewrite_handler == NULL) { dd("no rewrite handler found"); return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } dd("entered? %d", (int) ctx->entered_rewrite_phase); if (ctx->entered_rewrite_phase) { dd("rewriteby: calling wev handler"); rc = ctx->resume_handler(r); dd("rewriteby: wev handler returns %d", (int) rc); if (rc == NGX_OK) { rc = NGX_DECLINED; } if (rc == NGX_DECLINED) { if (r->header_sent) { dd("header already sent"); /* response header was already generated in access_by_lua*, * so it is no longer safe to proceed to later phases * which may generate responses again */ if (!ctx->eof) { dd("eof not yet sent"); rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc > NGX_OK) { return rc; } } return NGX_HTTP_OK; } } return rc; } if (ctx->waiting_more_body) { return NGX_DONE; } if (llcf->force_read_body && !ctx->read_body_done) { r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; rc = ngx_http_read_client_request_body(r, ngx_http_lua_generic_phase_post_read); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { #if (nginx_version < 1002006) || \ (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif return rc; } if (rc == NGX_AGAIN) { ctx->waiting_more_body = 1; return NGX_DONE; } } dd("calling rewrite handler"); return llcf->rewrite_handler(r); }
static ngx_int_t ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) { int co_ref; lua_State *co; ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_http_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; /* {{{ new coroutine to handle request */ co = ngx_http_lua_new_thread(r, L, &co_ref); if (co == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua: failed to create new coroutine to handle request"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* move code closure to new coroutine */ lua_xmove(L, co, 1); /* set closure's env table to new coroutine's globals table */ ngx_http_lua_get_globals_table(co); lua_setfenv(co, -2); /* save nginx request in coroutine globals table */ ngx_http_lua_set_req(co, r); /* {{{ initialize request context */ ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { return NGX_ERROR; } ngx_http_lua_reset_ctx(r, L, ctx); ctx->entered_rewrite_phase = 1; ctx->cur_co_ctx = &ctx->entry_co_ctx; ctx->cur_co_ctx->co = co; ctx->cur_co_ctx->co_ref = co_ref; #ifdef NGX_LUA_USE_ASSERT ctx->cur_co_ctx->co_top = 1; #endif /* }}} */ /* {{{ register request cleanup hooks */ if (ctx->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; } /* }}} */ ctx->context = NGX_HTTP_LUA_CONTEXT_REWRITE; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { r->read_event_handler = ngx_http_lua_rd_check_broken_connection; } else { r->read_event_handler = ngx_http_block_reading; } rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_ERROR || rc > NGX_OK) { return rc; } c = r->connection; if (rc == NGX_AGAIN) { rc = ngx_http_lua_run_posted_threads(c, L, r, ctx); } else if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); rc = ngx_http_lua_run_posted_threads(c, L, r, ctx); } if (rc == NGX_OK || rc == NGX_DECLINED) { if (r->header_sent) { dd("header already sent"); /* response header was already generated in access_by_lua*, * so it is no longer safe to proceed to later phases * which may generate responses again */ if (!ctx->eof) { dd("eof not yet sent"); rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc > NGX_OK) { return rc; } } return NGX_HTTP_OK; } return NGX_DECLINED; } return rc; }
static ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int nret) { int rc; int cc_ref; lua_State *cc; const char *err, *msg; /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); NGX_LUA_EXCEPTION_TRY { cc = ctx->cc; cc_ref = ctx->cc_ref; /* run code */ rc = lua_resume(cc, nret); dd("lua resume returns %d", (int) rc); switch (rc) { case LUA_YIELD: /* yielded, let event handler do the rest job */ /* FIXME: add io cmd dispatcher here */ lua_settop(cc, 0); return NGX_AGAIN; break; case 0: /* normal end */ ngx_http_lua_del_thread(r, L, cc_ref, 0); if (ctx->cleanup) { dd("cleaning up cleanup"); *ctx->cleanup = NULL; ctx->cleanup = NULL; } ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); return NGX_OK; break; case LUA_ERRRUN: err = "runtime error"; break; case LUA_ERRSYNTAX: err = "syntax error"; break; case LUA_ERRMEM: err = "memory allocation error"; break; case LUA_ERRERR: err = "error handler error"; break; default: err = "unknown error"; break; } if (lua_isstring(cc, -1)) { dd("user custom error msg"); msg = lua_tostring(cc, -1); } else { if (lua_isnil(cc, -1) && ctx->error_rc != 0) { dd("run here...throwing..."); ngx_http_lua_del_thread(r, L, cc_ref, 0); if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } return ctx->error_rc; } msg = "unknown reason"; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "content_by_lua prematurely ended: %s: %s", err, msg); ngx_http_lua_del_thread(r, L, cc_ref, 0); if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } dd("headers sent? %d", ctx->headers_sent ? 1 : 0); return ctx->headers_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; } NGX_LUA_EXCEPTION_CATCH { dd("NginX execution restored"); } return NGX_ERROR; }