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); 271457Sigor@sysoev.ru static void nxt_conn_accept_close_idle(nxt_task_t *task, 2862Sigor@sysoev.ru nxt_listen_event_t *lev); 291457Sigor@sysoev.ru static void nxt_conn_accept_close_idle_handler(nxt_task_t *task, void *obj, 301457Sigor@sysoev.ru void *data); 3162Sigor@sysoev.ru static void nxt_conn_listen_event_error(nxt_task_t *task, void *obj, 3262Sigor@sysoev.ru void *data); 3362Sigor@sysoev.ru static void nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, 3462Sigor@sysoev.ru void *data); 3562Sigor@sysoev.ru 3662Sigor@sysoev.ru 3762Sigor@sysoev.ru nxt_listen_event_t * 3862Sigor@sysoev.ru nxt_listen_event(nxt_task_t *task, nxt_listen_socket_t *ls) 3962Sigor@sysoev.ru { 4062Sigor@sysoev.ru nxt_listen_event_t *lev; 4162Sigor@sysoev.ru nxt_event_engine_t *engine; 4262Sigor@sysoev.ru 4362Sigor@sysoev.ru lev = nxt_zalloc(sizeof(nxt_listen_event_t)); 4462Sigor@sysoev.ru 4562Sigor@sysoev.ru if (nxt_fast_path(lev != NULL)) { 4662Sigor@sysoev.ru lev->socket.fd = ls->socket; 4762Sigor@sysoev.ru 4862Sigor@sysoev.ru engine = task->thread->engine; 4962Sigor@sysoev.ru lev->batch = engine->batch; 50683Sigor@sysoev.ru lev->count = 1; 5162Sigor@sysoev.ru 5262Sigor@sysoev.ru lev->socket.read_work_queue = &engine->accept_work_queue; 5362Sigor@sysoev.ru lev->socket.read_handler = nxt_conn_listen_handler; 5462Sigor@sysoev.ru lev->socket.error_handler = nxt_conn_listen_event_error; 5562Sigor@sysoev.ru lev->socket.log = &nxt_main_log; 5662Sigor@sysoev.ru 5762Sigor@sysoev.ru lev->accept = engine->event.io->accept; 5862Sigor@sysoev.ru 5962Sigor@sysoev.ru lev->listen = ls; 6062Sigor@sysoev.ru lev->work_queue = &engine->read_work_queue; 6162Sigor@sysoev.ru 6262Sigor@sysoev.ru lev->timer.work_queue = &engine->fast_work_queue; 6362Sigor@sysoev.ru lev->timer.handler = nxt_conn_listen_timer_handler; 6462Sigor@sysoev.ru lev->timer.log = &nxt_main_log; 6562Sigor@sysoev.ru 6662Sigor@sysoev.ru lev->task.thread = task->thread; 6762Sigor@sysoev.ru lev->task.log = &nxt_main_log; 6862Sigor@sysoev.ru lev->task.ident = nxt_task_next_ident(); 6962Sigor@sysoev.ru lev->socket.task = &lev->task; 7062Sigor@sysoev.ru lev->timer.task = &lev->task; 7162Sigor@sysoev.ru 7262Sigor@sysoev.ru if (nxt_conn_accept_alloc(task, lev) != NULL) { 7362Sigor@sysoev.ru nxt_fd_event_enable_accept(engine, &lev->socket); 7462Sigor@sysoev.ru 75115Sigor@sysoev.ru nxt_queue_insert_tail(&engine->listen_connections, &lev->link); 7662Sigor@sysoev.ru } 7762Sigor@sysoev.ru 7862Sigor@sysoev.ru return lev; 7962Sigor@sysoev.ru } 8062Sigor@sysoev.ru 8162Sigor@sysoev.ru return NULL; 8262Sigor@sysoev.ru } 8362Sigor@sysoev.ru 8462Sigor@sysoev.ru 8562Sigor@sysoev.ru static nxt_conn_t * 8662Sigor@sysoev.ru nxt_conn_accept_alloc(nxt_task_t *task, nxt_listen_event_t *lev) 8762Sigor@sysoev.ru { 88338Sigor@sysoev.ru nxt_mp_t *mp; 89338Sigor@sysoev.ru nxt_conn_t *c; 90338Sigor@sysoev.ru nxt_event_engine_t *engine; 9162Sigor@sysoev.ru 9262Sigor@sysoev.ru engine = task->thread->engine; 9362Sigor@sysoev.ru 9462Sigor@sysoev.ru if (engine->connections < engine->max_connections) { 9562Sigor@sysoev.ru 9665Sigor@sysoev.ru mp = nxt_mp_create(1024, 128, 256, 32); 9762Sigor@sysoev.ru 9862Sigor@sysoev.ru if (nxt_fast_path(mp != NULL)) { 9962Sigor@sysoev.ru c = nxt_conn_create(mp, lev->socket.task); 10065Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) { 10165Sigor@sysoev.ru goto fail; 10265Sigor@sysoev.ru } 10362Sigor@sysoev.ru 10462Sigor@sysoev.ru c->socket.read_work_queue = lev->socket.read_work_queue; 10562Sigor@sysoev.ru c->socket.write_ready = 1; 10662Sigor@sysoev.ru 107358Sigor@sysoev.ru c->remote = nxt_sockaddr_cache_alloc(engine, lev->listen); 108337Sigor@sysoev.ru if (nxt_fast_path(c->remote != NULL)) { 109*1458Sigor@sysoev.ru lev->next = c; 110337Sigor@sysoev.ru return c; 11165Sigor@sysoev.ru } 11262Sigor@sysoev.ru } 11365Sigor@sysoev.ru 11465Sigor@sysoev.ru fail: 11565Sigor@sysoev.ru 11665Sigor@sysoev.ru nxt_mp_destroy(mp); 11762Sigor@sysoev.ru } 11862Sigor@sysoev.ru 11962Sigor@sysoev.ru return NULL; 12062Sigor@sysoev.ru } 12162Sigor@sysoev.ru 12262Sigor@sysoev.ru 12362Sigor@sysoev.ru static void 12462Sigor@sysoev.ru nxt_conn_listen_handler(nxt_task_t *task, void *obj, void *data) 12562Sigor@sysoev.ru { 12662Sigor@sysoev.ru nxt_listen_event_t *lev; 12762Sigor@sysoev.ru 12862Sigor@sysoev.ru lev = obj; 12962Sigor@sysoev.ru lev->ready = lev->batch; 13062Sigor@sysoev.ru 13162Sigor@sysoev.ru lev->accept(task, lev, data); 13262Sigor@sysoev.ru } 13362Sigor@sysoev.ru 13462Sigor@sysoev.ru 13562Sigor@sysoev.ru void 13662Sigor@sysoev.ru nxt_conn_io_accept(nxt_task_t *task, void *obj, void *data) 13762Sigor@sysoev.ru { 138312Sigor@sysoev.ru socklen_t socklen; 13962Sigor@sysoev.ru nxt_conn_t *c; 14062Sigor@sysoev.ru nxt_socket_t s; 14162Sigor@sysoev.ru struct sockaddr *sa; 14262Sigor@sysoev.ru nxt_listen_event_t *lev; 14362Sigor@sysoev.ru 14462Sigor@sysoev.ru lev = obj; 14562Sigor@sysoev.ru c = lev->next; 14662Sigor@sysoev.ru 14762Sigor@sysoev.ru lev->ready--; 14862Sigor@sysoev.ru lev->socket.read_ready = (lev->ready != 0); 14962Sigor@sysoev.ru 150312Sigor@sysoev.ru sa = &c->remote->u.sockaddr; 151312Sigor@sysoev.ru socklen = c->remote->socklen; 152312Sigor@sysoev.ru /* 153312Sigor@sysoev.ru * The returned socklen is ignored here, because sockaddr_in and 154312Sigor@sysoev.ru * sockaddr_in6 socklens are not changed. As to unspecified sockaddr_un 155312Sigor@sysoev.ru * it is 3 byte length and already prepared, because old BSDs return zero 156312Sigor@sysoev.ru * socklen and do not update the sockaddr_un at all; Linux returns 2 byte 157312Sigor@sysoev.ru * socklen and updates only the sa_family part; other systems copy 3 bytes 158312Sigor@sysoev.ru * and truncate surplus zero part. Only bound sockaddr_un will be really 159312Sigor@sysoev.ru * truncated here. 160312Sigor@sysoev.ru */ 161312Sigor@sysoev.ru s = accept(lev->socket.fd, sa, &socklen); 16262Sigor@sysoev.ru 16362Sigor@sysoev.ru if (s == -1) { 16462Sigor@sysoev.ru nxt_conn_accept_error(task, lev, "accept", nxt_socket_errno); 16562Sigor@sysoev.ru return; 16662Sigor@sysoev.ru } 16762Sigor@sysoev.ru 16862Sigor@sysoev.ru c->socket.fd = s; 16962Sigor@sysoev.ru 17062Sigor@sysoev.ru #if (NXT_LINUX) 17162Sigor@sysoev.ru /* 17262Sigor@sysoev.ru * Linux does not inherit non-blocking mode 17362Sigor@sysoev.ru * from listen socket for accept()ed socket. 17462Sigor@sysoev.ru */ 17562Sigor@sysoev.ru if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) { 17662Sigor@sysoev.ru nxt_socket_close(task, s); 17762Sigor@sysoev.ru } 17862Sigor@sysoev.ru 17962Sigor@sysoev.ru #endif 18062Sigor@sysoev.ru 18162Sigor@sysoev.ru nxt_debug(task, "accept(%d): %d", lev->socket.fd, s); 18262Sigor@sysoev.ru 18362Sigor@sysoev.ru nxt_conn_accept(task, lev, c); 18462Sigor@sysoev.ru } 18562Sigor@sysoev.ru 18662Sigor@sysoev.ru 18762Sigor@sysoev.ru void 18862Sigor@sysoev.ru nxt_conn_accept(nxt_task_t *task, nxt_listen_event_t *lev, nxt_conn_t *c) 18962Sigor@sysoev.ru { 19062Sigor@sysoev.ru nxt_conn_t *next; 19162Sigor@sysoev.ru 19262Sigor@sysoev.ru nxt_sockaddr_text(c->remote); 19362Sigor@sysoev.ru 19462Sigor@sysoev.ru nxt_debug(task, "client: %*s", 195493Spluknet@nginx.com (size_t) c->remote->address_length, 196493Spluknet@nginx.com nxt_sockaddr_address(c->remote)); 19762Sigor@sysoev.ru 19862Sigor@sysoev.ru nxt_queue_insert_head(&task->thread->engine->idle_connections, &c->link); 19962Sigor@sysoev.ru 200683Sigor@sysoev.ru c->listen = lev; 201683Sigor@sysoev.ru lev->count++; 202*1458Sigor@sysoev.ru lev->next = NULL; 203683Sigor@sysoev.ru c->socket.data = NULL; 204683Sigor@sysoev.ru 20562Sigor@sysoev.ru c->read_work_queue = lev->work_queue; 20662Sigor@sysoev.ru c->write_work_queue = lev->work_queue; 20762Sigor@sysoev.ru 20862Sigor@sysoev.ru if (lev->listen->read_after_accept) { 20962Sigor@sysoev.ru 21062Sigor@sysoev.ru //c->socket.read_ready = 1; 211683Sigor@sysoev.ru // lev->listen->handler(task, c, lev); 21262Sigor@sysoev.ru nxt_work_queue_add(c->read_work_queue, lev->listen->handler, 213683Sigor@sysoev.ru &c->task, c, lev); 21462Sigor@sysoev.ru 21562Sigor@sysoev.ru } else { 21662Sigor@sysoev.ru nxt_work_queue_add(c->write_work_queue, lev->listen->handler, 217683Sigor@sysoev.ru &c->task, c, lev); 21862Sigor@sysoev.ru } 21962Sigor@sysoev.ru 22062Sigor@sysoev.ru next = nxt_conn_accept_next(task, lev); 22162Sigor@sysoev.ru 22262Sigor@sysoev.ru if (next != NULL && lev->socket.read_ready) { 22362Sigor@sysoev.ru nxt_work_queue_add(lev->socket.read_work_queue, 22462Sigor@sysoev.ru lev->accept, task, lev, next); 22562Sigor@sysoev.ru } 22662Sigor@sysoev.ru } 22762Sigor@sysoev.ru 22862Sigor@sysoev.ru 22962Sigor@sysoev.ru static nxt_conn_t * 23062Sigor@sysoev.ru nxt_conn_accept_next(nxt_task_t *task, nxt_listen_event_t *lev) 23162Sigor@sysoev.ru { 23262Sigor@sysoev.ru nxt_conn_t *c; 23362Sigor@sysoev.ru 234*1458Sigor@sysoev.ru c = lev->next; 23562Sigor@sysoev.ru 236*1458Sigor@sysoev.ru if (c == NULL) { 237*1458Sigor@sysoev.ru c = nxt_conn_accept_alloc(task, lev); 23862Sigor@sysoev.ru 239*1458Sigor@sysoev.ru if (nxt_slow_path(c == NULL)) { 240*1458Sigor@sysoev.ru nxt_conn_accept_close_idle(task, lev); 241*1458Sigor@sysoev.ru } 2421457Sigor@sysoev.ru } 24362Sigor@sysoev.ru 2441457Sigor@sysoev.ru return c; 24562Sigor@sysoev.ru } 24662Sigor@sysoev.ru 24762Sigor@sysoev.ru 2481457Sigor@sysoev.ru static void 24962Sigor@sysoev.ru nxt_conn_accept_close_idle(nxt_task_t *task, nxt_listen_event_t *lev) 25062Sigor@sysoev.ru { 2511457Sigor@sysoev.ru nxt_event_engine_t *engine; 2521457Sigor@sysoev.ru 2531457Sigor@sysoev.ru engine = task->thread->engine; 2541457Sigor@sysoev.ru 2551457Sigor@sysoev.ru nxt_work_queue_add(&engine->close_work_queue, 2561457Sigor@sysoev.ru nxt_conn_accept_close_idle_handler, task, NULL, NULL); 2571457Sigor@sysoev.ru 2581457Sigor@sysoev.ru nxt_timer_add(engine, &lev->timer, 100); 2591457Sigor@sysoev.ru 2601457Sigor@sysoev.ru nxt_fd_event_disable_read(engine, &lev->socket); 2611457Sigor@sysoev.ru 2621457Sigor@sysoev.ru nxt_alert(task, "new connections are not accepted within 100ms"); 2631457Sigor@sysoev.ru } 2641457Sigor@sysoev.ru 2651457Sigor@sysoev.ru 2661457Sigor@sysoev.ru static void 2671457Sigor@sysoev.ru nxt_conn_accept_close_idle_handler(nxt_task_t *task, void *obj, void *data) 2681457Sigor@sysoev.ru { 2691457Sigor@sysoev.ru nxt_uint_t times; 27062Sigor@sysoev.ru nxt_conn_t *c; 27162Sigor@sysoev.ru nxt_queue_t *idle; 2721457Sigor@sysoev.ru nxt_queue_link_t *link, *next; 27362Sigor@sysoev.ru nxt_event_engine_t *engine; 27462Sigor@sysoev.ru 27562Sigor@sysoev.ru static nxt_log_moderation_t nxt_idle_close_log_moderation = { 27662Sigor@sysoev.ru NXT_LOG_INFO, 2, "idle connections closed", NXT_LOG_MODERATION 27762Sigor@sysoev.ru }; 27862Sigor@sysoev.ru 2791457Sigor@sysoev.ru times = 10; 28062Sigor@sysoev.ru engine = task->thread->engine; 28162Sigor@sysoev.ru idle = &engine->idle_connections; 28262Sigor@sysoev.ru 28362Sigor@sysoev.ru for (link = nxt_queue_last(idle); 28462Sigor@sysoev.ru link != nxt_queue_head(idle); 2851457Sigor@sysoev.ru link = next) 28662Sigor@sysoev.ru { 2871457Sigor@sysoev.ru next = nxt_queue_next(link); 2881457Sigor@sysoev.ru 28962Sigor@sysoev.ru c = nxt_queue_link_data(link, nxt_conn_t, link); 29062Sigor@sysoev.ru 2911457Sigor@sysoev.ru nxt_debug(c->socket.task, "idle connection: %d rdy:%d", 2921457Sigor@sysoev.ru c->socket.fd, c->socket.read_ready); 2931457Sigor@sysoev.ru 29462Sigor@sysoev.ru if (!c->socket.read_ready) { 29562Sigor@sysoev.ru nxt_log_moderate(&nxt_idle_close_log_moderation, NXT_LOG_INFO, 29662Sigor@sysoev.ru task->log, "no available connections, " 29762Sigor@sysoev.ru "close idle connection"); 2981457Sigor@sysoev.ru 2991457Sigor@sysoev.ru c->read_state->close_handler(c->socket.task, c, c->socket.data); 30062Sigor@sysoev.ru 3011457Sigor@sysoev.ru times--; 3021457Sigor@sysoev.ru 3031457Sigor@sysoev.ru if (times == 0) { 3041457Sigor@sysoev.ru break; 3051457Sigor@sysoev.ru } 30662Sigor@sysoev.ru } 30762Sigor@sysoev.ru } 30862Sigor@sysoev.ru } 30962Sigor@sysoev.ru 31062Sigor@sysoev.ru 31162Sigor@sysoev.ru void 31262Sigor@sysoev.ru nxt_conn_accept_error(nxt_task_t *task, nxt_listen_event_t *lev, 31362Sigor@sysoev.ru const char *accept_syscall, nxt_err_t err) 31462Sigor@sysoev.ru { 31562Sigor@sysoev.ru static nxt_log_moderation_t nxt_accept_log_moderation = { 31662Sigor@sysoev.ru NXT_LOG_INFO, 2, "accept() failed", NXT_LOG_MODERATION 31762Sigor@sysoev.ru }; 31862Sigor@sysoev.ru 31962Sigor@sysoev.ru lev->socket.read_ready = 0; 32062Sigor@sysoev.ru 32162Sigor@sysoev.ru switch (err) { 32262Sigor@sysoev.ru 32362Sigor@sysoev.ru case NXT_EAGAIN: 32462Sigor@sysoev.ru nxt_debug(task, "%s(%d) %E", accept_syscall, lev->socket.fd, err); 32562Sigor@sysoev.ru return; 32662Sigor@sysoev.ru 32762Sigor@sysoev.ru case ECONNABORTED: 32862Sigor@sysoev.ru nxt_log_moderate(&nxt_accept_log_moderation, NXT_LOG_WARN, 32962Sigor@sysoev.ru task->log, "%s(%d) failed %E", 33062Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 33162Sigor@sysoev.ru return; 33262Sigor@sysoev.ru 33362Sigor@sysoev.ru case EMFILE: 33462Sigor@sysoev.ru case ENFILE: 33562Sigor@sysoev.ru case ENOBUFS: 33662Sigor@sysoev.ru case ENOMEM: 3371457Sigor@sysoev.ru nxt_alert(task, "%s(%d) failed %E", 3381457Sigor@sysoev.ru accept_syscall, lev->socket.fd, err); 33962Sigor@sysoev.ru 3401457Sigor@sysoev.ru nxt_conn_accept_close_idle(task, lev); 34162Sigor@sysoev.ru return; 34262Sigor@sysoev.ru 34362Sigor@sysoev.ru default: 344564Svbart@nginx.com nxt_alert(task, "%s(%d) failed %E", 345564Svbart@nginx.com accept_syscall, lev->socket.fd, err); 34662Sigor@sysoev.ru return; 34762Sigor@sysoev.ru } 34862Sigor@sysoev.ru } 34962Sigor@sysoev.ru 35062Sigor@sysoev.ru 35162Sigor@sysoev.ru static void 35262Sigor@sysoev.ru nxt_conn_listen_timer_handler(nxt_task_t *task, void *obj, void *data) 35362Sigor@sysoev.ru { 35462Sigor@sysoev.ru nxt_conn_t *c; 35562Sigor@sysoev.ru nxt_timer_t *timer; 35662Sigor@sysoev.ru nxt_listen_event_t *lev; 35762Sigor@sysoev.ru 35862Sigor@sysoev.ru timer = obj; 35962Sigor@sysoev.ru 36062Sigor@sysoev.ru lev = nxt_timer_data(timer, nxt_listen_event_t, timer); 36162Sigor@sysoev.ru 362*1458Sigor@sysoev.ru c = nxt_conn_accept_next(task, lev); 36362Sigor@sysoev.ru if (c == NULL) { 364*1458Sigor@sysoev.ru return; 36562Sigor@sysoev.ru } 36662Sigor@sysoev.ru 36762Sigor@sysoev.ru nxt_fd_event_enable_accept(task->thread->engine, &lev->socket); 36862Sigor@sysoev.ru 36962Sigor@sysoev.ru lev->accept(task, lev, c); 37062Sigor@sysoev.ru } 37162Sigor@sysoev.ru 37262Sigor@sysoev.ru 37362Sigor@sysoev.ru static void 37462Sigor@sysoev.ru nxt_conn_listen_event_error(nxt_task_t *task, void *obj, void *data) 37562Sigor@sysoev.ru { 37662Sigor@sysoev.ru nxt_fd_event_t *ev; 37762Sigor@sysoev.ru 37862Sigor@sysoev.ru ev = obj; 37962Sigor@sysoev.ru 380564Svbart@nginx.com nxt_alert(task, "accept(%d) event error", ev->fd); 38162Sigor@sysoev.ru } 382