diff --git a/config b/config index 14870a04e8..d89cb6f65d 100644 --- a/config +++ b/config @@ -300,6 +300,18 @@ HTTP_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_http_lua_worker_thread.c \ " +if echo "$EVENT_MODULES" | grep ngx_epoll_module 2>&1 >/dev/null; then + HTTP_LUA_SRCS="$HTTP_LUA_SRCS $ngx_addon_dir/src/event/ngx_http_lua_epoll.c" +fi + +if echo "$EVENT_MODULES" | grep ngx_poll_module 2>&1 >/dev/null; then + HTTP_LUA_SRCS="$HTTP_LUA_SRCS $ngx_addon_dir/src/event/ngx_http_lua_poll.c" +fi + +if echo "$EVENT_MODULES" | grep ngx_kqueue_module 2>&1 >/dev/null; then + HTTP_LUA_SRCS="$HTTP_LUA_SRCS $ngx_addon_dir/src/event/ngx_http_lua_kqueue.c" +fi + HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ddebug.h \ $ngx_addon_dir/src/ngx_http_lua_autoconf.h \ @@ -359,6 +371,7 @@ HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_http_lua_input_filters.h \ $ngx_addon_dir/src/ngx_http_lua_pipe.h \ $ngx_addon_dir/src/ngx_http_lua_worker_thread.h \ + $ngx_addon_dir/src/ngx_http_lua_event.h \ " # ---------------------------------------- diff --git a/src/event/ngx_http_lua_epoll.c b/src/event/ngx_http_lua_epoll.c new file mode 100644 index 0000000000..1667637f95 --- /dev/null +++ b/src/event/ngx_http_lua_epoll.c @@ -0,0 +1,211 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#include +#include +#include +#include "../ngx_http_lua_event.h" + + +static ngx_int_t ngx_http_lua_epoll_init_event(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_lua_epoll_set_event(ngx_event_t *ev, ngx_int_t event); +static ngx_int_t ngx_http_lua_epoll_clear_event(ngx_event_t *ev, + ngx_int_t event); +static ngx_int_t ngx_http_lua_epoll_process_event(ngx_http_request_t *r, + ngx_msec_t timer); + +static int epoll_created = -1; + +ngx_http_lua_event_actions_t ngx_http_lua_epoll = { + ngx_http_lua_epoll_init_event, + ngx_http_lua_epoll_set_event, + ngx_http_lua_epoll_clear_event, + ngx_http_lua_epoll_process_event, +}; + + +static ngx_int_t +ngx_http_lua_epoll_init_event(ngx_cycle_t *cycle) +{ + epoll_created = epoll_create(1); + + if (epoll_created == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "lua epoll_create() failed"); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_epoll_set_event(ngx_event_t *ev, ngx_int_t event) +{ + int op; + uint32_t events, prev; + ngx_connection_t *c; + ngx_event_t *e; + struct epoll_event ee; + + c = ev->data; + + events = (uint32_t) event; + + if (event == NGX_READ_EVENT) { + e = c->write; + prev = EPOLLOUT; +#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP) + events = EPOLLIN|EPOLLRDHUP; +#endif + + } else { + e = c->read; + prev = EPOLLIN|EPOLLRDHUP; +#if (NGX_WRITE_EVENT != EPOLLOUT) + events = EPOLLOUT; +#endif + } + + if (e->active) { + op = EPOLL_CTL_MOD; + events |= prev; + + } else { + op = EPOLL_CTL_ADD; + } + + ee.events = events; + ee.data.ptr = c; + + if (epoll_ctl(epoll_created, op, c->fd, &ee) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "lua epoll_ctl(EPOLL_CTL_ADD, %d) failed, add event: %d", + c->fd, events); + + return NGX_ERROR; + } + + ev->active = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_epoll_clear_event(ngx_event_t *ev, ngx_int_t event) +{ + ngx_event_t *e; + ngx_connection_t *c; + int op; + uint32_t prev; + struct epoll_event ee; + + c = ev->data; + + if (event == NGX_READ_EVENT) { + e = c->write; + prev = EPOLLOUT; + + } else { + e = c->read; + prev = EPOLLIN|EPOLLRDHUP; + } + + if (e->active) { + op = EPOLL_CTL_MOD; + ee.events = prev; + ee.data.ptr = c; + + } else { + op = EPOLL_CTL_DEL; + ee.events = 0; + ee.data.ptr = NULL; + } + + if (epoll_ctl(epoll_created, op, c->fd, &ee) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "lua epoll_ctl(EPOLL_CTL_DEL, %d) failed", c->fd); + + return NGX_ERROR; + } + + ev->active = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_epoll_process_event(ngx_http_request_t *r, ngx_msec_t timer) +{ + int events; + uint32_t revents; + ngx_err_t err; + ngx_event_t *rev, *wev; + ngx_connection_t *c; + struct epoll_event ee; + + events = epoll_wait(epoll_created, &ee, 1, timer); + + err = (events == -1) ? ngx_errno : 0; + + if (err) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, err, + "lua epoll_wait() failed"); + + return NGX_ERROR; + } + + if (events == 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "lua epoll_wait() returned no events without timeout"); + + return NGX_ERROR; + } + + c = ee.data.ptr; + revents = ee.events; + + if (revents & (EPOLLERR|EPOLLHUP)) { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, + "epoll_wait() error on fd:%d ev:%04XD", + c->fd, revents); + + /* + * if the error events were returned, add EPOLLIN and EPOLLOUT + * to handle the events at least in one active handler + */ + + revents |= EPOLLIN|EPOLLOUT; + } + + rev = c->read; + + if ((revents & EPOLLIN) && rev->active) { + +#if (NGX_HAVE_EPOLLRDHUP) + if (revents & EPOLLRDHUP) { + rev->pending_eof = 1; + } +#endif + + rev->ready = 1; + rev->available = -1; + } + + wev = c->write; + + if ((revents & EPOLLOUT) && wev->active) { + wev->ready = 1; +#if (NGX_THREADS) + wev->complete = 1; +#endif + } + + return NGX_OK; +} diff --git a/src/event/ngx_http_lua_kqueue.c b/src/event/ngx_http_lua_kqueue.c new file mode 100644 index 0000000000..f8ecf92ca4 --- /dev/null +++ b/src/event/ngx_http_lua_kqueue.c @@ -0,0 +1,139 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#include +#include +#include +#include "../ngx_http_lua_event.h" + + +static ngx_int_t ngx_http_lua_kqueue_init_event(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_lua_kqueue_set_event(ngx_event_t *ev, + ngx_int_t event); +static ngx_int_t ngx_http_lua_kqueue_clear_event(ngx_event_t *ev, + ngx_int_t event); +static ngx_int_t ngx_http_lua_kqueue_process_event(ngx_http_request_t *r, + ngx_msec_t timer); + +int kq = -1; +static struct kevent kch[2]; +static ngx_uint_t nchanges; + +ngx_http_lua_event_actions_t ngx_http_lua_kqueue = { + ngx_http_lua_kqueue_init_event, + ngx_http_lua_kqueue_set_event, + ngx_http_lua_kqueue_clear_event, + ngx_http_lua_kqueue_process_event, +}; + + +static ngx_int_t +ngx_http_lua_kqueue_init_event(ngx_cycle_t *cycle) +{ + nchanges = 0; + kq = kqueue(); + + if (kq == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "lua kqueue() failed"); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_kqueue_set_event(ngx_event_t *ev, ngx_int_t event) +{ + ngx_connection_t *c; + + c = ev->data; + + ev->active = 1; + + kch[nchanges].ident = c->fd; + kch[nchanges].filter = (short) event; + kch[nchanges].flags = EV_ADD|EV_ENABLE; + kch[nchanges].udata = NGX_KQUEUE_UDATA_T (ev); + + nchanges++; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_kqueue_clear_event(ngx_event_t *ev, ngx_int_t event) +{ + ngx_connection_t *c; + + c = ev->data; + + ev->active = 0; + + kch[0].ident = c->fd; + kch[0].filter = (short) event; + kch[0].flags = EV_DELETE; + kch[0].udata = NGX_KQUEUE_UDATA_T (ev); + + if (kevent(kq, kch, 1, NULL, 0, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "lua kevent() failed"); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_kqueue_process_event(ngx_http_request_t *r, ngx_msec_t timer) +{ + int events; + struct timespec ts; + ngx_event_t *ev; + ngx_err_t err; + struct kevent kev; + + ts.tv_sec = timer / 1000; + ts.tv_nsec = (timer % 1000) * 1000000; + + events = kevent(kq, kch, nchanges, &kev, 1, &ts); + + nchanges = 0; + err = (events == -1) ? ngx_errno : 0; + + if (err) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, err, + "lua kevent() failed"); + + return NGX_ERROR; + } + + if (events == 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "lua kevent() returned no events without timeout"); + + return NGX_ERROR; + } + + ev = (ngx_event_t *) kev.udata; + + ev->available = kev.data; + ev->ready = 1; + + if (kev.flags & EV_EOF) { + ev->pending_eof = 1; + + ngx_log_error(NGX_LOG_ALERT, r->connection->log, kev.fflags, + "lua kevent() reported that connect() failed"); + } + + return NGX_OK; +} diff --git a/src/event/ngx_http_lua_poll.c b/src/event/ngx_http_lua_poll.c new file mode 100644 index 0000000000..11790f689f --- /dev/null +++ b/src/event/ngx_http_lua_poll.c @@ -0,0 +1,120 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#include +#include +#include +#include "../ngx_http_lua_event.h" + + +static ngx_int_t ngx_http_lua_poll_init_event(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_lua_poll_set_event(ngx_event_t *ev, ngx_int_t event); +static ngx_int_t ngx_http_lua_poll_clear_event(ngx_event_t *ev, + ngx_int_t event); +static ngx_int_t ngx_http_lua_poll_process_event(ngx_http_request_t *r, + ngx_msec_t timer); + +static struct pollfd pev; + +ngx_http_lua_event_actions_t ngx_http_lua_poll = { + ngx_http_lua_poll_init_event, + ngx_http_lua_poll_set_event, + ngx_http_lua_poll_clear_event, + ngx_http_lua_poll_process_event, +}; + + +static ngx_int_t +ngx_http_lua_poll_init_event(ngx_cycle_t *cycle) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_poll_set_event(ngx_event_t *ev, ngx_int_t event) +{ + ngx_connection_t *c; + + c = ev->data; + + ev->active = 1; + + if (event == NGX_READ_EVENT) { +#if (NGX_READ_EVENT != POLLIN) + event = POLLIN; +#endif + + } else { +#if (NGX_WRITE_EVENT != POLLOUT) + event = POLLOUT; +#endif + } + + if (pev.fd != c->fd) { + pev.fd = c->fd; + pev.events = (short) event; + pev.revents = 0; + + } else { + pev.events |= (short) event; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_poll_clear_event(ngx_event_t *ev, ngx_int_t event) +{ + ev->active = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_poll_process_event(ngx_http_request_t *r, ngx_msec_t timer) +{ + int ready, revents; + ngx_event_t *ev; + ngx_err_t err; + ngx_connection_t *c; + + ready = poll(&pev, 1, (int) timer); + + err = (ready == -1) ? ngx_errno : 0; + + if (err) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, err, + "lua poll() failed"); + + return NGX_ERROR; + } + + if (ready == 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "lua poll() returned no events without timeout"); + + return NGX_ERROR; + } + + revents = pev.revents; + c = ngx_cycle->files[pev.fd]; + + if ((revents & POLLIN) && c->read->active) { + ev = c->read; + ev->ready = 1; + ev->available = -1; + } + + if ((revents & POLLOUT) && c->write->active) { + ev = c->write; + ev->ready = 1; + } + + return NGX_OK; +} diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index 017e2a0434..8e7230e294 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -141,6 +141,7 @@ typedef struct { #define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000 #define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x2000 #define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO 0x4000 +#define NGX_HTTP_LUA_CONTEXT_INIT 0x8000 #define NGX_HTTP_LUA_FFI_NO_REQ_CTX -100 diff --git a/src/ngx_http_lua_event.h b/src/ngx_http_lua_event.h new file mode 100644 index 0000000000..dd3cf92b32 --- /dev/null +++ b/src/ngx_http_lua_event.h @@ -0,0 +1,178 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_HTTP_LUA_EVENT_H_INCLUDED_ +#define _NGX_HTTP_LUA_EVENT_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +typedef struct { + ngx_int_t (*init_event)(ngx_cycle_t *cycle); + ngx_int_t (*set_event)(ngx_event_t *ev, ngx_int_t event); + ngx_int_t (*clear_event)(ngx_event_t *ev, ngx_int_t event); + ngx_int_t (*process_event)(ngx_http_request_t *r, ngx_msec_t timer); +} ngx_http_lua_event_actions_t; + + +extern ngx_http_lua_event_actions_t ngx_http_lua_event_actions; + +extern ngx_http_lua_event_actions_t ngx_http_lua_epoll; +extern ngx_http_lua_event_actions_t ngx_http_lua_poll; +extern ngx_http_lua_event_actions_t ngx_http_lua_kqueue; + +extern int ngx_http_lua_event_inited; + +#define ngx_http_lua_set_event ngx_http_lua_event_actions.set_event +#define ngx_http_lua_clear_event ngx_http_lua_event_actions.clear_event +#define ngx_http_lua_process_event ngx_http_lua_event_actions.process_event + + +static ngx_inline ngx_int_t +ngx_http_lua_init_event(ngx_cycle_t *cycle) +{ + ngx_module_t *module; + ngx_module_t **modules; + ngx_event_module_t *event_module; + ngx_uint_t i; + ngx_connection_t *c, *next; + ngx_event_t *rev, *wev; + + module = NULL; + +#if (nginx_version >= 1009011) + modules = cycle->modules; +#else + modules = ngx_modules; +#endif + + for (i = 0; modules[i]; i++) { + if (modules[i]->type != NGX_EVENT_MODULE) { + continue; + } + + event_module = modules[i]->ctx; + if (ngx_strcmp(event_module->name->data, "event_core") == 0) + { + continue; + } + + module = modules[i]; + break; + } + + if (module == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found"); + return NGX_ERROR; + } + + event_module = module->ctx; + + /* FIXME: should init event_module actions here, like: + * ``` + * if (event_module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) { + * ``` + * + * but `epcf` is not initial here before + * ``` + * static ngx_int_t + * ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) + * { + * ngx_epoll_conf_t *epcf; + * + * // Segmentation fault below + * epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module); + * ``` + */ + +#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL) + + if (ngx_strcmp(event_module->name->data, "epoll") == 0) { + ngx_http_lua_event_actions = ngx_http_lua_epoll; + + } else + +#endif + +#if (NGX_HAVE_POLL) + + if (ngx_strcmp(event_module->name->data, "poll") == 0) { + ngx_http_lua_event_actions = ngx_http_lua_poll; + + } else + +#endif + +#if (NGX_HAVE_KQUEUE) + + if (ngx_strcmp(event_module->name->data, "kqueue") == 0) { + ngx_http_lua_event_actions = ngx_http_lua_kqueue; + + } else + +#endif + + { + return NGX_ERROR; + } + + cycle->connection_n = 128; + cycle->connections = + ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log); + if (cycle->connections == NULL) { + return NGX_ERROR; + } + + c = cycle->connections; + + cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, + cycle->log); + if (cycle->read_events == NULL) { + return NGX_ERROR; + } + + rev = cycle->read_events; + for (i = 0; i < cycle->connection_n; i++) { + rev[i].closed = 1; + rev[i].instance = 1; + } + + cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, + cycle->log); + if (cycle->write_events == NULL) { + return NGX_ERROR; + } + + wev = cycle->write_events; + for (i = 0; i < cycle->connection_n; i++) { + wev[i].closed = 1; + } + + i = cycle->connection_n; + next = NULL; + + do { + i--; + + c[i].data = next; + c[i].read = &cycle->read_events[i]; + c[i].write = &cycle->write_events[i]; + c[i].fd = (ngx_socket_t) -1; + + next = &c[i]; + } while (i); + + cycle->free_connections = next; + cycle->free_connection_n = cycle->connection_n; + + return ngx_http_lua_event_actions.init_event(cycle); +} + + +#endif /* _NGX_HTTP_LUA_EVENT_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 09fd0a91f8..429d041317 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -32,6 +32,11 @@ #include "ngx_http_lua_ssl_session_fetchby.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_pipe.h" +#include "ngx_http_lua_event.h" + +int ngx_http_lua_event_inited = 0; + +ngx_http_lua_event_actions_t ngx_http_lua_event_actions; static void *ngx_http_lua_create_main_conf(ngx_conf_t *cf); @@ -715,6 +720,22 @@ ngx_http_lua_init(ngx_conf_t *cf) ngx_http_lua_main_conf_t *lmcf; ngx_pool_cleanup_t *cln; ngx_str_t name = ngx_string("host"); + ngx_connection_t *c = NULL; + ngx_http_request_t *r; + ngx_http_lua_ctx_t *ctx; + ngx_conf_t conf; + ngx_open_file_t *file, *ofile; + ngx_list_part_t *part; + ngx_cycle_t *fake_cycle, *cycle; + ngx_http_conf_ctx_t *conf_ctx, http_ctx; + ngx_conf_file_t cf_file; + char *rv; + void *cur, *prev; + ngx_uint_t i; + ngx_module_t **modules; + ngx_http_module_t *module; + ngx_http_lua_loc_conf_t *top_llcf; + ngx_http_core_loc_conf_t *top_clcf; if (ngx_process == NGX_PROCESS_SIGNALLER || ngx_test_config) { return NGX_OK; @@ -890,15 +911,233 @@ ngx_http_lua_init(ngx_conf_t *cf) ngx_http_lua_assert(lmcf->lua != NULL); if (!lmcf->requires_shm && lmcf->init_handler) { + cycle = cf->cycle; + + conf_ctx = (ngx_http_conf_ctx_t *) + cycle->conf_ctx[ngx_http_module.index]; + http_ctx.main_conf = conf_ctx->main_conf; + + top_clcf = conf_ctx->loc_conf[ngx_http_core_module.ctx_index]; + top_llcf = conf_ctx->loc_conf[ngx_http_lua_module.ctx_index]; + + ngx_memzero(&conf, sizeof(ngx_conf_t)); + + conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, cycle->log); + if (conf.temp_pool == NULL) { + return NGX_ERROR; + } + + conf.temp_pool->log = cf->cycle->log; + + /* we fake a temporary ngx_cycle_t here because some + * modules' merge conf handler may produce side effects in + * cf->cycle (like ngx_proxy vs cf->cycle->paths). + * also, we cannot allocate our temp cycle on the stack + * because some modules like ngx_http_core_module reference + * addresses within cf->cycle (i.e., via "&cf->cycle->new_log") + */ + fake_cycle = ngx_palloc(cycle->pool, sizeof(ngx_cycle_t)); + if (fake_cycle == NULL) { + goto failed; + } + + ngx_memcpy(fake_cycle, cycle, sizeof(ngx_cycle_t)); + + ngx_queue_init(&fake_cycle->reusable_connections_queue); + + if (ngx_array_init(&fake_cycle->listening, cycle->pool, + cycle->listening.nelts || 1, + sizeof(ngx_listening_t)) + != NGX_OK) + { + goto failed; + } + + if (ngx_array_init(&fake_cycle->paths, cycle->pool, cycle->paths.nelts || 1, + sizeof(ngx_path_t *)) + != NGX_OK) + { + goto failed; + } + + part = &cycle->open_files.part; + ofile = part->elts; + + if (ngx_list_init(&fake_cycle->open_files, cycle->pool, part->nelts || 1, + sizeof(ngx_open_file_t)) + != NGX_OK) + { + goto failed; + } + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + ofile = part->elts; + i = 0; + } + + file = ngx_list_push(&fake_cycle->open_files); + if (file == NULL) { + goto failed; + } + + ngx_memcpy(file, ofile, sizeof(ngx_open_file_t)); + } + + if (ngx_list_init(&fake_cycle->shared_memory, cycle->pool, 1, + sizeof(ngx_shm_zone_t)) + != NGX_OK) + { + goto failed; + } + saved_cycle = ngx_cycle; - ngx_cycle = cf->cycle; + ngx_cycle = fake_cycle; - rc = lmcf->init_handler(cf->log, lmcf, lmcf->lua); + if (ngx_http_lua_init_event(fake_cycle) == NGX_OK) { + ngx_http_lua_event_inited = 1; + } - ngx_cycle = saved_cycle; + conf.ctx = &http_ctx; + conf.cycle = fake_cycle; + conf.pool = fake_cycle->pool; + conf.log = cycle->log; + http_ctx.loc_conf = ngx_pcalloc(conf.pool, + sizeof(void *) * ngx_http_max_module); + if (http_ctx.loc_conf == NULL) { + goto failed; + } + + http_ctx.srv_conf = ngx_pcalloc(conf.pool, + sizeof(void *) * ngx_http_max_module); + if (http_ctx.srv_conf == NULL) { + goto failed; + } + + ngx_memzero(&cf_file, sizeof(cf_file)); + cf_file.file.name = cycle->conf_file; + conf.conf_file = &cf_file; + +#if (nginx_version >= 1009011) + modules = cf->cycle->modules; +#else + modules = ngx_modules; +#endif + + for (i = 0; modules[i]; i++) { + if (modules[i]->type != NGX_HTTP_MODULE) { + continue; + } + + module = modules[i]->ctx; + + if (module->create_srv_conf) { + cur = module->create_srv_conf(&conf); + if (cur == NULL) { + goto failed; + } + + http_ctx.srv_conf[modules[i]->ctx_index] = cur; + + if (module->merge_srv_conf) { + prev = module->create_srv_conf(&conf); + if (prev == NULL) { + goto failed; + } + + rv = module->merge_srv_conf(&conf, prev, cur); + if (rv != NGX_CONF_OK) { + goto failed; + } + } + } + + if (module->create_loc_conf) { + cur = module->create_loc_conf(&conf); + if (cur == NULL) { + goto failed; + } + + http_ctx.loc_conf[modules[i]->ctx_index] = cur; + + if (module->merge_loc_conf) { + if (modules[i] == &ngx_http_lua_module) { + prev = top_llcf; + + } else if (modules[i] == &ngx_http_core_module) { + prev = top_clcf; + + } else { + prev = module->create_loc_conf(&conf); + if (prev == NULL) { + goto failed; + } + } + + rv = module->merge_loc_conf(&conf, prev, cur); + if (rv != NGX_CONF_OK) { + goto failed; + } + } + } + } + + ngx_destroy_pool(conf.temp_pool); + conf.temp_pool = NULL; + + c = ngx_http_lua_create_fake_connection(NULL); + if (c == NULL) { + goto failed; + } + + c->log->handler = cf->log->handler; + + r = ngx_http_lua_create_fake_request(c); + if (r == NULL) { + goto failed; + } + + r->main_conf = http_ctx.main_conf; + r->srv_conf = http_ctx.srv_conf; + r->loc_conf = http_ctx.loc_conf; + + /* create ctx */ + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + goto failed; + } + + ctx->context = NGX_HTTP_LUA_CONTEXT_INIT; + + ngx_http_lua_set_req(lmcf->lua, r); + + rc = lmcf->init_handler(cf->log, lmcf, lmcf->lua); if (rc != NGX_OK) { /* an error happened */ + goto failed; + } + + ngx_cycle = saved_cycle; + + if (0) { + + failed: + + if (conf.temp_pool) { + ngx_destroy_pool(conf.temp_pool); + } + + if (c) { + ngx_http_lua_close_fake_connection(c); + } + return NGX_ERROR; } } diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 26467fddbc..feeaae9adb 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -17,6 +17,7 @@ #include "ngx_http_lua_output.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_probe.h" +#include "ngx_http_lua_event.h" static int ngx_http_lua_socket_tcp(lua_State *L); @@ -156,6 +157,15 @@ static void ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c); static int ngx_http_lua_ssl_free_session(lua_State *L); #endif static void ngx_http_lua_socket_tcp_close_connection(ngx_connection_t *c); +static void ngx_http_lua_socket_tcp_clean_events(ngx_connection_t *c); +static ngx_int_t ngx_http_lua_socket_tcp_blocked_conn(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static void ngx_http_lua_socket_tcp_blocked_sslhandshake( + ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); +static ngx_int_t ngx_http_lua_socket_tcp_blocked_write(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); +static ngx_int_t ngx_http_lua_socket_tcp_blocked_read(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u); enum { @@ -449,7 +459,19 @@ ngx_http_lua_socket_tcp(lua_State *L) return luaL_error(L, "no ctx found"); } - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + /* only a few events is suppported in init_by_* */ + if (ngx_http_lua_event_inited) { + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_COSOCKET); + + } else if (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET) { + return luaL_error(L, "API disabled in the context of %s except when " \ + "using the event handling methods of poll, epoll " \ + "or kqueue", + ngx_http_lua_context_name((ctx)->context)); + + } else { + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + } lua_createtable(L, 5 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask( @@ -683,7 +705,10 @@ ngx_http_lua_socket_tcp_connect_helper(lua_State *L, ngx_memzero(&url, sizeof(ngx_url_t)); url.url = host; url.default_port = port; - url.no_resolve = 1; + + if (ctx->context & NGX_HTTP_LUA_CONTEXT_YIELDABLE) { + url.no_resolve = 1; + } coctx = ctx->cur_co_ctx; @@ -891,7 +916,19 @@ ngx_http_lua_socket_tcp_connect(lua_State *L) return luaL_error(L, "no ctx found"); } - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + /* only a few events is suppported in init_worker_by_* */ + if (ngx_http_lua_event_inited) { + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_COSOCKET); + + } else if (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET) { + return luaL_error(L, "API disabled in the context of %s except when " \ + "using the event handling methods of poll, epoll " \ + "or kqueue", + ngx_http_lua_context_name((ctx)->context)); + + } else { + ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + } luaL_checktype(L, 1, LUA_TTABLE); @@ -1473,11 +1510,18 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, u->writer.last = &u->writer.out; #endif - ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); + dd("setting data to %p", u); - coctx = ctx->cur_co_ctx; + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); - dd("setting data to %p", u); + if (rc == NGX_AGAIN + && (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET)) + { + rc = ngx_http_lua_socket_tcp_blocked_conn(r, u); + if (rc == NGX_ERROR) { + return ngx_http_lua_socket_conn_error_retval_handler(r, u, L); + } + } if (rc == NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1513,6 +1557,8 @@ ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, /* rc == NGX_AGAIN */ + coctx = ctx->cur_co_ctx; + ngx_http_lua_cleanup_pending_operation(coctx); coctx->cleanup = ngx_http_lua_coctx_cleanup; coctx->data = u; @@ -1777,29 +1823,35 @@ ngx_http_lua_socket_tcp_sslhandshake(lua_State *L) dd("ngx_ssl_handshake returned %d", (int) rc); if (rc == NGX_AGAIN) { - if (c->write->timer_set) { - ngx_del_timer(c->write); - } + if (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET) { + ngx_http_lua_socket_tcp_blocked_sslhandshake(r, u); - ngx_add_timer(c->read, u->connect_timeout); + } else { + if (c->write->timer_set) { + ngx_del_timer(c->write); + } - u->conn_waiting = 1; - u->write_prepare_retvals = ngx_http_lua_ssl_handshake_retval_handler; + ngx_add_timer(c->read, u->connect_timeout); - ngx_http_lua_cleanup_pending_operation(coctx); - coctx->cleanup = ngx_http_lua_coctx_cleanup; - coctx->data = u; + u->conn_waiting = 1; + u->write_prepare_retvals = + ngx_http_lua_ssl_handshake_retval_handler; - c->ssl->handler = ngx_http_lua_ssl_handshake_handler; + ngx_http_lua_cleanup_pending_operation(coctx); + coctx->cleanup = ngx_http_lua_coctx_cleanup; + coctx->data = u; - if (ctx->entered_content_phase) { - r->write_event_handler = ngx_http_lua_content_wev_handler; + c->ssl->handler = ngx_http_lua_ssl_handshake_handler; - } else { - r->write_event_handler = ngx_http_core_run_phases; - } + if (ctx->entered_content_phase) { + r->write_event_handler = ngx_http_lua_content_wev_handler; - return lua_yield(L, 0); + } else { + r->write_event_handler = ngx_http_core_run_phases; + } + + return lua_yield(L, 0); + } } top = lua_gettop(L); @@ -1836,7 +1888,13 @@ ngx_http_lua_ssl_handshake_handler(ngx_connection_t *c) waiting = u->conn_waiting; dc = r->connection; - L = u->write_co_ctx->co; + + if (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET) { + L = ngx_http_lua_get_lua_vm(r, ctx); + + } else { + L = u->write_co_ctx->co; + } if (c->read->timedout) { lua_pushnil(L); @@ -2101,6 +2159,12 @@ ngx_http_lua_socket_tcp_receive_helper(ngx_http_request_t *r, rc = ngx_http_lua_socket_tcp_read(r, u); + if (rc == NGX_AGAIN + && (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET)) + { + rc = ngx_http_lua_socket_tcp_blocked_read(r, u); + } + if (rc == NGX_ERROR) { dd("read failed: %d", (int) u->ft_type); rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); @@ -2913,6 +2977,12 @@ ngx_http_lua_socket_tcp_send(lua_State *L) dd("socket send returned %d", (int) rc); + if (rc == NGX_AGAIN + && (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET)) + { + rc = ngx_http_lua_socket_tcp_blocked_write(r, u); + } + if (rc == NGX_ERROR) { return ngx_http_lua_socket_write_error_retval_handler(r, u, L); } @@ -4487,6 +4557,12 @@ ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) rc = ngx_http_lua_socket_tcp_read(r, u); + if (rc == NGX_AGAIN + && (ctx->context & NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET)) + { + rc = ngx_http_lua_socket_tcp_blocked_read(r, u); + } + if (rc == NGX_ERROR) { dd("read failed: %d", (int) u->ft_type); rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); @@ -6101,6 +6177,249 @@ ngx_http_lua_coctx_cleanup(void *data) } +static void +ngx_http_lua_socket_tcp_clean_events(ngx_connection_t *c) +{ + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->read->active) { + ngx_del_event(c->read, NGX_READ_EVENT, 0); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (c->write->active) { + ngx_del_event(c->write, NGX_WRITE_EVENT, 0); + } +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_blocked_conn(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_uint_t ft_type; + ngx_msec_t timeout, start_time; + + ft_type = 0; + c = u->peer.connection; + + ngx_http_lua_socket_tcp_clean_events(c); + + if (ngx_http_lua_set_event(c->write, NGX_WRITE_EVENT) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + goto finish; + } + + timeout = u->connect_timeout; + start_time = ngx_current_msec; + + if (ngx_http_lua_process_event(r, timeout) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + goto finish; + } + + ngx_time_update(); + + if (ngx_current_msec - start_time >= timeout) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_TIMEOUT; + } + +finish: + + if (ngx_http_lua_clear_event(c->write, NGX_WRITE_EVENT) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + } + + if (ft_type != 0) { + ngx_http_lua_socket_handle_conn_error(r, u, ft_type); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_http_lua_socket_tcp_blocked_sslhandshake(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_int_t rc; + ngx_msec_t timer, timeout, start_time; + + rc = NGX_OK; + c = u->peer.connection; + + ngx_http_lua_socket_tcp_clean_events(c); + + if (ngx_http_lua_set_event(c->read, NGX_READ_EVENT)) { + goto finish; + } + + if (ngx_http_lua_set_event(c->write, NGX_WRITE_EVENT)) { + goto finish; + } + + timeout = u->connect_timeout; + start_time = ngx_current_msec; + + for (;;) { + + timer = timeout - (ngx_current_msec - start_time); + + if (ngx_http_lua_process_event(r, timer) == NGX_ERROR) { + goto finish; + } + + ngx_time_update(); + + if (ngx_current_msec - start_time >= timeout) { + c->read->timedout = 1; + goto finish; + } + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_ERROR || rc == NGX_OK) { + break; + } + + /* NGX_AGAIN, continue in loop*/ + } + +finish: + + ngx_http_lua_clear_event(c->read, NGX_READ_EVENT); + ngx_http_lua_clear_event(c->write, NGX_WRITE_EVENT); +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_blocked_write(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_uint_t ft_type; + ngx_msec_t timeout, start_time; + + ft_type = 0; + c = u->peer.connection; + + ngx_http_lua_socket_tcp_clean_events(c); + + if (ngx_http_lua_set_event(c->write, NGX_WRITE_EVENT) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + goto finish; + } + + timeout = u->send_timeout; + start_time = ngx_current_msec; + + if (ngx_http_lua_process_event(r, timeout) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + goto finish; + } + + ngx_time_update(); + + if (ngx_current_msec - start_time >= timeout) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_TIMEOUT; + } + +finish: + + if (ngx_http_lua_clear_event(c->write, NGX_WRITE_EVENT) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + } + + if (ft_type != 0) { + ngx_http_lua_socket_handle_write_error(r, u, ft_type); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_lua_socket_tcp_blocked_read(ngx_http_request_t *r, + ngx_http_lua_socket_tcp_upstream_t *u) +{ + ngx_connection_t *c; + ngx_int_t rc; + ngx_uint_t ft_type; + ngx_msec_t timeout, timer, start_time; + + rc = NGX_OK; + ft_type = 0; + c = u->peer.connection; + + ngx_http_lua_socket_tcp_clean_events(c); + + if (ngx_http_lua_set_event(c->read, NGX_READ_EVENT) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + goto finish; + } + + timeout = u->read_timeout; + start_time = ngx_current_msec; + + for (;;) { + + timer = timeout - (ngx_current_msec - start_time); + + if (ngx_http_lua_process_event(r, timer) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + goto finish; + } + +#if (NGX_HAVE_KQUEUE) + if (c->read->pending_eof) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + goto finish; + } +#endif + + ngx_time_update(); + + if (ngx_current_msec - start_time >= timeout) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_TIMEOUT; + goto finish; + } + + rc = ngx_http_lua_socket_tcp_read(r, u); + + if (rc == NGX_ERROR || rc == NGX_OK) { + break; + } + + /* NGX_AGAIN, continue in loop*/ + } + +finish: + + if (ngx_http_lua_clear_event(c->read, NGX_READ_EVENT) == NGX_ERROR) { + ft_type = NGX_HTTP_LUA_SOCKET_FT_ERROR; + } + + if (ft_type != 0) { + ngx_http_lua_socket_handle_read_error(r, u, ft_type); + + return NGX_ERROR; + } + + return rc; +} + + #if (NGX_HTTP_SSL) static int diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index 9ad34b93ea..cf00692066 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -40,6 +40,14 @@ | NGX_HTTP_LUA_CONTEXT_SSL_CERT \ | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH) +#define NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET \ + (NGX_HTTP_LUA_CONTEXT_INIT \ + | NGX_HTTP_LUA_CONTEXT_EXIT_WORKER) + + +#define NGX_HTTP_LUA_CONTEXT_COSOCKET (NGX_HTTP_LUA_CONTEXT_YIELDABLE \ + | NGX_HTTP_LUA_CONTEXT_BLOCKED_COSOCKET) + /* key in Lua vm registry for all the "ngx.ctx" tables */ #define ngx_http_lua_ctx_tables_key "ngx_lua_ctx_tables" @@ -64,6 +72,7 @@ "ssl_session_store_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH ? \ "ssl_session_fetch_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_INIT ? "init_by_lua*" \ : "(unknown)") diff --git a/t/058-tcp-socket.t b/t/058-tcp-socket.t index 593e49461f..6488b11db6 100644 --- a/t/058-tcp-socket.t +++ b/t/058-tcp-socket.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua; repeat_each(2); -plan tests => repeat_each() * 231; +plan tests => repeat_each() * 234; our $HtmlDir = html_dir; @@ -4367,3 +4367,65 @@ connect failed: missing the port number finish --- no_error_log [error] + + + +=== TEST 73: run in init_worker_by_lua +--- http_config + init_by_lua_block { + local sock = ngx.socket.tcp() + local port = 80 + local ok, err = sock:connect("agentzh.org", port) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + ngx.log(ngx.INFO, "connected: ", ok) + + local req = "GET / HTTP/1.0\r\nHost: agentzh.org\r\nConnection: close\r\n\r\n" + -- req = "OK" + + local bytes, err = sock:send(req) + if not bytes then + ngx.log(ngx.ERR, "failed to send request: ", err) + return + end + + ngx.log(ngx.INFO, "request sent: ", bytes) + + local line, err = sock:receive() + if line then + ngx.log(ngx.INFO, "first line received: ", line) + + else + ngx.log(ngx.ERR, "failed to receive the first line: ", err) + end + + line, err = sock:receive() + if line then + ngx.log(ngx.INFO, "second line received: ", line) + + else + ngx.log(ngx.ERR, "failed to receive the second line: ", err) + end + } +--- config + location /t { + content_by_lua_block { + ngx.say("hello") + } + } +--- request +GET /t +--- response_body +hello +--- error_log_like +connected: 1 +request sent: 56 +first line received: HTTP\/1\.1 200 OK +second line received: (?:Date|Server): .*? +--- no_error_log +[error] +--- timeout: 10 +--- ONLY diff --git a/t/129-ssl-socket.t b/t/129-ssl-socket.t index c26f0cec53..646ba2572c 100644 --- a/t/129-ssl-socket.t +++ b/t/129-ssl-socket.t @@ -2732,11 +2732,6 @@ received: HTTP/1.1 200 OK close: 1 nil --- log_level: debug ---- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/ ---- grep_error_log_out eval -qr/^lua ssl save session: ([0-9A-F]+) -lua ssl free session: ([0-9A-F]+) -$/ --- error_log eval ['lua ssl server name: "test.com"', qr/SSL: TLSv1.3, cipher: "TLS_AES_128_GCM_SHA256 TLSv1.3/] @@ -2834,3 +2829,67 @@ SSL reused session [alert] [emerg] --- timeout: 10 + + + +=== TEST 35: access www.google.com in init_by_lua +--- http_config + init_by_lua_block { + local sock = ngx.socket.tcp() + sock:settimeout(2000) + local ok, err = sock:connect("www.google.com", 443) + if not ok then + ngx.log(ngx.ERR, "failed to connect: ", err) + return + end + + ngx.log(ngx.INFO, "connected: ", ok) + + local sess, err = sock:sslhandshake() + if not sess then + ngx.log(ngx.ERR, "failed to do SSL handshake: ", err) + return + end + + ngx.log(ngx.INFO, "ssl handshake: ", type(sess)) + + local req = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.log(ngx.ERR, "failed to send http request: ", err) + return + end + + ngx.log(ngx.INFO, "sent http request: ", bytes, " bytes.") + + local line, err = sock:receive() + if not line then + ngx.log(ngx.ERR, "failed to receive response status line: ", err) + return + end + + ngx.log(ngx.INFO, "received: ", line) + + local ok, err = sock:close() + ngx.log(ngx.INFO, "close: ", ok, " ", err) + } +--- config + location /t { + content_by_lua_block { + ngx.say("hello") + } + } +--- request +GET /t +--- response_body +hello +--- error_log_like chop +\Aconnected: 1 +ssl handshake: userdata +sent http request: 59 bytes. +received: HTTP/1.1 (?:200 OK|302 Found) +close: 1 nil +\z +[error] +--- timeout: 5 +--- ONLY