xref: /unit/src/nxt_conn_accept.c (revision 312)
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);
2762Sigor@sysoev.ru static nxt_int_t nxt_conn_accept_close_idle(nxt_task_t *task,
2862Sigor@sysoev.ru     nxt_listen_event_t *lev);
2962Sigor@sysoev.ru static void nxt_conn_listen_event_error(nxt_task_t *task, void *obj,
3062Sigor@sysoev.ru     void *data);
3162Sigor@sysoev.ru static void nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj,
3262Sigor@sysoev.ru     void *data);
3362Sigor@sysoev.ru 
3462Sigor@sysoev.ru 
3562Sigor@sysoev.ru nxt_listen_event_t *
3662Sigor@sysoev.ru nxt_listen_event(nxt_task_t *task, nxt_listen_socket_t *ls)
3762Sigor@sysoev.ru {
3862Sigor@sysoev.ru     nxt_listen_event_t  *lev;
3962Sigor@sysoev.ru     nxt_event_engine_t  *engine;
4062Sigor@sysoev.ru 
4162Sigor@sysoev.ru     lev = nxt_zalloc(sizeof(nxt_listen_event_t));
4262Sigor@sysoev.ru 
4362Sigor@sysoev.ru     if (nxt_fast_path(lev != NULL)) {
4462Sigor@sysoev.ru         lev->socket.fd = ls->socket;
4562Sigor@sysoev.ru 
4662Sigor@sysoev.ru         engine = task->thread->engine;
4762Sigor@sysoev.ru         lev->batch = engine->batch;
4862Sigor@sysoev.ru 
4962Sigor@sysoev.ru         lev->socket.read_work_queue = &engine->accept_work_queue;
5062Sigor@sysoev.ru         lev->socket.read_handler = nxt_conn_listen_handler;
5162Sigor@sysoev.ru         lev->socket.error_handler = nxt_conn_listen_event_error;
5262Sigor@sysoev.ru         lev->socket.log = &nxt_main_log;
5362Sigor@sysoev.ru 
5462Sigor@sysoev.ru         lev->accept = engine->event.io->accept;
5562Sigor@sysoev.ru 
5662Sigor@sysoev.ru         lev->listen = ls;
5762Sigor@sysoev.ru         lev->work_queue = &engine->read_work_queue;
5862Sigor@sysoev.ru 
5962Sigor@sysoev.ru         lev->timer.work_queue = &engine->fast_work_queue;
6062Sigor@sysoev.ru         lev->timer.handler = nxt_conn_listen_timer_handler;
6162Sigor@sysoev.ru         lev->timer.log = &nxt_main_log;
6262Sigor@sysoev.ru 
6362Sigor@sysoev.ru         lev->task.thread = task->thread;
6462Sigor@sysoev.ru         lev->task.log = &nxt_main_log;
6562Sigor@sysoev.ru         lev->task.ident = nxt_task_next_ident();
6662Sigor@sysoev.ru         lev->socket.task = &lev->task;
6762Sigor@sysoev.ru         lev->timer.task = &lev->task;
6862Sigor@sysoev.ru 
6962Sigor@sysoev.ru         if (nxt_conn_accept_alloc(task, lev) != NULL) {
7062Sigor@sysoev.ru             nxt_fd_event_enable_accept(engine, &lev->socket);
7162Sigor@sysoev.ru 
72115Sigor@sysoev.ru             nxt_queue_insert_tail(&engine->listen_connections, &lev->link);
7362Sigor@sysoev.ru         }
7462Sigor@sysoev.ru 
7562Sigor@sysoev.ru         return lev;
7662Sigor@sysoev.ru     }
7762Sigor@sysoev.ru 
7862Sigor@sysoev.ru     return NULL;
7962Sigor@sysoev.ru }
8062Sigor@sysoev.ru 
8162Sigor@sysoev.ru 
8262Sigor@sysoev.ru static nxt_conn_t *
8362Sigor@sysoev.ru nxt_conn_accept_alloc(nxt_task_t *task, nxt_listen_event_t *lev)
8462Sigor@sysoev.ru {
8565Sigor@sysoev.ru     nxt_mp_t             *mp;
8662Sigor@sysoev.ru     nxt_conn_t           *c;
8762Sigor@sysoev.ru     nxt_sockaddr_t       *sa, *remote;
8862Sigor@sysoev.ru     nxt_event_engine_t   *engine;
8962Sigor@sysoev.ru     nxt_listen_socket_t  *ls;
9062Sigor@sysoev.ru 
9162Sigor@sysoev.ru     engine = task->thread->engine;
9262Sigor@sysoev.ru 
9362Sigor@sysoev.ru     if (engine->connections < engine->max_connections) {
9462Sigor@sysoev.ru 
9565Sigor@sysoev.ru         mp = nxt_mp_create(1024, 128, 256, 32);
9662Sigor@sysoev.ru 
9762Sigor@sysoev.ru         if (nxt_fast_path(mp != NULL)) {
9862Sigor@sysoev.ru             c = nxt_conn_create(mp, lev->socket.task);
9965Sigor@sysoev.ru             if (nxt_slow_path(c == NULL)) {
10065Sigor@sysoev.ru                 goto fail;
10165Sigor@sysoev.ru             }
10262Sigor@sysoev.ru 
10362Sigor@sysoev.ru             lev->next = c;
10462Sigor@sysoev.ru             c->socket.read_work_queue = lev->socket.read_work_queue;
10562Sigor@sysoev.ru             c->socket.write_ready = 1;
10662Sigor@sysoev.ru             c->listen = lev;
10762Sigor@sysoev.ru 
10862Sigor@sysoev.ru             ls = lev->listen;
10965Sigor@sysoev.ru 
11062Sigor@sysoev.ru             remote = nxt_sockaddr_alloc(mp, ls->socklen, ls->address_length);
11165Sigor@sysoev.ru             if (nxt_slow_path(remote == NULL)) {
11265Sigor@sysoev.ru                 goto fail;
11365Sigor@sysoev.ru             }
11465Sigor@sysoev.ru 
11562Sigor@sysoev.ru             c->remote = remote;
11662Sigor@sysoev.ru 
11762Sigor@sysoev.ru             sa = ls->sockaddr;
11862Sigor@sysoev.ru             remote->type = sa->type;
11962Sigor@sysoev.ru             /*
120*312Sigor@sysoev.ru              * Set address family for unspecified Unix domain socket,
121*312Sigor@sysoev.ru              * because these sockaddr's are not updated by old BSD systems,
122*312Sigor@sysoev.ru              * see comment in nxt_conn_io_accept().
12362Sigor@sysoev.ru              */
12462Sigor@sysoev.ru             remote->u.sockaddr.sa_family = sa->u.sockaddr.sa_family;
12562Sigor@sysoev.ru 
12662Sigor@sysoev.ru             return c;
12762Sigor@sysoev.ru         }
12865Sigor@sysoev.ru 
12965Sigor@sysoev.ru     fail:
13065Sigor@sysoev.ru 
13165Sigor@sysoev.ru         nxt_mp_destroy(mp);
13262Sigor@sysoev.ru     }
13362Sigor@sysoev.ru 
13462Sigor@sysoev.ru     return NULL;
13562Sigor@sysoev.ru }
13662Sigor@sysoev.ru 
13762Sigor@sysoev.ru 
13862Sigor@sysoev.ru static void
13962Sigor@sysoev.ru nxt_conn_listen_handler(nxt_task_t *task, void *obj, void *data)
14062Sigor@sysoev.ru {
14162Sigor@sysoev.ru     nxt_listen_event_t  *lev;
14262Sigor@sysoev.ru 
14362Sigor@sysoev.ru     lev = obj;
14462Sigor@sysoev.ru     lev->ready = lev->batch;
14562Sigor@sysoev.ru 
14662Sigor@sysoev.ru     lev->accept(task, lev, data);
14762Sigor@sysoev.ru }
14862Sigor@sysoev.ru 
14962Sigor@sysoev.ru 
15062Sigor@sysoev.ru void
15162Sigor@sysoev.ru nxt_conn_io_accept(nxt_task_t *task, void *obj, void *data)
15262Sigor@sysoev.ru {
153*312Sigor@sysoev.ru     socklen_t           socklen;
15462Sigor@sysoev.ru     nxt_conn_t          *c;
15562Sigor@sysoev.ru     nxt_socket_t        s;
15662Sigor@sysoev.ru     struct sockaddr     *sa;
15762Sigor@sysoev.ru     nxt_listen_event_t  *lev;
15862Sigor@sysoev.ru 
15962Sigor@sysoev.ru     lev = obj;
16062Sigor@sysoev.ru     c = lev->next;
16162Sigor@sysoev.ru 
16262Sigor@sysoev.ru     lev->ready--;
16362Sigor@sysoev.ru     lev->socket.read_ready = (lev->ready != 0);
16462Sigor@sysoev.ru 
165*312Sigor@sysoev.ru     sa = &c->remote->u.sockaddr;
166*312Sigor@sysoev.ru     socklen = c->remote->socklen;
167*312Sigor@sysoev.ru     /*
168*312Sigor@sysoev.ru      * The returned socklen is ignored here, because sockaddr_in and
169*312Sigor@sysoev.ru      * sockaddr_in6 socklens are not changed.  As to unspecified sockaddr_un
170*312Sigor@sysoev.ru      * it is 3 byte length and already prepared, because old BSDs return zero
171*312Sigor@sysoev.ru      * socklen and do not update the sockaddr_un at all; Linux returns 2 byte
172*312Sigor@sysoev.ru      * socklen and updates only the sa_family part; other systems copy 3 bytes
173*312Sigor@sysoev.ru      * and truncate surplus zero part.  Only bound sockaddr_un will be really
174*312Sigor@sysoev.ru      * truncated here.
175*312Sigor@sysoev.ru      */
176*312Sigor@sysoev.ru     s = accept(lev->socket.fd, sa, &socklen);
17762Sigor@sysoev.ru 
17862Sigor@sysoev.ru     if (s == -1) {
17962Sigor@sysoev.ru         nxt_conn_accept_error(task, lev, "accept", nxt_socket_errno);
18062Sigor@sysoev.ru         return;
18162Sigor@sysoev.ru     }
18262Sigor@sysoev.ru 
18362Sigor@sysoev.ru     c->socket.fd = s;
18462Sigor@sysoev.ru 
18562Sigor@sysoev.ru #if (NXT_LINUX)
18662Sigor@sysoev.ru     /*
18762Sigor@sysoev.ru      * Linux does not inherit non-blocking mode
18862Sigor@sysoev.ru      * from listen socket for accept()ed socket.
18962Sigor@sysoev.ru      */
19062Sigor@sysoev.ru     if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) {
19162Sigor@sysoev.ru         nxt_socket_close(task, s);
19262Sigor@sysoev.ru     }
19362Sigor@sysoev.ru 
19462Sigor@sysoev.ru #endif
19562Sigor@sysoev.ru 
19662Sigor@sysoev.ru     nxt_debug(task, "accept(%d): %d", lev->socket.fd, s);
19762Sigor@sysoev.ru 
19862Sigor@sysoev.ru     nxt_conn_accept(task, lev, c);
19962Sigor@sysoev.ru }
20062Sigor@sysoev.ru 
20162Sigor@sysoev.ru 
20262Sigor@sysoev.ru void
20362Sigor@sysoev.ru nxt_conn_accept(nxt_task_t *task, nxt_listen_event_t *lev, nxt_conn_t *c)
20462Sigor@sysoev.ru {
20562Sigor@sysoev.ru     nxt_conn_t  *next;
20662Sigor@sysoev.ru 
20762Sigor@sysoev.ru     nxt_sockaddr_text(c->remote);
20862Sigor@sysoev.ru 
20962Sigor@sysoev.ru     nxt_debug(task, "client: %*s",
21062Sigor@sysoev.ru               c->remote->address_length, nxt_sockaddr_address(c->remote));
21162Sigor@sysoev.ru 
21262Sigor@sysoev.ru     nxt_queue_insert_head(&task->thread->engine->idle_connections, &c->link);
21362Sigor@sysoev.ru 
21462Sigor@sysoev.ru     c->read_work_queue = lev->work_queue;
21562Sigor@sysoev.ru     c->write_work_queue = lev->work_queue;
21662Sigor@sysoev.ru 
21762Sigor@sysoev.ru     if (lev->listen->read_after_accept) {
21862Sigor@sysoev.ru 
21962Sigor@sysoev.ru         //c->socket.read_ready = 1;
22062Sigor@sysoev.ru //        lev->listen->handler(task, c, lev->socket.data);
22162Sigor@sysoev.ru         nxt_work_queue_add(c->read_work_queue, lev->listen->handler,
222150Sigor@sysoev.ru                            &c->task, c, lev->socket.data);
22362Sigor@sysoev.ru 
22462Sigor@sysoev.ru     } else {
22562Sigor@sysoev.ru         nxt_work_queue_add(c->write_work_queue, lev->listen->handler,
226150Sigor@sysoev.ru                            &c->task, c, lev->socket.data);
22762Sigor@sysoev.ru     }
22862Sigor@sysoev.ru 
22962Sigor@sysoev.ru     next = nxt_conn_accept_next(task, lev);
23062Sigor@sysoev.ru 
23162Sigor@sysoev.ru     if (next != NULL && lev->socket.read_ready) {
23262Sigor@sysoev.ru         nxt_work_queue_add(lev->socket.read_work_queue,
23362Sigor@sysoev.ru                            lev->accept, task, lev, next);
23462Sigor@sysoev.ru     }
23562Sigor@sysoev.ru }
23662Sigor@sysoev.ru 
23762Sigor@sysoev.ru 
23862Sigor@sysoev.ru static nxt_conn_t *
23962Sigor@sysoev.ru nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev)
24062Sigor@sysoev.ru {
24162Sigor@sysoev.ru     nxt_conn_t  *c;
24262Sigor@sysoev.ru 
24362Sigor@sysoev.ru     lev->next = NULL;
24462Sigor@sysoev.ru 
24562Sigor@sysoev.ru     do {
24662Sigor@sysoev.ru         c = nxt_conn_accept_alloc(task, lev);
24762Sigor@sysoev.ru 
24862Sigor@sysoev.ru         if (nxt_fast_path(c != NULL)) {
24962Sigor@sysoev.ru             return c;
25062Sigor@sysoev.ru         }
25162Sigor@sysoev.ru 
25262Sigor@sysoev.ru     } while (nxt_conn_accept_close_idle(task, lev) == NXT_OK);
25362Sigor@sysoev.ru 
25462Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, "no available connections, "
25562Sigor@sysoev.ru                   "new connections are not accepted within 1s");
25662Sigor@sysoev.ru 
25762Sigor@sysoev.ru     return NULL;
25862Sigor@sysoev.ru }
25962Sigor@sysoev.ru 
26062Sigor@sysoev.ru 
26162Sigor@sysoev.ru static nxt_int_t
26262Sigor@sysoev.ru nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev)
26362Sigor@sysoev.ru {
26462Sigor@sysoev.ru     nxt_conn_t          *c;
26562Sigor@sysoev.ru     nxt_queue_t         *idle;
26662Sigor@sysoev.ru     nxt_queue_link_t    *link;
26762Sigor@sysoev.ru     nxt_event_engine_t  *engine;
26862Sigor@sysoev.ru 
26962Sigor@sysoev.ru     static nxt_log_moderation_t  nxt_idle_close_log_moderation = {
27062Sigor@sysoev.ru         NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION
27162Sigor@sysoev.ru     };
27262Sigor@sysoev.ru 
27362Sigor@sysoev.ru     engine = task->thread->engine;
27462Sigor@sysoev.ru 
27562Sigor@sysoev.ru     idle = &engine->idle_connections;
27662Sigor@sysoev.ru 
27762Sigor@sysoev.ru     for (link = nxt_queue_last(idle);
27862Sigor@sysoev.ru          link != nxt_queue_head(idle);
27962Sigor@sysoev.ru          link = nxt_queue_next(link))
28062Sigor@sysoev.ru     {
28162Sigor@sysoev.ru         c = nxt_queue_link_data(link, nxt_conn_t, link);
28262Sigor@sysoev.ru 
28362Sigor@sysoev.ru         if (!c->socket.read_ready) {
28462Sigor@sysoev.ru             nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO,
28562Sigor@sysoev.ru                              task->log, "no available connections, "
28662Sigor@sysoev.ru                              "close idle connection");
28762Sigor@sysoev.ru             nxt_queue_remove(link);
28862Sigor@sysoev.ru             nxt_conn_close(engine, c);
28962Sigor@sysoev.ru 
29062Sigor@sysoev.ru             return NXT_OK;
29162Sigor@sysoev.ru         }
29262Sigor@sysoev.ru     }
29362Sigor@sysoev.ru 
29462Sigor@sysoev.ru     nxt_timer_add(engine, &lev->timer, 1000);
29562Sigor@sysoev.ru 
29662Sigor@sysoev.ru     nxt_fd_event_disable_read(engine, &lev->socket);
29762Sigor@sysoev.ru 
29862Sigor@sysoev.ru     return NXT_DECLINED;
29962Sigor@sysoev.ru }
30062Sigor@sysoev.ru 
30162Sigor@sysoev.ru 
30262Sigor@sysoev.ru void
30362Sigor@sysoev.ru nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev,
30462Sigor@sysoev.ru     const char *accept_syscall, nxt_err_t err)
30562Sigor@sysoev.ru {
30662Sigor@sysoev.ru     static nxt_log_moderation_t  nxt_accept_log_moderation = {
30762Sigor@sysoev.ru         NXT_LOG_INFO, 2, "accept() failed", NXT_LOG_MODERATION
30862Sigor@sysoev.ru     };
30962Sigor@sysoev.ru 
31062Sigor@sysoev.ru     lev->socket.read_ready = 0;
31162Sigor@sysoev.ru 
31262Sigor@sysoev.ru     switch (err) {
31362Sigor@sysoev.ru 
31462Sigor@sysoev.ru     case NXT_EAGAIN:
31562Sigor@sysoev.ru         nxt_debug(task, "%s(%d) %E", accept_syscall, lev->socket.fd, err);
31662Sigor@sysoev.ru         return;
31762Sigor@sysoev.ru 
31862Sigor@sysoev.ru     case ECONNABORTED:
31962Sigor@sysoev.ru         nxt_log_moderate(&nxt_accept_log_moderation, NXT_LOG_WARN,
32062Sigor@sysoev.ru                          task->log, "%s(%d) failed %E",
32162Sigor@sysoev.ru                          accept_syscall, lev->socket.fd, err);
32262Sigor@sysoev.ru         return;
32362Sigor@sysoev.ru 
32462Sigor@sysoev.ru     case EMFILE:
32562Sigor@sysoev.ru     case ENFILE:
32662Sigor@sysoev.ru     case ENOBUFS:
32762Sigor@sysoev.ru     case ENOMEM:
32862Sigor@sysoev.ru         if (nxt_conn_accept_close_idle(task, lev) != NXT_OK) {
32962Sigor@sysoev.ru             nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E, "
33062Sigor@sysoev.ru                     "new connections are not accepted within 1s",
33162Sigor@sysoev.ru                     accept_syscall, lev->socket.fd, err);
33262Sigor@sysoev.ru         }
33362Sigor@sysoev.ru 
33462Sigor@sysoev.ru         return;
33562Sigor@sysoev.ru 
33662Sigor@sysoev.ru     default:
33762Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E",
33862Sigor@sysoev.ru                 accept_syscall, lev->socket.fd, err);
33962Sigor@sysoev.ru         return;
34062Sigor@sysoev.ru     }
34162Sigor@sysoev.ru }
34262Sigor@sysoev.ru 
34362Sigor@sysoev.ru 
34462Sigor@sysoev.ru static void
34562Sigor@sysoev.ru nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data)
34662Sigor@sysoev.ru {
34762Sigor@sysoev.ru     nxt_conn_t          *c;
34862Sigor@sysoev.ru     nxt_timer_t         *timer;
34962Sigor@sysoev.ru     nxt_listen_event_t  *lev;
35062Sigor@sysoev.ru 
35162Sigor@sysoev.ru     timer = obj;
35262Sigor@sysoev.ru 
35362Sigor@sysoev.ru     lev = nxt_timer_data(timer, nxt_listen_event_t, timer);
35462Sigor@sysoev.ru     c = lev->next;
35562Sigor@sysoev.ru 
35662Sigor@sysoev.ru     if (c == NULL) {
35762Sigor@sysoev.ru         c = nxt_conn_accept_next(task, lev);
35862Sigor@sysoev.ru 
35962Sigor@sysoev.ru         if (c == NULL) {
36062Sigor@sysoev.ru             return;
36162Sigor@sysoev.ru         }
36262Sigor@sysoev.ru     }
36362Sigor@sysoev.ru 
36462Sigor@sysoev.ru     nxt_fd_event_enable_accept(task->thread->engine, &lev->socket);
36562Sigor@sysoev.ru 
36662Sigor@sysoev.ru     lev->accept(task, lev, c);
36762Sigor@sysoev.ru }
36862Sigor@sysoev.ru 
36962Sigor@sysoev.ru 
37062Sigor@sysoev.ru static void
37162Sigor@sysoev.ru nxt_conn_listen_event_error(nxt_task_t *task, void *obj, void *data)
37262Sigor@sysoev.ru {
37362Sigor@sysoev.ru     nxt_fd_event_t  *ev;
37462Sigor@sysoev.ru 
37562Sigor@sysoev.ru     ev = obj;
37662Sigor@sysoev.ru 
37762Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, "accept(%d) event error", ev->fd);
37862Sigor@sysoev.ru }
379