1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_main.h>
8
9
10 static void nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data);
11 static void nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data);
12 static void nxt_conn_close_timer_handler(nxt_task_t *task, void *obj,
13 void *data);
14 static void nxt_conn_close_error_ignore(nxt_task_t *task, void *obj,
15 void *data);
16
17
18 void
nxt_conn_close(nxt_event_engine_t * engine,nxt_conn_t * c)19 nxt_conn_close(nxt_event_engine_t *engine, nxt_conn_t *c)
20 {
21 int ret;
22 nxt_work_queue_t *wq;
23 nxt_work_handler_t handler;
24
25 static const struct linger linger_off = {
26 .l_onoff = 1,
27 .l_linger = 0,
28 };
29
30 nxt_debug(c->socket.task, "conn close fd:%d, to:%d",
31 c->socket.fd, c->socket.timedout);
32
33 /*
34 * Disable all pending write operations because on success they
35 * will incorrectly call a ready handler set for nxt_conn_close().
36 */
37 c->write = NULL;
38
39 if (c->socket.timedout) {
40 /*
41 * Resetting of timed out connection on close
42 * releases kernel memory associated with socket.
43 * This also causes sending TCP/IP RST to a peer.
44 */
45 ret = setsockopt(c->socket.fd, SOL_SOCKET, SO_LINGER, &linger_off,
46 sizeof(struct linger));
47
48 if (nxt_slow_path(ret != 0)) {
49 nxt_alert(c->socket.task, "setsockopt(%d, SO_LINGER) failed %E",
50 c->socket.fd, nxt_socket_errno);
51 }
52 }
53
54 /*
55 * Event errors should be ignored here to avoid repeated nxt_conn_close()
56 * calls. nxt_conn_close_handler() or nxt_conn_close_timer_handler()
57 * will eventually close socket.
58 */
59 c->socket.error_handler = nxt_conn_close_error_ignore;
60
61 if (c->socket.error == 0 && !c->socket.closed && !c->socket.shutdown) {
62 wq = &engine->shutdown_work_queue;
63 handler = nxt_conn_shutdown_handler;
64
65 } else {
66 wq = &engine->close_work_queue;
67 handler = nxt_conn_close_handler;
68 }
69
70 nxt_work_queue_add(wq, handler, c->socket.task, c, engine);
71 }
72
73
74 static void
nxt_conn_shutdown_handler(nxt_task_t * task,void * obj,void * data)75 nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data)
76 {
77 nxt_conn_t *c;
78 nxt_event_engine_t *engine;
79
80 c = obj;
81 engine = data;
82
83 nxt_debug(task, "conn shutdown handler fd:%d", c->socket.fd);
84
85 c->socket.shutdown = 1;
86
87 nxt_socket_shutdown(task, c->socket.fd, SHUT_RDWR);
88
89 nxt_work_queue_add(&engine->close_work_queue, nxt_conn_close_handler,
90 task, c, engine);
91 }
92
93
94 static void
nxt_conn_close_handler(nxt_task_t * task,void * obj,void * data)95 nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data)
96 {
97 nxt_uint_t events_pending, timers_pending;
98 nxt_conn_t *c;
99 nxt_event_engine_t *engine;
100
101 c = obj;
102 engine = data;
103
104 nxt_debug(task, "conn close handler fd:%d", c->socket.fd);
105
106 /*
107 * Socket should be closed only after all pending socket event operations
108 * will be processed by the kernel. This could be achieved with zero-timer
109 * handler. Pending timer operations associated with the socket are
110 * processed before going to the kernel.
111 */
112
113 timers_pending = nxt_timer_delete(engine, &c->read_timer);
114 timers_pending += nxt_timer_delete(engine, &c->write_timer);
115
116 events_pending = nxt_fd_event_close(engine, &c->socket);
117
118 if (events_pending == 0) {
119 nxt_socket_close(task, c->socket.fd);
120 c->socket.fd = -1;
121
122 if (c->idle) {
123 engine->closed_conns_cnt++;
124 }
125
126 if (timers_pending == 0) {
127 nxt_work_queue_add(&engine->fast_work_queue,
128 c->write_state->ready_handler,
129 task, c, c->socket.data);
130 return;
131 }
132 }
133
134 c->write_timer.handler = nxt_conn_close_timer_handler;
135 c->write_timer.work_queue = &engine->fast_work_queue;
136
137 nxt_timer_add(engine, &c->write_timer, 0);
138 }
139
140
141 static void
nxt_conn_close_timer_handler(nxt_task_t * task,void * obj,void * data)142 nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data)
143 {
144 nxt_conn_t *c;
145 nxt_timer_t *timer;
146 nxt_event_engine_t *engine;
147
148 timer = obj;
149
150 c = nxt_write_timer_conn(timer);
151
152 nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd);
153
154 engine = task->thread->engine;
155
156 if (c->socket.fd != -1) {
157 nxt_socket_close(task, c->socket.fd);
158 c->socket.fd = -1;
159
160 if (c->idle) {
161 engine->closed_conns_cnt++;
162 }
163 }
164
165 nxt_work_queue_add(&engine->fast_work_queue, c->write_state->ready_handler,
166 task, c, c->socket.data);
167 }
168
169
170 static void
nxt_conn_close_error_ignore(nxt_task_t * task,void * obj,void * data)171 nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data)
172 {
173 nxt_debug(task, "conn close error ignore");
174 }
175