162Sigor@sysoev.ru
262Sigor@sysoev.ru /*
362Sigor@sysoev.ru * Copyright (C) Igor Sysoev
462Sigor@sysoev.ru * Copyright (C) NGINX, Inc.
562Sigor@sysoev.ru */
662Sigor@sysoev.ru
762Sigor@sysoev.ru #include <nxt_main.h>
862Sigor@sysoev.ru
962Sigor@sysoev.ru
1062Sigor@sysoev.ru /*
1162Sigor@sysoev.ru * A listen socket handler calls an event facility specific io_accept()
1262Sigor@sysoev.ru * method. The method accept()s a new connection and then calls
1362Sigor@sysoev.ru * nxt_event_conn_accept() to handle the new connection and to prepare
1462Sigor@sysoev.ru * for a next connection to avoid just dropping next accept()ed socket
1562Sigor@sysoev.ru * if no more connections allowed. If there are no available connections
1662Sigor@sysoev.ru * an idle connection would be closed. If there are no idle connections
1762Sigor@sysoev.ru * then new connections will not be accept()ed for 1 second.
1862Sigor@sysoev.ru */
1962Sigor@sysoev.ru
2062Sigor@sysoev.ru
2162Sigor@sysoev.ru static nxt_conn_t *nxt_conn_accept_alloc(nxt_task_t *task,
2262Sigor@sysoev.ru nxt_listen_event_t *lev);
2362Sigor@sysoev.ru static void nxt_conn_listen_handler(nxt_task_t *task, void *obj,
2462Sigor@sysoev.ru void *data);
2562Sigor@sysoev.ru static nxt_conn_t *nxt_conn_accept_next(nxt_task_t *task,
2662Sigor@sysoev.ru nxt_listen_event_t *lev);
271457Sigor@sysoev.ru static void nxt_conn_accept_close_idle(nxt_task_t *task,
2862Sigor@sysoev.ru nxt_listen_event_t *lev);
291457Sigor@sysoev.ru static void nxt_conn_accept_close_idle_handler(nxt_task_t *task, void *obj,
301457Sigor@sysoev.ru void *data);
3162Sigor@sysoev.ru static void nxt_conn_listen_event_error(nxt_task_t *task, void *obj,
3262Sigor@sysoev.ru void *data);
3362Sigor@sysoev.ru static void nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj,
3462Sigor@sysoev.ru void *data);
3562Sigor@sysoev.ru
3662Sigor@sysoev.ru
3762Sigor@sysoev.ru nxt_listen_event_t *
nxt_listen_event(nxt_task_t * task,nxt_listen_socket_t * ls)3862Sigor@sysoev.ru nxt_listen_event(nxt_task_t *task, nxt_listen_socket_t *ls)
3962Sigor@sysoev.ru {
4062Sigor@sysoev.ru nxt_listen_event_t *lev;
4162Sigor@sysoev.ru nxt_event_engine_t *engine;
4262Sigor@sysoev.ru
4362Sigor@sysoev.ru lev = nxt_zalloc(sizeof(nxt_listen_event_t));
4462Sigor@sysoev.ru
4562Sigor@sysoev.ru if (nxt_fast_path(lev != NULL)) {
4662Sigor@sysoev.ru lev->socket.fd = ls->socket;
4762Sigor@sysoev.ru
4862Sigor@sysoev.ru engine = task->thread->engine;
4962Sigor@sysoev.ru lev->batch = engine->batch;
50683Sigor@sysoev.ru lev->count = 1;
5162Sigor@sysoev.ru
5262Sigor@sysoev.ru lev->socket.read_work_queue = &engine->accept_work_queue;
5362Sigor@sysoev.ru lev->socket.read_handler = nxt_conn_listen_handler;
5462Sigor@sysoev.ru lev->socket.error_handler = nxt_conn_listen_event_error;
5562Sigor@sysoev.ru lev->socket.log = &nxt_main_log;
5662Sigor@sysoev.ru
5762Sigor@sysoev.ru lev->accept = engine->event.io->accept;
5862Sigor@sysoev.ru
5962Sigor@sysoev.ru lev->listen = ls;
6062Sigor@sysoev.ru lev->work_queue = &engine->read_work_queue;
6162Sigor@sysoev.ru
6262Sigor@sysoev.ru lev->timer.work_queue = &engine->fast_work_queue;
6362Sigor@sysoev.ru lev->timer.handler = nxt_conn_listen_timer_handler;
6462Sigor@sysoev.ru lev->timer.log = &nxt_main_log;
6562Sigor@sysoev.ru
6662Sigor@sysoev.ru lev->task.thread = task->thread;
6762Sigor@sysoev.ru lev->task.log = &nxt_main_log;
6862Sigor@sysoev.ru lev->task.ident = nxt_task_next_ident();
6962Sigor@sysoev.ru lev->socket.task = &lev->task;
7062Sigor@sysoev.ru lev->timer.task = &lev->task;
7162Sigor@sysoev.ru
7262Sigor@sysoev.ru if (nxt_conn_accept_alloc(task, lev) != NULL) {
7362Sigor@sysoev.ru nxt_fd_event_enable_accept(engine, &lev->socket);
7462Sigor@sysoev.ru
75115Sigor@sysoev.ru nxt_queue_insert_tail(&engine->listen_connections, &lev->link);
7662Sigor@sysoev.ru }
7762Sigor@sysoev.ru
7862Sigor@sysoev.ru return lev;
7962Sigor@sysoev.ru }
8062Sigor@sysoev.ru
8162Sigor@sysoev.ru return NULL;
8262Sigor@sysoev.ru }
8362Sigor@sysoev.ru
8462Sigor@sysoev.ru
8562Sigor@sysoev.ru static nxt_conn_t *
nxt_conn_accept_alloc(nxt_task_t * task,nxt_listen_event_t * lev)8662Sigor@sysoev.ru nxt_conn_accept_alloc(nxt_task_t *task, nxt_listen_event_t *lev)
8762Sigor@sysoev.ru {
88338Sigor@sysoev.ru nxt_mp_t *mp;
89338Sigor@sysoev.ru nxt_conn_t *c;
90338Sigor@sysoev.ru nxt_event_engine_t *engine;
9162Sigor@sysoev.ru
9262Sigor@sysoev.ru engine = task->thread->engine;
9362Sigor@sysoev.ru
9462Sigor@sysoev.ru if (engine->connections < engine->max_connections) {
9562Sigor@sysoev.ru
9665Sigor@sysoev.ru mp = nxt_mp_create(1024, 128, 256, 32);
9762Sigor@sysoev.ru
9862Sigor@sysoev.ru if (nxt_fast_path(mp != NULL)) {
9962Sigor@sysoev.ru c = nxt_conn_create(mp, lev->socket.task);
10065Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) {
1011533Svbart@nginx.com nxt_mp_destroy(mp);
1021533Svbart@nginx.com
1031533Svbart@nginx.com return NULL;
10465Sigor@sysoev.ru }
10562Sigor@sysoev.ru
10662Sigor@sysoev.ru c->socket.read_work_queue = lev->socket.read_work_queue;
10762Sigor@sysoev.ru c->socket.write_ready = 1;
10862Sigor@sysoev.ru
109358Sigor@sysoev.ru c->remote = nxt_sockaddr_cache_alloc(engine, lev->listen);
110337Sigor@sysoev.ru if (nxt_fast_path(c->remote != NULL)) {
1111458Sigor@sysoev.ru lev->next = c;
112337Sigor@sysoev.ru return c;
11365Sigor@sysoev.ru }
1141533Svbart@nginx.com
1151533Svbart@nginx.com nxt_conn_free(task, c);
11662Sigor@sysoev.ru }
11762Sigor@sysoev.ru }
11862Sigor@sysoev.ru
11962Sigor@sysoev.ru return NULL;
12062Sigor@sysoev.ru }
12162Sigor@sysoev.ru
12262Sigor@sysoev.ru
12362Sigor@sysoev.ru static void
nxt_conn_listen_handler(nxt_task_t * task,void * obj,void * data)12462Sigor@sysoev.ru nxt_conn_listen_handler(nxt_task_t *task, void *obj, void *data)
12562Sigor@sysoev.ru {
12662Sigor@sysoev.ru nxt_listen_event_t *lev;
12762Sigor@sysoev.ru
12862Sigor@sysoev.ru lev = obj;
12962Sigor@sysoev.ru lev->ready = lev->batch;
13062Sigor@sysoev.ru
13162Sigor@sysoev.ru lev->accept(task, lev, data);
13262Sigor@sysoev.ru }
13362Sigor@sysoev.ru
13462Sigor@sysoev.ru
13562Sigor@sysoev.ru void
nxt_conn_io_accept(nxt_task_t * task,void * obj,void * data)13662Sigor@sysoev.ru nxt_conn_io_accept(nxt_task_t *task, void *obj, void *data)
13762Sigor@sysoev.ru {
138312Sigor@sysoev.ru socklen_t socklen;
13962Sigor@sysoev.ru nxt_conn_t *c;
14062Sigor@sysoev.ru nxt_socket_t s;
14162Sigor@sysoev.ru struct sockaddr *sa;
14262Sigor@sysoev.ru nxt_listen_event_t *lev;
14362Sigor@sysoev.ru
14462Sigor@sysoev.ru lev = obj;
14562Sigor@sysoev.ru c = lev->next;
14662Sigor@sysoev.ru
14762Sigor@sysoev.ru lev->ready--;
14862Sigor@sysoev.ru lev->socket.read_ready = (lev->ready != 0);
14962Sigor@sysoev.ru
150312Sigor@sysoev.ru sa = &c->remote->u.sockaddr;
151312Sigor@sysoev.ru socklen = c->remote->socklen;
152312Sigor@sysoev.ru /*
153312Sigor@sysoev.ru * The returned socklen is ignored here, because sockaddr_in and
154312Sigor@sysoev.ru * sockaddr_in6 socklens are not changed. As to unspecified sockaddr_un
155312Sigor@sysoev.ru * it is 3 byte length and already prepared, because old BSDs return zero
156312Sigor@sysoev.ru * socklen and do not update the sockaddr_un at all; Linux returns 2 byte
157312Sigor@sysoev.ru * socklen and updates only the sa_family part; other systems copy 3 bytes
158312Sigor@sysoev.ru * and truncate surplus zero part. Only bound sockaddr_un will be really
159312Sigor@sysoev.ru * truncated here.
160312Sigor@sysoev.ru */
161312Sigor@sysoev.ru s = accept(lev->socket.fd, sa, &socklen);
16262Sigor@sysoev.ru
16362Sigor@sysoev.ru if (s == -1) {
16462Sigor@sysoev.ru nxt_conn_accept_error(task, lev, "accept", nxt_socket_errno);
16562Sigor@sysoev.ru return;
16662Sigor@sysoev.ru }
16762Sigor@sysoev.ru
16862Sigor@sysoev.ru c->socket.fd = s;
16962Sigor@sysoev.ru
17062Sigor@sysoev.ru #if (NXT_LINUX)
17162Sigor@sysoev.ru /*
17262Sigor@sysoev.ru * Linux does not inherit non-blocking mode
17362Sigor@sysoev.ru * from listen socket for accept()ed socket.
17462Sigor@sysoev.ru */
17562Sigor@sysoev.ru if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) {
17662Sigor@sysoev.ru nxt_socket_close(task, s);
17762Sigor@sysoev.ru }
17862Sigor@sysoev.ru
17962Sigor@sysoev.ru #endif
18062Sigor@sysoev.ru
18162Sigor@sysoev.ru nxt_debug(task, "accept(%d): %d", lev->socket.fd, s);
18262Sigor@sysoev.ru
18362Sigor@sysoev.ru nxt_conn_accept(task, lev, c);
18462Sigor@sysoev.ru }
18562Sigor@sysoev.ru
18662Sigor@sysoev.ru
18762Sigor@sysoev.ru void
nxt_conn_accept(nxt_task_t * task,nxt_listen_event_t * lev,nxt_conn_t * c)18862Sigor@sysoev.ru nxt_conn_accept(nxt_task_t *task, nxt_listen_event_t *lev, nxt_conn_t *c)
18962Sigor@sysoev.ru {
190*2185Svbart@nginx.com nxt_conn_t *next;
191*2185Svbart@nginx.com nxt_event_engine_t *engine;
19262Sigor@sysoev.ru
19362Sigor@sysoev.ru nxt_sockaddr_text(c->remote);
19462Sigor@sysoev.ru
19562Sigor@sysoev.ru nxt_debug(task, "client: %*s",
196493Spluknet@nginx.com (size_t) c->remote->address_length,
197493Spluknet@nginx.com nxt_sockaddr_address(c->remote));
19862Sigor@sysoev.ru
199*2185Svbart@nginx.com engine = task->thread->engine;
200*2185Svbart@nginx.com
201*2185Svbart@nginx.com engine->accepted_conns_cnt++;
202*2185Svbart@nginx.com
203*2185Svbart@nginx.com nxt_conn_idle(engine, c);
20462Sigor@sysoev.ru
205683Sigor@sysoev.ru c->listen = lev;
206683Sigor@sysoev.ru lev->count++;
2071458Sigor@sysoev.ru lev->next = NULL;
208683Sigor@sysoev.ru c->socket.data = NULL;
209683Sigor@sysoev.ru
21062Sigor@sysoev.ru c->read_work_queue = lev->work_queue;
21162Sigor@sysoev.ru c->write_work_queue = lev->work_queue;
21262Sigor@sysoev.ru
21362Sigor@sysoev.ru if (lev->listen->read_after_accept) {
21462Sigor@sysoev.ru
21562Sigor@sysoev.ru //c->socket.read_ready = 1;
216683Sigor@sysoev.ru // lev->listen->handler(task, c, lev);
21762Sigor@sysoev.ru nxt_work_queue_add(c->read_work_queue, lev->listen->handler,
218683Sigor@sysoev.ru &c->task, c, lev);
21962Sigor@sysoev.ru
22062Sigor@sysoev.ru } else {
22162Sigor@sysoev.ru nxt_work_queue_add(c->write_work_queue, lev->listen->handler,
222683Sigor@sysoev.ru &c->task, c, lev);
22362Sigor@sysoev.ru }
22462Sigor@sysoev.ru
22562Sigor@sysoev.ru next = nxt_conn_accept_next(task, lev);
22662Sigor@sysoev.ru
22762Sigor@sysoev.ru if (next != NULL && lev->socket.read_ready) {
22862Sigor@sysoev.ru nxt_work_queue_add(lev->socket.read_work_queue,
22962Sigor@sysoev.ru lev->accept, task, lev, next);
23062Sigor@sysoev.ru }
23162Sigor@sysoev.ru }
23262Sigor@sysoev.ru
23362Sigor@sysoev.ru
23462Sigor@sysoev.ru static nxt_conn_t *
nxt_conn_accept_next(nxt_task_t * task,nxt_listen_event_t * lev)23562Sigor@sysoev.ru nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev)
23662Sigor@sysoev.ru {
23762Sigor@sysoev.ru nxt_conn_t *c;
23862Sigor@sysoev.ru
2391458Sigor@sysoev.ru c = lev->next;
24062Sigor@sysoev.ru
2411458Sigor@sysoev.ru if (c == NULL) {
2421458Sigor@sysoev.ru c = nxt_conn_accept_alloc(task, lev);
24362Sigor@sysoev.ru
2441458Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) {
2451458Sigor@sysoev.ru nxt_conn_accept_close_idle(task, lev);
2461458Sigor@sysoev.ru }
2471457Sigor@sysoev.ru }
24862Sigor@sysoev.ru
2491457Sigor@sysoev.ru return c;
25062Sigor@sysoev.ru }
25162Sigor@sysoev.ru
25262Sigor@sysoev.ru
2531457Sigor@sysoev.ru static void
nxt_conn_accept_close_idle(nxt_task_t * task,nxt_listen_event_t * lev)25462Sigor@sysoev.ru nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev)
25562Sigor@sysoev.ru {
2561457Sigor@sysoev.ru nxt_event_engine_t *engine;
2571457Sigor@sysoev.ru
2581457Sigor@sysoev.ru engine = task->thread->engine;
2591457Sigor@sysoev.ru
2601457Sigor@sysoev.ru nxt_work_queue_add(&engine->close_work_queue,
2611457Sigor@sysoev.ru nxt_conn_accept_close_idle_handler, task, NULL, NULL);
2621457Sigor@sysoev.ru
2631457Sigor@sysoev.ru nxt_timer_add(engine, &lev->timer, 100);
2641457Sigor@sysoev.ru
2651457Sigor@sysoev.ru nxt_fd_event_disable_read(engine, &lev->socket);
2661457Sigor@sysoev.ru
2671457Sigor@sysoev.ru nxt_alert(task, "new connections are not accepted within 100ms");
2681457Sigor@sysoev.ru }
2691457Sigor@sysoev.ru
2701457Sigor@sysoev.ru
2711457Sigor@sysoev.ru static void
nxt_conn_accept_close_idle_handler(nxt_task_t * task,void * obj,void * data)2721457Sigor@sysoev.ru nxt_conn_accept_close_idle_handler(nxt_task_t *task, void *obj, void *data)
2731457Sigor@sysoev.ru {
2741457Sigor@sysoev.ru nxt_uint_t times;
27562Sigor@sysoev.ru nxt_conn_t *c;
27662Sigor@sysoev.ru nxt_queue_t *idle;
2771457Sigor@sysoev.ru nxt_queue_link_t *link, *next;
27862Sigor@sysoev.ru nxt_event_engine_t *engine;
27962Sigor@sysoev.ru
28062Sigor@sysoev.ru static nxt_log_moderation_t nxt_idle_close_log_moderation = {
28162Sigor@sysoev.ru NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION
28262Sigor@sysoev.ru };
28362Sigor@sysoev.ru
2841457Sigor@sysoev.ru times = 10;
28562Sigor@sysoev.ru engine = task->thread->engine;
28662Sigor@sysoev.ru idle = &engine->idle_connections;
28762Sigor@sysoev.ru
28862Sigor@sysoev.ru for (link = nxt_queue_last(idle);
28962Sigor@sysoev.ru link != nxt_queue_head(idle);
2901457Sigor@sysoev.ru link = next)
29162Sigor@sysoev.ru {
2921457Sigor@sysoev.ru next = nxt_queue_next(link);
2931457Sigor@sysoev.ru
29462Sigor@sysoev.ru c = nxt_queue_link_data(link, nxt_conn_t, link);
29562Sigor@sysoev.ru
2961457Sigor@sysoev.ru nxt_debug(c->socket.task, "idle connection: %d rdy:%d",
2971457Sigor@sysoev.ru c->socket.fd, c->socket.read_ready);
2981457Sigor@sysoev.ru
29962Sigor@sysoev.ru if (!c->socket.read_ready) {
30062Sigor@sysoev.ru nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO,
30162Sigor@sysoev.ru task->log, "no available connections, "
30262Sigor@sysoev.ru "close idle connection");
3031457Sigor@sysoev.ru
3041457Sigor@sysoev.ru c->read_state->close_handler(c->socket.task, c, c->socket.data);
30562Sigor@sysoev.ru
3061457Sigor@sysoev.ru times--;
3071457Sigor@sysoev.ru
3081457Sigor@sysoev.ru if (times == 0) {
3091457Sigor@sysoev.ru break;
3101457Sigor@sysoev.ru }
31162Sigor@sysoev.ru }
31262Sigor@sysoev.ru }
31362Sigor@sysoev.ru }
31462Sigor@sysoev.ru
31562Sigor@sysoev.ru
31662Sigor@sysoev.ru void
nxt_conn_accept_error(nxt_task_t * task,nxt_listen_event_t * lev,const char * accept_syscall,nxt_err_t err)31762Sigor@sysoev.ru nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev,
31862Sigor@sysoev.ru const char *accept_syscall, nxt_err_t err)
31962Sigor@sysoev.ru {
32062Sigor@sysoev.ru static nxt_log_moderation_t nxt_accept_log_moderation = {
32162Sigor@sysoev.ru NXT_LOG_INFO, 2, "accept() failed", NXT_LOG_MODERATION
32262Sigor@sysoev.ru };
32362Sigor@sysoev.ru
32462Sigor@sysoev.ru lev->socket.read_ready = 0;
32562Sigor@sysoev.ru
32662Sigor@sysoev.ru switch (err) {
32762Sigor@sysoev.ru
32862Sigor@sysoev.ru case NXT_EAGAIN:
32962Sigor@sysoev.ru nxt_debug(task, "%s(%d) %E", accept_syscall, lev->socket.fd, err);
33062Sigor@sysoev.ru return;
33162Sigor@sysoev.ru
33262Sigor@sysoev.ru case ECONNABORTED:
33362Sigor@sysoev.ru nxt_log_moderate(&nxt_accept_log_moderation, NXT_LOG_WARN,
33462Sigor@sysoev.ru task->log, "%s(%d) failed %E",
33562Sigor@sysoev.ru accept_syscall, lev->socket.fd, err);
33662Sigor@sysoev.ru return;
33762Sigor@sysoev.ru
33862Sigor@sysoev.ru case EMFILE:
33962Sigor@sysoev.ru case ENFILE:
34062Sigor@sysoev.ru case ENOBUFS:
34162Sigor@sysoev.ru case ENOMEM:
3421457Sigor@sysoev.ru nxt_alert(task, "%s(%d) failed %E",
3431457Sigor@sysoev.ru accept_syscall, lev->socket.fd, err);
34462Sigor@sysoev.ru
3451457Sigor@sysoev.ru nxt_conn_accept_close_idle(task, lev);
34662Sigor@sysoev.ru return;
34762Sigor@sysoev.ru
34862Sigor@sysoev.ru default:
349564Svbart@nginx.com nxt_alert(task, "%s(%d) failed %E",
350564Svbart@nginx.com accept_syscall, lev->socket.fd, err);
35162Sigor@sysoev.ru return;
35262Sigor@sysoev.ru }
35362Sigor@sysoev.ru }
35462Sigor@sysoev.ru
35562Sigor@sysoev.ru
35662Sigor@sysoev.ru static void
nxt_conn_listen_timer_handler(nxt_task_t * task,void * obj,void * data)35762Sigor@sysoev.ru nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data)
35862Sigor@sysoev.ru {
35962Sigor@sysoev.ru nxt_conn_t *c;
36062Sigor@sysoev.ru nxt_timer_t *timer;
36162Sigor@sysoev.ru nxt_listen_event_t *lev;
36262Sigor@sysoev.ru
36362Sigor@sysoev.ru timer = obj;
36462Sigor@sysoev.ru
36562Sigor@sysoev.ru lev = nxt_timer_data(timer, nxt_listen_event_t, timer);
36662Sigor@sysoev.ru
3671458Sigor@sysoev.ru c = nxt_conn_accept_next(task, lev);
36862Sigor@sysoev.ru if (c == NULL) {
3691458Sigor@sysoev.ru return;
37062Sigor@sysoev.ru }
37162Sigor@sysoev.ru
37262Sigor@sysoev.ru nxt_fd_event_enable_accept(task->thread->engine, &lev->socket);
37362Sigor@sysoev.ru
37462Sigor@sysoev.ru lev->accept(task, lev, c);
37562Sigor@sysoev.ru }
37662Sigor@sysoev.ru
37762Sigor@sysoev.ru
37862Sigor@sysoev.ru static void
nxt_conn_listen_event_error(nxt_task_t * task,void * obj,void * data)37962Sigor@sysoev.ru nxt_conn_listen_event_error(nxt_task_t *task, void *obj, void *data)
38062Sigor@sysoev.ru {
38162Sigor@sysoev.ru nxt_fd_event_t *ev;
38262Sigor@sysoev.ru
38362Sigor@sysoev.ru ev = obj;
38462Sigor@sysoev.ru
385564Svbart@nginx.com nxt_alert(task, "accept(%d) event error", ev->fd);
38662Sigor@sysoev.ru }
387