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 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_log(c->socket.task, NXT_LOG_CRIT, 50 "setsockopt(%d, SO_LINGER) failed %E", 51 c->socket.fd, nxt_socket_errno); 52 } 53 } 54 55 /* 56 * Event errors should be ignored here to avoid repeated nxt_conn_close() 57 * calls. nxt_conn_close_handler() or nxt_conn_close_timer_handler() 58 * will eventually close socket. 59 */ 60 c->socket.error_handler = nxt_conn_close_error_ignore; 61 62 if (c->socket.error == 0 && !c->socket.closed && !c->socket.shutdown) { 63 wq = &engine->shutdown_work_queue; 64 handler = nxt_conn_shutdown_handler; 65 66 } else { 67 wq = &engine->close_work_queue; 68 handler = nxt_conn_close_handler; 69 } 70 71 nxt_work_queue_add(wq, handler, c->socket.task, c, engine); 72 } 73 74 75 static void 76 nxt_conn_shutdown_handler(nxt_task_t *task, void *obj, void *data) 77 { 78 nxt_conn_t *c; 79 nxt_event_engine_t *engine; 80 81 c = obj; 82 engine = data; 83 84 nxt_debug(task, "conn shutdown handler fd:%d", c->socket.fd); 85 86 c->socket.shutdown = 1; 87 88 nxt_socket_shutdown(task, c->socket.fd, SHUT_RDWR); 89 90 nxt_work_queue_add(&engine->close_work_queue, nxt_conn_close_handler, 91 task, c, engine); 92 } 93 94 95 static void 96 nxt_conn_close_handler(nxt_task_t *task, void *obj, void *data) 97 { 98 nxt_uint_t events_pending, timers_pending; 99 nxt_conn_t *c; 100 nxt_event_engine_t *engine; 101 102 c = obj; 103 engine = data; 104 105 nxt_debug(task, "conn close handler fd:%d", c->socket.fd); 106 107 /* 108 * Socket should be closed only after all pending socket event operations 109 * will be processed by the kernel. This could be achieved with zero-timer 110 * handler. Pending timer operations associated with the socket are 111 * processed before going to the kernel. 112 */ 113 114 timers_pending = nxt_timer_delete(engine, &c->read_timer); 115 timers_pending += nxt_timer_delete(engine, &c->write_timer); 116 117 events_pending = nxt_fd_event_close(engine, &c->socket); 118 119 if (events_pending == 0) { 120 nxt_socket_close(task, c->socket.fd); 121 c->socket.fd = -1; 122 123 if (timers_pending == 0) { 124 nxt_work_queue_add(&engine->fast_work_queue, 125 c->write_state->ready_handler, 126 task, c, c->socket.data); 127 return; 128 } 129 } 130 131 c->write_timer.handler = nxt_conn_close_timer_handler; 132 c->write_timer.work_queue = &engine->fast_work_queue; 133 134 nxt_timer_add(engine, &c->write_timer, 0); 135 } 136 137 138 static void 139 nxt_conn_close_timer_handler(nxt_task_t *task, void *obj, void *data) 140 { 141 nxt_conn_t *c; 142 nxt_timer_t *timer; 143 144 timer = obj; 145 146 c = nxt_write_timer_conn(timer); 147 148 nxt_debug(task, "conn close timer handler fd:%d", c->socket.fd); 149 150 if (c->socket.fd != -1) { 151 nxt_socket_close(task, c->socket.fd); 152 c->socket.fd = -1; 153 } 154 155 nxt_work_queue_add(&task->thread->engine->fast_work_queue, 156 c->write_state->ready_handler, 157 task, c, c->socket.data); 158 } 159 160 161 static void 162 nxt_conn_close_error_ignore(nxt_task_t *task, void *obj, void *data) 163 { 164 nxt_debug(task, "conn close error ignore"); 165 } 166