xref: /unit/src/nxt_conn_accept.c (revision 62)
1*62Sigor@sysoev.ru 
2*62Sigor@sysoev.ru /*
3*62Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4*62Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5*62Sigor@sysoev.ru  */
6*62Sigor@sysoev.ru 
7*62Sigor@sysoev.ru #include <nxt_main.h>
8*62Sigor@sysoev.ru 
9*62Sigor@sysoev.ru 
10*62Sigor@sysoev.ru /*
11*62Sigor@sysoev.ru  * A listen socket handler calls an event facility specific io_accept()
12*62Sigor@sysoev.ru  * method.  The method accept()s a new connection and then calls
13*62Sigor@sysoev.ru  * nxt_event_conn_accept() to handle the new connection and to prepare
14*62Sigor@sysoev.ru  * for a next connection to avoid just dropping next accept()ed socket
15*62Sigor@sysoev.ru  * if no more connections allowed.  If there are no available connections
16*62Sigor@sysoev.ru  * an idle connection would be closed.  If there are no idle connections
17*62Sigor@sysoev.ru  * then new connections will not be accept()ed for 1 second.
18*62Sigor@sysoev.ru  */
19*62Sigor@sysoev.ru 
20*62Sigor@sysoev.ru 
21*62Sigor@sysoev.ru static nxt_conn_t *nxt_conn_accept_alloc(nxt_task_t *task,
22*62Sigor@sysoev.ru     nxt_listen_event_t *lev);
23*62Sigor@sysoev.ru static void nxt_conn_listen_handler(nxt_task_t *task, void *obj,
24*62Sigor@sysoev.ru     void *data);
25*62Sigor@sysoev.ru static nxt_conn_t *nxt_conn_accept_next(nxt_task_t *task,
26*62Sigor@sysoev.ru     nxt_listen_event_t *lev);
27*62Sigor@sysoev.ru static nxt_int_t nxt_conn_accept_close_idle(nxt_task_t *task,
28*62Sigor@sysoev.ru     nxt_listen_event_t *lev);
29*62Sigor@sysoev.ru static void nxt_conn_listen_event_error(nxt_task_t *task, void *obj,
30*62Sigor@sysoev.ru     void *data);
31*62Sigor@sysoev.ru static void nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj,
32*62Sigor@sysoev.ru     void *data);
33*62Sigor@sysoev.ru 
34*62Sigor@sysoev.ru 
35*62Sigor@sysoev.ru nxt_listen_event_t *
36*62Sigor@sysoev.ru nxt_listen_event(nxt_task_t *task, nxt_listen_socket_t *ls)
37*62Sigor@sysoev.ru {
38*62Sigor@sysoev.ru     nxt_listen_event_t  *lev;
39*62Sigor@sysoev.ru     nxt_event_engine_t  *engine;
40*62Sigor@sysoev.ru 
41*62Sigor@sysoev.ru     lev = nxt_zalloc(sizeof(nxt_listen_event_t));
42*62Sigor@sysoev.ru 
43*62Sigor@sysoev.ru     if (nxt_fast_path(lev != NULL)) {
44*62Sigor@sysoev.ru         lev->socket.fd = ls->socket;
45*62Sigor@sysoev.ru 
46*62Sigor@sysoev.ru         engine = task->thread->engine;
47*62Sigor@sysoev.ru         lev->batch = engine->batch;
48*62Sigor@sysoev.ru 
49*62Sigor@sysoev.ru         lev->socket.read_work_queue = &engine->accept_work_queue;
50*62Sigor@sysoev.ru         lev->socket.read_handler = nxt_conn_listen_handler;
51*62Sigor@sysoev.ru         lev->socket.error_handler = nxt_conn_listen_event_error;
52*62Sigor@sysoev.ru         lev->socket.log = &nxt_main_log;
53*62Sigor@sysoev.ru 
54*62Sigor@sysoev.ru         lev->accept = engine->event.io->accept;
55*62Sigor@sysoev.ru 
56*62Sigor@sysoev.ru         lev->listen = ls;
57*62Sigor@sysoev.ru         lev->work_queue = &engine->read_work_queue;
58*62Sigor@sysoev.ru 
59*62Sigor@sysoev.ru         lev->timer.work_queue = &engine->fast_work_queue;
60*62Sigor@sysoev.ru         lev->timer.handler = nxt_conn_listen_timer_handler;
61*62Sigor@sysoev.ru         lev->timer.log = &nxt_main_log;
62*62Sigor@sysoev.ru 
63*62Sigor@sysoev.ru         lev->task.thread = task->thread;
64*62Sigor@sysoev.ru         lev->task.log = &nxt_main_log;
65*62Sigor@sysoev.ru         lev->task.ident = nxt_task_next_ident();
66*62Sigor@sysoev.ru         lev->socket.task = &lev->task;
67*62Sigor@sysoev.ru         lev->timer.task = &lev->task;
68*62Sigor@sysoev.ru 
69*62Sigor@sysoev.ru         if (nxt_conn_accept_alloc(task, lev) != NULL) {
70*62Sigor@sysoev.ru             nxt_fd_event_enable_accept(engine, &lev->socket);
71*62Sigor@sysoev.ru 
72*62Sigor@sysoev.ru             nxt_queue_insert_head(&engine->listen_connections, &lev->link);
73*62Sigor@sysoev.ru         }
74*62Sigor@sysoev.ru 
75*62Sigor@sysoev.ru         return lev;
76*62Sigor@sysoev.ru     }
77*62Sigor@sysoev.ru 
78*62Sigor@sysoev.ru     return NULL;
79*62Sigor@sysoev.ru }
80*62Sigor@sysoev.ru 
81*62Sigor@sysoev.ru 
82*62Sigor@sysoev.ru static nxt_conn_t *
83*62Sigor@sysoev.ru nxt_conn_accept_alloc(nxt_task_t *task, nxt_listen_event_t *lev)
84*62Sigor@sysoev.ru {
85*62Sigor@sysoev.ru     nxt_conn_t           *c;
86*62Sigor@sysoev.ru     nxt_sockaddr_t       *sa, *remote;
87*62Sigor@sysoev.ru     nxt_mem_pool_t       *mp;
88*62Sigor@sysoev.ru     nxt_event_engine_t   *engine;
89*62Sigor@sysoev.ru     nxt_listen_socket_t  *ls;
90*62Sigor@sysoev.ru 
91*62Sigor@sysoev.ru     engine = task->thread->engine;
92*62Sigor@sysoev.ru 
93*62Sigor@sysoev.ru     if (engine->connections < engine->max_connections) {
94*62Sigor@sysoev.ru 
95*62Sigor@sysoev.ru         mp = nxt_mem_pool_create(lev->listen->mem_pool_size);
96*62Sigor@sysoev.ru 
97*62Sigor@sysoev.ru         if (nxt_fast_path(mp != NULL)) {
98*62Sigor@sysoev.ru             /* This allocation cannot fail. */
99*62Sigor@sysoev.ru             c = nxt_conn_create(mp, lev->socket.task);
100*62Sigor@sysoev.ru 
101*62Sigor@sysoev.ru             lev->next = c;
102*62Sigor@sysoev.ru             c->socket.read_work_queue = lev->socket.read_work_queue;
103*62Sigor@sysoev.ru             c->socket.write_ready = 1;
104*62Sigor@sysoev.ru             c->listen = lev;
105*62Sigor@sysoev.ru 
106*62Sigor@sysoev.ru             ls = lev->listen;
107*62Sigor@sysoev.ru             /* This allocation cannot fail. */
108*62Sigor@sysoev.ru             remote = nxt_sockaddr_alloc(mp, ls->socklen, ls->address_length);
109*62Sigor@sysoev.ru             c->remote = remote;
110*62Sigor@sysoev.ru 
111*62Sigor@sysoev.ru             sa = ls->sockaddr;
112*62Sigor@sysoev.ru             remote->type = sa->type;
113*62Sigor@sysoev.ru             /*
114*62Sigor@sysoev.ru              * Set address family for unspecified Unix domain,
115*62Sigor@sysoev.ru              * because these sockaddr's are not be passed to accept().
116*62Sigor@sysoev.ru              */
117*62Sigor@sysoev.ru             remote->u.sockaddr.sa_family = sa->u.sockaddr.sa_family;
118*62Sigor@sysoev.ru 
119*62Sigor@sysoev.ru             return c;
120*62Sigor@sysoev.ru         }
121*62Sigor@sysoev.ru     }
122*62Sigor@sysoev.ru 
123*62Sigor@sysoev.ru     return NULL;
124*62Sigor@sysoev.ru }
125*62Sigor@sysoev.ru 
126*62Sigor@sysoev.ru 
127*62Sigor@sysoev.ru static void
128*62Sigor@sysoev.ru nxt_conn_listen_handler(nxt_task_t *task, void *obj, void *data)
129*62Sigor@sysoev.ru {
130*62Sigor@sysoev.ru     nxt_listen_event_t  *lev;
131*62Sigor@sysoev.ru 
132*62Sigor@sysoev.ru     lev = obj;
133*62Sigor@sysoev.ru     lev->ready = lev->batch;
134*62Sigor@sysoev.ru 
135*62Sigor@sysoev.ru     lev->accept(task, lev, data);
136*62Sigor@sysoev.ru }
137*62Sigor@sysoev.ru 
138*62Sigor@sysoev.ru 
139*62Sigor@sysoev.ru void
140*62Sigor@sysoev.ru nxt_conn_io_accept(nxt_task_t *task, void *obj, void *data)
141*62Sigor@sysoev.ru {
142*62Sigor@sysoev.ru     socklen_t           len;
143*62Sigor@sysoev.ru     nxt_conn_t          *c;
144*62Sigor@sysoev.ru     nxt_socket_t        s;
145*62Sigor@sysoev.ru     struct sockaddr     *sa;
146*62Sigor@sysoev.ru     nxt_listen_event_t  *lev;
147*62Sigor@sysoev.ru 
148*62Sigor@sysoev.ru     lev = obj;
149*62Sigor@sysoev.ru     c = lev->next;
150*62Sigor@sysoev.ru 
151*62Sigor@sysoev.ru     lev->ready--;
152*62Sigor@sysoev.ru     lev->socket.read_ready = (lev->ready != 0);
153*62Sigor@sysoev.ru 
154*62Sigor@sysoev.ru     len = c->remote->socklen;
155*62Sigor@sysoev.ru 
156*62Sigor@sysoev.ru     if (len >= sizeof(struct sockaddr)) {
157*62Sigor@sysoev.ru         sa = &c->remote->u.sockaddr;
158*62Sigor@sysoev.ru 
159*62Sigor@sysoev.ru     } else {
160*62Sigor@sysoev.ru         sa = NULL;
161*62Sigor@sysoev.ru         len = 0;
162*62Sigor@sysoev.ru     }
163*62Sigor@sysoev.ru 
164*62Sigor@sysoev.ru     s = accept(lev->socket.fd, sa, &len);
165*62Sigor@sysoev.ru 
166*62Sigor@sysoev.ru     if (s == -1) {
167*62Sigor@sysoev.ru         nxt_conn_accept_error(task, lev, "accept", nxt_socket_errno);
168*62Sigor@sysoev.ru         return;
169*62Sigor@sysoev.ru     }
170*62Sigor@sysoev.ru 
171*62Sigor@sysoev.ru     c->socket.fd = s;
172*62Sigor@sysoev.ru 
173*62Sigor@sysoev.ru #if (NXT_LINUX)
174*62Sigor@sysoev.ru     /*
175*62Sigor@sysoev.ru      * Linux does not inherit non-blocking mode
176*62Sigor@sysoev.ru      * from listen socket for accept()ed socket.
177*62Sigor@sysoev.ru      */
178*62Sigor@sysoev.ru     if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) {
179*62Sigor@sysoev.ru         nxt_socket_close(task, s);
180*62Sigor@sysoev.ru     }
181*62Sigor@sysoev.ru 
182*62Sigor@sysoev.ru #endif
183*62Sigor@sysoev.ru 
184*62Sigor@sysoev.ru     nxt_debug(task, "accept(%d): %d", lev->socket.fd, s);
185*62Sigor@sysoev.ru 
186*62Sigor@sysoev.ru     nxt_conn_accept(task, lev, c);
187*62Sigor@sysoev.ru }
188*62Sigor@sysoev.ru 
189*62Sigor@sysoev.ru 
190*62Sigor@sysoev.ru void
191*62Sigor@sysoev.ru nxt_conn_accept(nxt_task_t *task, nxt_listen_event_t *lev, nxt_conn_t *c)
192*62Sigor@sysoev.ru {
193*62Sigor@sysoev.ru     nxt_conn_t  *next;
194*62Sigor@sysoev.ru 
195*62Sigor@sysoev.ru     nxt_sockaddr_text(c->remote);
196*62Sigor@sysoev.ru 
197*62Sigor@sysoev.ru     nxt_debug(task, "client: %*s",
198*62Sigor@sysoev.ru               c->remote->address_length, nxt_sockaddr_address(c->remote));
199*62Sigor@sysoev.ru 
200*62Sigor@sysoev.ru     nxt_queue_insert_head(&task->thread->engine->idle_connections, &c->link);
201*62Sigor@sysoev.ru 
202*62Sigor@sysoev.ru     c->read_work_queue = lev->work_queue;
203*62Sigor@sysoev.ru     c->write_work_queue = lev->work_queue;
204*62Sigor@sysoev.ru 
205*62Sigor@sysoev.ru     if (lev->listen->read_after_accept) {
206*62Sigor@sysoev.ru 
207*62Sigor@sysoev.ru         //c->socket.read_ready = 1;
208*62Sigor@sysoev.ru //        lev->listen->handler(task, c, lev->socket.data);
209*62Sigor@sysoev.ru         nxt_work_queue_add(c->read_work_queue, lev->listen->handler,
210*62Sigor@sysoev.ru                            task, c, lev->socket.data);
211*62Sigor@sysoev.ru 
212*62Sigor@sysoev.ru     } else {
213*62Sigor@sysoev.ru         nxt_work_queue_add(c->write_work_queue, lev->listen->handler,
214*62Sigor@sysoev.ru                            task, c, lev->socket.data);
215*62Sigor@sysoev.ru     }
216*62Sigor@sysoev.ru 
217*62Sigor@sysoev.ru     next = nxt_conn_accept_next(task, lev);
218*62Sigor@sysoev.ru 
219*62Sigor@sysoev.ru     if (next != NULL && lev->socket.read_ready) {
220*62Sigor@sysoev.ru         nxt_work_queue_add(lev->socket.read_work_queue,
221*62Sigor@sysoev.ru                            lev->accept, task, lev, next);
222*62Sigor@sysoev.ru     }
223*62Sigor@sysoev.ru }
224*62Sigor@sysoev.ru 
225*62Sigor@sysoev.ru 
226*62Sigor@sysoev.ru static nxt_conn_t *
227*62Sigor@sysoev.ru nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev)
228*62Sigor@sysoev.ru {
229*62Sigor@sysoev.ru     nxt_conn_t  *c;
230*62Sigor@sysoev.ru 
231*62Sigor@sysoev.ru     lev->next = NULL;
232*62Sigor@sysoev.ru 
233*62Sigor@sysoev.ru     do {
234*62Sigor@sysoev.ru         c = nxt_conn_accept_alloc(task, lev);
235*62Sigor@sysoev.ru 
236*62Sigor@sysoev.ru         if (nxt_fast_path(c != NULL)) {
237*62Sigor@sysoev.ru             return c;
238*62Sigor@sysoev.ru         }
239*62Sigor@sysoev.ru 
240*62Sigor@sysoev.ru     } while (nxt_conn_accept_close_idle(task, lev) == NXT_OK);
241*62Sigor@sysoev.ru 
242*62Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, "no available connections, "
243*62Sigor@sysoev.ru                   "new connections are not accepted within 1s");
244*62Sigor@sysoev.ru 
245*62Sigor@sysoev.ru     return NULL;
246*62Sigor@sysoev.ru }
247*62Sigor@sysoev.ru 
248*62Sigor@sysoev.ru 
249*62Sigor@sysoev.ru static nxt_int_t
250*62Sigor@sysoev.ru nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev)
251*62Sigor@sysoev.ru {
252*62Sigor@sysoev.ru     nxt_conn_t          *c;
253*62Sigor@sysoev.ru     nxt_queue_t         *idle;
254*62Sigor@sysoev.ru     nxt_queue_link_t    *link;
255*62Sigor@sysoev.ru     nxt_event_engine_t  *engine;
256*62Sigor@sysoev.ru 
257*62Sigor@sysoev.ru     static nxt_log_moderation_t  nxt_idle_close_log_moderation = {
258*62Sigor@sysoev.ru         NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION
259*62Sigor@sysoev.ru     };
260*62Sigor@sysoev.ru 
261*62Sigor@sysoev.ru     engine = task->thread->engine;
262*62Sigor@sysoev.ru 
263*62Sigor@sysoev.ru     idle = &engine->idle_connections;
264*62Sigor@sysoev.ru 
265*62Sigor@sysoev.ru     for (link = nxt_queue_last(idle);
266*62Sigor@sysoev.ru          link != nxt_queue_head(idle);
267*62Sigor@sysoev.ru          link = nxt_queue_next(link))
268*62Sigor@sysoev.ru     {
269*62Sigor@sysoev.ru         c = nxt_queue_link_data(link, nxt_conn_t, link);
270*62Sigor@sysoev.ru 
271*62Sigor@sysoev.ru         if (!c->socket.read_ready) {
272*62Sigor@sysoev.ru             nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO,
273*62Sigor@sysoev.ru                              task->log, "no available connections, "
274*62Sigor@sysoev.ru                              "close idle connection");
275*62Sigor@sysoev.ru             nxt_queue_remove(link);
276*62Sigor@sysoev.ru             nxt_conn_close(engine, c);
277*62Sigor@sysoev.ru 
278*62Sigor@sysoev.ru             return NXT_OK;
279*62Sigor@sysoev.ru         }
280*62Sigor@sysoev.ru     }
281*62Sigor@sysoev.ru 
282*62Sigor@sysoev.ru     nxt_timer_add(engine, &lev->timer, 1000);
283*62Sigor@sysoev.ru 
284*62Sigor@sysoev.ru     nxt_fd_event_disable_read(engine, &lev->socket);
285*62Sigor@sysoev.ru 
286*62Sigor@sysoev.ru     return NXT_DECLINED;
287*62Sigor@sysoev.ru }
288*62Sigor@sysoev.ru 
289*62Sigor@sysoev.ru 
290*62Sigor@sysoev.ru void
291*62Sigor@sysoev.ru nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev,
292*62Sigor@sysoev.ru     const char *accept_syscall, nxt_err_t err)
293*62Sigor@sysoev.ru {
294*62Sigor@sysoev.ru     static nxt_log_moderation_t  nxt_accept_log_moderation = {
295*62Sigor@sysoev.ru         NXT_LOG_INFO, 2, "accept() failed", NXT_LOG_MODERATION
296*62Sigor@sysoev.ru     };
297*62Sigor@sysoev.ru 
298*62Sigor@sysoev.ru     lev->socket.read_ready = 0;
299*62Sigor@sysoev.ru 
300*62Sigor@sysoev.ru     switch (err) {
301*62Sigor@sysoev.ru 
302*62Sigor@sysoev.ru     case NXT_EAGAIN:
303*62Sigor@sysoev.ru         nxt_debug(task, "%s(%d) %E", accept_syscall, lev->socket.fd, err);
304*62Sigor@sysoev.ru         return;
305*62Sigor@sysoev.ru 
306*62Sigor@sysoev.ru     case ECONNABORTED:
307*62Sigor@sysoev.ru         nxt_log_moderate(&nxt_accept_log_moderation, NXT_LOG_WARN,
308*62Sigor@sysoev.ru                          task->log, "%s(%d) failed %E",
309*62Sigor@sysoev.ru                          accept_syscall, lev->socket.fd, err);
310*62Sigor@sysoev.ru         return;
311*62Sigor@sysoev.ru 
312*62Sigor@sysoev.ru     case EMFILE:
313*62Sigor@sysoev.ru     case ENFILE:
314*62Sigor@sysoev.ru     case ENOBUFS:
315*62Sigor@sysoev.ru     case ENOMEM:
316*62Sigor@sysoev.ru         if (nxt_conn_accept_close_idle(task, lev) != NXT_OK) {
317*62Sigor@sysoev.ru             nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E, "
318*62Sigor@sysoev.ru                     "new connections are not accepted within 1s",
319*62Sigor@sysoev.ru                     accept_syscall, lev->socket.fd, err);
320*62Sigor@sysoev.ru         }
321*62Sigor@sysoev.ru 
322*62Sigor@sysoev.ru         return;
323*62Sigor@sysoev.ru 
324*62Sigor@sysoev.ru     default:
325*62Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E",
326*62Sigor@sysoev.ru                 accept_syscall, lev->socket.fd, err);
327*62Sigor@sysoev.ru         return;
328*62Sigor@sysoev.ru     }
329*62Sigor@sysoev.ru }
330*62Sigor@sysoev.ru 
331*62Sigor@sysoev.ru 
332*62Sigor@sysoev.ru static void
333*62Sigor@sysoev.ru nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data)
334*62Sigor@sysoev.ru {
335*62Sigor@sysoev.ru     nxt_conn_t          *c;
336*62Sigor@sysoev.ru     nxt_timer_t         *timer;
337*62Sigor@sysoev.ru     nxt_listen_event_t  *lev;
338*62Sigor@sysoev.ru 
339*62Sigor@sysoev.ru     timer = obj;
340*62Sigor@sysoev.ru 
341*62Sigor@sysoev.ru     lev = nxt_timer_data(timer, nxt_listen_event_t, timer);
342*62Sigor@sysoev.ru     c = lev->next;
343*62Sigor@sysoev.ru 
344*62Sigor@sysoev.ru     if (c == NULL) {
345*62Sigor@sysoev.ru         c = nxt_conn_accept_next(task, lev);
346*62Sigor@sysoev.ru 
347*62Sigor@sysoev.ru         if (c == NULL) {
348*62Sigor@sysoev.ru             return;
349*62Sigor@sysoev.ru         }
350*62Sigor@sysoev.ru     }
351*62Sigor@sysoev.ru 
352*62Sigor@sysoev.ru     nxt_fd_event_enable_accept(task->thread->engine, &lev->socket);
353*62Sigor@sysoev.ru 
354*62Sigor@sysoev.ru     lev->accept(task, lev, c);
355*62Sigor@sysoev.ru }
356*62Sigor@sysoev.ru 
357*62Sigor@sysoev.ru 
358*62Sigor@sysoev.ru static void
359*62Sigor@sysoev.ru nxt_conn_listen_event_error(nxt_task_t *task, void *obj, void *data)
360*62Sigor@sysoev.ru {
361*62Sigor@sysoev.ru     nxt_fd_event_t  *ev;
362*62Sigor@sysoev.ru 
363*62Sigor@sysoev.ru     ev = obj;
364*62Sigor@sysoev.ru 
365*62Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, "accept(%d) event error", ev->fd);
366*62Sigor@sysoev.ru }
367