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 7262Sigor@sysoev.ru nxt_queue_insert_head(&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 { 85*65Sigor@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 95*65Sigor@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); 99*65Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) { 100*65Sigor@sysoev.ru goto fail; 101*65Sigor@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; 109*65Sigor@sysoev.ru 11062Sigor@sysoev.ru remote = nxt_sockaddr_alloc(mp, ls->socklen, ls->address_length); 111*65Sigor@sysoev.ru if (nxt_slow_path(remote == NULL)) { 112*65Sigor@sysoev.ru goto fail; 113*65Sigor@sysoev.ru } 114*65Sigor@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 /* 12062Sigor@sysoev.ru * Set address family for unspecified Unix domain, 12162Sigor@sysoev.ru * because these sockaddr's are not be passed to accept(). 12262Sigor@sysoev.ru */ 12362Sigor@sysoev.ru remote->u.sockaddr.sa_family = sa->u.sockaddr.sa_family; 12462Sigor@sysoev.ru 12562Sigor@sysoev.ru return c; 12662Sigor@sysoev.ru } 127*65Sigor@sysoev.ru 128*65Sigor@sysoev.ru fail: 129*65Sigor@sysoev.ru 130*65Sigor@sysoev.ru nxt_mp_destroy(mp); 13162Sigor@sysoev.ru } 13262Sigor@sysoev.ru 13362Sigor@sysoev.ru return NULL; 13462Sigor@sysoev.ru } 13562Sigor@sysoev.ru 13662Sigor@sysoev.ru 13762Sigor@sysoev.ru static void 13862Sigor@sysoev.ru nxt_conn_listen_handler(nxt_task_t *task, void *obj, void *data) 13962Sigor@sysoev.ru { 14062Sigor@sysoev.ru nxt_listen_event_t *lev; 14162Sigor@sysoev.ru 14262Sigor@sysoev.ru lev = obj; 14362Sigor@sysoev.ru lev->ready = lev->batch; 14462Sigor@sysoev.ru 14562Sigor@sysoev.ru lev->accept(task, lev, data); 14662Sigor@sysoev.ru } 14762Sigor@sysoev.ru 14862Sigor@sysoev.ru 14962Sigor@sysoev.ru void 15062Sigor@sysoev.ru nxt_conn_io_accept(nxt_task_t *task, void *obj, void *data) 15162Sigor@sysoev.ru { 15262Sigor@sysoev.ru socklen_t len; 15362Sigor@sysoev.ru nxt_conn_t *c; 15462Sigor@sysoev.ru nxt_socket_t s; 15562Sigor@sysoev.ru struct sockaddr *sa; 15662Sigor@sysoev.ru nxt_listen_event_t *lev; 15762Sigor@sysoev.ru 15862Sigor@sysoev.ru lev = obj; 15962Sigor@sysoev.ru c = lev->next; 16062Sigor@sysoev.ru 16162Sigor@sysoev.ru lev->ready--; 16262Sigor@sysoev.ru lev->socket.read_ready = (lev->ready != 0); 16362Sigor@sysoev.ru 16462Sigor@sysoev.ru len = c->remote->socklen; 16562Sigor@sysoev.ru 16662Sigor@sysoev.ru if (len >= sizeof(struct sockaddr)) { 16762Sigor@sysoev.ru sa = &c->remote->u.sockaddr; 16862Sigor@sysoev.ru 16962Sigor@sysoev.ru } else { 17062Sigor@sysoev.ru sa = NULL; 17162Sigor@sysoev.ru len = 0; 17262Sigor@sysoev.ru } 17362Sigor@sysoev.ru 17462Sigor@sysoev.ru s = accept(lev->socket.fd, sa, &len); 17562Sigor@sysoev.ru 17662Sigor@sysoev.ru if (s == -1) { 17762Sigor@sysoev.ru nxt_conn_accept_error(task, lev, "accept", nxt_socket_errno); 17862Sigor@sysoev.ru return; 17962Sigor@sysoev.ru } 18062Sigor@sysoev.ru 18162Sigor@sysoev.ru c->socket.fd = s; 18262Sigor@sysoev.ru 18362Sigor@sysoev.ru #if (NXT_LINUX) 18462Sigor@sysoev.ru /* 18562Sigor@sysoev.ru * Linux does not inherit non-blocking mode 18662Sigor@sysoev.ru * from listen socket for accept()ed socket. 18762Sigor@sysoev.ru */ 18862Sigor@sysoev.ru if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) { 18962Sigor@sysoev.ru nxt_socket_close(task, s); 19062Sigor@sysoev.ru } 19162Sigor@sysoev.ru 19262Sigor@sysoev.ru #endif 19362Sigor@sysoev.ru 19462Sigor@sysoev.ru nxt_debug(task, "accept(%d): %d", lev->socket.fd, s); 19562Sigor@sysoev.ru 19662Sigor@sysoev.ru nxt_conn_accept(task, lev, c); 19762Sigor@sysoev.ru } 19862Sigor@sysoev.ru 19962Sigor@sysoev.ru 20062Sigor@sysoev.ru void 20162Sigor@sysoev.ru nxt_conn_accept(nxt_task_t *task, nxt_listen_event_t *lev, nxt_conn_t *c) 20262Sigor@sysoev.ru { 20362Sigor@sysoev.ru nxt_conn_t *next; 20462Sigor@sysoev.ru 20562Sigor@sysoev.ru nxt_sockaddr_text(c->remote); 20662Sigor@sysoev.ru 20762Sigor@sysoev.ru nxt_debug(task, "client: %*s", 20862Sigor@sysoev.ru c->remote->address_length, nxt_sockaddr_address(c->remote)); 20962Sigor@sysoev.ru 21062Sigor@sysoev.ru nxt_queue_insert_head(&task->thread->engine->idle_connections, &c->link); 21162Sigor@sysoev.ru 21262Sigor@sysoev.ru c->read_work_queue = lev->work_queue; 21362Sigor@sysoev.ru c->write_work_queue = lev->work_queue; 21462Sigor@sysoev.ru 21562Sigor@sysoev.ru if (lev->listen->read_after_accept) { 21662Sigor@sysoev.ru 21762Sigor@sysoev.ru //c->socket.read_ready = 1; 21862Sigor@sysoev.ru // lev->listen->handler(task, c, lev->socket.data); 21962Sigor@sysoev.ru nxt_work_queue_add(c->read_work_queue, lev->listen->handler, 22062Sigor@sysoev.ru task, c, lev->socket.data); 22162Sigor@sysoev.ru 22262Sigor@sysoev.ru } else { 22362Sigor@sysoev.ru nxt_work_queue_add(c->write_work_queue, lev->listen->handler, 22462Sigor@sysoev.ru task, c, lev->socket.data); 22562Sigor@sysoev.ru } 22662Sigor@sysoev.ru 22762Sigor@sysoev.ru next = nxt_conn_accept_next(task, lev); 22862Sigor@sysoev.ru 22962Sigor@sysoev.ru if (next != NULL && lev->socket.read_ready) { 23062Sigor@sysoev.ru nxt_work_queue_add(lev->socket.read_work_queue, 23162Sigor@sysoev.ru lev->accept, task, lev, next); 23262Sigor@sysoev.ru } 23362Sigor@sysoev.ru } 23462Sigor@sysoev.ru 23562Sigor@sysoev.ru 23662Sigor@sysoev.ru static nxt_conn_t * 23762Sigor@sysoev.ru nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev) 23862Sigor@sysoev.ru { 23962Sigor@sysoev.ru nxt_conn_t *c; 24062Sigor@sysoev.ru 24162Sigor@sysoev.ru lev->next = NULL; 24262Sigor@sysoev.ru 24362Sigor@sysoev.ru do { 24462Sigor@sysoev.ru c = nxt_conn_accept_alloc(task, lev); 24562Sigor@sysoev.ru 24662Sigor@sysoev.ru if (nxt_fast_path(c != NULL)) { 24762Sigor@sysoev.ru return c; 24862Sigor@sysoev.ru } 24962Sigor@sysoev.ru 25062Sigor@sysoev.ru } while (nxt_conn_accept_close_idle(task, lev) == NXT_OK); 25162Sigor@sysoev.ru 25262Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "no available connections, " 25362Sigor@sysoev.ru "new connections are not accepted within 1s"); 25462Sigor@sysoev.ru 25562Sigor@sysoev.ru return NULL; 25662Sigor@sysoev.ru } 25762Sigor@sysoev.ru 25862Sigor@sysoev.ru 25962Sigor@sysoev.ru static nxt_int_t 26062Sigor@sysoev.ru nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev) 26162Sigor@sysoev.ru { 26262Sigor@sysoev.ru nxt_conn_t *c; 26362Sigor@sysoev.ru nxt_queue_t *idle; 26462Sigor@sysoev.ru nxt_queue_link_t *link; 26562Sigor@sysoev.ru nxt_event_engine_t *engine; 26662Sigor@sysoev.ru 26762Sigor@sysoev.ru static nxt_log_moderation_t nxt_idle_close_log_moderation = { 26862Sigor@sysoev.ru NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION 26962Sigor@sysoev.ru }; 27062Sigor@sysoev.ru 27162Sigor@sysoev.ru engine = task->thread->engine; 27262Sigor@sysoev.ru 27362Sigor@sysoev.ru idle = &engine->idle_connections; 27462Sigor@sysoev.ru 27562Sigor@sysoev.ru for (link = nxt_queue_last(idle); 27662Sigor@sysoev.ru link != nxt_queue_head(idle); 27762Sigor@sysoev.ru link = nxt_queue_next(link)) 27862Sigor@sysoev.ru { 27962Sigor@sysoev.ru c = nxt_queue_link_data(link, nxt_conn_t, link); 28062Sigor@sysoev.ru 28162Sigor@sysoev.ru if (!c->socket.read_ready) { 28262Sigor@sysoev.ru nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO, 28362Sigor@sysoev.ru task->log, "no available connections, " 28462Sigor@sysoev.ru "close idle connection"); 28562Sigor@sysoev.ru nxt_queue_remove(link); 28662Sigor@sysoev.ru nxt_conn_close(engine, c); 28762Sigor@sysoev.ru 28862Sigor@sysoev.ru return NXT_OK; 28962Sigor@sysoev.ru } 29062Sigor@sysoev.ru } 29162Sigor@sysoev.ru 29262Sigor@sysoev.ru nxt_timer_add(engine, &lev->timer, 1000); 29362Sigor@sysoev.ru 29462Sigor@sysoev.ru nxt_fd_event_disable_read(engine, &lev->socket); 29562Sigor@sysoev.ru 29662Sigor@sysoev.ru return NXT_DECLINED; 29762Sigor@sysoev.ru } 29862Sigor@sysoev.ru 29962Sigor@sysoev.ru 30062Sigor@sysoev.ru void 30162Sigor@sysoev.ru nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev, 30262Sigor@sysoev.ru const char *accept_syscall, nxt_err_t err) 30362Sigor@sysoev.ru { 30462Sigor@sysoev.ru static nxt_log_moderation_t nxt_accept_log_moderation = { 30562Sigor@sysoev.ru NXT_LOG_INFO, 2, "accept() failed", NXT_LOG_MODERATION 30662Sigor@sysoev.ru }; 30762Sigor@sysoev.ru 30862Sigor@sysoev.ru lev->socket.read_ready = 0; 30962Sigor@sysoev.ru 31062Sigor@sysoev.ru switch (err) { 31162Sigor@sysoev.ru 31262Sigor@sysoev.ru case NXT_EAGAIN: 31362Sigor@sysoev.ru nxt_debug(task, "%s(%d) %E", accept_syscall, lev->socket.fd, err); 31462Sigor@sysoev.ru return; 31562Sigor@sysoev.ru 31662Sigor@sysoev.ru case ECONNABORTED: 31762Sigor@sysoev.ru nxt_log_moderate(&nxt_accept_log_moderation, NXT_LOG_WARN, 31862Sigor@sysoev.ru task->log, "%s(%d) failed %E", 31962Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 32062Sigor@sysoev.ru return; 32162Sigor@sysoev.ru 32262Sigor@sysoev.ru case EMFILE: 32362Sigor@sysoev.ru case ENFILE: 32462Sigor@sysoev.ru case ENOBUFS: 32562Sigor@sysoev.ru case ENOMEM: 32662Sigor@sysoev.ru if (nxt_conn_accept_close_idle(task, lev) != NXT_OK) { 32762Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E, " 32862Sigor@sysoev.ru "new connections are not accepted within 1s", 32962Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 33062Sigor@sysoev.ru } 33162Sigor@sysoev.ru 33262Sigor@sysoev.ru return; 33362Sigor@sysoev.ru 33462Sigor@sysoev.ru default: 33562Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E", 33662Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 33762Sigor@sysoev.ru return; 33862Sigor@sysoev.ru } 33962Sigor@sysoev.ru } 34062Sigor@sysoev.ru 34162Sigor@sysoev.ru 34262Sigor@sysoev.ru static void 34362Sigor@sysoev.ru nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data) 34462Sigor@sysoev.ru { 34562Sigor@sysoev.ru nxt_conn_t *c; 34662Sigor@sysoev.ru nxt_timer_t *timer; 34762Sigor@sysoev.ru nxt_listen_event_t *lev; 34862Sigor@sysoev.ru 34962Sigor@sysoev.ru timer = obj; 35062Sigor@sysoev.ru 35162Sigor@sysoev.ru lev = nxt_timer_data(timer, nxt_listen_event_t, timer); 35262Sigor@sysoev.ru c = lev->next; 35362Sigor@sysoev.ru 35462Sigor@sysoev.ru if (c == NULL) { 35562Sigor@sysoev.ru c = nxt_conn_accept_next(task, lev); 35662Sigor@sysoev.ru 35762Sigor@sysoev.ru if (c == NULL) { 35862Sigor@sysoev.ru return; 35962Sigor@sysoev.ru } 36062Sigor@sysoev.ru } 36162Sigor@sysoev.ru 36262Sigor@sysoev.ru nxt_fd_event_enable_accept(task->thread->engine, &lev->socket); 36362Sigor@sysoev.ru 36462Sigor@sysoev.ru lev->accept(task, lev, c); 36562Sigor@sysoev.ru } 36662Sigor@sysoev.ru 36762Sigor@sysoev.ru 36862Sigor@sysoev.ru static void 36962Sigor@sysoev.ru nxt_conn_listen_event_error(nxt_task_t *task, void *obj, void *data) 37062Sigor@sysoev.ru { 37162Sigor@sysoev.ru nxt_fd_event_t *ev; 37262Sigor@sysoev.ru 37362Sigor@sysoev.ru ev = obj; 37462Sigor@sysoev.ru 37562Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "accept(%d) event error", ev->fd); 37662Sigor@sysoev.ru } 377