2 * libwebsockets - small server side websockets and web server implementation
4 * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 #include "private-lib-core.h"
26 __lws_free_wsi(struct lws *wsi)
32 * Protocol user data may be allocated either internally by lws
33 * or by specified the user. We should only free what we allocated.
35 if (wsi->protocol && wsi->protocol->per_session_data_size &&
36 wsi->user_space && !wsi->user_space_externally_allocated)
37 lws_free(wsi->user_space);
39 lws_buflist_destroy_all_segments(&wsi->buflist);
40 lws_buflist_destroy_all_segments(&wsi->buflist_out);
41 lws_free_set_NULL(wsi->udp);
43 if (wsi->vhost && wsi->vhost->lserv_wsi == wsi)
44 wsi->vhost->lserv_wsi = NULL;
45 #if !defined(LWS_NO_CLIENT)
47 lws_dll2_remove(&wsi->dll_cli_active_conns);
49 wsi->context->count_wsi_allocated--;
51 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
52 __lws_header_table_detach(wsi, 0);
54 __lws_same_vh_protocol_remove(wsi);
55 #if !defined(LWS_NO_CLIENT)
56 lws_client_stash_destroy(wsi);
57 lws_free_set_NULL(wsi->cli_hostname_copy);
60 if (wsi->role_ops->destroy_role)
61 wsi->role_ops->destroy_role(wsi);
63 #if defined(LWS_WITH_PEER_LIMITS)
64 lws_peer_track_wsi_close(wsi->context, wsi->peer);
68 /* since we will destroy the wsi, make absolutely sure now */
70 #if defined(LWS_WITH_OPENSSL)
71 __lws_ssl_remove_wsi_from_buffered_list(wsi);
73 __lws_wsi_remove_from_sul(wsi);
75 if (wsi->context->event_loop_ops->destroy_wsi)
76 wsi->context->event_loop_ops->destroy_wsi(wsi);
78 lws_vhost_unbind_wsi(wsi);
80 lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
81 wsi->context->count_wsi_allocated);
88 lws_remove_child_from_any_parent(struct lws *wsi)
96 /* detach ourselves from parent's child list */
97 pwsi = &wsi->parent->child_list;
100 lwsl_info("%s: detach %p from parent %p\n", __func__,
103 if (wsi->parent->protocol)
104 wsi->parent->protocol->callback(wsi,
105 LWS_CALLBACK_CHILD_CLOSING,
106 wsi->parent->user_space, wsi, 0);
108 *pwsi = wsi->sibling_list;
112 pwsi = &(*pwsi)->sibling_list;
115 lwsl_err("%s: failed to detach from parent\n", __func__);
120 #if !defined(LWS_NO_CLIENT)
122 lws_close_trans_q_leader(struct lws_dll2 *d, void *user)
124 struct lws *w = lws_container_of(d, struct lws, dll2_cli_txn_queue);
126 __lws_close_free_wsi(w, -1, "trans q leader closing");
132 lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len)
134 if (wsi->already_did_cce)
137 wsi->already_did_cce = 1;
138 lws_stats_bump(&wsi->context->pt[(int)wsi->tsi],
139 LWSSTATS_C_CONNS_CLIENT_FAILED, 1);
144 wsi->protocol->callback(wsi,
145 LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
146 wsi->user_space, arg, len);
151 __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
154 struct lws_context_per_thread *pt;
155 struct lws *wsi1, *wsi2;
156 struct lws_context *context;
157 #if !defined(LWS_NO_CLIENT)
158 long rl = (long)(int)reason;
162 lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller);
169 context = wsi->context;
170 pt = &context->pt[(int)wsi->tsi];
171 lws_stats_bump(pt, LWSSTATS_C_API_CLOSE, 1);
173 #if !defined(LWS_NO_CLIENT)
175 lws_free_set_NULL(wsi->cli_hostname_copy);
178 * if we have wsi in our transaction queue, if we are closing we
179 * must go through and close all those first
183 /* we are no longer an active client connection that can piggyback */
184 lws_dll2_remove(&wsi->dll_cli_active_conns);
187 lws_vhost_lock(wsi->vhost);
189 lws_dll2_foreach_safe(&wsi->dll2_cli_txn_queue_owner, NULL,
190 lws_close_trans_q_leader);
193 * !!! If we are closing, but we have pending pipelined
194 * transaction results we already sent headers for, that's going
195 * to destroy sync for HTTP/1 and leave H2 stream with no live
198 * However this is normal if we are being closed because the
199 * transaction queue leader is closing.
201 lws_dll2_remove(&wsi->dll2_cli_txn_queue);
203 lws_vhost_unlock(wsi->vhost);
207 /* if we have children, close them first */
208 if (wsi->child_list) {
209 wsi2 = wsi->child_list;
211 wsi1 = wsi2->sibling_list;
213 /* stop it doing shutdown processing */
214 wsi2->socket_is_permanently_unusable = 1;
215 __lws_close_free_wsi(wsi2, reason,
216 "general child recurse");
219 wsi->child_list = NULL;
222 if (wsi->role_ops == &role_ops_raw_file) {
223 lws_remove_child_from_any_parent(wsi);
224 __remove_wsi_socket_from_fds(wsi);
226 wsi->protocol->callback(wsi, wsi->role_ops->close_cb[0],
227 wsi->user_space, NULL, 0);
231 wsi->wsistate_pre_close = wsi->wsistate;
234 if (wsi->role_ops == &role_ops_cgi) {
236 // lwsl_debug("%s: closing stdwsi index %d\n", __func__, (int)wsi->cgi_channel);
238 /* we are not a network connection, but a handler for CGI io */
239 if (wsi->parent && wsi->parent->http.cgi) {
241 if (wsi->parent->child_list == wsi && !wsi->sibling_list)
242 lws_cgi_remove_and_kill(wsi->parent);
244 /* end the binding between us and master */
245 wsi->parent->http.cgi->stdwsi[(int)wsi->cgi_channel] =
248 wsi->socket_is_permanently_unusable = 1;
250 goto just_kill_connection;
254 lws_cgi_remove_and_kill(wsi);
257 #if !defined(LWS_NO_CLIENT)
258 lws_client_stash_destroy(wsi);
261 if (wsi->role_ops == &role_ops_raw_skt) {
262 wsi->socket_is_permanently_unusable = 1;
263 goto just_kill_connection;
265 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
266 if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
267 wsi->http.fop_fd != NULL)
268 lws_vfs_file_close(&wsi->http.fop_fd);
271 if (lwsi_state(wsi) == LRS_DEAD_SOCKET)
274 if (wsi->socket_is_permanently_unusable ||
275 reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
276 lwsi_state(wsi) == LRS_SHUTDOWN)
277 goto just_kill_connection;
279 switch (lwsi_state_PRE_CLOSE(wsi)) {
280 case LRS_DEAD_SOCKET:
283 /* we tried the polite way... */
284 case LRS_WAITING_TO_SEND_CLOSE:
285 case LRS_AWAITING_CLOSE_ACK:
286 case LRS_RETURNED_CLOSE:
287 goto just_kill_connection;
289 case LRS_FLUSHING_BEFORE_CLOSE:
290 if (lws_has_buffered_out(wsi)
291 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
292 || wsi->http.comp_ctx.buflist_comp ||
293 wsi->http.comp_ctx.may_have_more
296 lws_callback_on_writable(wsi);
299 lwsl_info("%p: end LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
300 goto just_kill_connection;
302 if (lws_has_buffered_out(wsi)
303 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
304 || wsi->http.comp_ctx.buflist_comp ||
305 wsi->http.comp_ctx.may_have_more
308 lwsl_info("%p: LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
309 lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
310 __lws_set_timeout(wsi,
311 PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
317 if (lwsi_state(wsi) == LRS_WAITING_CONNECT ||
318 lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE)
319 goto just_kill_connection;
321 if (!wsi->told_user_closed && wsi->user_space && wsi->protocol &&
322 wsi->protocol_bind_balance) {
323 wsi->protocol->callback(wsi,
324 wsi->role_ops->protocol_unbind_cb[
325 !!lwsi_role_server(wsi)],
326 wsi->user_space, (void *)__func__, 0);
327 wsi->protocol_bind_balance = 0;
331 * signal we are closing, lws_write will
332 * add any necessary version-specific stuff. If the write fails,
333 * no worries we are closing anyway. If we didn't initiate this
334 * close, then our state has been changed to
335 * LRS_RETURNED_CLOSE and we will skip this.
337 * Likewise if it's a second call to close this connection after we
338 * sent the close indication to the peer already, we are in state
339 * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time.
342 if (wsi->role_ops->close_via_role_protocol &&
343 wsi->role_ops->close_via_role_protocol(wsi, reason))
346 just_kill_connection:
348 #if defined(LWS_WITH_HTTP_PROXY)
349 if (wsi->http.buflist_post_body)
350 lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body);
353 if (wsi->role_ops->close_kill_connection)
354 wsi->role_ops->close_kill_connection(wsi, reason);
358 if (!wsi->told_user_closed && wsi->user_space &&
359 wsi->protocol_bind_balance && wsi->protocol) {
360 lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
361 wsi->protocol ? wsi->protocol->name: "NULL");
363 wsi->protocol->callback(wsi,
364 wsi->role_ops->protocol_unbind_cb[
365 !!lwsi_role_server(wsi)],
366 wsi->user_space, (void *)__func__, 0);
367 wsi->protocol_bind_balance = 0;
370 #if !defined(LWS_NO_CLIENT)
371 if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY ||
372 lwsi_state(wsi) == LRS_WAITING_CONNECT) &&
373 !wsi->already_did_cce && wsi->protocol)
374 lws_inform_client_conn_fail(wsi,
375 (void *)"closed before established", 24);
379 * Testing with ab shows that we have to stage the socket close when
380 * the system is under stress... shutdown any further TX, change the
381 * state to one that won't emit anything more, and wait with a timeout
382 * for the POLLIN to show a zero-size rx before coming back and doing
385 if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) &&
386 lwsi_state(wsi) != LRS_SHUTDOWN &&
387 lwsi_state(wsi) != LRS_UNCONNECTED &&
388 reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
389 !wsi->socket_is_permanently_unusable) {
391 #if defined(LWS_WITH_TLS)
392 if (lws_is_ssl(wsi) && wsi->tls.ssl) {
394 switch (__lws_tls_shutdown(wsi)) {
395 case LWS_SSL_CAPABLE_DONE:
396 case LWS_SSL_CAPABLE_ERROR:
397 case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
398 case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
399 case LWS_SSL_CAPABLE_MORE_SERVICE:
405 lwsl_info("%s: shutdown conn: %p (sk %d, state 0x%x)\n",
406 __func__, wsi, (int)(long)wsi->desc.sockfd,
408 if (!wsi->socket_is_permanently_unusable &&
409 lws_socket_is_valid(wsi->desc.sockfd)) {
410 wsi->socket_is_permanently_unusable = 1;
411 n = shutdown(wsi->desc.sockfd, SHUT_WR);
415 lwsl_debug("closing: shutdown (state 0x%x) ret %d\n",
416 lwsi_state(wsi), LWS_ERRNO);
419 * This causes problems on WINCE / ESP32 with disconnection
420 * when the events are half closing connection
422 #if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP32)
423 /* libuv: no event available to guarantee completion */
424 if (!wsi->socket_is_permanently_unusable &&
425 lws_socket_is_valid(wsi->desc.sockfd) &&
426 lwsi_state(wsi) != LRS_SHUTDOWN &&
427 context->event_loop_ops->periodic_events_available) {
428 __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
429 lwsi_set_state(wsi, LRS_SHUTDOWN);
430 __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
431 context->timeout_secs);
438 lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
439 wsi, wsi->desc.sockfd);
441 #ifdef LWS_WITH_HUBBUB
443 lws_rewrite_destroy(wsi->http.rw);
448 if (wsi->http.pending_return_headers)
449 lws_free_set_NULL(wsi->http.pending_return_headers);
452 * we won't be servicing or receiving anything further from this guy
453 * delete socket from the internal poll list if still present
455 __lws_ssl_remove_wsi_from_buffered_list(wsi);
456 __lws_wsi_remove_from_sul(wsi);
458 //if (wsi->told_event_loop_closed) // cgi std close case (dummy-callback)
461 // lwsl_notice("%s: wsi %p, fd %d\n", __func__, wsi, wsi->desc.sockfd);
463 /* checking return redundant since we anyway close */
464 if (wsi->desc.sockfd != LWS_SOCK_INVALID)
465 __remove_wsi_socket_from_fds(wsi);
467 __lws_same_vh_protocol_remove(wsi);
469 lwsi_set_state(wsi, LRS_DEAD_SOCKET);
470 lws_buflist_destroy_all_segments(&wsi->buflist);
471 lws_dll2_remove(&wsi->dll_buflist);
473 if (wsi->role_ops->close_role)
474 wsi->role_ops->close_role(pt, wsi);
476 /* tell the user it's all over for this guy */
478 if ((lwsi_state_est_PRE_CLOSE(wsi) ||
479 /* raw skt adopted but didn't complete tls hs should CLOSE */
480 (wsi->role_ops == &role_ops_raw_skt && !lwsi_role_client(wsi)) ||
481 lwsi_state_PRE_CLOSE(wsi) == LRS_WAITING_SERVER_REPLY) &&
482 !wsi->told_user_closed &&
483 wsi->role_ops->close_cb[lwsi_role_server(wsi)]) {
484 const struct lws_protocols *pro = wsi->protocol;
486 if (!wsi->protocol && wsi->vhost && wsi->vhost->protocols)
487 pro = &wsi->vhost->protocols[0];
489 if (pro && (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi)))
491 * The network wsi for a client h2 connection shouldn't
492 * call back for its role: the child stream connections
493 * own the role. Otherwise h2 will call back closed
494 * one too many times as the children do it and then
495 * the closing network stream.
498 wsi->role_ops->close_cb[lwsi_role_server(wsi)],
499 wsi->user_space, NULL, 0);
500 wsi->told_user_closed = 1;
504 lws_remove_child_from_any_parent(wsi);
505 wsi->socket_is_permanently_unusable = 1;
507 if (wsi->context->event_loop_ops->wsi_logical_close)
508 if (wsi->context->event_loop_ops->wsi_logical_close(wsi))
511 __lws_close_free_wsi_final(wsi);
515 __lws_close_free_wsi_final(struct lws *wsi)
520 lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) {
521 lwsl_debug("%s: wsi %p: fd %d\n", __func__, wsi, wsi->desc.sockfd);
522 n = compatible_close(wsi->desc.sockfd);
524 lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
526 wsi->desc.sockfd = LWS_SOCK_INVALID;
529 /* outermost destroy notification for wsi (user_space still intact) */
531 wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
532 wsi->user_space, NULL, 0);
537 for (n = 0; n < 3; n++) {
538 if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] == 0)
539 lwsl_err("ZERO FD IN CGI CLOSE");
541 if (wsi->http.cgi->pipe_fds[n][!!(n == 0)] >= 0) {
542 close(wsi->http.cgi->pipe_fds[n][!!(n == 0)]);
543 wsi->http.cgi->pipe_fds[n][!!(n == 0)] = LWS_SOCK_INVALID;
547 lws_free_set_NULL(wsi->http.cgi);
556 lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller)
558 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
560 lws_pt_lock(pt, __func__);
561 __lws_close_free_wsi(wsi, reason, caller);