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; 48*683Sigor@sysoev.ru lev->count = 1; 4962Sigor@sysoev.ru 5062Sigor@sysoev.ru lev->socket.read_work_queue = &engine->accept_work_queue; 5162Sigor@sysoev.ru lev->socket.read_handler = nxt_conn_listen_handler; 5262Sigor@sysoev.ru lev->socket.error_handler = nxt_conn_listen_event_error; 5362Sigor@sysoev.ru lev->socket.log = &nxt_main_log; 5462Sigor@sysoev.ru 5562Sigor@sysoev.ru lev->accept = engine->event.io->accept; 5662Sigor@sysoev.ru 5762Sigor@sysoev.ru lev->listen = ls; 5862Sigor@sysoev.ru lev->work_queue = &engine->read_work_queue; 5962Sigor@sysoev.ru 6062Sigor@sysoev.ru lev->timer.work_queue = &engine->fast_work_queue; 6162Sigor@sysoev.ru lev->timer.handler = nxt_conn_listen_timer_handler; 6262Sigor@sysoev.ru lev->timer.log = &nxt_main_log; 6362Sigor@sysoev.ru 6462Sigor@sysoev.ru lev->task.thread = task->thread; 6562Sigor@sysoev.ru lev->task.log = &nxt_main_log; 6662Sigor@sysoev.ru lev->task.ident = nxt_task_next_ident(); 6762Sigor@sysoev.ru lev->socket.task = &lev->task; 6862Sigor@sysoev.ru lev->timer.task = &lev->task; 6962Sigor@sysoev.ru 7062Sigor@sysoev.ru if (nxt_conn_accept_alloc(task, lev) != NULL) { 7162Sigor@sysoev.ru nxt_fd_event_enable_accept(engine, &lev->socket); 7262Sigor@sysoev.ru 73115Sigor@sysoev.ru nxt_queue_insert_tail(&engine->listen_connections, &lev->link); 7462Sigor@sysoev.ru } 7562Sigor@sysoev.ru 7662Sigor@sysoev.ru return lev; 7762Sigor@sysoev.ru } 7862Sigor@sysoev.ru 7962Sigor@sysoev.ru return NULL; 8062Sigor@sysoev.ru } 8162Sigor@sysoev.ru 8262Sigor@sysoev.ru 8362Sigor@sysoev.ru static nxt_conn_t * 8462Sigor@sysoev.ru nxt_conn_accept_alloc(nxt_task_t *task, nxt_listen_event_t *lev) 8562Sigor@sysoev.ru { 86338Sigor@sysoev.ru nxt_mp_t *mp; 87338Sigor@sysoev.ru nxt_conn_t *c; 88338Sigor@sysoev.ru nxt_event_engine_t *engine; 8962Sigor@sysoev.ru 9062Sigor@sysoev.ru engine = task->thread->engine; 9162Sigor@sysoev.ru 9262Sigor@sysoev.ru if (engine->connections < engine->max_connections) { 9362Sigor@sysoev.ru 9465Sigor@sysoev.ru mp = nxt_mp_create(1024, 128, 256, 32); 9562Sigor@sysoev.ru 9662Sigor@sysoev.ru if (nxt_fast_path(mp != NULL)) { 9762Sigor@sysoev.ru c = nxt_conn_create(mp, lev->socket.task); 9865Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) { 9965Sigor@sysoev.ru goto fail; 10065Sigor@sysoev.ru } 10162Sigor@sysoev.ru 10262Sigor@sysoev.ru lev->next = c; 10362Sigor@sysoev.ru c->socket.read_work_queue = lev->socket.read_work_queue; 10462Sigor@sysoev.ru c->socket.write_ready = 1; 10562Sigor@sysoev.ru 106358Sigor@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", 193493Spluknet@nginx.com (size_t) c->remote->address_length, 194493Spluknet@nginx.com nxt_sockaddr_address(c->remote)); 19562Sigor@sysoev.ru 19662Sigor@sysoev.ru nxt_queue_insert_head(&task->thread->engine->idle_connections, &c->link); 19762Sigor@sysoev.ru 198*683Sigor@sysoev.ru c->listen = lev; 199*683Sigor@sysoev.ru lev->count++; 200*683Sigor@sysoev.ru c->socket.data = NULL; 201*683Sigor@sysoev.ru 20262Sigor@sysoev.ru c->read_work_queue = lev->work_queue; 20362Sigor@sysoev.ru c->write_work_queue = lev->work_queue; 20462Sigor@sysoev.ru 20562Sigor@sysoev.ru if (lev->listen->read_after_accept) { 20662Sigor@sysoev.ru 20762Sigor@sysoev.ru //c->socket.read_ready = 1; 208*683Sigor@sysoev.ru // lev->listen->handler(task, c, lev); 20962Sigor@sysoev.ru nxt_work_queue_add(c->read_work_queue, lev->listen->handler, 210*683Sigor@sysoev.ru &c->task, c, lev); 21162Sigor@sysoev.ru 21262Sigor@sysoev.ru } else { 21362Sigor@sysoev.ru nxt_work_queue_add(c->write_work_queue, lev->listen->handler, 214*683Sigor@sysoev.ru &c->task, c, lev); 21562Sigor@sysoev.ru } 21662Sigor@sysoev.ru 21762Sigor@sysoev.ru next = nxt_conn_accept_next(task, lev); 21862Sigor@sysoev.ru 21962Sigor@sysoev.ru if (next != NULL && lev->socket.read_ready) { 22062Sigor@sysoev.ru nxt_work_queue_add(lev->socket.read_work_queue, 22162Sigor@sysoev.ru lev->accept, task, lev, next); 22262Sigor@sysoev.ru } 22362Sigor@sysoev.ru } 22462Sigor@sysoev.ru 22562Sigor@sysoev.ru 22662Sigor@sysoev.ru static nxt_conn_t * 22762Sigor@sysoev.ru nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev) 22862Sigor@sysoev.ru { 22962Sigor@sysoev.ru nxt_conn_t *c; 23062Sigor@sysoev.ru 23162Sigor@sysoev.ru lev->next = NULL; 23262Sigor@sysoev.ru 23362Sigor@sysoev.ru do { 23462Sigor@sysoev.ru c = nxt_conn_accept_alloc(task, lev); 23562Sigor@sysoev.ru 23662Sigor@sysoev.ru if (nxt_fast_path(c != NULL)) { 23762Sigor@sysoev.ru return c; 23862Sigor@sysoev.ru } 23962Sigor@sysoev.ru 24062Sigor@sysoev.ru } while (nxt_conn_accept_close_idle(task, lev) == NXT_OK); 24162Sigor@sysoev.ru 242564Svbart@nginx.com nxt_alert(task, "no available connections, " 243564Svbart@nginx.com "new connections are not accepted within 1s"); 24462Sigor@sysoev.ru 24562Sigor@sysoev.ru return NULL; 24662Sigor@sysoev.ru } 24762Sigor@sysoev.ru 24862Sigor@sysoev.ru 24962Sigor@sysoev.ru static nxt_int_t 25062Sigor@sysoev.ru nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev) 25162Sigor@sysoev.ru { 25262Sigor@sysoev.ru nxt_conn_t *c; 25362Sigor@sysoev.ru nxt_queue_t *idle; 25462Sigor@sysoev.ru nxt_queue_link_t *link; 25562Sigor@sysoev.ru nxt_event_engine_t *engine; 25662Sigor@sysoev.ru 25762Sigor@sysoev.ru static nxt_log_moderation_t nxt_idle_close_log_moderation = { 25862Sigor@sysoev.ru NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION 25962Sigor@sysoev.ru }; 26062Sigor@sysoev.ru 26162Sigor@sysoev.ru engine = task->thread->engine; 26262Sigor@sysoev.ru 26362Sigor@sysoev.ru idle = &engine->idle_connections; 26462Sigor@sysoev.ru 26562Sigor@sysoev.ru for (link = nxt_queue_last(idle); 26662Sigor@sysoev.ru link != nxt_queue_head(idle); 26762Sigor@sysoev.ru link = nxt_queue_next(link)) 26862Sigor@sysoev.ru { 26962Sigor@sysoev.ru c = nxt_queue_link_data(link, nxt_conn_t, link); 27062Sigor@sysoev.ru 27162Sigor@sysoev.ru if (!c->socket.read_ready) { 27262Sigor@sysoev.ru nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO, 27362Sigor@sysoev.ru task->log, "no available connections, " 27462Sigor@sysoev.ru "close idle connection"); 27562Sigor@sysoev.ru nxt_queue_remove(link); 27662Sigor@sysoev.ru nxt_conn_close(engine, c); 27762Sigor@sysoev.ru 27862Sigor@sysoev.ru return NXT_OK; 27962Sigor@sysoev.ru } 28062Sigor@sysoev.ru } 28162Sigor@sysoev.ru 28262Sigor@sysoev.ru nxt_timer_add(engine, &lev->timer, 1000); 28362Sigor@sysoev.ru 28462Sigor@sysoev.ru nxt_fd_event_disable_read(engine, &lev->socket); 28562Sigor@sysoev.ru 28662Sigor@sysoev.ru return NXT_DECLINED; 28762Sigor@sysoev.ru } 28862Sigor@sysoev.ru 28962Sigor@sysoev.ru 29062Sigor@sysoev.ru void 29162Sigor@sysoev.ru nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev, 29262Sigor@sysoev.ru const char *accept_syscall, nxt_err_t err) 29362Sigor@sysoev.ru { 29462Sigor@sysoev.ru static nxt_log_moderation_t nxt_accept_log_moderation = { 29562Sigor@sysoev.ru NXT_LOG_INFO, 2, "accept() failed", NXT_LOG_MODERATION 29662Sigor@sysoev.ru }; 29762Sigor@sysoev.ru 29862Sigor@sysoev.ru lev->socket.read_ready = 0; 29962Sigor@sysoev.ru 30062Sigor@sysoev.ru switch (err) { 30162Sigor@sysoev.ru 30262Sigor@sysoev.ru case NXT_EAGAIN: 30362Sigor@sysoev.ru nxt_debug(task, "%s(%d) %E", accept_syscall, lev->socket.fd, err); 30462Sigor@sysoev.ru return; 30562Sigor@sysoev.ru 30662Sigor@sysoev.ru case ECONNABORTED: 30762Sigor@sysoev.ru nxt_log_moderate(&nxt_accept_log_moderation, NXT_LOG_WARN, 30862Sigor@sysoev.ru task->log, "%s(%d) failed %E", 30962Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 31062Sigor@sysoev.ru return; 31162Sigor@sysoev.ru 31262Sigor@sysoev.ru case EMFILE: 31362Sigor@sysoev.ru case ENFILE: 31462Sigor@sysoev.ru case ENOBUFS: 31562Sigor@sysoev.ru case ENOMEM: 31662Sigor@sysoev.ru if (nxt_conn_accept_close_idle(task, lev) != NXT_OK) { 317564Svbart@nginx.com nxt_alert(task, "%s(%d) failed %E, " 318564Svbart@nginx.com "new connections are not accepted within 1s", 319564Svbart@nginx.com accept_syscall, lev->socket.fd, err); 32062Sigor@sysoev.ru } 32162Sigor@sysoev.ru 32262Sigor@sysoev.ru return; 32362Sigor@sysoev.ru 32462Sigor@sysoev.ru default: 325564Svbart@nginx.com nxt_alert(task, "%s(%d) failed %E", 326564Svbart@nginx.com accept_syscall, lev->socket.fd, err); 32762Sigor@sysoev.ru return; 32862Sigor@sysoev.ru } 32962Sigor@sysoev.ru } 33062Sigor@sysoev.ru 33162Sigor@sysoev.ru 33262Sigor@sysoev.ru static void 33362Sigor@sysoev.ru nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data) 33462Sigor@sysoev.ru { 33562Sigor@sysoev.ru nxt_conn_t *c; 33662Sigor@sysoev.ru nxt_timer_t *timer; 33762Sigor@sysoev.ru nxt_listen_event_t *lev; 33862Sigor@sysoev.ru 33962Sigor@sysoev.ru timer = obj; 34062Sigor@sysoev.ru 34162Sigor@sysoev.ru lev = nxt_timer_data(timer, nxt_listen_event_t, timer); 34262Sigor@sysoev.ru c = lev->next; 34362Sigor@sysoev.ru 34462Sigor@sysoev.ru if (c == NULL) { 34562Sigor@sysoev.ru c = nxt_conn_accept_next(task, lev); 34662Sigor@sysoev.ru 34762Sigor@sysoev.ru if (c == NULL) { 34862Sigor@sysoev.ru return; 34962Sigor@sysoev.ru } 35062Sigor@sysoev.ru } 35162Sigor@sysoev.ru 35262Sigor@sysoev.ru nxt_fd_event_enable_accept(task->thread->engine, &lev->socket); 35362Sigor@sysoev.ru 35462Sigor@sysoev.ru lev->accept(task, lev, c); 35562Sigor@sysoev.ru } 35662Sigor@sysoev.ru 35762Sigor@sysoev.ru 35862Sigor@sysoev.ru static void 35962Sigor@sysoev.ru nxt_conn_listen_event_error(nxt_task_t *task, void *obj, void *data) 36062Sigor@sysoev.ru { 36162Sigor@sysoev.ru nxt_fd_event_t *ev; 36262Sigor@sysoev.ru 36362Sigor@sysoev.ru ev = obj; 36462Sigor@sysoev.ru 365564Svbart@nginx.com nxt_alert(task, "accept(%d) event error", ev->fd); 36662Sigor@sysoev.ru } 367