Coverage Report

Created: 2026-08-31 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/core-net/wsi.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
27
0
const char *lws_wsi_tag(struct lws *wsi) {
28
0
  if (!wsi)
29
0
    return "[null wsi]";
30
0
  return lws_lc_tag(&wsi->lc);
31
0
}
32
33
#if defined(_DEBUG)
34
0
void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) {
35
0
  wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role;
36
37
0
  lwsl_wsi_debug(wsi, "state 0x%lx", (unsigned long)wsi->wsistate);
38
0
}
39
40
0
void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs) {
41
0
  lws_wsi_state_t old = wsi->wsistate;
42
43
0
  wsi->wsistate = (old & (unsigned int)(~LRS_MASK)) | lrs;
44
45
0
  lwsl_wsi_debug(wsi, "lwsi_set_state 0x%lx -> 0x%lx", (unsigned long)old,
46
0
      (unsigned long)wsi->wsistate);
47
0
}
48
#endif
49
50
0
void lws_log_prepend_wsi(struct lws_log_cx *cx, void *obj, char **p, char *e) {
51
0
  struct lws *wsi = (struct lws *)obj;
52
53
0
  *p += lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), "%s: ", lws_wsi_tag(wsi));
54
0
}
55
56
0
void lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) {
57
0
  if (wsi->a.vhost == vh)
58
0
    return;
59
60
0
  lws_context_lock(vh->context, __func__); /* ---------- context { */
61
0
  wsi->a.vhost = vh;
62
63
#if defined(LWS_WITH_TLS_JIT_TRUST)
64
  if (!vh->count_bound_wsi && vh->grace_after_unref) {
65
    lwsl_wsi_info(wsi, "in use");
66
    lws_sul_cancel(&vh->sul_unref);
67
  }
68
#endif
69
70
0
  vh->count_bound_wsi++;
71
0
  lws_context_unlock(vh->context); /* } context ---------- */
72
73
0
  lwsl_wsi_debug(wsi, "vh %s: wsi %s/%s, count_bound_wsi %d\n", vh->name,
74
0
      wsi->role_ops ? wsi->role_ops->name : "none",
75
0
      wsi->a.protocol ? wsi->a.protocol->name : "none",
76
0
      vh->count_bound_wsi);
77
0
  assert(wsi->a.vhost->count_bound_wsi > 0);
78
0
}
79
80
/* req cx lock... acquires vh lock */
81
0
void __lws_vhost_unbind_wsi(struct lws *wsi) {
82
0
  struct lws_vhost *vh = wsi->a.vhost;
83
84
0
  if (!vh)
85
0
    return;
86
87
0
  lws_context_assert_lock_held(wsi->a.context);
88
89
0
  lws_vhost_lock(vh);
90
91
0
  assert(vh->count_bound_wsi > 0);
92
0
  vh->count_bound_wsi--;
93
94
#if defined(LWS_WITH_TLS_JIT_TRUST)
95
  if (!vh->count_bound_wsi && vh->grace_after_unref)
96
    lws_tls_jit_trust_vh_start_grace(vh);
97
#endif
98
99
0
  lwsl_wsi_debug(wsi, "vh %s: count_bound_wsi %d", vh->name,
100
0
      vh->count_bound_wsi);
101
102
0
  lws_vhost_unlock(vh);
103
104
0
  if (!vh->count_bound_wsi && vh->being_destroyed)
105
    /*
106
     * We have closed all wsi that were bound to this vhost
107
     * by any pt: nothing can be servicing any wsi belonging
108
     * to it any more.
109
     *
110
     * Finalize the vh destruction... must drop vh lock
111
     */
112
0
    __lws_vhost_destroy2(vh);
113
114
0
  wsi->a.vhost = NULL;
115
0
}
116
117
0
struct lws *lws_get_network_wsi(struct lws *wsi) {
118
0
  if (!wsi)
119
0
    return NULL;
120
121
0
#if defined(LWS_WITH_HTTP2) || defined(LWS_ROLE_MQTT) || defined(LWS_ROLE_QUIC)
122
0
  if (!wsi->mux_substream
123
0
#if defined(LWS_WITH_CLIENT)
124
0
      && !wsi->client_mux_substream
125
0
#endif
126
0
     )
127
0
    return wsi;
128
129
0
  while (wsi->mux.parent_wsi)
130
0
    wsi = wsi->mux.parent_wsi;
131
0
#endif
132
133
0
  return wsi;
134
0
}
135
136
const struct lws_protocols *lws_vhost_name_to_protocol(struct lws_vhost *vh,
137
0
    const char *name) {
138
0
  int n;
139
140
0
  for (n = 0; n < vh->count_protocols; n++)
141
0
    if (vh->protocols[n].name && !strcmp(name, vh->protocols[n].name))
142
0
      return &vh->protocols[n];
143
144
0
  return NULL;
145
0
}
146
147
int lws_callback_all_protocol(struct lws_context *context,
148
    const struct lws_protocols *protocol,
149
0
    int reason) {
150
0
  struct lws_context_per_thread *pt = &context->pt[0];
151
0
  unsigned int n, m = context->count_threads;
152
0
  struct lws *wsi;
153
154
0
  while (m--) {
155
0
    for (n = 0; n < pt->fds_count; n++) {
156
0
      wsi = wsi_from_fd(context, pt->fds[n].fd);
157
0
      if (!wsi || !wsi->a.protocol)
158
0
        continue;
159
0
      if (wsi->a.protocol->callback == protocol->callback &&
160
0
          !strcmp(protocol->name, wsi->a.protocol->name))
161
0
        protocol->callback(wsi, (enum lws_callback_reasons)reason,
162
0
            wsi->user_space, NULL, 0);
163
0
    }
164
0
    pt++;
165
0
  }
166
167
0
  return 0;
168
0
}
169
170
0
void *lws_evlib_wsi_to_evlib_pt(struct lws *wsi) {
171
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
172
173
0
  return pt->evlib_pt;
174
0
}
175
176
0
void *lws_evlib_tsi_to_evlib_pt(struct lws_context *cx, int tsi) {
177
0
  struct lws_context_per_thread *pt = &cx->pt[tsi];
178
179
0
  return pt->evlib_pt;
180
0
}
181
182
int lws_callback_all_protocol_vhost_args(struct lws_vhost *vh,
183
    const struct lws_protocols *protocol,
184
0
    int reason, void *argp, size_t len) {
185
0
  struct lws_context *context = vh->context;
186
0
  struct lws_context_per_thread *pt = &context->pt[0];
187
0
  unsigned int n, m = context->count_threads;
188
0
  struct lws *wsi;
189
190
0
  while (m--) {
191
0
    for (n = 0; n < pt->fds_count; n++) {
192
0
      wsi = wsi_from_fd(context, pt->fds[n].fd);
193
194
0
      if (!wsi || !wsi->a.protocol || wsi->a.vhost != vh)
195
0
        continue;
196
197
0
      if (protocol && wsi->a.protocol->callback != protocol->callback &&
198
0
          strcmp(protocol->name, wsi->a.protocol->name))
199
0
        continue;
200
201
0
      wsi->a.protocol->callback(wsi, (enum lws_callback_reasons)reason,
202
0
          wsi->user_space, argp, len);
203
0
    }
204
0
    pt++;
205
0
  }
206
207
0
  return 0;
208
0
}
209
210
int lws_callback_all_protocol_vhost(struct lws_vhost *vh,
211
    const struct lws_protocols *protocol,
212
0
    int reason) {
213
0
  return lws_callback_all_protocol_vhost_args(vh, protocol, reason, NULL, 0);
214
0
}
215
216
int lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in,
217
0
    size_t len) {
218
0
  int n;
219
220
0
  for (n = 0; n < wsi->a.vhost->count_protocols; n++)
221
0
    if (wsi->a.vhost->protocols[n].callback(
222
0
          wsi, (enum lws_callback_reasons)reason, NULL, in, len))
223
0
      return 1;
224
225
0
  return 0;
226
0
}
227
228
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
229
/*
230
 * We want to inject a fault that makes it feel like the peer hung up on us,
231
 * or we were otherwise cut off.
232
 */
233
void lws_wsi_fault_timedclose_cb(lws_sorted_usec_list_t *s) {
234
  struct lws *wsi = lws_container_of(s, struct lws, sul_fault_timedclose);
235
236
  lwsl_wsi_warn(wsi, "force-closing");
237
  lws_wsi_close(wsi, LWS_TO_KILL_ASYNC);
238
}
239
#endif
240
241
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
242
void lws_wsi_fault_timedclose(struct lws *wsi) {
243
  uint64_t u;
244
245
  if (!lws_fi(&wsi->fic, "timedclose"))
246
    return;
247
248
  if (lws_fi_range(&wsi->fic, "timedclose_ms", &u))
249
    return;
250
251
  lwsl_wsi_warn(wsi, "injecting close in %ums", (unsigned int)u);
252
  lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->sul_fault_timedclose,
253
      lws_wsi_fault_timedclose_cb, (lws_usec_t)(u * 1000ull));
254
}
255
#endif
256
257
/*
258
 * We need the context lock
259
 *
260
 * If we returned a wsi rather than NULL, it is listed on the
261
 * context->pre_natal_owner list of wild wsi not yet part of
262
 * a vhost or on the fd list.
263
 */
264
265
struct lws *__lws_wsi_create_with_role(struct lws_context *context, int tsi,
266
    const struct lws_role_ops *ops,
267
0
    lws_log_cx_t *log_cx_template) {
268
0
  struct lws_context_per_thread *pt = &context->pt[tsi];
269
0
  size_t s = sizeof(struct lws);
270
0
  struct lws *wsi;
271
272
0
  assert(tsi >= 0 && tsi < LWS_MAX_SMP);
273
274
0
  lws_context_assert_lock_held(context);
275
276
#if defined(LWS_WITH_EVENT_LIBS)
277
  s += context->event_loop_ops->evlib_size_wsi;
278
#endif
279
280
0
  wsi = lws_zalloc(s, __func__);
281
282
0
  if (!wsi) {
283
0
    lwsl_cx_err(context, "OOM");
284
0
    return NULL;
285
0
  }
286
287
0
  if (log_cx_template)
288
0
    wsi->lc.log_cx = log_cx_template;
289
0
  else
290
0
    wsi->lc.log_cx = context->log_cx;
291
292
#if defined(LWS_WITH_EVENT_LIBS)
293
  wsi->evlib_wsi = (uint8_t *)wsi + sizeof(*wsi);
294
#endif
295
0
  wsi->a.context = context;
296
0
  lws_role_transition(wsi, 0, LRS_UNCONNECTED, ops);
297
0
  wsi->pending_timeout = NO_PENDING_TIMEOUT;
298
0
  wsi->a.protocol = NULL;
299
0
  wsi->tsi = (char)tsi;
300
0
  wsi->a.vhost = NULL;
301
0
  wsi->desc.sockfd = LWS_SOCK_INVALID;
302
0
  wsi->position_in_fds_table = LWS_NO_FDS_POS;
303
304
#if defined(LWS_WITH_SYS_FAULT_INJECTION)
305
  lws_xos_init(&wsi->fic.xos, lws_xos(&context->fic.xos));
306
#endif
307
308
0
  lws_fi_inherit_copy(&wsi->fic, &context->fic, "wsi", NULL);
309
310
0
  if (lws_fi(&wsi->fic, "createfail")) {
311
0
    lws_dll2_remove(&wsi->pre_natal);
312
0
    lws_fi_destroy(&wsi->fic);
313
0
    lws_free(wsi);
314
0
    return NULL;
315
0
  }
316
317
0
  lws_pt_lock(pt, __func__); /* -------------- pt { */
318
0
  lws_dll2_add_head(&wsi->pre_natal, &pt->pre_natal_wsi_owner);
319
0
  lws_pt_unlock(pt); /* } pt --------------- */
320
321
0
  return wsi;
322
0
}
323
324
0
int lws_wsi_inject_to_loop(struct lws_context_per_thread *pt, struct lws *wsi) {
325
0
  int ret = 1;
326
327
0
  lws_pt_lock(pt, __func__); /* -------------- pt { */
328
329
0
  if (pt->context->event_loop_ops->sock_accept)
330
0
    if (pt->context->event_loop_ops->sock_accept(wsi))
331
0
      goto bail;
332
333
0
  if (__insert_wsi_socket_into_fds(pt->context, wsi))
334
0
    goto bail;
335
336
0
  lws_dll2_remove(&wsi->pre_natal);
337
0
  ret = 0;
338
339
0
bail:
340
0
  lws_pt_unlock(pt);
341
342
0
  return ret;
343
0
}
344
345
/*
346
 * Take a copy of wsi->desc.sockfd before calling this, then close it
347
 * afterwards
348
 */
349
350
0
int lws_wsi_extract_from_loop(struct lws *wsi) {
351
0
  if (lws_socket_is_valid(wsi->desc.sockfd))
352
0
    __remove_wsi_socket_from_fds(wsi);
353
354
0
  if (!wsi->a.context->event_loop_ops->destroy_wsi &&
355
0
      wsi->a.context->event_loop_ops->wsi_logical_close) {
356
0
    wsi->a.context->event_loop_ops->wsi_logical_close(wsi);
357
0
    return 1; /* close / destroy continues async */
358
0
  }
359
360
0
  if (wsi->a.context->event_loop_ops->destroy_wsi)
361
0
    wsi->a.context->event_loop_ops->destroy_wsi(wsi);
362
363
0
  return 0; /* he is destroyed */
364
0
}
365
366
int lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason,
367
0
    void *in, size_t len) {
368
0
  int n;
369
0
  struct lws *wsi = lws_zalloc(sizeof(*wsi), "fake wsi");
370
371
0
  if (!wsi)
372
0
    return 1;
373
374
0
  wsi->a.context = vh->context;
375
0
  lws_vhost_bind_wsi(vh, wsi);
376
377
0
  for (n = 0; n < wsi->a.vhost->count_protocols; n++) {
378
0
    wsi->a.protocol = &vh->protocols[n];
379
0
    if (wsi->a.protocol->callback(wsi, (enum lws_callback_reasons)reason, NULL,
380
0
          in, len)) {
381
0
      lws_free(wsi);
382
0
      return 1;
383
0
    }
384
0
  }
385
386
0
  lws_free(wsi);
387
388
0
  return 0;
389
0
}
390
391
0
int lws_rx_flow_control(struct lws *wsi, int _enable) {
392
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
393
0
  int en = _enable;
394
395
  // h2 ignores rx flow control atm
396
0
  if (lwsi_role_h2(wsi) || wsi->mux_substream ||
397
0
      lwsi_role_h2_ENCAPSULATION(wsi))
398
0
    return 0;
399
400
0
  lwsl_wsi_info(wsi, "0x%x", _enable);
401
402
0
  if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) {
403
    /*
404
     * convert user bool style to bitmap style... in user simple
405
     * bool style _enable = 0 = flow control it, = 1 = allow rx
406
     */
407
0
    en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL;
408
0
    if (_enable & 1)
409
0
      en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT;
410
0
  }
411
412
0
  lws_pt_lock(pt, __func__);
413
414
  /* any bit set in rxflow_bitmap DISABLEs rxflow control */
415
0
  if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT)
416
0
    wsi->rxflow_bitmap = (uint8_t)(wsi->rxflow_bitmap & ~(en & 0xff));
417
0
  else
418
0
    wsi->rxflow_bitmap = (uint8_t)(wsi->rxflow_bitmap | (en & 0xff));
419
420
0
  if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) ==
421
0
      wsi->rxflow_change_to)
422
0
    goto skip;
423
424
0
  wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap);
425
426
0
  lwsl_wsi_info(wsi, "bitmap 0x%x: en 0x%x, ch 0x%x", wsi->rxflow_bitmap, en,
427
0
      wsi->rxflow_change_to);
428
429
0
  if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW ||
430
0
      !wsi->rxflow_will_be_applied) {
431
0
    en = __lws_rx_flow_control(wsi);
432
0
    lws_pt_unlock(pt);
433
434
0
    return en;
435
0
  }
436
437
0
skip:
438
0
  lws_pt_unlock(pt);
439
440
0
  return 0;
441
0
}
442
443
void lws_rx_flow_allow_all_protocol(const struct lws_context *context,
444
0
    const struct lws_protocols *protocol) {
445
0
  const struct lws_context_per_thread *pt = &context->pt[0];
446
0
  struct lws *wsi;
447
0
  unsigned int n, m = context->count_threads;
448
449
0
  while (m--) {
450
0
    for (n = 0; n < pt->fds_count; n++) {
451
0
      wsi = wsi_from_fd(context, pt->fds[n].fd);
452
0
      if (!wsi || !wsi->a.protocol)
453
0
        continue;
454
0
      if (wsi->a.protocol->callback == protocol->callback &&
455
0
          !strcmp(protocol->name, wsi->a.protocol->name))
456
0
        lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
457
0
    }
458
0
    pt++;
459
0
  }
460
0
}
461
462
int user_callback_handle_rxflow(lws_callback_function callback_function,
463
    struct lws *wsi,
464
    enum lws_callback_reasons reason, void *user,
465
0
    void *in, size_t len) {
466
0
  int n;
467
468
0
  wsi->rxflow_will_be_applied = 1;
469
0
  n = callback_function(wsi, reason, user, in, len);
470
0
  wsi->rxflow_will_be_applied = 0;
471
0
  if (!n)
472
0
    n = __lws_rx_flow_control(wsi);
473
474
0
  return n;
475
0
}
476
477
0
int __lws_rx_flow_control(struct lws *wsi) {
478
479
  // h2 ignores rx flow control atm
480
0
  if (lwsi_role_h2(wsi) || wsi->mux_substream ||
481
0
      lwsi_role_h2_ENCAPSULATION(wsi))
482
0
    return 0;
483
484
  /* if he has children, do those if they were changed */
485
0
  lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&wsi->child_list_owner)) {
486
0
    struct lws *wsic = lws_container_of(d, struct lws, sibling_list);
487
488
0
    if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)
489
0
      __lws_rx_flow_control(wsic);
490
0
  } lws_end_foreach_dll(d);
491
492
  /* there is no pending change */
493
0
  if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
494
0
    return 0;
495
496
  /* stuff is still buffered, not ready to really accept new input */
497
0
  if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
498
    /* get ourselves called back to deal with stashed buffer */
499
0
    lws_callback_on_writable(wsi);
500
501
0
  }
502
503
  /* now the pending is cleared, we can change rxflow state */
504
505
0
  wsi->rxflow_change_to &= (~LWS_RXFLOW_PENDING_CHANGE) & 3;
506
507
0
  lwsl_wsi_info(wsi, "rxflow: change_to %d",
508
0
      wsi->rxflow_change_to & LWS_RXFLOW_ALLOW);
509
510
  /* adjust the pollfd for this wsi */
511
512
0
  if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
513
0
    lwsl_wsi_info(wsi, "reenable POLLIN");
514
515
0
    if (__lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
516
0
      lwsl_wsi_info(wsi, "fail");
517
0
      return -1;
518
0
    }
519
0
  } else if (__lws_change_pollfd(wsi, LWS_POLLIN, 0))
520
0
    return -1;
521
522
0
  return 0;
523
0
}
524
525
0
const struct lws_protocols *lws_get_protocol(struct lws *wsi) {
526
0
  return wsi->a.protocol;
527
0
}
528
529
0
LWS_VISIBLE int lws_ensure_user_space(struct lws *wsi) {
530
0
  if (!wsi->a.protocol)
531
0
    return 0;
532
533
  /* allocate the per-connection user memory (if any) */
534
535
0
  if (!wsi->user_space) {
536
0
    size_t s = wsi->a.protocol->per_session_data_size;
537
538
0
    if (!s)
539
0
      s = (size_t)wsi->a.protocol->callback(wsi,
540
0
          LWS_CALLBACK_GET_PSS_SIZE, NULL, NULL, 0);
541
542
0
    if (s) {
543
0
      wsi->user_space = lws_zalloc(s, "user space");
544
0
      if (!wsi->user_space) {
545
0
        lwsl_wsi_err(wsi, "OOM");
546
0
        return 1;
547
0
      }
548
0
    }
549
0
  } else
550
0
    lwsl_wsi_debug(wsi, "protocol pss %lu, user_space=%p",
551
0
        (long)wsi->a.protocol->per_session_data_size,
552
0
        wsi->user_space);
553
0
  return 0;
554
0
}
555
556
0
void *lws_adjust_protocol_psds(struct lws *wsi, size_t new_size) {
557
0
  ((struct lws_protocols *)lws_get_protocol(wsi))->per_session_data_size =
558
0
    new_size;
559
560
0
  if (lws_ensure_user_space(wsi))
561
0
    return NULL;
562
563
0
  return wsi->user_space;
564
0
}
565
566
0
int lws_get_tsi(struct lws *wsi) { return (int)wsi->tsi; }
567
568
0
int lws_is_ssl(struct lws *wsi) {
569
0
#if defined(LWS_WITH_TLS)
570
0
  return wsi->tls.use_ssl & LCCSCF_USE_SSL;
571
#else
572
  (void)wsi;
573
  return 0;
574
#endif
575
0
}
576
577
#if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS)
578
0
lws_tls_conn *lws_get_ssl(struct lws *wsi) { return wsi->tls.ssl; }
579
#endif
580
581
0
int lws_has_buffered_out(struct lws *wsi) {
582
0
  if (wsi->buflist_out) {
583
0
    lwsl_info("lws_has_buffered_out: %s has buflist_out\n", lws_wsi_tag(wsi));
584
0
    return 1;
585
0
  }
586
587
0
#if defined(LWS_ROLE_H2)
588
0
  {
589
0
    struct lws *nwsi = lws_get_network_wsi(wsi);
590
591
0
    if (nwsi && nwsi->buflist_out) {
592
0
      lwsl_info("lws_has_buffered_out: network wsi %s has buflist_out\n", lws_wsi_tag(nwsi));
593
0
      return 1;
594
0
    }
595
0
  }
596
0
#endif
597
598
#if defined(LWS_ROLE_QUIC)
599
  if (wsi->quic.qs) {
600
    struct lws *nwsi = lws_get_network_wsi(wsi);
601
    struct lws_quic_netconn *qn = nwsi ? nwsi->quic.qn : NULL;
602
    if (qn) {
603
      uint64_t sid = wsi->quic.qs->stream_id;
604
      int i;
605
606
      for (i = 0; i < LWS_QUIC_LEVEL_COUNT; i++) {
607
        lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->pending_tx[i].head) {
608
          struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list);
609
          if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) ||
610
              (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) {
611
            lwsl_info("lws_has_buffered_out: %s has pending_tx stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid);
612
            return 1;
613
          }
614
        } lws_end_foreach_dll_safe(d, d1);
615
616
        lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->in_flight[i].head) {
617
          struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list);
618
          if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) ||
619
              (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) {
620
            lwsl_info("lws_has_buffered_out: %s has in_flight stream/blocked frame on sid %llu\n", lws_wsi_tag(wsi), (unsigned long long)sid);
621
            return 1;
622
          }
623
        } lws_end_foreach_dll_safe(d, d1);
624
      }
625
    }
626
  }
627
#endif
628
629
0
  return 0;
630
0
}
631
632
0
int lws_has_unsent_buffered_out(struct lws *wsi) {
633
0
  if (wsi->buflist_out)
634
0
    return 1;
635
636
0
#if defined(LWS_ROLE_H2)
637
0
  {
638
0
    struct lws *nwsi = lws_get_network_wsi(wsi);
639
640
0
    if (nwsi && nwsi->buflist_out)
641
0
      return 1;
642
0
  }
643
0
#endif
644
645
#if defined(LWS_ROLE_QUIC)
646
  if (wsi->quic.qs) {
647
    struct lws *nwsi = lws_get_network_wsi(wsi);
648
    struct lws_quic_netconn *qn = nwsi ? nwsi->quic.qn : NULL;
649
    if (qn) {
650
      uint64_t sid = wsi->quic.qs->stream_id;
651
      int i;
652
653
      for (i = 0; i < LWS_QUIC_LEVEL_COUNT; i++) {
654
        lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, qn->pending_tx[i].head) {
655
          struct lws_quic_tx_frame *f = lws_container_of(d, struct lws_quic_tx_frame, list);
656
          if (((f->type & 0xf8) == LWS_QUIC_FT_STREAM && f->stream_id == sid) ||
657
              (f->type == LWS_QUIC_FT_STREAM_DATA_BLOCKED && f->stream_id == sid)) {
658
            return 1;
659
          }
660
        } lws_end_foreach_dll_safe(d, d1);
661
      }
662
    }
663
  }
664
#endif
665
666
0
  return 0;
667
0
}
668
669
0
int lws_partial_buffered(struct lws *wsi) { return lws_has_buffered_out(wsi); }
670
671
0
lws_fileofs_t lws_get_peer_write_allowance(struct lws *wsi) {
672
0
  if (!lws_rops_fidx(wsi->role_ops, LWS_ROPS_tx_credit))
673
0
    return -1;
674
675
0
  return lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_tx_credit)
676
0
    .tx_credit(wsi, LWSTXCR_US_TO_PEER, 0);
677
0
}
678
679
void lws_role_transition(struct lws *wsi, enum lwsi_role role,
680
    enum lwsi_state state,
681
0
    const struct lws_role_ops *ops) {
682
0
#if (_LWS_ENABLED_LOGS & LLL_DEBUG)
683
0
  const char *name = "(unset)";
684
0
#endif
685
0
  wsi->wsistate = (unsigned int)role | (unsigned int)state;
686
0
  if (ops)
687
0
    wsi->role_ops = ops;
688
0
#if (_LWS_ENABLED_LOGS & LLL_DEBUG)
689
0
  if (wsi->role_ops)
690
0
    name = wsi->role_ops->name;
691
0
  lwsl_wsi_debug(wsi, "wsistate 0x%lx, ops %s", (unsigned long)wsi->wsistate,
692
0
      name);
693
0
#endif
694
0
}
695
696
int lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
697
0
    const char **path) {
698
0
  const char *end;
699
0
  char unix_skt = 0;
700
701
  /* cut up the location into address, port and path */
702
0
  *prot = p;
703
0
  while (*p && (*p != ':' || p[1] != '/' || p[2] != '/'))
704
0
    p++;
705
0
  if (!*p) {
706
0
    end = p;
707
0
    p = (char *)*prot;
708
0
    *prot = end;
709
0
  } else {
710
0
    *p = '\0';
711
0
    p += 3;
712
0
  }
713
0
  if (*p == '+') /* unix skt */
714
0
    unix_skt = 1;
715
716
0
  *ads = p;
717
0
  if (!strcmp(*prot, "http") || !strcmp(*prot, "ws"))
718
0
    *port = 80;
719
0
  else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss"))
720
0
    *port = 443;
721
722
0
  if (*p == '[') {
723
0
    ++(*ads);
724
0
    while (*p && *p != ']')
725
0
      p++;
726
0
    if (*p)
727
0
      *p++ = '\0';
728
0
  } else
729
0
    while (*p && *p != ':' && (unix_skt || *p != '/'))
730
0
      p++;
731
732
0
  if (*p == ':') {
733
0
    *p++ = '\0';
734
0
    *port = atoi(p);
735
0
    while (*p && *p != '/')
736
0
      p++;
737
0
  }
738
0
  *path = "/";
739
0
  if (*p) {
740
0
    *p++ = '\0';
741
0
    if (*p)
742
0
      *path = p;
743
0
  }
744
745
0
  return 0;
746
0
}
747
748
lws_parse_uri_t *
749
lws_parse_uri_create(const char *uri)
750
0
{
751
0
  const char *p = uri;
752
0
  int len = (int)strlen(uri);
753
0
  lws_parse_uri_t *u;
754
0
  char unix_skt = 0;
755
0
  char *dest;
756
0
  const char *end;
757
758
0
  u = lws_zalloc(sizeof(*u) + (size_t)len + 4, __func__);
759
0
  if (!u)
760
0
    return NULL;
761
762
0
  dest = (char *)(u + 1);
763
764
  /* cut up the location into scheme, port and path */
765
0
  end = uri;
766
0
  while (*end && (*end != ':' || end[1] != '/' || end[2] != '/'))
767
0
    end++;
768
769
0
  u->scheme = dest;
770
0
  if (*end) {
771
    /* scheme */
772
0
    int slen = lws_ptr_diff(end, p);
773
0
    lws_strncpy(dest, p, (size_t)slen + 1);
774
0
    dest += slen + 1;
775
0
    p = end + 3;
776
0
  } else {
777
0
    *dest++ = '\0';
778
0
  }
779
780
0
  if (*p == '+')
781
0
    unix_skt = 1;
782
783
0
  const char *ads = p;
784
0
  if (u->scheme[0]) {
785
0
    if (!strcmp(u->scheme, "http") || !strcmp(u->scheme, "ws"))
786
0
      u->port = 80;
787
0
    else if (!strcmp(u->scheme, "https") || !strcmp(u->scheme, "wss"))
788
0
      u->port = 443;
789
0
  }
790
791
0
  u->host = dest;
792
0
  if (*p == '[') {
793
0
    ++ads;
794
0
    while (*p && *p != ']')
795
0
      p++;
796
0
    if (*p) {
797
0
      int hlen = lws_ptr_diff(p, ads);
798
0
      lws_strncpy(dest, ads, (size_t)hlen + 1);
799
0
      dest += hlen + 1;
800
0
      p++;
801
0
    } else {
802
0
      int hlen = lws_ptr_diff(p, ads);
803
0
      lws_strncpy(dest, ads, (size_t)hlen + 1);
804
0
      dest += hlen + 1;
805
0
    }
806
0
  } else {
807
0
    while (*p && *p != ':' && (unix_skt || (*p != '/' && *p != '?')))
808
0
      p++;
809
0
    int hlen = lws_ptr_diff(p, ads);
810
0
    lws_strncpy(dest, ads, (size_t)hlen + 1);
811
0
    dest += hlen + 1;
812
0
  }
813
814
0
  if (*p == ':') {
815
0
    p++;
816
0
    u->port = (uint16_t)atoi(p);
817
0
    while (*p && *p != '/' && *p != '?')
818
0
      p++;
819
0
  }
820
821
0
  u->unix_skt = unix_skt;
822
823
0
  u->path = dest;
824
0
  if (*p) {
825
0
    if (*p == '/') {
826
0
      p++;
827
0
      if (*p) {
828
0
        int plen = lws_ptr_diff(uri + len, p);
829
0
        lws_strncpy(dest, p, (size_t)plen + 1);
830
0
      } else
831
0
        lws_strncpy(dest, "/", 2);
832
0
    } else if (*p == '?') {
833
      /* the ? is kept in the path */
834
0
      int plen = lws_ptr_diff(uri + len, p);
835
0
      lws_strncpy(dest, p, (size_t)plen + 1);
836
0
    }
837
0
  } else
838
0
    lws_strncpy(dest, "/", 2);
839
840
0
  return u;
841
0
}
842
843
void
844
lws_parse_uri_destroy(lws_parse_uri_t **pcuri)
845
0
{
846
0
  if (*pcuri) {
847
0
    lws_free(*pcuri);
848
0
    *pcuri = NULL;
849
0
  }
850
0
}
851
852
/* ... */
853
854
int lws_get_urlarg_by_name_safe(struct lws *wsi, const char *name, char *buf,
855
0
    int len) {
856
0
  struct allocated_headers *ah = wsi->http.ah;
857
0
  int sl = (int)strlen(name);
858
0
  int fi;
859
860
0
  if (!ah || !len)
861
0
    return -1;
862
863
  /*
864
   * Walk the URI-args fragment chain directly.  We must match the "name="
865
   * prefix without requiring the *whole* "name=value" to fit into the
866
   * caller's value-sized buf: lws_hdr_copy_fragment() returns -2 when the
867
   * fragment exceeds the buffer it is given, so passing the caller's buf
868
   * (commonly sized only for the value, eg a 16-byte buffer for
869
   * response_type=code which is 18 bytes) made any such fragment
870
   * unmatchable -- it silently read back empty.  Comparing the prefix
871
   * against the in-place ah data decouples matching from buf size.
872
   */
873
874
0
  fi = ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS];
875
0
  while (fi) {
876
0
    struct lws_fragments *f = &ah->frags[fi];
877
878
0
    if (f->len >= sl &&
879
0
        !strncmp((const char *)&ah->data[f->offset],
880
0
           name, (size_t)sl)) {
881
0
      ah_data_idx_t vo; /* value start in ah->data */
882
0
      int ol;     /* value length to copy */
883
0
      int a_sl = sl;
884
885
      /*
886
       * If he left off the trailing =, trim the single '='
887
       * separator from the result
888
       */
889
0
      if (name[sl - 1] != '=' && sl < f->len &&
890
0
          ah->data[f->offset + (ah_data_idx_t)sl] == '=')
891
0
        a_sl++;
892
893
0
      vo = f->offset + (ah_data_idx_t)a_sl;
894
0
      ol = (int)f->len - a_sl;
895
0
      if (ol >= len)   /* value won't fit caller buf */
896
0
        return -2;
897
898
0
      memcpy(buf, &ah->data[vo], (size_t)ol);
899
0
      buf[ol] = '\0';
900
901
0
      return ol;
902
0
    }
903
0
    fi = f->nfrag;
904
0
  }
905
906
0
  return -1;
907
0
}
908
909
const char *lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf,
910
0
    int len) {
911
0
  int n = lws_get_urlarg_by_name_safe(wsi, name, buf, len);
912
913
0
  return n < 0 ? NULL : buf;
914
0
}
915
916
/*
917
 * For a server-side wsi, return a pointer to the request URI stored in the
918
 * ah, scanning the possible method / URI tokens in a side-effect-free way
919
 * (unlike lws_http_get_uri_and_method(), which logs on its failure paths).
920
 * Returns NULL if the wsi has no ah or no request URI is set.  The returned
921
 * pointer is NUL-terminated and valid while the ah stays attached.
922
 */
923
const char *
924
lws_wsi_request_uri(struct lws *wsi)
925
0
{
926
0
  static const enum lws_token_indexes tokens[] = {
927
0
    WSI_TOKEN_GET_URI,
928
0
    WSI_TOKEN_POST_URI,
929
0
#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL)
930
0
    WSI_TOKEN_OPTIONS_URI,
931
0
    WSI_TOKEN_PUT_URI,
932
0
    WSI_TOKEN_PATCH_URI,
933
0
    WSI_TOKEN_DELETE_URI,
934
0
#endif
935
0
    WSI_TOKEN_CONNECT,
936
0
    WSI_TOKEN_HEAD_URI,
937
0
#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_H3) || defined(LWS_HTTP_HEADERS_ALL)
938
0
    WSI_TOKEN_HTTP_COLON_PATH,
939
0
#endif
940
0
  };
941
0
  unsigned int n;
942
943
0
  if (!wsi->http.ah)
944
0
    return NULL;
945
946
0
  for (n = 0; n < LWS_ARRAY_SIZE(tokens); n++)
947
0
    if (lws_hdr_total_length(wsi, tokens[n]))
948
0
      return lws_hdr_simple_ptr(wsi, tokens[n]);
949
950
0
  return NULL;
951
0
}
952
953
#if defined(LWS_WITHOUT_EXTENSIONS)
954
955
/* we need to provide dummy callbacks for internal exts
956
 * so user code runs when faced with a lib compiled with
957
 * extensions disabled.
958
 */
959
960
int lws_extension_callback_pm_deflate(
961
    struct lws_context *context, const struct lws_extension *ext,
962
    struct lws *wsi, enum lws_extension_callback_reasons reason, void *user,
963
0
    void *in, size_t len) {
964
0
  (void)context;
965
0
  (void)ext;
966
0
  (void)wsi;
967
0
  (void)reason;
968
0
  (void)user;
969
0
  (void)in;
970
0
  (void)len;
971
972
0
  return 0;
973
0
}
974
975
int lws_set_extension_option(struct lws *wsi, const char *ext_name,
976
0
    const char *opt_name, const char *opt_val) {
977
0
  return -1;
978
0
}
979
#endif
980
981
0
int lws_is_cgi(struct lws *wsi) {
982
#ifdef LWS_WITH_CGI
983
  return !!wsi->http.cgi;
984
#else
985
0
  return 0;
986
0
#endif
987
0
}
988
989
const struct lws_protocol_vhost_options *
990
lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name)
991
0
{
992
0
  while (pvo) {
993
0
    if (!strcmp(pvo->name, name))
994
0
      break;
995
996
0
    pvo = pvo->next;
997
0
  }
998
999
0
  return pvo;
1000
0
}
1001
1002
int lws_pvo_get_str(void *in, const char *name, const char **result)
1003
0
{
1004
0
  const struct lws_protocol_vhost_options *pv =
1005
0
    lws_pvo_search((const struct lws_protocol_vhost_options *)in, name);
1006
1007
0
  if (!pv)
1008
0
    return 1;
1009
1010
0
  *result = (const char *)pv->value;
1011
1012
0
  return 0;
1013
0
}
1014
1015
const struct lws_protocol_vhost_options *
1016
lws_pmo_search(const struct lws_http_mount *mount, const char *name)
1017
0
{
1018
0
  if (!mount)
1019
0
    return NULL;
1020
1021
0
  return lws_pvo_search(mount->cgienv, name);
1022
0
}
1023
1024
int
1025
lws_pmo_get_str(const struct lws_http_mount *mount, const char *name,
1026
    const char **result)
1027
0
{
1028
0
  if (!mount)
1029
0
    return 1;
1030
1031
0
  return lws_pvo_get_str((void *)mount->cgienv, name, result);
1032
0
}
1033
1034
1035
int lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in,
1036
0
    size_t len) {
1037
0
  struct lws_vhost *v = lws_vhost_first(pt->context);
1038
0
  lws_fakewsi_def_plwsa(pt);
1039
0
  int n, ret = 0;
1040
1041
0
  lws_fakewsi_prep_plwsa_ctx(pt->context);
1042
#if !defined(LWS_PLAT_FREERTOS) && LWS_MAX_SMP > 1
1043
  ((struct lws *)plwsa)->tsi = (char)(int)(pt - &pt->context->pt[0]);
1044
#endif
1045
1046
0
  while (v) {
1047
0
    const struct lws_protocols *p = v->protocols;
1048
1049
0
    plwsa->vhost = v; /* not a real bound wsi */
1050
1051
0
    for (n = 0; n < v->count_protocols; n++) {
1052
0
      plwsa->protocol = p;
1053
0
      if (p->callback &&
1054
0
          p->callback((struct lws *)plwsa, (enum lws_callback_reasons)reason,
1055
0
            NULL, in, len))
1056
0
        ret |= 1;
1057
0
      p++;
1058
0
    }
1059
1060
0
    v = lws_vhost_next(v);
1061
0
  }
1062
1063
0
  return ret;
1064
0
}
1065
1066
0
void *lws_wsi_user(struct lws *wsi) { return wsi->user_space; }
1067
1068
0
int lws_wsi_tsi(struct lws *wsi) { return wsi->tsi; }
1069
1070
0
void lws_set_wsi_user(struct lws *wsi, void *data) {
1071
0
  if (!wsi->user_space_externally_allocated && wsi->user_space)
1072
0
    lws_free(wsi->user_space);
1073
1074
0
  wsi->user_space_externally_allocated = 1;
1075
0
  wsi->user_space = data;
1076
0
}
1077
1078
0
struct lws *lws_get_parent(const struct lws *wsi) { return wsi->parent; }
1079
1080
struct lws *lws_get_child(const struct lws *wsi)
1081
0
{
1082
0
  struct lws_dll2 *d = lws_dll2_get_head(&wsi->child_list_owner);
1083
1084
0
  return d ? lws_container_of(d, struct lws, sibling_list) : NULL;
1085
0
}
1086
1087
0
void *lws_get_opaque_parent_data(const struct lws *wsi) {
1088
0
  return wsi->opaque_parent_data;
1089
0
}
1090
1091
0
void lws_set_opaque_parent_data(struct lws *wsi, void *data) {
1092
0
  wsi->opaque_parent_data = data;
1093
0
}
1094
1095
0
void *lws_get_opaque_user_data(const struct lws *wsi) {
1096
0
  return wsi->a.opaque_user_data;
1097
0
}
1098
1099
0
void lws_set_opaque_user_data(struct lws *wsi, void *data) {
1100
0
  wsi->a.opaque_user_data = data;
1101
0
}
1102
1103
0
int lws_get_child_pending_on_writable(const struct lws *wsi) {
1104
0
  return wsi->parent_pending_cb_on_writable;
1105
0
}
1106
1107
0
void lws_clear_child_pending_on_writable(struct lws *wsi) {
1108
0
  wsi->parent_pending_cb_on_writable = 0;
1109
0
}
1110
1111
0
const char *lws_get_vhost_name(struct lws_vhost *vhost) { return vhost->name; }
1112
1113
0
int lws_get_vhost_port(struct lws_vhost *vhost) { return vhost->listen_port; }
1114
1115
0
void *lws_get_vhost_user(struct lws_vhost *vhost) { return vhost->user; }
1116
1117
0
const char *lws_get_vhost_iface(struct lws_vhost *vhost) {
1118
0
  return vhost->iface;
1119
0
}
1120
1121
0
lws_sockfd_type lws_get_socket_fd(struct lws *wsi) {
1122
0
  if (!wsi)
1123
0
    return -1;
1124
0
  return wsi->desc.sockfd;
1125
0
}
1126
1127
0
struct lws_vhost *lws_vhost_get(struct lws *wsi) { return wsi->a.vhost; }
1128
1129
0
struct lws_vhost *lws_get_vhost(struct lws *wsi) { return wsi->a.vhost; }
1130
1131
0
const struct lws_protocols *lws_protocol_get(struct lws *wsi) {
1132
0
  return wsi->a.protocol;
1133
0
}
1134
1135
#if defined(LWS_WITH_UDP)
1136
0
const struct lws_udp *lws_get_udp(const struct lws *wsi) { return wsi->udp; }
1137
#endif
1138
1139
0
struct lws_context *lws_get_context(const struct lws *wsi) {
1140
0
  return wsi->a.context;
1141
0
}
1142
1143
0
struct lws_log_cx *lwsl_wsi_get_cx(struct lws *wsi) {
1144
0
  if (!wsi)
1145
0
    return NULL;
1146
1147
0
  return wsi->lc.log_cx;
1148
0
}
1149
1150
#if defined(LWS_WITH_CLIENT)
1151
int _lws_generic_transaction_completed_active_conn(struct lws **_wsi,
1152
0
    char take_vh_lock) {
1153
0
  struct lws *wnew, *wsi = *_wsi;
1154
1155
  /*
1156
   * Are we constitutionally capable of having a queue, ie, we are on
1157
   * the "active client connections" list?
1158
   *
1159
   * If not, that's it for us.
1160
   */
1161
1162
0
  if (lws_dll2_is_detached(&wsi->dll_cli_active_conns))
1163
0
    return 0; /* no new transaction */
1164
1165
  /*
1166
   * With h1 queuing, the original "active client" moves his attributes
1167
   * like fd, ssl, queue and active client list entry to the next guy in
1168
   * the queue before closing... it's because the user code knows the
1169
   * individual wsi and the action must take place in the correct wsi
1170
   * context.  Note this means we don't truly pipeline headers.
1171
   *
1172
   * Trying to keep the original "active client" in place to do the work
1173
   * of the wsi breaks down when dealing with queued POSTs otherwise; it's
1174
   * also competing with the real mux child arrangements and complicating
1175
   * the code.
1176
   *
1177
   * For that reason, see if we have any queued child now...
1178
   */
1179
1180
0
  if(lws_dll2_is_empty(&wsi->dll2_cli_txn_queue_owner)) {
1181
    /*
1182
     * Nothing pipelined... we should hang around a bit
1183
     * in case something turns up... otherwise we'll close
1184
     */
1185
0
    lwsl_wsi_info(wsi, "nothing pipelined waiting");
1186
0
    lwsi_set_state(wsi, LRS_IDLING);
1187
1188
0
    lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, wsi->keep_warm_secs);
1189
1190
0
    return 0; /* no new transaction right now */
1191
0
  }
1192
1193
  /*
1194
   * We have a queued child wsi we should bequeath our assets to, before
1195
   * closing ourself
1196
   */
1197
1198
0
  if (take_vh_lock)
1199
0
    lws_vhost_lock(wsi->a.vhost);
1200
1201
0
  wnew = lws_container_of(lws_dll2_get_head(&wsi->dll2_cli_txn_queue_owner), struct lws,
1202
0
      dll2_cli_txn_queue);
1203
1204
0
  assert(wsi != wnew);
1205
1206
0
  lws_dll2_remove(&wnew->dll2_cli_txn_queue);
1207
1208
0
  assert(lws_socket_is_valid(wsi->desc.sockfd));
1209
1210
0
  __lws_change_pollfd(wsi, LWS_POLLOUT | LWS_POLLIN, 0);
1211
1212
  /* copy the fd */
1213
0
  wnew->desc = wsi->desc;
1214
1215
0
  assert(lws_socket_is_valid(wnew->desc.sockfd));
1216
1217
  /* disconnect the fd from association with old wsi */
1218
1219
0
  if (__remove_wsi_socket_from_fds(wsi))
1220
0
    return -1;
1221
1222
0
  sanity_assert_no_wsi_traces(wsi->a.context, wsi);
1223
0
  sanity_assert_no_sockfd_traces(wsi->a.context, wsi->desc.sockfd);
1224
0
  wsi->desc.sockfd = LWS_SOCK_INVALID;
1225
1226
0
  __lws_wsi_remove_from_sul(wsi);
1227
1228
  /*
1229
   * ... we're doing some magic here in terms of handing off the socket
1230
   * that has been active to a wsi that has not yet itself been active...
1231
   * depending on the event lib we may need to give a magic spark to the
1232
   * new guy and snuff out the old guy's magic spark at that level as well
1233
   */
1234
1235
#if defined(LWS_WITH_EVENT_LIBS)
1236
  if (wsi->a.context->event_loop_ops->destroy_wsi)
1237
    wsi->a.context->event_loop_ops->destroy_wsi(wsi);
1238
  if (wsi->a.context->event_loop_ops->sock_accept)
1239
    wsi->a.context->event_loop_ops->sock_accept(wnew);
1240
#endif
1241
1242
  /* point the fd table entry to new guy */
1243
1244
0
  assert(lws_socket_is_valid(wnew->desc.sockfd));
1245
1246
0
  if (__insert_wsi_socket_into_fds(wsi->a.context, wnew))
1247
0
    return -1;
1248
1249
0
#if defined(LWS_WITH_TLS)
1250
  /* pass on the tls */
1251
1252
#if defined(LWS_TLS_SYNTHESIZE_CB)
1253
  lws_sul_cancel(&wsi->tls.sul_cb_synth);
1254
  lws_sess_cache_synth_cb(&wsi->tls.sul_cb_synth);
1255
#endif
1256
1257
0
  wnew->tls = wsi->tls;
1258
0
  wsi->tls.client_bio = NULL;
1259
0
  wsi->tls.ssl = NULL;
1260
0
  wsi->tls.use_ssl = 0;
1261
0
#endif
1262
1263
  /* take over his copy of his endpoint as an active connection */
1264
1265
0
  if (!wnew->cli_hostname_copy && wsi->cli_hostname_copy) {
1266
0
    wnew->cli_hostname_copy = wsi->cli_hostname_copy;
1267
0
    wsi->cli_hostname_copy = NULL;
1268
0
  }
1269
0
  wnew->keep_warm_secs = wsi->keep_warm_secs;
1270
1271
  /*
1272
   * selected queued guy now replaces the original leader on the
1273
   * active client conn list
1274
   */
1275
1276
0
  lws_dll2_remove(&wsi->dll_cli_active_conns);
1277
0
  lws_dll2_add_tail(&wnew->dll_cli_active_conns,
1278
0
      &wsi->a.vhost->dll_cli_active_conns_owner);
1279
1280
  /* move any queued guys to queue on new active conn */
1281
1282
0
  lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1283
0
      lws_dll2_get_head(&wsi->dll2_cli_txn_queue_owner)) {
1284
0
    struct lws *ww = lws_container_of(d, struct lws, dll2_cli_txn_queue);
1285
1286
0
    lws_dll2_remove(&ww->dll2_cli_txn_queue);
1287
0
    lws_dll2_add_tail(&ww->dll2_cli_txn_queue, &wnew->dll2_cli_txn_queue_owner);
1288
0
  }
1289
0
  lws_end_foreach_dll_safe(d, d1);
1290
1291
0
  if (take_vh_lock)
1292
0
    lws_vhost_unlock(wsi->a.vhost);
1293
1294
  /*
1295
   * The original leader who passed on all his powers already can die...
1296
   * in the call stack above us there are guys who still want to touch
1297
   * him, so have him die next time around the event loop, not now.
1298
   */
1299
1300
0
  wsi->already_did_cce = 1; /* so the close doesn't trigger a CCE */
1301
0
  lws_set_timeout(wsi, 1, LWS_TO_KILL_ASYNC);
1302
1303
  /* after the first one, they can only be coming from the queue */
1304
0
  wnew->transaction_from_pipeline_queue = 1;
1305
1306
0
  lwsl_wsi_info(wsi, " pipeline queue passed -> %s", lws_wsi_tag(wnew));
1307
1308
0
  *_wsi = wnew; /* inform caller we swapped */
1309
1310
0
  return 1; /* new transaction */
1311
0
}
1312
#endif
1313
1314
0
int LWS_WARN_UNUSED_RESULT lws_raw_transaction_completed(struct lws *wsi) {
1315
0
  if (lws_has_buffered_out(wsi)) {
1316
    /*
1317
     * ...so he tried to send something large, but it went out
1318
     * as a partial, but he immediately called us to say he wants
1319
     * to close the connection.
1320
     *
1321
     * Defer the close until the last part of the partial is sent.
1322
     *
1323
     */
1324
1325
0
    lwsl_wsi_debug(wsi, "deferring due to partial");
1326
0
    wsi->close_when_buffered_out_drained = 1;
1327
0
    lws_callback_on_writable(wsi);
1328
1329
0
    return 0;
1330
0
  }
1331
1332
0
  return -1;
1333
0
}
1334
1335
int lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p,
1336
0
    const char *reason) {
1337
  //  if (wsi->a.protocol == p)
1338
  //    return 0;
1339
0
  const struct lws_protocols *vp = wsi->a.vhost->protocols, *vpo;
1340
1341
0
  if (wsi->a.protocol && wsi->protocol_bind_balance) {
1342
0
    wsi->a.protocol->callback(
1343
0
        wsi, wsi->role_ops->protocol_unbind_cb[!!lwsi_role_server(wsi)],
1344
0
        wsi->user_space, (void *)reason, 0);
1345
0
    wsi->protocol_bind_balance = 0;
1346
0
  }
1347
0
  if (!wsi->user_space_externally_allocated)
1348
0
    lws_free_set_NULL(wsi->user_space);
1349
1350
0
  lws_same_vh_protocol_remove(wsi);
1351
1352
0
  wsi->a.protocol = p;
1353
0
  if (!p)
1354
0
    return 0;
1355
1356
0
  if (lws_ensure_user_space(wsi))
1357
0
    return 1;
1358
1359
0
  if (p > vp && p < &vp[wsi->a.vhost->count_protocols])
1360
0
    lws_same_vh_protocol_insert(wsi, (int)(p - vp));
1361
0
  else {
1362
0
    int n = wsi->a.vhost->count_protocols;
1363
0
    int hit = 0;
1364
1365
0
    vpo = vp;
1366
1367
0
    while (n--) {
1368
0
      if (p->name && vp->name && !strcmp(p->name, vp->name)) {
1369
0
        hit = 1;
1370
0
        lws_same_vh_protocol_insert(wsi, (int)(vp - vpo));
1371
0
        break;
1372
0
      }
1373
0
      vp++;
1374
0
    }
1375
0
    if (!hit)
1376
0
      lwsl_err("%s: %p is not in vhost '%s' protocols list\n", __func__, p,
1377
0
          wsi->a.vhost->name);
1378
0
  }
1379
1380
0
  if (wsi->a.protocol->callback(
1381
0
        wsi, wsi->role_ops->protocol_bind_cb[!!lwsi_role_server(wsi)],
1382
0
        wsi->user_space, NULL, 0))
1383
0
    return 1;
1384
1385
0
  wsi->protocol_bind_balance = 1;
1386
1387
0
  return 0;
1388
0
}
1389
1390
0
void lws_http_close_immortal(struct lws *wsi) {
1391
0
  struct lws *nwsi;
1392
1393
0
  if (!wsi->mux_substream)
1394
0
    return;
1395
1396
0
  assert(wsi->mux_stream_immortal);
1397
0
  wsi->mux_stream_immortal = 0;
1398
1399
0
  nwsi = lws_get_network_wsi(wsi);
1400
0
  lwsl_wsi_debug(wsi, "%s (%d)", lws_wsi_tag(nwsi),
1401
0
      nwsi->immortal_substream_count);
1402
0
  assert(nwsi->immortal_substream_count);
1403
0
  nwsi->immortal_substream_count--;
1404
0
  if (!nwsi->immortal_substream_count)
1405
    /*
1406
     * since we closed the only immortal stream on this nwsi, we
1407
     * need to reapply a normal timeout regime to the nwsi
1408
     */
1409
0
    lws_set_timeout(nwsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE,
1410
0
        lws_wsi_keepalive_timeout_eff(nwsi));
1411
0
}
1412
1413
0
void lws_mux_mark_immortal(struct lws *wsi) {
1414
0
  struct lws *nwsi;
1415
1416
0
  lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1417
1418
0
  if (!wsi->mux_substream
1419
0
#if defined(LWS_WITH_CLIENT)
1420
0
      && !wsi->client_mux_substream
1421
0
#endif
1422
0
     ) {
1423
    // lwsl_wsi_err(wsi, "not mux substream");
1424
0
    return;
1425
0
  }
1426
1427
0
  if (wsi->mux_stream_immortal)
1428
    /* only need to handle it once per child wsi */
1429
0
    return;
1430
1431
0
  nwsi = lws_get_network_wsi(wsi);
1432
0
  if (!nwsi)
1433
0
    return;
1434
1435
0
  lwsl_wsi_debug(wsi, "%s (%d)\n", lws_wsi_tag(nwsi),
1436
0
      nwsi->immortal_substream_count);
1437
1438
0
  wsi->mux_stream_immortal = 1;
1439
0
  assert(nwsi->immortal_substream_count < 255); /* largest count */
1440
0
   nwsi->immortal_substream_count++;
1441
0
  if (nwsi->immortal_substream_count == 1)
1442
0
    lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0);
1443
0
}
1444
1445
0
int lws_http_mark_sse(struct lws *wsi) {
1446
0
  if (!wsi)
1447
0
    return 0;
1448
1449
0
  lws_http_headers_detach(wsi);
1450
0
  lws_mux_mark_immortal(wsi);
1451
1452
0
  if (wsi->mux_substream)
1453
0
    wsi->h2_stream_carries_sse = 1;
1454
1455
0
  return 0;
1456
0
}
1457
1458
#if defined(LWS_WITH_CLIENT)
1459
1460
const char *lws_wsi_client_stash_item(struct lws *wsi, int stash_idx,
1461
0
    int hdr_idx) {
1462
  /* try the generic client stash */
1463
0
  if (wsi->stash)
1464
0
    return wsi->stash->cis[stash_idx];
1465
1466
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
1467
  /* if not, use the ah stash if applicable */
1468
0
  return lws_hdr_simple_ptr(wsi, (enum lws_token_indexes)hdr_idx);
1469
#else
1470
  return NULL;
1471
#endif
1472
0
}
1473
#endif
1474
1475
0
int lws_wsi_keepalive_timeout_eff(struct lws *wsi) {
1476
0
  int ds = wsi->a.vhost->keepalive_timeout;
1477
1478
0
#if defined(LWS_WITH_SERVER)
1479
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
1480
0
  if (wsi->http.mount_specific_keepalive_timeout_secs)
1481
0
    ds = (int)wsi->http.mount_specific_keepalive_timeout_secs;
1482
1483
0
  if (wsi->parent &&
1484
0
      (int)wsi->parent->http.mount_specific_keepalive_timeout_secs > ds)
1485
0
    ds = (int)wsi->parent->http.mount_specific_keepalive_timeout_secs;
1486
0
#endif
1487
0
#endif
1488
1489
0
  if (!ds)
1490
0
    ds = 31;
1491
1492
  // lwsl_wsi_notice(wsi, "Eff keepalive_timeout %ds ===================\n",
1493
  // ds);
1494
1495
0
  return ds;
1496
0
}
1497
1498
#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) || defined(LWS_ROLE_QUIC)
1499
1500
void lws_wsi_mux_insert(struct lws *wsi, struct lws *parent_wsi,
1501
0
    uint64_t sid) {
1502
0
  lwsl_wsi_info(wsi, "par %s: assign sid %llu (curr %llu)", lws_wsi_tag(parent_wsi),
1503
0
      (unsigned long long)sid, (unsigned long long)wsi->mux.my_sid);
1504
1505
0
  if (wsi->mux.my_sid && wsi->mux.my_sid != sid)
1506
0
    assert(0);
1507
1508
0
  wsi->mux.my_sid = sid;
1509
0
  wsi->mux.parent_wsi = parent_wsi;
1510
0
  if (!wsi->role_ops)
1511
0
    wsi->role_ops = parent_wsi->role_ops;
1512
1513
#if defined(LWS_WITH_PEER_LIMITS)
1514
  if (parent_wsi->peer && !wsi->peer) {
1515
    wsi->peer = parent_wsi->peer;
1516
    lws_context_lock(wsi->a.context, "mux peer child adopt");
1517
    wsi->peer->count_wsi++;
1518
    lws_context_unlock(wsi->a.context);
1519
  }
1520
#endif
1521
1522
  /* new guy becomes the head child of the parent's mux child list */
1523
0
  lws_dll2_add_head(&wsi->mux.sibling_list, &parent_wsi->mux.child_list_owner);
1524
0
}
1525
1526
0
struct lws *lws_wsi_mux_from_id(struct lws *parent_wsi, unsigned int sid) {
1527
0
  lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&parent_wsi->mux.child_list_owner)) {
1528
0
    struct lws *wsi = lws_container_of(d, struct lws, mux.sibling_list);
1529
0
    if ((unsigned int)wsi->mux.my_sid == sid)
1530
0
      return wsi;
1531
0
  }
1532
0
  lws_end_foreach_dll(d);
1533
1534
0
  return NULL;
1535
0
}
1536
1537
0
void lws_wsi_mux_dump_children(struct lws *wsi) {
1538
0
#if defined(_DEBUG) && (_LWS_ENABLED_LOGS & LLL_INFO)
1539
0
  struct lws *parent;
1540
1541
0
  if (!wsi->mux.parent_wsi)
1542
0
    return;
1543
1544
0
  parent = wsi->mux.parent_wsi;
1545
1546
0
  lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&parent->mux.child_list_owner)) {
1547
0
    struct lws *w = lws_container_of(d, struct lws, mux.sibling_list);
1548
0
    lwsl_wsi_info(wsi, "   \\---- child %s %s\n",
1549
0
        w->role_ops ? w->role_ops->name : "?", lws_wsi_tag(w));
1550
0
  }
1551
0
  lws_end_foreach_dll(d);
1552
0
#endif
1553
0
}
1554
1555
0
void lws_wsi_mux_close_children(struct lws *wsi, int reason) {
1556
1557
0
  if(lws_dll2_is_empty(&wsi->mux.child_list_owner))
1558
0
    return;
1559
1560
0
  lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1561
0
           lws_dll2_get_head(&wsi->mux.child_list_owner)) {
1562
0
    struct lws *w = lws_container_of(d, struct lws, mux.sibling_list);
1563
1564
0
    lwsl_wsi_info(w, "   closing child");
1565
0
    w->socket_is_permanently_unusable = 1;
1566
0
    __lws_close_free_wsi(w, (enum lws_close_status)reason,
1567
0
        "mux child recurse");
1568
0
  }
1569
0
  lws_end_foreach_dll_safe(d, d1);
1570
0
}
1571
1572
0
void lws_wsi_mux_sibling_disconnect(struct lws *wsi) {
1573
1574
0
  if (!wsi->mux.parent_wsi)
1575
0
    return;
1576
1577
0
  lws_dll2_remove(&wsi->mux.sibling_list);
1578
0
  lwsl_wsi_debug(wsi, " disentangled from mux parent %s",
1579
0
           lws_wsi_tag(wsi->mux.parent_wsi));
1580
1581
0
  wsi->mux.parent_wsi = NULL;
1582
0
}
1583
1584
0
void lws_wsi_mux_dump_waiting_children(struct lws *wsi) {
1585
0
#if defined(_DEBUG) && (_LWS_ENABLED_LOGS & LLL_INFO)
1586
0
  lwsl_info("%s: %s: children waiting for POLLOUT service:\n", __func__,
1587
0
      lws_wsi_tag(wsi));
1588
1589
0
  lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&wsi->mux.child_list_owner)) {
1590
0
    struct lws *w = lws_container_of(d, struct lws, mux.sibling_list);
1591
0
    lwsl_wsi_info(w, "  %c sid %llu: 0x%x %s %s",
1592
0
        w->mux.requested_POLLOUT ? '*' : ' ',
1593
0
        (unsigned long long)w->mux.my_sid,
1594
0
        lwsi_state(w), w->role_ops->name,
1595
0
        w->a.protocol ? w->a.protocol->name : "noprotocol");
1596
0
  }
1597
0
  lws_end_foreach_dll(d);
1598
0
#endif
1599
0
}
1600
1601
0
int lws_wsi_mux_mark_parents_needing_writeable(struct lws *wsi) {
1602
0
  struct lws /* *network_wsi = lws_get_network_wsi(wsi), */ *wsi2;
1603
  // int already = network_wsi->mux.requested_POLLOUT;
1604
1605
  /* mark everybody above him as requesting pollout */
1606
1607
0
  wsi2 = wsi;
1608
0
  while (wsi2) {
1609
0
    wsi2->mux.requested_POLLOUT = 1;
1610
0
    lwsl_wsi_debug(wsi2, "sid %llu, pending writable",
1611
0
             (unsigned long long)wsi2->mux.my_sid);
1612
0
    wsi2 = wsi2->mux.parent_wsi;
1613
0
  }
1614
1615
0
  return 0; // already;
1616
0
}
1617
1618
/*
1619
 * Move the head mux child of parent_wsi to the tail of its sibling list, and
1620
 * clear its requested_POLLOUT.  Returns the moved child (formerly the head),
1621
 * or NULL if there are no children.  This implements the fair-share rotation
1622
 * used by the POLLOUT service loops.
1623
 */
1624
0
struct lws *lws_wsi_mux_move_child_to_tail(struct lws *parent_wsi) {
1625
0
  struct lws_dll2 *head;
1626
0
  struct lws *w;
1627
1628
0
  head = lws_dll2_get_head(&parent_wsi->mux.child_list_owner);
1629
0
  if (!head)
1630
0
    return NULL;
1631
1632
0
  w = lws_container_of(head, struct lws, mux.sibling_list);
1633
1634
0
  lws_dll2_remove(&w->mux.sibling_list);
1635
0
  lws_dll2_add_tail(&w->mux.sibling_list,
1636
0
        &parent_wsi->mux.child_list_owner);
1637
1638
  /* clear the waiting for POLLOUT on the guy that was chosen */
1639
0
  w->mux.requested_POLLOUT = 0;
1640
1641
0
  return w;
1642
0
}
1643
1644
0
int lws_wsi_mux_action_pending_writeable_reqs(struct lws *wsi) {
1645
0
  struct lws *nwsi = lws_get_network_wsi(wsi);
1646
1647
0
  if (wsi->mux.requested_POLLOUT) {
1648
0
    if (lws_change_pollfd(nwsi, 0, LWS_POLLOUT))
1649
0
      return -1;
1650
0
    return 0;
1651
0
  }
1652
1653
0
  lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&wsi->mux.child_list_owner)) {
1654
0
    struct lws *w = lws_container_of(d, struct lws, mux.sibling_list);
1655
1656
0
    if (w->mux.requested_POLLOUT) {
1657
0
      if (lws_change_pollfd(nwsi, 0, LWS_POLLOUT))
1658
0
        return -1;
1659
0
      return 0;
1660
0
    }
1661
0
  }
1662
0
  lws_end_foreach_dll(d);
1663
1664
0
  if (lws_change_pollfd(nwsi, LWS_POLLOUT, 0))
1665
0
    return -1;
1666
1667
0
  return 0;
1668
0
}
1669
1670
0
int lws_wsi_txc_check_skint(struct lws_tx_credit *txc, int32_t tx_cr) {
1671
0
  if (tx_cr <= 0) {
1672
    /*
1673
     * If other side is not able to cope with us sending any DATA
1674
     * so no matter if we have POLLOUT on our side if it's DATA we
1675
     * want to send.
1676
     */
1677
1678
0
    if (!txc->skint)
1679
0
      lwsl_info("%s: %p: skint (%d)\n", __func__, txc, (int)tx_cr);
1680
1681
0
    txc->skint = 1;
1682
1683
0
    return 1;
1684
0
  }
1685
1686
0
  if (txc->skint)
1687
0
    lwsl_info("%s: %p: unskint (%d)\n", __func__, txc, (int)tx_cr);
1688
1689
0
  txc->skint = 0;
1690
1691
0
  return 0;
1692
0
}
1693
1694
#if defined(_DEBUG)
1695
void lws_wsi_txc_describe(struct lws_tx_credit *txc, const char *at,
1696
0
    uint64_t sid) {
1697
0
  lwsl_info("%s: %p: %s: sid %llu: %speer-to-us: %d, us-to-peer: %d\n", __func__,
1698
0
      txc, at, (unsigned long long)sid, txc->skint ? "SKINT, " : "",
1699
0
      (int)txc->peer_tx_cr_est, (int)txc->tx_cr);
1700
0
}
1701
#endif
1702
1703
0
int lws_wsi_tx_credit(struct lws *wsi, char peer_to_us, int add) {
1704
0
  if (wsi->role_ops && lws_rops_fidx(wsi->role_ops, LWS_ROPS_tx_credit))
1705
0
    return lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_tx_credit)
1706
0
      .tx_credit(wsi, peer_to_us, add);
1707
1708
0
  return 0;
1709
0
}
1710
1711
/*
1712
 * Let the protocol know about incoming tx credit window updates if it's
1713
 * managing the flow control manually (it may want to proxy this information)
1714
 */
1715
1716
0
int lws_wsi_txc_report_manual_txcr_in(struct lws *wsi, int32_t bump) {
1717
0
  if (!wsi->txc.manual)
1718
    /*
1719
     * If we don't care about managing it manually, no need to
1720
     * report it
1721
     */
1722
0
    return 0;
1723
1724
0
  return user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
1725
0
      LWS_CALLBACK_WSI_TX_CREDIT_GET,
1726
0
      wsi->user_space, NULL, (size_t)bump);
1727
0
}
1728
1729
#if defined(LWS_WITH_CLIENT)
1730
1731
0
int lws_wsi_mux_apply_queue(struct lws *wsi) {
1732
  /* we have a transaction queue that wants to pipeline */
1733
1734
0
  lws_context_lock(wsi->a.context, __func__); /* -------------- cx { */
1735
0
  lws_vhost_lock(wsi->a.vhost);
1736
1737
0
  lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
1738
0
      lws_dll2_get_head(&wsi->dll2_cli_txn_queue_owner)) {
1739
0
    struct lws *w = lws_container_of(d, struct lws, dll2_cli_txn_queue);
1740
1741
0
    lwsl_wsi_notice(wsi, "evaluating queued conn %s (state 0x%x, par role %s)",
1742
0
        lws_wsi_tag(w), lwsi_state(w), wsi->role_ops ? wsi->role_ops->name : "none");
1743
1744
0
#if defined(LWS_ROLE_H2)
1745
0
    if (lwsi_role_h2(wsi) &&
1746
0
        lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS) {
1747
0
      lwsl_wsi_info(w, "cli pipeq to be h2");
1748
1749
0
      lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2);
1750
1751
      /* remove ourselves from client queue */
1752
0
      lws_dll2_remove(&w->dll2_cli_txn_queue);
1753
0
      lws_set_timeout(w, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
1754
0
          (int)wsi->a.context->timeout_secs);
1755
1756
      /* attach ourselves as an h2 stream */
1757
0
      lws_wsi_h2_adopt(wsi, w);
1758
0
    }
1759
0
#endif
1760
1761
#if defined(LWS_ROLE_H3)
1762
    if ((wsi->role_ops && (!strcmp(wsi->role_ops->name, "quic") || !strcmp(wsi->role_ops->name, "h3"))) &&
1763
        lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS) {
1764
      
1765
      if (!lws_wsi_h3_can_adopt(wsi)) {
1766
        lwsl_wsi_notice(wsi, "h3 can_adopt returned false!");
1767
        break;
1768
      }
1769
1770
      lwsl_wsi_notice(w, "cli pipeq to be h3");
1771
1772
      lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2);
1773
1774
      /* remove ourselves from client queue */
1775
      lws_dll2_remove(&w->dll2_cli_txn_queue);
1776
      lws_set_timeout(w, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
1777
          (int)wsi->a.context->timeout_secs);
1778
1779
      /* attach ourselves as an h3 stream */
1780
      lws_wsi_h3_adopt(wsi, w);
1781
    }
1782
#endif
1783
1784
#if defined(LWS_ROLE_MQTT)
1785
    if (lwsi_role_mqtt(wsi) && lwsi_state(wsi) == LRS_ESTABLISHED) {
1786
      lwsl_wsi_info(w, "cli pipeq to be mqtt\n");
1787
1788
      /* remove ourselves from client queue */
1789
      lws_dll2_remove(&w->dll2_cli_txn_queue);
1790
1791
      /* attach ourselves as an h2 stream */
1792
      lws_wsi_mqtt_adopt(wsi, w);
1793
    }
1794
#endif
1795
0
  }
1796
0
  lws_end_foreach_dll_safe(d, d1);
1797
1798
0
  lws_vhost_unlock(wsi->a.vhost);
1799
0
  lws_context_unlock(wsi->a.context); /* } cx --------------  */
1800
1801
0
  return 0;
1802
0
}
1803
1804
#endif
1805
1806
#endif