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 { 85338Sigor@sysoev.ru nxt_mp_t *mp; 86338Sigor@sysoev.ru nxt_conn_t *c; 87338Sigor@sysoev.ru nxt_event_engine_t *engine; 8862Sigor@sysoev.ru 8962Sigor@sysoev.ru engine = task->thread->engine; 9062Sigor@sysoev.ru 9162Sigor@sysoev.ru if (engine->connections < engine->max_connections) { 9262Sigor@sysoev.ru 9365Sigor@sysoev.ru mp = nxt_mp_create(1024, 128, 256, 32); 9462Sigor@sysoev.ru 9562Sigor@sysoev.ru if (nxt_fast_path(mp != NULL)) { 9662Sigor@sysoev.ru c = nxt_conn_create(mp, lev->socket.task); 9765Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) { 9865Sigor@sysoev.ru goto fail; 9965Sigor@sysoev.ru } 10062Sigor@sysoev.ru 10162Sigor@sysoev.ru lev->next = c; 10262Sigor@sysoev.ru c->socket.read_work_queue = lev->socket.read_work_queue; 10362Sigor@sysoev.ru c->socket.write_ready = 1; 10462Sigor@sysoev.ru c->listen = lev; 10562Sigor@sysoev.ru 106*358Sigor@sysoev.ru c->remote = nxt_sockaddr_cache_alloc(engine, lev->listen); 107337Sigor@sysoev.ru if (nxt_fast_path(c->remote != NULL)) { 108337Sigor@sysoev.ru return c; 10965Sigor@sysoev.ru } 11062Sigor@sysoev.ru } 11165Sigor@sysoev.ru 11265Sigor@sysoev.ru fail: 11365Sigor@sysoev.ru 11465Sigor@sysoev.ru nxt_mp_destroy(mp); 11562Sigor@sysoev.ru } 11662Sigor@sysoev.ru 11762Sigor@sysoev.ru return NULL; 11862Sigor@sysoev.ru } 11962Sigor@sysoev.ru 12062Sigor@sysoev.ru 12162Sigor@sysoev.ru static void 12262Sigor@sysoev.ru nxt_conn_listen_handler(nxt_task_t *task, void *obj, void *data) 12362Sigor@sysoev.ru { 12462Sigor@sysoev.ru nxt_listen_event_t *lev; 12562Sigor@sysoev.ru 12662Sigor@sysoev.ru lev = obj; 12762Sigor@sysoev.ru lev->ready = lev->batch; 12862Sigor@sysoev.ru 12962Sigor@sysoev.ru lev->accept(task, lev, data); 13062Sigor@sysoev.ru } 13162Sigor@sysoev.ru 13262Sigor@sysoev.ru 13362Sigor@sysoev.ru void 13462Sigor@sysoev.ru nxt_conn_io_accept(nxt_task_t *task, void *obj, void *data) 13562Sigor@sysoev.ru { 136312Sigor@sysoev.ru socklen_t socklen; 13762Sigor@sysoev.ru nxt_conn_t *c; 13862Sigor@sysoev.ru nxt_socket_t s; 13962Sigor@sysoev.ru struct sockaddr *sa; 14062Sigor@sysoev.ru nxt_listen_event_t *lev; 14162Sigor@sysoev.ru 14262Sigor@sysoev.ru lev = obj; 14362Sigor@sysoev.ru c = lev->next; 14462Sigor@sysoev.ru 14562Sigor@sysoev.ru lev->ready--; 14662Sigor@sysoev.ru lev->socket.read_ready = (lev->ready != 0); 14762Sigor@sysoev.ru 148312Sigor@sysoev.ru sa = &c->remote->u.sockaddr; 149312Sigor@sysoev.ru socklen = c->remote->socklen; 150312Sigor@sysoev.ru /* 151312Sigor@sysoev.ru * The returned socklen is ignored here, because sockaddr_in and 152312Sigor@sysoev.ru * sockaddr_in6 socklens are not changed. As to unspecified sockaddr_un 153312Sigor@sysoev.ru * it is 3 byte length and already prepared, because old BSDs return zero 154312Sigor@sysoev.ru * socklen and do not update the sockaddr_un at all; Linux returns 2 byte 155312Sigor@sysoev.ru * socklen and updates only the sa_family part; other systems copy 3 bytes 156312Sigor@sysoev.ru * and truncate surplus zero part. Only bound sockaddr_un will be really 157312Sigor@sysoev.ru * truncated here. 158312Sigor@sysoev.ru */ 159312Sigor@sysoev.ru s = accept(lev->socket.fd, sa, &socklen); 16062Sigor@sysoev.ru 16162Sigor@sysoev.ru if (s == -1) { 16262Sigor@sysoev.ru nxt_conn_accept_error(task, lev, "accept", nxt_socket_errno); 16362Sigor@sysoev.ru return; 16462Sigor@sysoev.ru } 16562Sigor@sysoev.ru 16662Sigor@sysoev.ru c->socket.fd = s; 16762Sigor@sysoev.ru 16862Sigor@sysoev.ru #if (NXT_LINUX) 16962Sigor@sysoev.ru /* 17062Sigor@sysoev.ru * Linux does not inherit non-blocking mode 17162Sigor@sysoev.ru * from listen socket for accept()ed socket. 17262Sigor@sysoev.ru */ 17362Sigor@sysoev.ru if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) { 17462Sigor@sysoev.ru nxt_socket_close(task, s); 17562Sigor@sysoev.ru } 17662Sigor@sysoev.ru 17762Sigor@sysoev.ru #endif 17862Sigor@sysoev.ru 17962Sigor@sysoev.ru nxt_debug(task, "accept(%d): %d", lev->socket.fd, s); 18062Sigor@sysoev.ru 18162Sigor@sysoev.ru nxt_conn_accept(task, lev, c); 18262Sigor@sysoev.ru } 18362Sigor@sysoev.ru 18462Sigor@sysoev.ru 18562Sigor@sysoev.ru void 18662Sigor@sysoev.ru nxt_conn_accept(nxt_task_t *task, nxt_listen_event_t *lev, nxt_conn_t *c) 18762Sigor@sysoev.ru { 18862Sigor@sysoev.ru nxt_conn_t *next; 18962Sigor@sysoev.ru 19062Sigor@sysoev.ru nxt_sockaddr_text(c->remote); 19162Sigor@sysoev.ru 19262Sigor@sysoev.ru nxt_debug(task, "client: %*s", 19362Sigor@sysoev.ru c->remote->address_length, nxt_sockaddr_address(c->remote)); 19462Sigor@sysoev.ru 19562Sigor@sysoev.ru nxt_queue_insert_head(&task->thread->engine->idle_connections, &c->link); 19662Sigor@sysoev.ru 19762Sigor@sysoev.ru c->read_work_queue = lev->work_queue; 19862Sigor@sysoev.ru c->write_work_queue = lev->work_queue; 19962Sigor@sysoev.ru 20062Sigor@sysoev.ru if (lev->listen->read_after_accept) { 20162Sigor@sysoev.ru 20262Sigor@sysoev.ru //c->socket.read_ready = 1; 20362Sigor@sysoev.ru // lev->listen->handler(task, c, lev->socket.data); 20462Sigor@sysoev.ru nxt_work_queue_add(c->read_work_queue, lev->listen->handler, 205150Sigor@sysoev.ru &c->task, c, lev->socket.data); 20662Sigor@sysoev.ru 20762Sigor@sysoev.ru } else { 20862Sigor@sysoev.ru nxt_work_queue_add(c->write_work_queue, lev->listen->handler, 209150Sigor@sysoev.ru &c->task, c, lev->socket.data); 21062Sigor@sysoev.ru } 21162Sigor@sysoev.ru 21262Sigor@sysoev.ru next = nxt_conn_accept_next(task, lev); 21362Sigor@sysoev.ru 21462Sigor@sysoev.ru if (next != NULL && lev->socket.read_ready) { 21562Sigor@sysoev.ru nxt_work_queue_add(lev->socket.read_work_queue, 21662Sigor@sysoev.ru lev->accept, task, lev, next); 21762Sigor@sysoev.ru } 21862Sigor@sysoev.ru } 21962Sigor@sysoev.ru 22062Sigor@sysoev.ru 22162Sigor@sysoev.ru static nxt_conn_t * 22262Sigor@sysoev.ru nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev) 22362Sigor@sysoev.ru { 22462Sigor@sysoev.ru nxt_conn_t *c; 22562Sigor@sysoev.ru 22662Sigor@sysoev.ru lev->next = NULL; 22762Sigor@sysoev.ru 22862Sigor@sysoev.ru do { 22962Sigor@sysoev.ru c = nxt_conn_accept_alloc(task, lev); 23062Sigor@sysoev.ru 23162Sigor@sysoev.ru if (nxt_fast_path(c != NULL)) { 23262Sigor@sysoev.ru return c; 23362Sigor@sysoev.ru } 23462Sigor@sysoev.ru 23562Sigor@sysoev.ru } while (nxt_conn_accept_close_idle(task, lev) == NXT_OK); 23662Sigor@sysoev.ru 23762Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "no available connections, " 23862Sigor@sysoev.ru "new connections are not accepted within 1s"); 23962Sigor@sysoev.ru 24062Sigor@sysoev.ru return NULL; 24162Sigor@sysoev.ru } 24262Sigor@sysoev.ru 24362Sigor@sysoev.ru 24462Sigor@sysoev.ru static nxt_int_t 24562Sigor@sysoev.ru nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev) 24662Sigor@sysoev.ru { 24762Sigor@sysoev.ru nxt_conn_t *c; 24862Sigor@sysoev.ru nxt_queue_t *idle; 24962Sigor@sysoev.ru nxt_queue_link_t *link; 25062Sigor@sysoev.ru nxt_event_engine_t *engine; 25162Sigor@sysoev.ru 25262Sigor@sysoev.ru static nxt_log_moderation_t nxt_idle_close_log_moderation = { 25362Sigor@sysoev.ru NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION 25462Sigor@sysoev.ru }; 25562Sigor@sysoev.ru 25662Sigor@sysoev.ru engine = task->thread->engine; 25762Sigor@sysoev.ru 25862Sigor@sysoev.ru idle = &engine->idle_connections; 25962Sigor@sysoev.ru 26062Sigor@sysoev.ru for (link = nxt_queue_last(idle); 26162Sigor@sysoev.ru link != nxt_queue_head(idle); 26262Sigor@sysoev.ru link = nxt_queue_next(link)) 26362Sigor@sysoev.ru { 26462Sigor@sysoev.ru c = nxt_queue_link_data(link, nxt_conn_t, link); 26562Sigor@sysoev.ru 26662Sigor@sysoev.ru if (!c->socket.read_ready) { 26762Sigor@sysoev.ru nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO, 26862Sigor@sysoev.ru task->log, "no available connections, " 26962Sigor@sysoev.ru "close idle connection"); 27062Sigor@sysoev.ru nxt_queue_remove(link); 27162Sigor@sysoev.ru nxt_conn_close(engine, c); 27262Sigor@sysoev.ru 27362Sigor@sysoev.ru return NXT_OK; 27462Sigor@sysoev.ru } 27562Sigor@sysoev.ru } 27662Sigor@sysoev.ru 27762Sigor@sysoev.ru nxt_timer_add(engine, &lev->timer, 1000); 27862Sigor@sysoev.ru 27962Sigor@sysoev.ru nxt_fd_event_disable_read(engine, &lev->socket); 28062Sigor@sysoev.ru 28162Sigor@sysoev.ru return NXT_DECLINED; 28262Sigor@sysoev.ru } 28362Sigor@sysoev.ru 28462Sigor@sysoev.ru 28562Sigor@sysoev.ru void 28662Sigor@sysoev.ru nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev, 28762Sigor@sysoev.ru const char *accept_syscall, nxt_err_t err) 28862Sigor@sysoev.ru { 28962Sigor@sysoev.ru static nxt_log_moderation_t nxt_accept_log_moderation = { 29062Sigor@sysoev.ru NXT_LOG_INFO, 2, "accept() failed", NXT_LOG_MODERATION 29162Sigor@sysoev.ru }; 29262Sigor@sysoev.ru 29362Sigor@sysoev.ru lev->socket.read_ready = 0; 29462Sigor@sysoev.ru 29562Sigor@sysoev.ru switch (err) { 29662Sigor@sysoev.ru 29762Sigor@sysoev.ru case NXT_EAGAIN: 29862Sigor@sysoev.ru nxt_debug(task, "%s(%d) %E", accept_syscall, lev->socket.fd, err); 29962Sigor@sysoev.ru return; 30062Sigor@sysoev.ru 30162Sigor@sysoev.ru case ECONNABORTED: 30262Sigor@sysoev.ru nxt_log_moderate(&nxt_accept_log_moderation, NXT_LOG_WARN, 30362Sigor@sysoev.ru task->log, "%s(%d) failed %E", 30462Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 30562Sigor@sysoev.ru return; 30662Sigor@sysoev.ru 30762Sigor@sysoev.ru case EMFILE: 30862Sigor@sysoev.ru case ENFILE: 30962Sigor@sysoev.ru case ENOBUFS: 31062Sigor@sysoev.ru case ENOMEM: 31162Sigor@sysoev.ru if (nxt_conn_accept_close_idle(task, lev) != NXT_OK) { 31262Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E, " 31362Sigor@sysoev.ru "new connections are not accepted within 1s", 31462Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 31562Sigor@sysoev.ru } 31662Sigor@sysoev.ru 31762Sigor@sysoev.ru return; 31862Sigor@sysoev.ru 31962Sigor@sysoev.ru default: 32062Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "%s(%d) failed %E", 32162Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 32262Sigor@sysoev.ru return; 32362Sigor@sysoev.ru } 32462Sigor@sysoev.ru } 32562Sigor@sysoev.ru 32662Sigor@sysoev.ru 32762Sigor@sysoev.ru static void 32862Sigor@sysoev.ru nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data) 32962Sigor@sysoev.ru { 33062Sigor@sysoev.ru nxt_conn_t *c; 33162Sigor@sysoev.ru nxt_timer_t *timer; 33262Sigor@sysoev.ru nxt_listen_event_t *lev; 33362Sigor@sysoev.ru 33462Sigor@sysoev.ru timer = obj; 33562Sigor@sysoev.ru 33662Sigor@sysoev.ru lev = nxt_timer_data(timer, nxt_listen_event_t, timer); 33762Sigor@sysoev.ru c = lev->next; 33862Sigor@sysoev.ru 33962Sigor@sysoev.ru if (c == NULL) { 34062Sigor@sysoev.ru c = nxt_conn_accept_next(task, lev); 34162Sigor@sysoev.ru 34262Sigor@sysoev.ru if (c == NULL) { 34362Sigor@sysoev.ru return; 34462Sigor@sysoev.ru } 34562Sigor@sysoev.ru } 34662Sigor@sysoev.ru 34762Sigor@sysoev.ru nxt_fd_event_enable_accept(task->thread->engine, &lev->socket); 34862Sigor@sysoev.ru 34962Sigor@sysoev.ru lev->accept(task, lev, c); 35062Sigor@sysoev.ru } 35162Sigor@sysoev.ru 35262Sigor@sysoev.ru 35362Sigor@sysoev.ru static void 35462Sigor@sysoev.ru nxt_conn_listen_event_error(nxt_task_t *task, void *obj, void *data) 35562Sigor@sysoev.ru { 35662Sigor@sysoev.ru nxt_fd_event_t *ev; 35762Sigor@sysoev.ru 35862Sigor@sysoev.ru ev = obj; 35962Sigor@sysoev.ru 36062Sigor@sysoev.ru nxt_log(task, NXT_LOG_CRIT, "accept(%d) event error", ev->fd); 36162Sigor@sysoev.ru } 362