Coverage Report

Created: 2026-09-04 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/core-net/close.c
Line
Count
Source
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
#include "private-lib-async-dns.h"
27
28
#if defined(LWS_WITH_CLIENT)
29
static int
30
lws_close_trans_q_leader(struct lws_dll2 *d, void *user)
31
0
{
32
0
  struct lws *w = lws_container_of(d, struct lws, dll2_cli_txn_queue);
33
34
0
  __lws_close_free_wsi(w, (enum lws_close_status)-1, "trans q leader closing");
35
36
0
  return 0;
37
0
}
38
#endif
39
40
void
41
__lws_reset_wsi(struct lws *wsi)
42
0
{
43
0
  if (!wsi)
44
0
    return;
45
46
0
#if defined(LWS_WITH_CLIENT)
47
48
0
  lws_free_set_NULL(wsi->cli_hostname_copy);
49
50
0
#if defined(LWS_WITH_CONMON)
51
52
0
  if (wsi->conmon.dns_results_copy) {
53
0
    lws_conmon_addrinfo_destroy(wsi->conmon.dns_results_copy);
54
0
    wsi->conmon.dns_results_copy = NULL;
55
0
  }
56
57
0
  wsi->conmon.ciu_dns =
58
0
    wsi->conmon.ciu_sockconn =
59
0
    wsi->conmon.ciu_tls =
60
0
    wsi->conmon.ciu_txn_resp = 0;
61
0
#endif
62
63
  /*
64
   * if we have wsi in our transaction queue, if we are closing we
65
   * must go through and close all those first
66
   */
67
0
  if (wsi->a.vhost) {
68
69
    /* we are no longer an active client connection that can piggyback */
70
0
    lws_dll2_remove(&wsi->dll_cli_active_conns);
71
72
0
    lws_dll2_foreach_safe(&wsi->dll2_cli_txn_queue_owner, NULL,
73
0
              lws_close_trans_q_leader);
74
75
    /*
76
     * !!! If we are closing, but we have pending pipelined
77
     * transaction results we already sent headers for, that's going
78
     * to destroy sync for HTTP/1 and leave H2 stream with no live
79
     * swsi.`
80
     *
81
     * However this is normal if we are being closed because the
82
     * transaction queue leader is closing.
83
     */
84
0
    lws_dll2_remove(&wsi->dll2_cli_txn_queue);
85
0
  }
86
0
#endif
87
88
0
  if (wsi->a.vhost) {
89
0
    lws_vhost_lock(wsi->a.vhost);
90
0
    lws_dll2_remove(&wsi->vh_awaiting_socket);
91
0
    lws_vhost_unlock(wsi->a.vhost);
92
0
  }
93
94
  /*
95
   * Protocol user data may be allocated either internally by lws
96
   * or by specified the user. We should only free what we allocated.
97
   */
98
0
  if (wsi->a.protocol && wsi->a.protocol->per_session_data_size &&
99
0
      wsi->user_space && !wsi->user_space_externally_allocated) {
100
    /* confirm no sul left scheduled in user data itself */
101
0
    lws_sul_debug_zombies(wsi->a.context, wsi->user_space,
102
0
        wsi->a.protocol->per_session_data_size, __func__);
103
0
    lws_free_set_NULL(wsi->user_space);
104
0
  }
105
106
  /*
107
   * Don't let buflist content or state from the wsi's previous life
108
   * carry over to the new life
109
   */
110
111
0
  lws_buflist_destroy_all_segments(&wsi->buflist);
112
0
  lws_dll2_remove(&wsi->dll_buflist);
113
0
  lws_buflist_destroy_all_segments(&wsi->buflist_out);
114
0
#if defined(LWS_WITH_UDP)
115
0
  if (wsi->udp) {
116
    /* confirm no sul left scheduled in wsi->udp itself */
117
0
    lws_sul_debug_zombies(wsi->a.context, wsi->udp,
118
0
              sizeof(*wsi->udp), "close udp wsi");
119
0
    lws_free_set_NULL(wsi->udp);
120
0
  }
121
0
#endif
122
0
  wsi->retry = 0;
123
0
  wsi->mount_hit = 0;
124
125
0
#if defined(LWS_WITH_CLIENT)
126
0
  lws_dll2_remove(&wsi->dll2_cli_txn_queue);
127
0
  lws_dll2_remove(&wsi->dll_cli_active_conns);
128
0
  if (wsi->cli_hostname_copy)
129
0
    lws_free_set_NULL(wsi->cli_hostname_copy);
130
0
#endif
131
132
#if defined(LWS_WITH_SYS_ASYNC_DNS)
133
  lws_async_dns_cancel(wsi);
134
#endif
135
136
#if defined(LWS_WITH_HTTP_PROXY)
137
  if (wsi->http.buflist_post_body)
138
    lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body);
139
#endif
140
141
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
142
0
#if defined(LWS_WITH_HTTP_DIGEST_AUTH)
143
0
  if (wsi->http.digest_auth_hdr) {
144
0
    lws_free(wsi->http.digest_auth_hdr);
145
0
    wsi->http.digest_auth_hdr = NULL;
146
0
  }
147
0
#endif
148
0
#endif
149
150
0
#if defined(LWS_WITH_SERVER)
151
0
  lws_dll2_remove(&wsi->listen_list);
152
0
#endif
153
154
0
#if defined(LWS_WITH_CLIENT)
155
0
  if (wsi->a.vhost)
156
0
    lws_dll2_remove(&wsi->dll_cli_active_conns);
157
0
#endif
158
159
0
  __lws_same_vh_protocol_remove(wsi);
160
0
#if defined(LWS_WITH_CLIENT)
161
  //lws_free_set_NULL(wsi->stash);
162
0
  lws_free_set_NULL(wsi->cli_hostname_copy);
163
0
#endif
164
165
#if defined(LWS_WITH_PEER_LIMITS)
166
  lws_peer_track_wsi_close(wsi->a.context, wsi->peer);
167
  wsi->peer = NULL;
168
#endif
169
170
  /* since we will destroy the wsi, make absolutely sure now */
171
172
0
#if defined(LWS_WITH_TLS)
173
0
  __lws_ssl_remove_wsi_from_buffered_list(wsi);
174
0
#endif
175
0
  __lws_wsi_remove_from_sul(wsi);
176
177
0
  if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_destroy_role))
178
0
    lws_rops_func_fidx(wsi->role_ops,
179
0
           LWS_ROPS_destroy_role).destroy_role(wsi);
180
181
0
#if defined(LWS_ROLE_WS)
182
  /*
183
   * A ws struct can have been attached to the wsi while it was still
184
   * in an http role, for example a ws upgrade that was refused, or a
185
   * client ws bind on a connection that then failed.  If the wsi is
186
   * destroyed under its old role, the role destroy above cannot have
187
   * freed it.  Take care of any leftover; for an established ws wsi,
188
   * the ws role destroy has already freed and NULLed it.
189
   */
190
0
  lws_free_set_NULL(wsi->ws);
191
0
#endif
192
193
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
194
0
  __lws_header_table_detach(wsi, 0);
195
0
#endif
196
197
0
#if defined(LWS_ROLE_H2)
198
  /*
199
   * Let's try to clean out the h2-ness of the wsi
200
   */
201
202
0
  memset(&wsi->h2, 0, sizeof(wsi->h2));
203
204
0
  wsi->hdr_parsing_completed = wsi->mux_substream =
205
0
  wsi->upgraded_to_http2 = wsi->mux_stream_immortal =
206
0
  wsi->h2_acked_settings = wsi->seen_nonpseudoheader =
207
0
  wsi->socket_is_permanently_unusable = wsi->favoured_pollin =
208
0
  wsi->already_did_cce = wsi->told_user_closed =
209
0
  wsi->waiting_to_send_close_frame = wsi->close_needs_ack =
210
0
  wsi->parent_pending_cb_on_writable = wsi->seen_zero_length_recv =
211
0
  wsi->close_when_buffered_out_drained = wsi->could_have_pending = 0;
212
0
#endif
213
214
#if defined(LWS_ROLE_QUIC)
215
  lws_quic_stream_cleanup(wsi);
216
  memset(&wsi->quic, 0, sizeof(wsi->quic));
217
#endif
218
219
#if defined(LWS_ROLE_H3)
220
  memset(&wsi->h3, 0, sizeof(wsi->h3));
221
#endif
222
223
0
#if defined(LWS_WITH_CLIENT)
224
0
  wsi->do_ws = wsi->chunked = wsi->client_rx_avail =
225
0
  wsi->client_http_body_pending = wsi->transaction_from_pipeline_queue =
226
0
  wsi->keepalive_active = wsi->keepalive_rejected =
227
0
  wsi->redirected_to_get = wsi->client_pipeline = wsi->client_h2_alpn =
228
0
  wsi->client_mux_substream = wsi->client_mux_migrated =
229
0
  wsi->tls_session_reused = wsi->perf_done =
230
0
    wsi->tried_quic = 0;
231
232
0
  wsi->immortal_substream_count = 0;
233
#if defined(LWS_ROLE_H3) || defined(LWS_ROLE_QUIC)
234
  wsi->quic_alt_port = 0;
235
#endif
236
0
#endif
237
0
}
238
239
/* req cx lock */
240
241
void
242
__lws_free_wsi(struct lws *wsi)
243
0
{
244
0
  struct lws_vhost *vh;
245
246
0
  if (!wsi)
247
0
    return;
248
249
0
  lws_context_assert_lock_held(wsi->a.context);
250
251
  /* just in case */
252
0
  lws_dll2_remove(&wsi->pre_natal);
253
254
0
#if defined(LWS_WITH_SECURE_STREAMS)
255
0
  if (wsi->for_ss) {
256
257
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
258
    if (wsi->client_bound_sspc) {
259
      lws_sspc_handle_t *h = (lws_sspc_handle_t *)
260
              wsi->a.opaque_user_data;
261
      if (h) {
262
        h->txp_path.priv_onw = NULL;
263
        wsi->a.opaque_user_data = NULL;
264
      }
265
    } else
266
#endif
267
0
    {
268
      /*
269
       * Make certain it is disconnected from the ss by now
270
       */
271
0
      lws_ss_handle_t *h = (lws_ss_handle_t *)
272
0
              wsi->a.opaque_user_data;
273
274
0
      if (h) {
275
0
        h->wsi = NULL;
276
0
        wsi->a.opaque_user_data = NULL;
277
0
      }
278
0
    }
279
0
  }
280
0
#endif
281
282
0
  vh = wsi->a.vhost;
283
284
0
  __lws_reset_wsi(wsi);
285
0
  __lws_wsi_remove_from_sul(wsi);
286
287
0
  if (vh)
288
    /* this may destroy vh */
289
0
    __lws_vhost_unbind_wsi(wsi); /* req cx + vh lock */
290
291
0
#if defined(LWS_WITH_CLIENT)
292
0
  if (wsi->stash)
293
0
    lws_free_set_NULL(wsi->stash);
294
0
#endif
295
296
0
  if (wsi->a.context->event_loop_ops->destroy_wsi)
297
0
    wsi->a.context->event_loop_ops->destroy_wsi(wsi);
298
299
0
  lwsl_wsi_debug(wsi, "tsi fds count %d\n",
300
0
      wsi->a.context->pt[(int)wsi->tsi].fds_count);
301
302
  /* confirm no sul left scheduled in wsi itself */
303
0
  lws_sul_debug_zombies(wsi->a.context, wsi, sizeof(*wsi), __func__);
304
305
0
  wsi->socket_is_permanently_unusable = 1; // !!!
306
307
0
  __lws_lc_untag(wsi->a.context, &wsi->lc);
308
0
  lws_free(wsi);
309
0
}
310
311
312
void
313
lws_remove_child_from_any_parent(struct lws *wsi)
314
0
{
315
0
  if (!wsi->parent)
316
0
    return;
317
318
0
  if (lws_dll2_is_detached(&wsi->sibling_list)) {
319
0
    lwsl_wsi_err(wsi, "failed to detach from parent");
320
0
    wsi->parent = NULL;
321
0
    return;
322
0
  }
323
324
0
  lwsl_wsi_info(wsi, "detach from parent %s",
325
0
          lws_wsi_tag(wsi->parent));
326
327
0
  if (wsi->parent->a.protocol)
328
0
    wsi->parent->a.protocol->callback(wsi,
329
0
        LWS_CALLBACK_CHILD_CLOSING,
330
0
        wsi->parent->user_space, wsi, 0);
331
332
0
  lws_dll2_remove(&wsi->sibling_list);
333
0
  wsi->parent = NULL;
334
0
}
335
336
#if defined(LWS_WITH_CLIENT)
337
void
338
lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len)
339
0
{
340
0
  if (wsi->already_did_cce)
341
0
    return;
342
343
#if defined(LWS_ROLE_H3) || defined(LWS_ROLE_QUIC)
344
  if (wsi->tried_quic && wsi->role_ops && !strcmp(wsi->role_ops->name, "quic")) {
345
    const char *path = wsi->stash ? wsi->stash->cis[CIS_PATH] : lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
346
    const char *host = wsi->stash ? wsi->stash->cis[CIS_HOST] : lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST);
347
    const char *ads = wsi->stash ? wsi->stash->cis[CIS_ADDRESS] : wsi->cli_hostname_copy;
348
    
349
    if (lws_dll2_count(&wsi->dns_sorted_list)) {
350
      struct lws_dll2 *d = lws_dll2_get_head(&wsi->dns_sorted_list);
351
      lws_dns_sort_t *ds = lws_container_of(d, lws_dns_sort_t, list);
352
      char ads_fallback[48];
353
354
      lws_sa46_write_numeric_address(&ds->dest, ads_fallback, sizeof(ads_fallback));
355
      lws_dll2_remove(d);
356
      lws_free(ds);
357
358
      if (ads_fallback[0] && host && path) {
359
        lwsl_wsi_notice(wsi, "QUIC fail, trying next DNS result %s", ads_fallback);
360
        lws_addrinfo_clean(wsi);
361
        if (lws_client_reset(&wsi,
362
            !!(wsi->tls.use_ssl & LCCSCF_USE_SSL),
363
            ads_fallback, wsi->c_port, path, host, 1)) {
364
          return;
365
        }
366
      }
367
    }
368
369
    if (!ads) ads = host;
370
371
    if (ads && host && path) {
372
      wsi->tried_quic = 0;
373
      lwsl_wsi_notice(wsi, "QUIC connection failed, falling back to TCP");
374
      lws_free_set_NULL(wsi->udp);
375
      /*
376
       * Forget any learned h3 alternative for this origin,
377
       * it just failed... per RFC 7838 return to the origin
378
       * until the alternative is advertised again
379
       */
380
      lws_client_alt_svc_forget(wsi);
381
      lws_addrinfo_clean(wsi);
382
383
      /*
384
       * Invalidate any cached h3 ALPN for this host:port so
385
       * the post-reset connect path does not immediately try
386
       * QUIC again.  We write "h2" with a short (60 s) TTL so
387
       * the preference is temporary -- QUIC may work again
388
       * later when transient conditions clear.
389
       */
390
      if (wsi->a.context->alpn_cache && wsi->c_port) {
391
        char _key[256];
392
        void *_p;
393
        lws_snprintf(_key, sizeof(_key), "alpn_%s_%u",
394
               ads, wsi->c_port);
395
        lws_cache_write_through(wsi->a.context->alpn_cache,
396
              _key, (const uint8_t *)"h2", 3,
397
              lws_now_usecs() +
398
              (60LL * LWS_US_PER_SEC),
399
              &_p);
400
      }
401
      /*
402
       * Clear discovered ALPN so connect_2_restart does not
403
       * see h3 from the cache-hit path.  Keep the original
404
       * offered ALPN for the retry, so an h1 streamtype does
405
       * not silently become h2... but if h3 was explicitly
406
       * offered, fall back to the TCP alpns for the retry.
407
       */
408
      wsi->alpn_discovered[0] = '\0';
409
      /* the QUIC attempt had set wsi alpn to h3, recover
410
       * the original from the ah headers, or TCP alpns */
411
      if (strstr(wsi->alpn, "h3")) {
412
        const char *orig = lws_hdr_simple_ptr(wsi,
413
              _WSI_TOKEN_CLIENT_ALPN);
414
415
        if (!orig || strstr(orig, "h3"))
416
          orig = "h2,http/1.1";
417
418
        lws_strncpy(wsi->alpn, orig,
419
              sizeof(wsi->alpn));
420
      }
421
422
      if (lws_client_reset(&wsi,
423
          !!(wsi->tls.use_ssl & LCCSCF_USE_SSL),
424
          ads, wsi->c_port, path, host, 1)) {
425
        /* Successfully scheduled fallback */
426
        return;
427
      }
428
    }
429
  }
430
#endif
431
432
0
  lws_addrinfo_clean(wsi);
433
434
0
  wsi->already_did_cce = 1;
435
436
0
  if (!wsi->a.protocol || (wsi->a.context && wsi->a.context->being_destroyed))
437
0
    return;
438
439
0
  if (!wsi->client_suppress_CONNECTION_ERROR)
440
0
    wsi->a.protocol->callback(wsi,
441
0
          LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
442
0
          wsi->user_space, arg, len);
443
0
}
444
#endif
445
446
void
447
lws_addrinfo_clean(struct lws *wsi)
448
0
{
449
0
#if defined(LWS_WITH_CLIENT)
450
0
  struct lws_dll2 *d = lws_dll2_get_head(&wsi->dns_sorted_list), *d1;
451
452
0
  while (d) {
453
0
    lws_dns_sort_t *r = lws_container_of(d, lws_dns_sort_t, list);
454
455
0
    d1 = lws_dll2_get_next(d);
456
0
    lws_dll2_remove(d);
457
0
    lws_free(r);
458
459
0
    d = d1;
460
0
  }
461
0
#endif
462
0
}
463
464
#if defined(LWS_WITH_ASYNC_QUEUE)
465
static void
466
lws_async_worker_wait_and_reap(struct lws *wsi)
467
{
468
469
  while (1) {
470
    pthread_mutex_lock(&wsi->a.context->async_worker_mutex);
471
    if (!wsi->async_worker_job) {
472
      pthread_mutex_unlock(&wsi->a.context->async_worker_mutex);
473
      break;
474
    }
475
    struct lws_async_job *job = wsi->async_worker_job;
476
    if (lws_dll2_owner(&job->list) == &wsi->a.context->async_worker_waiting ||
477
        lws_dll2_owner(&job->list) == &wsi->a.context->async_worker_finished ||
478
        job->handled_by_main) {
479
      /* Not actively running. We can safely detach it and reap it. */
480
      wsi->async_worker_job = NULL;
481
      lws_dll2_remove(&job->list);
482
      lws_free(job);
483
      pthread_mutex_unlock(&wsi->a.context->async_worker_mutex);
484
      break;
485
    }
486
    pthread_mutex_unlock(&wsi->a.context->async_worker_mutex);
487
    /* The background thread is actively modifying this WSI or its SSL contexts.
488
     * It is catastrophic to continue closing or freeing this WSI until it is done.
489
     * Because this happens very infrequently (shutdown collisions), we briefly yield.
490
     */
491
    usleep(1000);
492
  }
493
}
494
#endif
495
496
/* requires cx and pt lock */
497
498
void
499
__lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason,
500
         const char *caller)
501
0
{
502
0
  struct lws_context_per_thread *pt;
503
0
  const struct lws_protocols *pro;
504
0
#if defined(LWS_WITH_SECURE_STREAMS)
505
0
  lws_ss_handle_t *hh = NULL;
506
0
#endif
507
0
  struct lws_context *context;
508
0
  struct lws *wsi2;
509
0
  int n, ccb;
510
511
0
  if (!wsi)
512
0
    return;
513
514
0
  lwsl_wsi_info(wsi, "caller: %s", caller);
515
516
0
  lws_access_log(wsi);
517
518
0
  if (!lws_dll2_is_detached(&wsi->dll_buflist))
519
0
    lwsl_wsi_info(wsi, "going down with stuff in buflist");
520
521
0
  context = wsi->a.context;
522
0
  pt = &context->pt[(int)wsi->tsi];
523
524
0
  if (pt->pipe_wsi == wsi) {
525
0
    if (lws_socket_is_valid(wsi->desc.sockfd)) {
526
0
      __remove_wsi_socket_from_fds(wsi);
527
0
      if (lws_socket_is_valid(wsi->desc.sockfd))
528
0
        delete_from_fd(wsi->a.context, wsi->desc.sockfd);
529
0
#if !defined(LWS_PLAT_FREERTOS) && !defined(WIN32) && !defined(LWS_PLAT_OPTEE)
530
0
      delete_from_fdwsi(wsi->a.context, wsi);
531
0
#endif
532
0
    }
533
0
  }
534
535
#if defined(LWS_WITH_SYS_METRICS) && \
536
    (defined(LWS_WITH_CLIENT) || defined(LWS_WITH_SERVER))
537
  /* wsi level: only reports if dangling caliper */
538
  if (wsi->cal_conn.mt && wsi->cal_conn.us_start) {
539
    if ((lws_metrics_priv_to_pub(wsi->cal_conn.mt)->flags) & LWSMTFL_REPORT_HIST) {
540
      lws_metrics_caliper_report_hist(wsi->cal_conn, (struct lws *)NULL);
541
    } else {
542
      lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
543
      lws_metrics_caliper_done(wsi->cal_conn);
544
    }
545
  } else
546
    lws_metrics_caliper_done(wsi->cal_conn);
547
#endif
548
549
#if defined(LWS_WITH_SYS_ASYNC_DNS)
550
  /* is this wsi handling the interface to a dns server? */
551
  {
552
    lws_async_dns_server_t *dsrv =
553
      __lws_async_dns_server_find_wsi(&context->async_dns, wsi);
554
555
    if (dsrv)
556
      dsrv->wsi = NULL;
557
  }
558
#endif
559
560
0
  lws_pt_assert_lock_held(pt);
561
562
0
#if defined(LWS_WITH_CLIENT)
563
#if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_WITH_GNUTLS)
564
  /*
565
   * For multiplexed connections (H3/H2), the TLS session belongs
566
   * to the network-level WSI, not to individual stream WSIs.  Don't
567
   * perform the (expensive) session ticket snapshot on every mux
568
   * substream close — only on the actual network wsi.
569
   */
570
  if (!wsi->client_mux_substream)
571
    lws_tls_session_new_gnutls(wsi);
572
#endif
573
574
0
  lws_free_set_NULL(wsi->cli_hostname_copy);
575
0
  wsi->client_mux_substream_was = wsi->client_mux_substream;
576
577
0
  lws_addrinfo_clean(wsi);
578
0
#endif
579
580
0
#if defined(LWS_WITH_HTTP2)
581
0
  if (wsi->mux_stream_immortal)
582
0
    lws_http_close_immortal(wsi);
583
0
#endif
584
585
  /* if we have children, close them first */
586
0
  while(!lws_dll2_is_empty(&wsi->child_list_owner)) {
587
0
    wsi2 = lws_container_of(lws_dll2_get_head(&wsi->child_list_owner),
588
0
          struct lws, sibling_list);
589
    /* stop it doing shutdown processing */
590
0
    wsi2->socket_is_permanently_unusable = 1;
591
0
    __lws_close_free_wsi(wsi2, reason, "general child recurse");
592
0
    if (lws_dll2_get_head(&wsi->child_list_owner) == &wsi2->sibling_list) {
593
      /*
594
       * The child's close processing did not detach it from
595
       * us: only possible when it bailed at the
596
       * LRS_DEAD_SOCKET check, ie, we were reentrantly closed
597
       * from the child's own close callbacks.  It's still
598
       * alive, just unlink it so we cannot spin.
599
       */
600
0
      lws_dll2_remove(&wsi2->sibling_list);
601
0
      wsi2->parent = NULL;
602
0
    }
603
0
  }
604
605
0
#if defined(LWS_ROLE_RAW_FILE)
606
0
  if (wsi->role_ops == &role_ops_raw_file) {
607
0
    lws_remove_child_from_any_parent(wsi);
608
0
    __remove_wsi_socket_from_fds(wsi);
609
0
    if (wsi->a.protocol)
610
0
      wsi->a.protocol->callback(wsi, wsi->role_ops->close_cb[0],
611
0
          wsi->user_space, NULL, 0);
612
0
    goto async_close;
613
0
  }
614
0
#endif
615
616
0
  wsi->wsistate_pre_close = wsi->wsistate;
617
618
#ifdef LWS_WITH_CGI
619
  if (wsi->role_ops == &role_ops_cgi) {
620
621
    // lwsl_debug("%s: closing stdwsi index %d\n", __func__, (int)wsi->lsp_channel);
622
623
    /* we are not a network connection, but a handler for CGI io */
624
    if (wsi->parent && wsi->parent->http.cgi) {
625
626
      /*
627
       * We need to keep the logical cgi around so we can
628
       * drain it
629
       */
630
631
      /* end the binding between us and network connection */
632
      if (wsi->parent->http.cgi && wsi->parent->http.cgi->lsp)
633
        wsi->parent->http.cgi->lsp->stdwsi[(int)wsi->lsp_channel] =
634
                  NULL;
635
    }
636
    wsi->socket_is_permanently_unusable = 1;
637
638
    goto just_kill_connection;
639
  }
640
641
  if (wsi->http.cgi)
642
    lws_cgi_remove_and_kill(wsi);
643
#endif
644
645
0
#if defined(LWS_WITH_CLIENT)
646
0
  if (!wsi->close_is_redirect)
647
0
    lws_free_set_NULL(wsi->stash);
648
0
#endif
649
650
#if defined(LWS_WITH_ASYNC_QUEUE)
651
  lws_async_worker_wait_and_reap(wsi);
652
#endif
653
654
0
  if (wsi->role_ops == &role_ops_raw_skt) {
655
0
    wsi->socket_is_permanently_unusable = 1;
656
0
    goto just_kill_connection;
657
0
  }
658
0
#if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
659
0
  if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
660
0
      wsi->http.fop_fd != NULL)
661
0
    lws_vfs_file_close(&wsi->http.fop_fd);
662
0
#endif
663
664
0
  if (lwsi_state(wsi) == LRS_DEAD_SOCKET)
665
0
    return;
666
667
0
  if (wsi->socket_is_permanently_unusable ||
668
0
      reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
669
0
      lwsi_state(wsi) == LRS_SHUTDOWN)
670
0
    goto just_kill_connection;
671
672
0
  switch (lwsi_state_PRE_CLOSE(wsi)) {
673
0
  case LRS_DEAD_SOCKET:
674
0
    return;
675
676
  /* we tried the polite way... */
677
0
  case LRS_WAITING_TO_SEND_CLOSE:
678
0
  case LRS_AWAITING_CLOSE_ACK:
679
0
  case LRS_RETURNED_CLOSE:
680
0
    goto just_kill_connection;
681
682
0
  case LRS_FLUSHING_BEFORE_CLOSE:
683
0
    if (lws_has_buffered_out(wsi)
684
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
685
        || wsi->http.comp_ctx.buflist_comp ||
686
        wsi->http.comp_ctx.may_have_more
687
#endif
688
0
     ) {
689
0
      lws_callback_on_writable(wsi);
690
0
      return;
691
0
    }
692
0
    lwsl_wsi_info(wsi, " end LRS_FLUSHING_BEFORE_CLOSE");
693
0
    goto just_kill_connection;
694
0
  default:
695
0
    if (lws_has_buffered_out(wsi)
696
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
697
        || wsi->http.comp_ctx.buflist_comp ||
698
        wsi->http.comp_ctx.may_have_more
699
#endif
700
0
    ) {
701
0
      lwsl_wsi_info(wsi, "LRS_FLUSHING_BEFORE_CLOSE");
702
0
      lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
703
0
      __lws_set_timeout(wsi,
704
0
        PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
705
0
      return;
706
0
    }
707
0
    break;
708
0
  }
709
710
0
  if (lwsi_state(wsi) == LRS_WAITING_CONNECT ||
711
0
      lwsi_state(wsi) == LRS_WAITING_DNS ||
712
0
      lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE)
713
0
    goto just_kill_connection;
714
715
0
  if (!wsi->told_user_closed && wsi->user_space && wsi->a.protocol &&
716
0
      wsi->protocol_bind_balance) {
717
0
    wsi->a.protocol->callback(wsi,
718
0
        wsi->role_ops->protocol_unbind_cb[
719
0
               !!lwsi_role_server(wsi)],
720
0
               wsi->user_space, (void *)__func__, 0);
721
0
    wsi->protocol_bind_balance = 0;
722
0
  }
723
724
  /*
725
   * signal we are closing, lws_write will
726
   * add any necessary version-specific stuff.  If the write fails,
727
   * no worries we are closing anyway.  If we didn't initiate this
728
   * close, then our state has been changed to
729
   * LRS_RETURNED_CLOSE and we will skip this.
730
   *
731
   * Likewise if it's a second call to close this connection after we
732
   * sent the close indication to the peer already, we are in state
733
   * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time.
734
   */
735
736
0
  if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_via_role_protocol) &&
737
0
      lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_close_via_role_protocol).
738
0
           close_via_role_protocol(wsi, reason)) {
739
0
    lwsl_wsi_info(wsi, "close_via_role took over (sockfd %d)",
740
0
            wsi->desc.sockfd);
741
0
    return;
742
0
  }
743
744
0
just_kill_connection:
745
746
0
  lwsl_wsi_debug(wsi, "real just_kill_connection A: (sockfd %d)",
747
0
      wsi->desc.sockfd);
748
749
#if defined(LWS_WITH_THREADPOOL) && defined(LWS_HAVE_PTHREAD_H)
750
  lws_threadpool_wsi_closing(wsi);
751
#endif
752
753
754
0
#if defined(LWS_WITH_FILE_OPS) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
755
0
  if (lwsi_role_http(wsi) && lwsi_role_server(wsi) &&
756
0
      wsi->http.fop_fd != NULL)
757
0
    lws_vfs_file_close(&wsi->http.fop_fd);
758
0
#endif
759
760
0
  lws_sul_cancel(&wsi->sul_connect_timeout);
761
#if defined(WIN32)
762
  lws_sul_cancel(&wsi->win32_sul_connect_async_check);
763
#endif
764
0
#if defined(LWS_WITH_CLIENT)
765
0
  lws_sul_cancel(&wsi->sul_happy_eyeballs);
766
#if defined(LWS_ROLE_H3) || defined(LWS_ROLE_QUIC)
767
  lws_sul_cancel(&wsi->sul_h3_grace);
768
#endif
769
0
  for (int m = 0; m < wsi->parallel_count; m++) {
770
0
    if (wsi->parallel_conns[m].is_valid) {
771
0
      lws_remove_parallel_fd_safely(wsi, m);
772
0
    }
773
0
  }
774
0
  wsi->parallel_count = 0;
775
0
#endif
776
#if defined(LWS_WITH_SYS_ASYNC_DNS)
777
  lws_async_dns_cancel(wsi);
778
#endif
779
780
#if defined(LWS_WITH_HTTP_PROXY)
781
  if (wsi->http.buflist_post_body)
782
    lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body);
783
#endif
784
785
0
  if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection))
786
0
    lws_rops_func_fidx(wsi->role_ops,
787
0
           LWS_ROPS_close_kill_connection).
788
0
              close_kill_connection(wsi, reason);
789
790
0
  n = 0;
791
792
0
  if (!wsi->told_user_closed && wsi->user_space &&
793
0
      wsi->protocol_bind_balance && wsi->a.protocol) {
794
0
    lwsl_debug("%s: %s: DROP_PROTOCOL %s\n", __func__, lws_wsi_tag(wsi),
795
0
         wsi->a.protocol ? wsi->a.protocol->name: "NULL");
796
0
    if (wsi->a.protocol)
797
0
      wsi->a.protocol->callback(wsi,
798
0
        wsi->role_ops->protocol_unbind_cb[
799
0
               !!lwsi_role_server(wsi)],
800
0
               wsi->user_space, (void *)__func__, 0);
801
0
    wsi->protocol_bind_balance = 0;
802
0
  }
803
804
0
#if defined(LWS_WITH_CLIENT)
805
0
  if ((
806
0
#if defined(LWS_ROLE_WS)
807
    /*
808
     * If our goal is a ws upgrade, effectively we did not reach
809
     * ESTABLISHED if we did not get the upgrade server reply
810
     */
811
0
    (lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY &&
812
0
     wsi->role_ops == &role_ops_ws) ||
813
0
#endif
814
0
       lwsi_state(wsi) == LRS_WAITING_DNS ||
815
0
       lwsi_state(wsi) == LRS_WAITING_CONNECT ||
816
0
       (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)) &&
817
0
       !wsi->already_did_cce && wsi->a.protocol &&
818
0
       !wsi->close_is_redirect) {
819
0
    static const char _reason[] = "closed before established";
820
821
0
    lwsl_wsi_debug(wsi, "closing in unestablished state 0x%x "
822
0
        "(fd %d, parallels %d, redirect %d)",
823
0
        lwsi_state(wsi),
824
0
        (int)wsi->desc.sockfd, wsi->parallel_count,
825
0
        wsi->close_is_redirect);
826
0
    wsi->socket_is_permanently_unusable = 1;
827
828
0
    lws_inform_client_conn_fail(wsi,
829
0
      (void *)_reason, sizeof(_reason) - 1);
830
0
  }
831
0
#endif
832
833
  /*
834
   * Testing with ab shows that we have to stage the socket close when
835
   * the system is under stress... shutdown any further TX, change the
836
   * state to one that won't emit anything more, and wait with a timeout
837
   * for the POLLIN to show a zero-size rx before coming back and doing
838
   * the actual close.
839
   */
840
0
  if (wsi->role_ops != &role_ops_raw_skt && !lwsi_role_client(wsi) &&
841
0
      lwsi_state(wsi) != LRS_SHUTDOWN &&
842
0
      lwsi_state(wsi) != LRS_UNCONNECTED &&
843
0
      reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
844
0
      !wsi->socket_is_permanently_unusable) {
845
846
0
#if defined(LWS_WITH_TLS)
847
0
    if (lws_is_ssl(wsi) && wsi->tls.ssl) {
848
0
      n = 0;
849
0
      switch (__lws_tls_shutdown(wsi)) {
850
0
      case LWS_SSL_CAPABLE_DONE:
851
0
      case LWS_SSL_CAPABLE_ERROR:
852
0
      case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
853
0
      case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
854
0
        if (wsi->lsp_channel++ == 8) {
855
0
          lwsl_wsi_info(wsi, "avoiding shutdown spin");
856
0
          lwsi_set_state(wsi, LRS_SHUTDOWN);
857
0
        }
858
0
        break;
859
0
      }
860
0
    } else
861
0
#endif
862
0
    {
863
0
      lwsl_info("%s: shutdown conn: %s (sk %d, state 0x%x)\n",
864
0
          __func__, lws_wsi_tag(wsi), (int)(lws_intptr_t)wsi->desc.sockfd,
865
0
          lwsi_state(wsi));
866
0
      if (!wsi->socket_is_permanently_unusable &&
867
0
          lws_socket_is_valid(wsi->desc.sockfd)) {
868
0
        wsi->socket_is_permanently_unusable = 1;
869
0
        n = shutdown(wsi->desc.sockfd, SHUT_WR);
870
0
      }
871
0
    }
872
0
    if (n)
873
0
      lwsl_wsi_debug(wsi, "closing: shutdown (state 0x%x) ret %d",
874
0
           lwsi_state(wsi), LWS_ERRNO);
875
876
    /*
877
     * This causes problems on WINCE / ESP32 with disconnection
878
     * when the events are half closing connection
879
     */
880
0
#if !defined(_WIN32_WCE) && !defined(LWS_PLAT_FREERTOS)
881
    /* libuv: no event available to guarantee completion */
882
0
    if (!wsi->socket_is_permanently_unusable &&
883
0
#if defined(LWS_WITH_CLIENT)
884
0
        !wsi->close_is_redirect &&
885
0
#endif
886
0
        lws_socket_is_valid(wsi->desc.sockfd) &&
887
0
        lwsi_state(wsi) != LRS_SHUTDOWN &&
888
0
        (context->event_loop_ops->flags & LELOF_ISPOLL)) {
889
0
      __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
890
0
      lwsi_set_state(wsi, LRS_SHUTDOWN);
891
0
      __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
892
0
            (int)context->timeout_secs);
893
894
0
      return;
895
0
    }
896
0
#endif
897
0
  }
898
899
0
  lwsl_wsi_info(wsi, "real just_kill_connection: sockfd %d\n",
900
0
      wsi->desc.sockfd);
901
902
#ifdef LWS_WITH_HUBBUB
903
  if (wsi->http.rw) {
904
    lws_rewrite_destroy(wsi->http.rw);
905
    wsi->http.rw = NULL;
906
  }
907
#endif
908
909
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
910
0
  if (wsi->http.pending_return_headers)
911
0
    lws_free_set_NULL(wsi->http.pending_return_headers);
912
0
#endif
913
914
  /*
915
   * we won't be servicing or receiving anything further from this guy
916
   * delete socket from the internal poll list if still present
917
   */
918
0
  __lws_ssl_remove_wsi_from_buffered_list(wsi);
919
0
  __lws_wsi_remove_from_sul(wsi);
920
921
  //if (wsi->told_event_loop_closed) // cgi std close case (dummy-callback)
922
  //  return;
923
924
  /* checking return redundant since we anyway close */
925
0
  __remove_wsi_socket_from_fds(wsi);
926
927
0
  lwsi_set_state(wsi, LRS_DEAD_SOCKET);
928
0
  lws_buflist_destroy_all_segments(&wsi->buflist);
929
0
  lws_dll2_remove(&wsi->dll_buflist);
930
931
0
  if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_role))
932
0
    lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_close_role).
933
0
              close_role(pt, wsi);
934
935
  /* tell the user it's all over for this guy */
936
937
0
  ccb = 0;
938
0
  if ((lwsi_state_est_PRE_CLOSE(wsi) ||
939
      /* raw skt adopted but didn't complete tls hs should CLOSE */
940
0
      (wsi->role_ops == &role_ops_raw_skt && !lwsi_role_client(wsi)) ||
941
0
       lwsi_state_PRE_CLOSE(wsi) == LRS_WAITING_SERVER_REPLY) &&
942
0
      !wsi->told_user_closed &&
943
0
      wsi->role_ops->close_cb[lwsi_role_server(wsi)]) {
944
0
    if (!wsi->upgraded_to_http2 || !lwsi_role_client(wsi))
945
0
      ccb = 1;
946
      /*
947
       * The network wsi for a client h2 connection shouldn't
948
       * call back for its role: the child stream connections
949
       * own the role.  Otherwise h2 will call back closed
950
       * one too many times as the children do it and then
951
       * the closing network stream.
952
       */
953
0
  }
954
955
0
  if (!wsi->told_user_closed &&
956
0
      !lws_dll2_is_detached(&wsi->vh_awaiting_socket))
957
    /*
958
     * He's a guy who go started with dns, but failed or is
959
     * caught with a shutdown before he got the result.  We have
960
     * to issclient_mux_substream_wasue him a close cb
961
     */
962
0
    ccb = 1;
963
964
0
  lwsl_wsi_info(wsi, "cce=%d", ccb);
965
966
0
  pro = wsi->a.protocol;
967
968
0
  if (wsi->already_did_cce)
969
    /*
970
     * If we handled this by CLIENT_CONNECTION_ERROR, it's
971
     * mutually exclusive with CLOSE
972
     */
973
0
    ccb = 0;
974
975
0
#if defined(LWS_WITH_CLIENT)
976
0
  if (!wsi->close_is_redirect && !ccb &&
977
0
      (lwsi_state_PRE_CLOSE(wsi) & LWSIFS_NOT_EST) &&
978
0
      lwsi_role_client(wsi)) {
979
0
    lws_inform_client_conn_fail(wsi, "Closed before conn", 18);
980
0
  }
981
0
#endif
982
0
  if (ccb
983
0
#if defined(LWS_WITH_CLIENT)
984
0
      && !wsi->close_is_redirect
985
0
#endif
986
0
  ) {
987
988
0
    if (!wsi->a.protocol && wsi->a.vhost && wsi->a.vhost->protocols)
989
0
      pro = &wsi->a.vhost->protocols[0];
990
991
0
    if (pro && pro->callback)
992
0
      pro->callback(wsi,
993
0
        wsi->role_ops->close_cb[lwsi_role_server(wsi)],
994
0
        wsi->user_space, NULL, 0);
995
0
    wsi->told_user_closed = 1;
996
0
  }
997
998
0
#if defined(LWS_ROLE_RAW_FILE)
999
0
async_close:
1000
0
#endif
1001
1002
0
#if defined(LWS_WITH_SECURE_STREAMS)
1003
0
  if (wsi->for_ss) {
1004
0
    lwsl_wsi_debug(wsi, "for_ss");
1005
    /*
1006
     * We were adopted for a particular ss, but, eg, we may not
1007
     * have succeeded with the connection... we are closing which is
1008
     * good, but we have to invalidate any pointer the related ss
1009
     * handle may be holding on us
1010
     */
1011
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
1012
1013
    if (wsi->client_proxy_onward) {
1014
      /*
1015
       * We are an onward proxied wsi at the proxy,
1016
       * opaque is proxing "conn", we must remove its pointer
1017
       * to us since we are destroying
1018
       */
1019
      lws_proxy_clean_conn_ss(wsi);
1020
    } else
1021
1022
      if (wsi->client_bound_sspc) {
1023
        lws_sspc_handle_t *h = (lws_sspc_handle_t *)wsi->a.opaque_user_data;
1024
1025
        if (h) { // && (h->info.flags & LWSSSINFLAGS_ACCEPTED)) {
1026
1027
#if defined(LWS_WITH_SYS_METRICS)
1028
          /*
1029
           * If any hanging caliper measurement, dump it, and free any tags
1030
           */
1031
          lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL);
1032
#endif
1033
1034
          h->txp_path.priv_onw = NULL;
1035
          //wsi->a.opaque_user_data = NULL;
1036
        }
1037
      } else
1038
#endif
1039
0
    {
1040
0
      hh = (lws_ss_handle_t *)wsi->a.opaque_user_data;
1041
1042
0
      if (hh) { // && (h->info.flags & LWSSSINFLAGS_ACCEPTED)) {
1043
1044
        /*
1045
         * ss level: only reports if dangling caliper
1046
         * not already reported
1047
         */
1048
0
        lws_metrics_caliper_report_hist(hh->cal_txn, wsi);
1049
1050
        /*
1051
         * If we're surviving this close as a redirect
1052
         * (eg, QUIC attempt failed and we're falling
1053
         * back to TCP on the same wsi), don't cut the
1054
         * binding between the ss handle and the wsi
1055
         * that is about to restart
1056
         */
1057
0
        if (!wsi->close_is_redirect) {
1058
0
          hh->wsi = NULL;
1059
0
          wsi->a.opaque_user_data = NULL;
1060
0
        }
1061
0
      }
1062
0
    }
1063
0
  }
1064
0
#endif
1065
1066
1067
0
  lws_remove_child_from_any_parent(wsi);
1068
0
  wsi->socket_is_permanently_unusable = 1;
1069
1070
0
  if (wsi->a.context->event_loop_ops->wsi_logical_close)
1071
0
    if (wsi->a.context->event_loop_ops->wsi_logical_close(wsi))
1072
0
      return;
1073
1074
0
  __lws_close_free_wsi_final(wsi);
1075
1076
0
#if defined(LWS_WITH_SECURE_STREAMS)
1077
0
  if (hh && hh->ss_dangling_connected &&
1078
0
      lws_ss_event_helper(hh, LWSSSCS_DISCONNECTED) == LWSSSSRET_DESTROY_ME)
1079
0
    lws_ss_destroy(&hh);
1080
0
#endif
1081
0
}
1082
1083
1084
/* cx + vh lock */
1085
1086
void
1087
__lws_close_free_wsi_final(struct lws *wsi)
1088
0
{
1089
0
  int n, ssl_handled = 0;
1090
1091
#if defined(LWS_WITH_ASYNC_QUEUE)
1092
  lws_async_worker_wait_and_reap(wsi);
1093
#endif
1094
1095
0
  if (!wsi->shadow)
1096
0
    ssl_handled = lws_ssl_close(wsi);
1097
1098
0
  if (!wsi->shadow &&
1099
0
      lws_socket_is_valid(wsi->desc.sockfd) && !ssl_handled) {
1100
0
    lwsl_wsi_debug(wsi, "fd %d", wsi->desc.sockfd);
1101
1102
0
    __remove_wsi_socket_from_fds(wsi);
1103
0
    if (lws_socket_is_valid(wsi->desc.sockfd))
1104
0
      delete_from_fd(wsi->a.context, wsi->desc.sockfd);
1105
1106
    /*
1107
     * if this is the pt pipe, skip the actual close,
1108
     * go through the motions though so we will reach 0 open wsi
1109
     * on the pt, and trigger the pt destroy to close the pipe fds
1110
     */
1111
0
    if (!lws_plat_pipe_is_fd_assocated(wsi->a.context, wsi->tsi,
1112
0
               wsi->desc.sockfd)) {
1113
0
      n = compatible_close(wsi->desc.sockfd);
1114
0
      if (n)
1115
0
        lwsl_wsi_debug(wsi, "closing: close ret %d",
1116
0
                 LWS_ERRNO);
1117
0
    }
1118
1119
0
#if !defined(LWS_PLAT_FREERTOS) && !defined(WIN32) && !defined(LWS_PLAT_OPTEE)
1120
0
    delete_from_fdwsi(wsi->a.context, wsi);
1121
0
#endif
1122
1123
0
    sanity_assert_no_sockfd_traces(wsi->a.context, wsi->desc.sockfd);
1124
0
  }
1125
1126
  /* ... if we're closing the cancel pipe, account for it */
1127
0
  {
1128
0
    struct lws_context_per_thread *pt =
1129
0
        &wsi->a.context->pt[(int)wsi->tsi];
1130
1131
0
    if (pt->pipe_wsi == wsi) {
1132
0
      lws_plat_pipe_close(wsi);
1133
0
      pt->pipe_wsi = NULL;
1134
0
    }
1135
0
    if (pt->dummy_pipe_fds[0] == wsi->desc.sockfd)
1136
0
               {
1137
0
#if !defined(LWS_PLAT_FREERTOS)
1138
0
      pt->dummy_pipe_fds[0] = LWS_SOCK_INVALID;
1139
0
#endif
1140
0
               }
1141
0
  }
1142
1143
0
  wsi->desc.sockfd = LWS_SOCK_INVALID;
1144
1145
0
#if defined(LWS_WITH_CLIENT)
1146
0
  lws_free_set_NULL(wsi->cli_hostname_copy);
1147
0
  if (wsi->close_is_redirect) {
1148
1149
0
    wsi->close_is_redirect = 0;
1150
1151
0
    lwsl_wsi_info(wsi, "picking up redirection");
1152
1153
0
#if defined(LWS_ROLE_H1)
1154
0
    lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
1155
0
            &role_ops_h1);
1156
#elif defined(LWS_ROLE_H2)
1157
    lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
1158
            &role_ops_h2);
1159
#elif defined(LWS_ROLE_H3)
1160
    lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
1161
            &role_ops_h3);
1162
#else
1163
    wsi->socket_is_permanently_unusable = 1;
1164
#endif
1165
1166
0
    wsi->wsistate_pre_close = 0;
1167
1168
0
#if defined(LWS_WITH_HTTP2)
1169
0
    if (wsi->client_mux_substream_was)
1170
0
      wsi->h2.END_STREAM = wsi->h2.END_HEADERS = 0;
1171
0
#endif
1172
0
#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) || defined(LWS_ROLE_H3)
1173
0
    if (wsi->mux.parent_wsi)
1174
0
      lws_wsi_mux_sibling_disconnect(wsi);
1175
0
#endif
1176
1177
0
#if defined(LWS_WITH_TLS)
1178
0
    memset(&wsi->tls, 0, sizeof(wsi->tls));
1179
0
#endif
1180
1181
  //  wsi->a.protocol = NULL;
1182
0
    if (wsi->a.protocol)
1183
0
      lws_bind_protocol(wsi, wsi->a.protocol, "client_reset");
1184
0
    wsi->pending_timeout = NO_PENDING_TIMEOUT;
1185
0
    wsi->hdr_parsing_completed = 0;
1186
1187
0
#if defined(LWS_WITH_TLS)
1188
0
    if (wsi->stash->cis[CIS_ALPN])
1189
0
      lws_strncpy(wsi->alpn, wsi->stash->cis[CIS_ALPN],
1190
0
            sizeof(wsi->alpn));
1191
0
#endif
1192
1193
0
    if (lws_header_table_attach(wsi, 0)) {
1194
0
      lwsl_wsi_err(wsi, "failed to get ah");
1195
0
      return;
1196
0
    }
1197
//    }
1198
    //_lws_header_table_reset(wsi->http.ah);
1199
1200
0
#if defined(LWS_WITH_TLS)
1201
0
    wsi->tls.use_ssl = (unsigned int)wsi->flags;
1202
0
#endif
1203
1204
#if defined(LWS_WITH_TLS_JIT_TRUST)
1205
    if (wsi->stash && wsi->stash->cis[CIS_ADDRESS]) {
1206
      struct lws_vhost *vh = NULL;
1207
      lws_tls_jit_trust_vhost_bind(wsi->a.context,
1208
                 wsi->stash->cis[CIS_ADDRESS],
1209
                 &vh);
1210
      if (vh) {
1211
        if (!vh->count_bound_wsi && vh->grace_after_unref) {
1212
          lwsl_wsi_info(wsi, "%s in use\n",
1213
                vh->lc.gutag);
1214
          lws_sul_cancel(&vh->sul_unref);
1215
        }
1216
        vh->count_bound_wsi++;
1217
        wsi->a.vhost = vh;
1218
      }
1219
    }
1220
#endif
1221
1222
0
    return;
1223
0
  }
1224
0
#endif
1225
1226
  /* outermost destroy notification for wsi (user_space still intact) */
1227
0
  if (wsi->a.vhost)
1228
0
    wsi->a.vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
1229
0
              wsi->user_space, NULL, 0);
1230
1231
#ifdef LWS_WITH_CGI
1232
  if (wsi->http.cgi) {
1233
    lws_spawn_piped_destroy(&wsi->http.cgi->lsp);
1234
    lws_sul_cancel(&wsi->http.cgi->sul_grace);
1235
    lws_free_set_NULL(wsi->http.cgi);
1236
  }
1237
#endif
1238
1239
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
1240
  lws_fi_destroy(&wsi->fic);
1241
#endif
1242
1243
0
  __lws_wsi_remove_from_sul(wsi);
1244
0
  sanity_assert_no_wsi_traces(wsi->a.context, wsi);
1245
0
  __lws_free_wsi(wsi);
1246
0
}
1247
1248
1249
void
1250
lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller)
1251
0
{
1252
0
  struct lws_context *cx = wsi->a.context;
1253
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
1254
1255
0
  lws_context_lock(cx, __func__);
1256
1257
0
  lws_pt_lock(pt, __func__);
1258
  /* may destroy vhost, cannot hold vhost lock outside it */
1259
0
  __lws_close_free_wsi(wsi, reason, caller);
1260
0
  lws_pt_unlock(pt);
1261
1262
0
  lws_context_unlock(cx);
1263
0
}
1264
1265