/* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_sleep.h" #include "ngx_http_lua_contentby.h" static int ngx_http_lua_ngx_sleep(lua_State *L); static void ngx_http_lua_sleep_handler(ngx_event_t *ev); static void ngx_http_lua_sleep_cleanup(void *data); static ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r); static int ngx_http_lua_ngx_sleep(lua_State *L) { int n; ngx_int_t delay; /* in msec */ ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; 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 1", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000); if (delay < 0) { return luaL_error(L, "invalid sleep duration \"%d\"", delay); } 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 | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } ngx_http_lua_cleanup_pending_operation(coctx); coctx->cleanup = ngx_http_lua_sleep_cleanup; coctx->data = r; coctx->sleep.handler = ngx_http_lua_sleep_handler; coctx->sleep.data = coctx; coctx->sleep.log = r->connection->log; dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay, (int) r->uri.len, r->uri.data); ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua ready to sleep for %d ms", delay); return lua_yield(L, 0); } void ngx_http_lua_sleep_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_log_ctx_t *log_ctx; ngx_http_lua_co_ctx_t *coctx; coctx = ev->data; r = coctx->data; c = r->connection; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } if (c->fd != (ngx_socket_t) -1) { /* not a fake connection */ log_ctx = c->log->data; log_ctx->current_request = r; } coctx->cleanup = NULL; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua sleep timer expired: \"%V?%V\"", &r->uri, &r->args); ctx->cur_co_ctx = coctx; if (ctx->entered_content_phase) { (void) ngx_http_lua_sleep_resume(r); } else { ctx->resume_handler = ngx_http_lua_sleep_resume; ngx_http_core_run_phases(r); } ngx_http_run_posted_requests(c); } void ngx_http_lua_inject_sleep_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_sleep); lua_setfield(L, -2, "sleep"); } static void ngx_http_lua_sleep_cleanup(void *data) { ngx_http_lua_co_ctx_t *coctx = data; if (coctx->sleep.timer_set) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua clean up the timer for pending ngx.sleep"); ngx_del_timer(&coctx->sleep); } } static ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r) { lua_State *vm; ngx_connection_t *c; ngx_int_t rc; 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; c = r->connection; vm = ngx_http_lua_get_lua_vm(r, ctx); 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); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */