1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26
27 #if defined(LWS_WITH_CLIENT)
28 static int
lws_close_trans_q_leader(struct lws_dll2 * d,void * user)29 lws_close_trans_q_leader(struct lws_dll2 *d, void *user)
30 {
31 struct lws *w = lws_container_of(d, struct lws, dll2_cli_txn_queue);
32
33 __lws_close_free_wsi(w, -1, "trans q leader closing");
34
35 return 0;
36 }
37 #endif
38
39 void
__lws_reset_wsi(struct lws * wsi)40 __lws_reset_wsi(struct lws *wsi)
41 {
42 if (!wsi)
43 return;
44
45 #if defined(LWS_WITH_CLIENT)
46
47 lws_free_set_NULL(wsi->cli_hostname_copy);
48
49 /*
50 * if we have wsi in our transaction queue, if we are closing we
51 * must go through and close all those first
52 */
53 if (wsi->vhost) {
54
55 /* we are no longer an active client connection that can piggyback */
56 lws_dll2_remove(&wsi->dll_cli_active_conns);
57
58 lws_dll2_foreach_safe(&wsi->dll2_cli_txn_queue_owner, NULL,
59 lws_close_trans_q_leader);
60
61 /*
62 * !!! If we are closing, but we have pending pipelined
63 * transaction results we already sent headers for, that's going
64 * to destroy sync for HTTP/1 and leave H2 stream with no live
65 * swsi.`
66 *
67 * However this is normal if we are being closed because the
68 * transaction queue leader is closing.
69 */
70 lws_dll2_remove(&wsi->dll2_cli_txn_queue);
71 }
72 #endif
73
74 if (wsi->vhost)
75 lws_dll2_remove(&wsi->vh_awaiting_socket);
76
77 /*
78 * Protocol user data may be allocated either internally by lws
79 * or by specified the user. We should only free what we allocated.
80 */
81 if (wsi->protocol && wsi->protocol->per_session_data_size &&
82 wsi->user_space && !wsi->user_space_externally_allocated)
83 lws_free_set_NULL(wsi->user_space);
84
85 lws_buflist_destroy_all_segments(&wsi->buflist);
86 lws_buflist_destroy_all_segments(&wsi->buflist_out);
87 #if defined(LWS_WITH_UDP)
88 lws_free_set_NULL(wsi->udp);
89 #endif
90 wsi->retry = 0;
91
92 #if defined(LWS_WITH_CLIENT)
93 lws_dll2_remove(&wsi->dll2_cli_txn_queue);
94 lws_dll2_remove(&wsi->dll_cli_active_conns);
95 #endif
96
97 #if defined(LWS_WITH_SYS_ASYNC_DNS)
98 lws_async_dns_cancel(wsi);
99 #endif
100
101 #if defined(LWS_WITH_HTTP_PROXY)
102 if (wsi->http.buflist_post_body)
103 lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body);
104 #endif
105
106 if (wsi->vhost && wsi->vhost->lserv_wsi == wsi)
107 wsi->vhost->lserv_wsi = NULL;
108 #if defined(LWS_WITH_CLIENT)
109 if (wsi->vhost)
110 lws_dll2_remove(&wsi->dll_cli_active_conns);
111 #endif
112 wsi->context->count_wsi_allocated--;
113
114 __lws_same_vh_protocol_remove(wsi);
115 #if defined(LWS_WITH_CLIENT)
116 lws_free_set_NULL(wsi->stash);
117 lws_free_set_NULL(wsi->cli_hostname_copy);
118 #endif
119
120 #if defined(LWS_WITH_PEER_LIMITS)
121 lws_peer_track_wsi_close(wsi->context, wsi->peer);
122 wsi->peer = NULL;
123 #endif
124
125 /* since we will destroy the wsi, make absolutely sure now */
126
127 #if defined(LWS_WITH_OPENSSL)
128 __lws_ssl_remove_wsi_from_buffered_list(wsi);
129 #endif
130 __lws_wsi_remove_from_sul(wsi);
131
132 if (wsi->role_ops->destroy_role)
133 wsi->role_ops->destroy_role(wsi);
134
135 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
136 __lws_header_table_detach(wsi, 0);
137 #endif
138 }
139
140 void
__lws_free_wsi(struct lws * wsi)141 __lws_free_wsi(struct lws *wsi)
142 {
143 if (!wsi)
144 return;
145
146 __lws_reset_wsi(wsi);
147
148 if (wsi->context->event_loop_ops->destroy_wsi)
149 wsi->context->event_loop_ops->destroy_wsi(wsi);
150
151 lws_vhost_unbind_wsi(wsi);
152
153 lwsl_debug("%s: %p, remaining wsi %d, tsi fds count %d\n", __func__, wsi,
154 wsi->context->count_wsi_allocated,
155 wsi->context->pt[(int)wsi->tsi].fds_count);
156
157 lws_free(wsi);
158 }
159
160
161 void
lws_remove_child_from_any_parent(struct lws * wsi)162 lws_remove_child_from_any_parent(struct lws *wsi)
163 {
164 struct lws **pwsi;
165 int seen = 0;
166
167 if (!wsi->parent)
168 return;
169
170 /* detach ourselves from parent's child list */
171 pwsi = &wsi->parent->child_list;
172 while (*pwsi) {
173 if (*pwsi == wsi) {
174 lwsl_info("%s: detach %p from parent %p\n", __func__,
175 wsi, wsi->parent);
176
177 if (wsi->parent->protocol)
178 wsi->parent->protocol->callback(wsi,
179 LWS_CALLBACK_CHILD_CLOSING,
180 wsi->parent->user_space, wsi, 0);
181
182 *pwsi = wsi->sibling_list;
183 seen = 1;
184 break;
185 }
186 pwsi = &(*pwsi)->sibling_list;
187 }
188 if (!seen)
189 lwsl_err("%s: failed to detach from parent\n", __func__);
190
191 wsi->parent = NULL;
192 }
193
194 #if defined(LWS_WITH_CLIENT)
195 void
lws_inform_client_conn_fail(struct lws * wsi,void * arg,size_t len)196 lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len)
197 {
198 lws_addrinfo_clean(wsi);
199
200 if (wsi->already_did_cce)
201 return;
202
203 wsi->already_did_cce = 1;
204 lws_stats_bump(&wsi->context->pt[(int)wsi->tsi],
205 LWSSTATS_C_CONNS_CLIENT_FAILED, 1);
206
207 if (!wsi->protocol)
208 return;
209
210 wsi->protocol->callback(wsi,
211 LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
212 wsi->user_space, arg, len);
213 }
214 #endif
215
216 void
lws_addrinfo_clean(struct lws * wsi)217 lws_addrinfo_clean(struct lws *wsi)
218 {
219 #if defined(LWS_WITH_CLIENT)
220 if (!wsi->dns_results)
221 return;
222
223 #if defined(LWS_WITH_SYS_ASYNC_DNS)
224 lws_async_dns_freeaddrinfo(&wsi->dns_results);
225 #else
226 freeaddrinfo((struct addrinfo *)wsi->dns_results);
227 #endif
228 wsi->dns_results = NULL;
229 #endif
230 }
231
232 void
__lws_close_free_wsi(struct lws * wsi,enum lws_close_status reason,const char * caller)233 __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
234 const char *caller)
235 {
236 struct lws_context_per_thread *pt;
237 const struct lws_protocols *pro;
238 struct lws_context *context;
239 struct lws *wsi1, *wsi2;
240 int n, ccb;
241
242 lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller);
243
244 if (!wsi)
245 return;
246
247 lws_access_log(wsi);
248
249 if (!lws_dll2_is_detached(&wsi->dll_buflist)) {
250 lwsl_info("%s: wsi %p: going down with stuff in buflist\n",
251 __func__, wsi); }
252
253 context = wsi->context;
254 pt = &context->pt[(int)wsi->tsi];
255 lws_stats_bump(pt, LWSSTATS_C_API_CLOSE, 1);
256
257 #if defined(LWS_WITH_CLIENT)
258
259 lws_free_set_NULL(wsi->cli_hostname_copy);
260
261 lws_addrinfo_clean(wsi);
262 #endif
263
264 #if defined(LWS_WITH_HTTP2)
265 if (wsi->mux_stream_immortal)
266 lws_http_close_immortal(wsi);
267 #endif
268
269 /* if we have children, close them first */
270 if (wsi->child_list) {
271 wsi2 = wsi->child_list;
272 while (wsi2) {
273 wsi1 = wsi2->sibling_list;
274 wsi2->parent = NULL;
275 /* stop it doing shutdown processing */
276 wsi2->socket_is_permanently_unusable = 1;
277 __lws_close_free_wsi(wsi2, reason,
278 "general child recurse");
279 wsi2 = wsi1;
280 }
281 wsi->child_list = NULL;
282 }
283
284 #if defined(LWS_ROLE_RAW_FILE)
285 if (wsi->role_ops == &role_ops_raw_file) {
286 lws_remove_child_from_any_parent(wsi);
287 __remove_wsi_socket_from_fds(wsi);
288 if (wsi->protocol)
289 wsi->protocol->callback(wsi, wsi->role_ops->close_cb[0],
290 wsi->user_space, NULL, 0);
291 goto async_close;
292 }
293 #endif
294
295 wsi->wsistate_pre_close = wsi->wsistate;
296
297 #ifdef LWS_WITH_CGI
298 if (wsi->role_ops == &role_ops_cgi) {
299
300 // lwsl_debug("%s: closing stdwsi index %d\n", __func__, (int)wsi->lsp_channel);
301
302 /* we are not a network connection, but a handler for CGI io */
303 if (wsi->parent && wsi->parent->http.cgi) {
304
305 if (wsi->parent->child_list == wsi && !wsi->sibling_list)
306 lws_cgi_remove_and_kill(wsi->parent);
307
308 /* end the binding between us and master */
309 if (wsi->parent->http.cgi)
310 wsi->parent->http.cgi->lsp->stdwsi[(int)wsi->lsp_channel] =
311 NULL;
312 }
313 wsi->socket_is_permanently_unusable = 1;
314
315 goto just_kill_connection;
316 }
317
318 if (wsi->http.cgi)
319 lws_cgi_remove_and_kill(wsi);
320 #endif
321
322 #if defined(LWS_WITH_CLIENT)
323 lws_free_set_NULL(wsi->stash);
324 #endif
325
326 if (wsi->role_ops == &role_ops_raw_skt) {
327 wsi->socket_is_permanently_unusable = 1;
328 goto just_kill_connection;
329 }
330 #if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
331 if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
332 wsi->http.fop_fd != NULL)
333 lws_vfs_file_close(&wsi->http.fop_fd);
334 #endif
335
336 if (lwsi_state(wsi) == LRS_DEAD_SOCKET)
337 return;
338
339 if (wsi->socket_is_permanently_unusable ||
340 reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
341 lwsi_state(wsi) == LRS_SHUTDOWN)
342 goto just_kill_connection;
343
344 switch (lwsi_state_PRE_CLOSE(wsi)) {
345 case LRS_DEAD_SOCKET:
346 return;
347
348 /* we tried the polite way... */
349 case LRS_WAITING_TO_SEND_CLOSE:
350 case LRS_AWAITING_CLOSE_ACK:
351 case LRS_RETURNED_CLOSE:
352 goto just_kill_connection;
353
354 case LRS_FLUSHING_BEFORE_CLOSE:
355 if (lws_has_buffered_out(wsi)
356 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
357 || wsi->http.comp_ctx.buflist_comp ||
358 wsi->http.comp_ctx.may_have_more
359 #endif
360 ) {
361 lws_callback_on_writable(wsi);
362 return;
363 }
364 lwsl_info("%p: end LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
365 goto just_kill_connection;
366 default:
367 if (lws_has_buffered_out(wsi)
368 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
369 || wsi->http.comp_ctx.buflist_comp ||
370 wsi->http.comp_ctx.may_have_more
371 #endif
372 ) {
373 lwsl_info("%p: LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
374 lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
375 __lws_set_timeout(wsi,
376 PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
377 return;
378 }
379 break;
380 }
381
382 if (lwsi_state(wsi) == LRS_WAITING_CONNECT ||
383 lwsi_state(wsi) == LRS_WAITING_DNS ||
384 lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE)
385 goto just_kill_connection;
386
387 if (!wsi->told_user_closed && wsi->user_space && wsi->protocol &&
388 wsi->protocol_bind_balance) {
389 wsi->protocol->callback(wsi,
390 wsi->role_ops->protocol_unbind_cb[
391 !!lwsi_role_server(wsi)],
392 wsi->user_space, (void *)__func__, 0);
393 wsi->protocol_bind_balance = 0;
394 }
395
396 /*
397 * signal we are closing, lws_write will
398 * add any necessary version-specific stuff. If the write fails,
399 * no worries we are closing anyway. If we didn't initiate this
400 * close, then our state has been changed to
401 * LRS_RETURNED_CLOSE and we will skip this.
402 *
403 * Likewise if it's a second call to close this connection after we
404 * sent the close indication to the peer already, we are in state
405 * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time.
406 */
407
408 if (wsi->role_ops->close_via_role_protocol &&
409 wsi->role_ops->close_via_role_protocol(wsi, reason))
410 return;
411
412 just_kill_connection:
413
414 #if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
415 if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
416 wsi->http.fop_fd != NULL)
417 lws_vfs_file_close(&wsi->http.fop_fd);
418 #endif
419
420 #if defined(LWS_WITH_SYS_ASYNC_DNS)
421 lws_async_dns_cancel(wsi);
422 #endif
423
424 #if defined(LWS_WITH_HTTP_PROXY)
425 if (wsi->http.buflist_post_body)
426 lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body);
427 #endif
428 #if defined(LWS_WITH_UDP)
429 if (wsi->udp)
430 lws_free_set_NULL(wsi->udp);
431 #endif
432
433 if (wsi->role_ops->close_kill_connection)
434 wsi->role_ops->close_kill_connection(wsi, reason);
435
436 n = 0;
437
438 if (!wsi->told_user_closed && wsi->user_space &&
439 wsi->protocol_bind_balance && wsi->protocol) {
440 lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
441 wsi->protocol ? wsi->protocol->name: "NULL");
442 if (wsi->protocol)
443 wsi->protocol->callback(wsi,
444 wsi->role_ops->protocol_unbind_cb[
445 !!lwsi_role_server(wsi)],
446 wsi->user_space, (void *)__func__, 0);
447 wsi->protocol_bind_balance = 0;
448 }
449
450 #if defined(LWS_WITH_CLIENT)
451 if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY ||
452 lwsi_state(wsi) == LRS_WAITING_DNS ||
453 lwsi_state(wsi) == LRS_WAITING_CONNECT) &&
454 !wsi->already_did_cce && wsi->protocol) {
455 static const char _reason[] = "closed before established";
456
457 lws_inform_client_conn_fail(wsi,
458 (void *)_reason, sizeof(_reason));
459 }
460 #endif
461
462 /*
463 * Testing with ab shows that we have to stage the socket close when
464 * the system is under stress... shutdown any further TX, change the
465 * state to one that won't emit anything more, and wait with a timeout
466 * for the POLLIN to show a zero-size rx before coming back and doing
467 * the actual close.
468 */
469 if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) &&
470 lwsi_state(wsi) != LRS_SHUTDOWN &&
471 lwsi_state(wsi) != LRS_UNCONNECTED &&
472 reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
473 !wsi->socket_is_permanently_unusable) {
474
475 #if defined(LWS_WITH_TLS)
476 if (lws_is_ssl(wsi) && wsi->tls.ssl) {
477 n = 0;
478 switch (__lws_tls_shutdown(wsi)) {
479 case LWS_SSL_CAPABLE_DONE:
480 case LWS_SSL_CAPABLE_ERROR:
481 case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
482 case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
483 case LWS_SSL_CAPABLE_MORE_SERVICE:
484 break;
485 }
486 } else
487 #endif
488 {
489 lwsl_info("%s: shutdown conn: %p (sk %d, state 0x%x)\n",
490 __func__, wsi, (int)(long)wsi->desc.sockfd,
491 lwsi_state(wsi));
492 if (!wsi->socket_is_permanently_unusable &&
493 lws_socket_is_valid(wsi->desc.sockfd)) {
494 wsi->socket_is_permanently_unusable = 1;
495 n = shutdown(wsi->desc.sockfd, SHUT_WR);
496 }
497 }
498 if (n)
499 lwsl_debug("closing: shutdown (state 0x%x) ret %d\n",
500 lwsi_state(wsi), LWS_ERRNO);
501
502 /*
503 * This causes problems on WINCE / ESP32 with disconnection
504 * when the events are half closing connection
505 */
506 #if !defined(_WIN32_WCE) && !defined(LWS_PLAT_FREERTOS)
507 /* libuv: no event available to guarantee completion */
508 if (!wsi->socket_is_permanently_unusable &&
509 lws_socket_is_valid(wsi->desc.sockfd) &&
510 lwsi_state(wsi) != LRS_SHUTDOWN &&
511 (context->event_loop_ops->flags & LELOF_ISPOLL)) {
512 __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
513 lwsi_set_state(wsi, LRS_SHUTDOWN);
514 __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
515 context->timeout_secs);
516
517 return;
518 }
519 #endif
520 }
521
522 lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__,
523 wsi, wsi->desc.sockfd);
524
525 #ifdef LWS_WITH_HUBBUB
526 if (wsi->http.rw) {
527 lws_rewrite_destroy(wsi->http.rw);
528 wsi->http.rw = NULL;
529 }
530 #endif
531
532 if (wsi->http.pending_return_headers)
533 lws_free_set_NULL(wsi->http.pending_return_headers);
534
535 /*
536 * we won't be servicing or receiving anything further from this guy
537 * delete socket from the internal poll list if still present
538 */
539 __lws_ssl_remove_wsi_from_buffered_list(wsi);
540 __lws_wsi_remove_from_sul(wsi);
541
542 //if (wsi->told_event_loop_closed) // cgi std close case (dummy-callback)
543 // return;
544
545 // lwsl_notice("%s: wsi %p, fd %d\n", __func__, wsi, wsi->desc.sockfd);
546
547 /* checking return redundant since we anyway close */
548 if (wsi->desc.sockfd != LWS_SOCK_INVALID)
549 __remove_wsi_socket_from_fds(wsi);
550 else
551 __lws_same_vh_protocol_remove(wsi);
552
553 lwsi_set_state(wsi, LRS_DEAD_SOCKET);
554 lws_buflist_destroy_all_segments(&wsi->buflist);
555 lws_dll2_remove(&wsi->dll_buflist);
556
557 if (wsi->role_ops->close_role)
558 wsi->role_ops->close_role(pt, wsi);
559
560 /* tell the user it's all over for this guy */
561
562 ccb = 0;
563 if ((lwsi_state_est_PRE_CLOSE(wsi) ||
564 /* raw skt adopted but didn't complete tls hs should CLOSE */
565 (wsi->role_ops == &role_ops_raw_skt && !lwsi_role_client(wsi)) ||
566 lwsi_state_PRE_CLOSE(wsi) == LRS_WAITING_SERVER_REPLY) &&
567 !wsi->told_user_closed &&
568 wsi->role_ops->close_cb[lwsi_role_server(wsi)]) {
569 if (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi))
570 ccb = 1;
571 /*
572 * The network wsi for a client h2 connection shouldn't
573 * call back for its role: the child stream connections
574 * own the role. Otherwise h2 will call back closed
575 * one too many times as the children do it and then
576 * the closing network stream.
577 */
578 }
579
580 if (!wsi->told_user_closed &&
581 !lws_dll2_is_detached(&wsi->vh_awaiting_socket))
582 /*
583 * He's a guy who go started with dns, but failed or is
584 * caught with a shutdown before he got the result. We have
585 * to issue him a close cb
586 */
587 ccb = 1;
588
589 pro = wsi->protocol;
590
591 #if defined(LWS_WITH_CLIENT)
592 if (!ccb && (lwsi_state_PRE_CLOSE(wsi) & LWSIFS_NOT_EST) &&
593 lwsi_role_client(wsi)) {
594 lws_inform_client_conn_fail(wsi, "Closed before conn", 18);
595 }
596 #endif
597 if (ccb) {
598
599 if (!wsi->protocol && wsi->vhost && wsi->vhost->protocols)
600 pro = &wsi->vhost->protocols[0];
601
602 if (pro)
603 pro->callback(wsi,
604 wsi->role_ops->close_cb[lwsi_role_server(wsi)],
605 wsi->user_space, NULL, 0);
606 wsi->told_user_closed = 1;
607 }
608
609 #if defined(LWS_ROLE_RAW_FILE)
610 async_close:
611 #endif
612 lws_remove_child_from_any_parent(wsi);
613 wsi->socket_is_permanently_unusable = 1;
614
615 if (wsi->context->event_loop_ops->wsi_logical_close)
616 if (wsi->context->event_loop_ops->wsi_logical_close(wsi))
617 return;
618
619 __lws_close_free_wsi_final(wsi);
620 }
621
622 void
__lws_close_free_wsi_final(struct lws * wsi)623 __lws_close_free_wsi_final(struct lws *wsi)
624 {
625 int n;
626
627 if (!wsi->shadow &&
628 lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) {
629 lwsl_debug("%s: wsi %p: fd %d\n", __func__, wsi, wsi->desc.sockfd);
630 n = compatible_close(wsi->desc.sockfd);
631 if (n)
632 lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
633
634 wsi->desc.sockfd = LWS_SOCK_INVALID;
635 }
636
637 /* outermost destroy notification for wsi (user_space still intact) */
638 if (wsi->vhost)
639 wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
640 wsi->user_space, NULL, 0);
641
642 #ifdef LWS_WITH_CGI
643 if (wsi->http.cgi) {
644 lws_spawn_piped_destroy(&wsi->http.cgi->lsp);
645 lws_free_set_NULL(wsi->http.cgi);
646 }
647 #endif
648
649 __lws_free_wsi(wsi);
650 }
651
652
653 void
lws_close_free_wsi(struct lws * wsi,enum lws_close_status reason,const char * caller)654 lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller)
655 {
656 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
657
658 lws_pt_lock(pt, __func__);
659 __lws_close_free_wsi(wsi, reason, caller);
660 lws_pt_unlock(pt);
661 }
662
663
664