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