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