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)) {
49564Svbart@nginx.com nxt_alert(c->socket.task, "setsockopt(%d, SO_LINGER) failed %E",
50564Svbart@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
122*2204Sz.hong@f5.com if (c->idle) {
123*2204Sz.hong@f5.com engine->closed_conns_cnt++;
124*2204Sz.hong@f5.com }
1252185Svbart@nginx.com
12653Sigor@sysoev.ru if (timers_pending == 0) {
12753Sigor@sysoev.ru nxt_work_queue_add(&engine->fast_work_queue,
12853Sigor@sysoev.ru c->write_state->ready_handler,
12953Sigor@sysoev.ru task, c, c->socket.data);
13053Sigor@sysoev.ru return;
13153Sigor@sysoev.ru }
13253Sigor@sysoev.ru }
13353Sigor@sysoev.ru
13453Sigor@sysoev.ru c->write_timer.handler = nxt_conn_close_timer_handler;
13553Sigor@sysoev.ru c->write_timer.work_queue = &engine->fast_work_queue;
13653Sigor@sysoev.ru
13753Sigor@sysoev.ru nxt_timer_add(engine, &c->write_timer, 0);
13853Sigor@sysoev.ru }
13953Sigor@sysoev.ru
14053Sigor@sysoev.ru
14153Sigor@sysoev.ru static void
nxt_conn_close_timer_handler(nxt_task_t * task,void * obj,void * data)14253Sigor@sysoev.ru nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data)
14353Sigor@sysoev.ru {
1442185Svbart@nginx.com nxt_conn_t *c;
1452185Svbart@nginx.com nxt_timer_t *timer;
1462185Svbart@nginx.com nxt_event_engine_t *engine;
14753Sigor@sysoev.ru
14853Sigor@sysoev.ru timer = obj;
14953Sigor@sysoev.ru
15062Sigor@sysoev.ru c = nxt_write_timer_conn(timer);
15153Sigor@sysoev.ru
15253Sigor@sysoev.ru nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd);
15353Sigor@sysoev.ru
1542185Svbart@nginx.com engine = task->thread->engine;
1552185Svbart@nginx.com
15653Sigor@sysoev.ru if (c->socket.fd != -1) {
15753Sigor@sysoev.ru nxt_socket_close(task, c->socket.fd);
15853Sigor@sysoev.ru c->socket.fd = -1;
1592185Svbart@nginx.com
160*2204Sz.hong@f5.com if (c->idle) {
161*2204Sz.hong@f5.com engine->closed_conns_cnt++;
162*2204Sz.hong@f5.com }
16353Sigor@sysoev.ru }
16453Sigor@sysoev.ru
1652185Svbart@nginx.com nxt_work_queue_add(&engine->fast_work_queue, c->write_state->ready_handler,
16653Sigor@sysoev.ru task, c, c->socket.data);
16753Sigor@sysoev.ru }
16853Sigor@sysoev.ru
16953Sigor@sysoev.ru
17053Sigor@sysoev.ru static void
nxt_conn_close_error_ignore(nxt_task_t * task,void * obj,void * data)17153Sigor@sysoev.ru nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data)
17253Sigor@sysoev.ru {
17353Sigor@sysoev.ru nxt_debug(task, "conn close error ignore");
17453Sigor@sysoev.ru }
175