153Sigor@sysoev.ru
253Sigor@sysoev.ru /*
353Sigor@sysoev.ru * Copyright (C) Igor Sysoev
453Sigor@sysoev.ru * Copyright (C) NGINX, Inc.
553Sigor@sysoev.ru */
653Sigor@sysoev.ru
753Sigor@sysoev.ru #include <nxt_main.h>
853Sigor@sysoev.ru
953Sigor@sysoev.ru
1053Sigor@sysoev.ru static void nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data);
1153Sigor@sysoev.ru static void nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data);
1253Sigor@sysoev.ru static void nxt_conn_close_timer_handler(nxt_task_t *task, void *obj,
1353Sigor@sysoev.ru void *data);
1453Sigor@sysoev.ru static void nxt_conn_close_error_ignore(nxt_task_t *task, void *obj,
1553Sigor@sysoev.ru void *data);
1653Sigor@sysoev.ru
1753Sigor@sysoev.ru
1853Sigor@sysoev.ru void
nxt_conn_close(nxt_event_engine_t * engine,nxt_conn_t * c)1962Sigor@sysoev.ru nxt_conn_close(nxt_event_engine_t *engine, nxt_conn_t *c)
2053Sigor@sysoev.ru {
2153Sigor@sysoev.ru int ret;
2253Sigor@sysoev.ru nxt_work_queue_t *wq;
2353Sigor@sysoev.ru nxt_work_handler_t handler;
2453Sigor@sysoev.ru
25109Sigor@sysoev.ru static const struct linger linger_off = {
26109Sigor@sysoev.ru .l_onoff = 1,
27109Sigor@sysoev.ru .l_linger = 0,
28109Sigor@sysoev.ru };
29109Sigor@sysoev.ru
3053Sigor@sysoev.ru nxt_debug(c->socket.task, "conn close fd:%d, to:%d",
3153Sigor@sysoev.ru c->socket.fd, c->socket.timedout);
3253Sigor@sysoev.ru
33419Sigor@sysoev.ru /*
34419Sigor@sysoev.ru * Disable all pending write operations because on success they
35419Sigor@sysoev.ru * will incorrectly call a ready handler set for nxt_conn_close().
36419Sigor@sysoev.ru */
37419Sigor@sysoev.ru c->write = NULL;
38419Sigor@sysoev.ru
3953Sigor@sysoev.ru if (c->socket.timedout) {
4053Sigor@sysoev.ru /*
4153Sigor@sysoev.ru * Resetting of timed out connection on close
4253Sigor@sysoev.ru * releases kernel memory associated with socket.
4353Sigor@sysoev.ru * This also causes sending TCP/IP RST to a peer.
4453Sigor@sysoev.ru */
45109Sigor@sysoev.ru ret = setsockopt(c->socket.fd, SOL_SOCKET, SO_LINGER, &linger_off,
46109Sigor@sysoev.ru sizeof(struct linger));
4753Sigor@sysoev.ru
4853Sigor@sysoev.ru if (nxt_slow_path(ret != 0)) {
49*564Svbart@nginx.com nxt_alert(c->socket.task, "setsockopt(%d, SO_LINGER) failed %E",
50*564Svbart@nginx.com c->socket.fd, nxt_socket_errno);
5153Sigor@sysoev.ru }
5253Sigor@sysoev.ru }
5353Sigor@sysoev.ru
5453Sigor@sysoev.ru /*
5553Sigor@sysoev.ru * Event errors should be ignored here to avoid repeated nxt_conn_close()
5653Sigor@sysoev.ru * calls. nxt_conn_close_handler() or nxt_conn_close_timer_handler()
5753Sigor@sysoev.ru * will eventually close socket.
5853Sigor@sysoev.ru */
5953Sigor@sysoev.ru c->socket.error_handler = nxt_conn_close_error_ignore;
6053Sigor@sysoev.ru
6153Sigor@sysoev.ru if (c->socket.error == 0 && !c->socket.closed && !c->socket.shutdown) {
6253Sigor@sysoev.ru wq = &engine->shutdown_work_queue;
6353Sigor@sysoev.ru handler = nxt_conn_shutdown_handler;
6453Sigor@sysoev.ru
65521Szelenkov@nginx.com } else {
6653Sigor@sysoev.ru wq = &engine->close_work_queue;
6753Sigor@sysoev.ru handler = nxt_conn_close_handler;
6853Sigor@sysoev.ru }
6953Sigor@sysoev.ru
7053Sigor@sysoev.ru nxt_work_queue_add(wq, handler, c->socket.task, c, engine);
7153Sigor@sysoev.ru }
7253Sigor@sysoev.ru
7353Sigor@sysoev.ru
7453Sigor@sysoev.ru static void
nxt_conn_shutdown_handler(nxt_task_t * task,void * obj,void * data)7553Sigor@sysoev.ru nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data)
7653Sigor@sysoev.ru {
7762Sigor@sysoev.ru nxt_conn_t *c;
7853Sigor@sysoev.ru nxt_event_engine_t *engine;
7953Sigor@sysoev.ru
8053Sigor@sysoev.ru c = obj;
8153Sigor@sysoev.ru engine = data;
8253Sigor@sysoev.ru
8353Sigor@sysoev.ru nxt_debug(task, "conn shutdown handler fd:%d", c->socket.fd);
8453Sigor@sysoev.ru
8553Sigor@sysoev.ru c->socket.shutdown = 1;
8653Sigor@sysoev.ru
8753Sigor@sysoev.ru nxt_socket_shutdown(task, c->socket.fd, SHUT_RDWR);
8853Sigor@sysoev.ru
8953Sigor@sysoev.ru nxt_work_queue_add(&engine->close_work_queue, nxt_conn_close_handler,
9053Sigor@sysoev.ru task, c, engine);
9153Sigor@sysoev.ru }
9253Sigor@sysoev.ru
9353Sigor@sysoev.ru
9453Sigor@sysoev.ru static void
nxt_conn_close_handler(nxt_task_t * task,void * obj,void * data)9553Sigor@sysoev.ru nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data)
9653Sigor@sysoev.ru {
9753Sigor@sysoev.ru nxt_uint_t events_pending, timers_pending;
9862Sigor@sysoev.ru nxt_conn_t *c;
9953Sigor@sysoev.ru nxt_event_engine_t *engine;
10053Sigor@sysoev.ru
10153Sigor@sysoev.ru c = obj;
10253Sigor@sysoev.ru engine = data;
10353Sigor@sysoev.ru
10453Sigor@sysoev.ru nxt_debug(task, "conn close handler fd:%d", c->socket.fd);
10553Sigor@sysoev.ru
10653Sigor@sysoev.ru /*
10753Sigor@sysoev.ru * Socket should be closed only after all pending socket event operations
10853Sigor@sysoev.ru * will be processed by the kernel. This could be achieved with zero-timer
10953Sigor@sysoev.ru * handler. Pending timer operations associated with the socket are
11053Sigor@sysoev.ru * processed before going to the kernel.
11153Sigor@sysoev.ru */
11253Sigor@sysoev.ru
11353Sigor@sysoev.ru timers_pending = nxt_timer_delete(engine, &c->read_timer);
11453Sigor@sysoev.ru timers_pending += nxt_timer_delete(engine, &c->write_timer);
11553Sigor@sysoev.ru
11653Sigor@sysoev.ru events_pending = nxt_fd_event_close(engine, &c->socket);
11753Sigor@sysoev.ru
11853Sigor@sysoev.ru if (events_pending == 0) {
11953Sigor@sysoev.ru nxt_socket_close(task, c->socket.fd);
12053Sigor@sysoev.ru c->socket.fd = -1;
12153Sigor@sysoev.ru
12253Sigor@sysoev.ru if (timers_pending == 0) {
12353Sigor@sysoev.ru nxt_work_queue_add(&engine->fast_work_queue,
12453Sigor@sysoev.ru c->write_state->ready_handler,
12553Sigor@sysoev.ru task, c, c->socket.data);
12653Sigor@sysoev.ru return;
12753Sigor@sysoev.ru }
12853Sigor@sysoev.ru }
12953Sigor@sysoev.ru
13053Sigor@sysoev.ru c->write_timer.handler = nxt_conn_close_timer_handler;
13153Sigor@sysoev.ru c->write_timer.work_queue = &engine->fast_work_queue;
13253Sigor@sysoev.ru
13353Sigor@sysoev.ru nxt_timer_add(engine, &c->write_timer, 0);
13453Sigor@sysoev.ru }
13553Sigor@sysoev.ru
13653Sigor@sysoev.ru
13753Sigor@sysoev.ru static void
nxt_conn_close_timer_handler(nxt_task_t * task,void * obj,void * data)13853Sigor@sysoev.ru nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data)
13953Sigor@sysoev.ru {
14062Sigor@sysoev.ru nxt_conn_t *c;
14162Sigor@sysoev.ru nxt_timer_t *timer;
14253Sigor@sysoev.ru
14353Sigor@sysoev.ru timer = obj;
14453Sigor@sysoev.ru
14562Sigor@sysoev.ru c = nxt_write_timer_conn(timer);
14653Sigor@sysoev.ru
14753Sigor@sysoev.ru nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd);
14853Sigor@sysoev.ru
14953Sigor@sysoev.ru if (c->socket.fd != -1) {
15053Sigor@sysoev.ru nxt_socket_close(task, c->socket.fd);
15153Sigor@sysoev.ru c->socket.fd = -1;
15253Sigor@sysoev.ru }
15353Sigor@sysoev.ru
15453Sigor@sysoev.ru nxt_work_queue_add(&task->thread->engine->fast_work_queue,
15553Sigor@sysoev.ru c->write_state->ready_handler,
15653Sigor@sysoev.ru task, c, c->socket.data);
15753Sigor@sysoev.ru }
15853Sigor@sysoev.ru
15953Sigor@sysoev.ru
16053Sigor@sysoev.ru static void
nxt_conn_close_error_ignore(nxt_task_t * task,void * obj,void * data)16153Sigor@sysoev.ru nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data)
16253Sigor@sysoev.ru {
16353Sigor@sysoev.ru nxt_debug(task, "conn close error ignore");
16453Sigor@sysoev.ru }
165