Coverage Report

Created: 2025-11-24 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/roles/ws/client-ws.c
Line
Count
Source
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2021 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
/*
28
 * In-place str to lower case
29
 */
30
31
static void
32
strtolower(char *s)
33
0
{
34
0
  while (*s) {
35
#ifdef LWS_PLAT_OPTEE
36
    int tolower_optee(int c);
37
    *s = tolower_optee((int)*s);
38
#else
39
0
    *s = (char)tolower((int)*s);
40
0
#endif
41
0
    s++;
42
0
  }
43
0
}
44
45
int
46
lws_create_client_ws_object(const struct lws_client_connect_info *i,
47
          struct lws *wsi)
48
0
{
49
0
  int v = SPEC_LATEST_SUPPORTED;
50
51
  /* allocate the ws struct for the wsi */
52
0
  wsi->ws = lws_zalloc(sizeof(*wsi->ws), "client ws struct");
53
0
  if (!wsi->ws) {
54
0
    lwsl_wsi_notice(wsi, "OOM");
55
0
    return 1;
56
0
  }
57
58
  /* -1 means just use latest supported */
59
0
  if (i->ietf_version_or_minus_one != -1 &&
60
0
      i->ietf_version_or_minus_one)
61
0
    v = i->ietf_version_or_minus_one;
62
63
0
  wsi->ws->ietf_spec_revision = (uint8_t)v;
64
65
0
  if (i->allow_reserved_bits)
66
0
    wsi->ws->allow_reserved_bits = 1;
67
68
0
  if (i->allow_unknown_opcode)
69
0
    wsi->ws->allow_unknown_opcode = 1;
70
71
0
  return 0;
72
0
}
73
74
#if defined(LWS_WITH_CLIENT)
75
76
/*
77
 * Returns either LWS_HPI_RET_PLEASE_CLOSE_ME or LWS_HPI_RET_HANDLED
78
 */
79
80
lws_handling_result_t
81
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
82
0
{
83
0
  const uint8_t **cbuf = (const uint8_t **)buf;
84
0
  unsigned char *start_buf = *buf;
85
86
0
  if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
87
0
      (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
88
0
      (lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
89
0
      !lwsi_role_client(wsi))
90
0
    return LWS_HPI_RET_HANDLED;
91
92
0
  lwsl_wsi_debug(wsi, "hs client has %d in", (int)len);
93
94
0
  if (lws_is_flowcontrolled(wsi)) {
95
0
    lwsl_wsi_debug(wsi, "caching %ld", (long)len);
96
0
    if (lws_rxflow_cache(wsi, *buf, 0, len) == LWSRXFC_TRIMMED) {
97
0
      lwsl_wsi_info(wsi, "trimming inside rxflow cache");
98
      /* we indicate we used nothing, caller will not advance */
99
0
    } else {
100
0
      *buf += len; /* we indicate we used it all */
101
0
    }
102
0
    return LWS_HPI_RET_HANDLED;
103
0
  }
104
105
#if !defined(LWS_WITHOUT_EXTENSIONS)
106
  if (wsi->ws->rx_draining_ext) {
107
    lwsl_wsi_info(wsi, "draining ext");
108
    if (lws_ws_client_rx_sm(wsi, 0) == LWS_HPI_RET_PLEASE_CLOSE_ME)
109
      return LWS_HPI_RET_PLEASE_CLOSE_ME;
110
  }
111
#endif
112
113
0
  if (len) {
114
0
    if (lws_ws_client_rx_parser_block(wsi, cbuf, &len) !=
115
0
              LWS_HPI_RET_HANDLED) {
116
0
      lwsl_wsi_info(wsi, "client_rx_parser exited, closing");
117
0
      *buf = start_buf + len; /* Update how much we used */
118
0
      return LWS_HPI_RET_PLEASE_CLOSE_ME;
119
0
    }
120
0
  }
121
122
0
  return LWS_HPI_RET_HANDLED;
123
0
}
124
#endif
125
126
char *
127
lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1, size_t p_len)
128
0
{
129
0
  char buf[128], hash[20], key_b64[40], *end = p + p_len;
130
0
  size_t s;
131
#if !defined(LWS_WITHOUT_EXTENSIONS)
132
  const struct lws_extension *ext;
133
  int ext_count = 0;
134
#endif
135
136
  /*
137
   * create the random key
138
   */
139
0
  if (lws_get_random(wsi->a.context, hash, 16) != 16) {
140
0
    lwsl_wsi_err(wsi, "Unable to read from random dev %s",
141
0
       SYSTEM_RANDOM_FILEPATH);
142
0
    return NULL;
143
0
  }
144
145
  /* coverity[tainted_scalar] */
146
0
  lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
147
148
0
  p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
149
0
        "Upgrade: websocket\x0d\x0a"
150
0
        "Connection: %sUpgrade\x0d\x0a"
151
0
        "Sec-WebSocket-Key: ", conn1);
152
153
0
  if (lws_ptr_diff_size_t(end, p) < strlen(key_b64) + 2 + 128)
154
0
    return NULL;
155
156
0
  lws_strncpy(p, key_b64, lws_ptr_diff_size_t(end, p));
157
0
  p += strlen(key_b64);
158
0
  p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\x0d\x0a");
159
0
  if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
160
0
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
161
0
          "Sec-WebSocket-Protocol: %s\x0d\x0a",
162
0
         lws_hdr_simple_ptr(wsi,
163
0
             _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
164
165
  /* tell the server what extensions we could support */
166
167
#if !defined(LWS_WITHOUT_EXTENSIONS)
168
  ext = wsi->a.vhost->ws.extensions;
169
  while (ext && ext->callback) {
170
171
    int n = wsi->a.vhost->protocols[0].callback(wsi,
172
      LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
173
        wsi->user_space, (char *)ext->name, 0);
174
175
    /*
176
     * zero return from callback means go ahead and allow
177
     * the extension, it's what we get if the callback is
178
     * unhandled
179
     */
180
181
    if (n) {
182
      ext++;
183
      continue;
184
    }
185
186
    /* apply it */
187
188
    if (ext_count)
189
      *p++ = ',';
190
    else
191
      p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
192
            "Sec-WebSocket-Extensions: ");
193
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
194
          "%s", ext->client_offer);
195
    ext_count++;
196
197
    ext++;
198
  }
199
  if (ext_count)
200
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\x0d\x0a");
201
#endif
202
203
0
  if (wsi->ws->ietf_spec_revision)
204
0
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
205
0
          "Sec-WebSocket-Version: %d\x0d\x0a",
206
0
          wsi->ws->ietf_spec_revision);
207
208
  /* prepare the expected server accept response */
209
210
0
  key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
211
212
0
  s = strlen(key_b64);
213
0
  memcpy(buf, key_b64, s);
214
0
  memcpy(buf + s, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", 36);
215
0
  s += 36;
216
217
0
  lws_SHA1((unsigned char *)buf, (unsigned int)s, (unsigned char *)hash);
218
219
0
  lws_b64_encode_string(hash, 20,
220
0
      wsi->http.ah->initial_handshake_hash_base64,
221
0
      sizeof(wsi->http.ah->initial_handshake_hash_base64));
222
223
0
  return p;
224
0
}
225
226
int
227
lws_client_ws_upgrade(struct lws *wsi, const char **cce)
228
0
{
229
0
  struct lws_context *context = wsi->a.context;
230
0
  struct lws_tokenize ts;
231
0
  int n, len, okay = 0;
232
0
  lws_tokenize_elem e;
233
0
  char *p, buf[64];
234
0
  const char *pc;
235
#if !defined(LWS_WITHOUT_EXTENSIONS)
236
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
237
  char *sb = (char *)&pt->serv_buf[0];
238
  const struct lws_ext_options *opts;
239
  const struct lws_extension *ext;
240
  char ext_name[128];
241
  const char *c, *a;
242
  int more = 1;
243
  char ignore;
244
#endif
245
246
0
  if (wsi->client_mux_substream) {/* !!! client ws-over-h2 not there yet */
247
0
    lwsl_wsi_warn(wsi, "client ws-over-h2 upgrade not supported yet");
248
0
    *cce = "HS: h2 / ws upgrade unsupported";
249
0
    goto bail3;
250
0
  }
251
252
0
  if (wsi->http.ah->http_response == 401) {
253
0
    lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
254
0
            (long)wsi->http.ah->http_response);
255
0
    *cce = "HS: ws upgrade unauthorized";
256
0
    goto bail3;
257
0
  }
258
259
0
  if (wsi->http.ah->http_response != 101) {
260
0
    lwsl_wsi_warn(wsi, "got bad HTTP response '%ld'",
261
0
            (long)wsi->http.ah->http_response);
262
0
    *cce = "HS: ws upgrade response not 101";
263
0
    goto bail3;
264
0
  }
265
266
0
  if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
267
0
    lwsl_wsi_info(wsi, "no ACCEPT");
268
0
    *cce = "HS: ACCEPT missing";
269
0
    goto bail3;
270
0
  }
271
272
0
  p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
273
0
  if (!p) {
274
0
    lwsl_wsi_info(wsi, "no UPGRADE");
275
0
    *cce = "HS: UPGRADE missing";
276
0
    goto bail3;
277
0
  }
278
0
  strtolower(p);
279
0
  if (strcmp(p, "websocket")) {
280
0
    lwsl_wsi_warn(wsi, "got bad Upgrade header '%s'", p);
281
0
    *cce = "HS: Upgrade to something other than websocket";
282
0
    goto bail3;
283
0
  }
284
285
  /* connection: must have "upgrade" */
286
287
0
  lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_COMMA_SEP_LIST |
288
0
            LWS_TOKENIZE_F_MINUS_NONTERM);
289
0
  n = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_CONNECTION);
290
0
  if (n <= 0) /* won't fit, or absent */
291
0
    goto bad_conn_format;
292
0
  ts.len = (unsigned int)n;
293
294
0
  do {
295
0
    e = lws_tokenize(&ts);
296
0
    switch (e) {
297
0
    case LWS_TOKZE_TOKEN:
298
0
      if (!strncasecmp(ts.token, "upgrade", ts.token_len))
299
0
        e = LWS_TOKZE_ENDED;
300
0
      break;
301
302
0
    case LWS_TOKZE_DELIMITER:
303
0
      break;
304
305
0
    default: /* includes ENDED found by the tokenizer itself */
306
0
bad_conn_format:
307
0
      lwsl_wsi_info(wsi, "malformed connection '%s'", buf);
308
0
      *cce = "HS: UPGRADE malformed";
309
0
      goto bail3;
310
0
    }
311
0
  } while (e > 0);
312
313
0
  pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
314
0
#if defined(_DEBUG)
315
0
  if (!pc)
316
0
    lwsl_wsi_parser(wsi, "lws_client_int_s_hs: no protocol list");
317
0
  else
318
0
    lwsl_wsi_parser(wsi, "lws_client_int_s_hs: protocol list '%s'", pc);
319
0
#endif
320
321
  /*
322
   * confirm the protocol the server wants to talk was in the list
323
   * of protocols we offered
324
   */
325
326
0
  len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
327
0
  if (!len) {
328
0
    lwsl_wsi_info(wsi, "WSI_TOKEN_PROTOCOL is null");
329
    /*
330
     * no protocol name to work from, if we don't already have one
331
     * default to first protocol
332
     */
333
334
0
    if (wsi->a.protocol) {
335
0
      p = (char *)wsi->a.protocol->name;
336
0
      goto identify_protocol;
337
0
    }
338
339
    /* no choice but to use the default protocol */
340
341
0
    n = 0;
342
0
    wsi->a.protocol = &wsi->a.vhost->protocols[0];
343
0
    goto check_extensions;
344
0
  }
345
346
0
  p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
347
0
  len = (int)strlen(p);
348
349
0
  while (pc && *pc && !okay) {
350
0
    if (!strncmp(pc, p, (unsigned int)len) &&
351
0
        (pc[len] == ',' || pc[len] == '\0')) {
352
0
      okay = 1;
353
0
      continue;
354
0
    }
355
0
    while (*pc && *pc++ != ',')
356
0
      ;
357
0
    while (*pc == ' ')
358
0
      pc++;
359
0
  }
360
361
0
  if (!okay) {
362
0
    lwsl_wsi_info(wsi, "got bad protocol %s", p);
363
0
    *cce = "HS: PROTOCOL malformed";
364
0
    goto bail2;
365
0
  }
366
367
0
identify_protocol:
368
369
#if defined(LWS_WITH_HTTP_PROXY)
370
  lws_strncpy(wsi->ws->actual_protocol, p,
371
        sizeof(wsi->ws->actual_protocol));
372
#endif
373
374
  /*
375
   * identify the selected protocol struct and set it
376
   */
377
0
  n = 0;
378
  /* keep client connection pre-bound protocol */
379
0
  if (!lwsi_role_client(wsi))
380
0
    wsi->a.protocol = NULL;
381
382
0
  while (n < wsi->a.vhost->count_protocols) {
383
0
    if (!wsi->a.protocol &&
384
0
        strcmp(p, wsi->a.vhost->protocols[n].name) == 0) {
385
0
      wsi->a.protocol = &wsi->a.vhost->protocols[n];
386
0
      break;
387
0
    }
388
0
    n++;
389
0
  }
390
391
0
  if (n == wsi->a.vhost->count_protocols) { /* no match */
392
    /* if server, that's already fatal */
393
0
    if (!lwsi_role_client(wsi)) {
394
0
      lwsl_wsi_info(wsi, "fail protocol %s", p);
395
0
      *cce = "HS: Cannot match protocol";
396
0
      goto bail2;
397
0
    }
398
399
    /* for client, find the index of our pre-bound protocol */
400
401
0
    n = 0;
402
0
    while (wsi->a.vhost->protocols[n].callback) {
403
0
      if (wsi->a.protocol && strcmp(wsi->a.protocol->name,
404
0
           wsi->a.vhost->protocols[n].name) == 0) {
405
0
        wsi->a.protocol = &wsi->a.vhost->protocols[n];
406
0
        break;
407
0
      }
408
0
      n++;
409
0
    }
410
411
0
    if (!wsi->a.vhost->protocols[n].callback) {
412
0
      if (wsi->a.protocol)
413
0
        lwsl_wsi_err(wsi, "Failed to match protocol %s",
414
0
            wsi->a.protocol->name);
415
0
      else
416
0
        lwsl_wsi_err(wsi, "No protocol on client");
417
0
      *cce = "ws protocol no match";
418
0
      goto bail2;
419
0
    }
420
0
  }
421
422
0
  lwsl_wsi_debug(wsi, "Selected protocol %s", wsi->a.protocol ?
423
0
               wsi->a.protocol->name : "no pcol");
424
425
0
check_extensions:
426
  /*
427
   * stitch protocol choice into the vh protocol linked list
428
   * We always insert ourselves at the start of the list
429
   *
430
   * X <-> B
431
   * X <-> pAn <-> pB
432
   */
433
434
0
  lws_same_vh_protocol_insert(wsi, n);
435
436
#if !defined(LWS_WITHOUT_EXTENSIONS)
437
  /* instantiate the accepted extensions */
438
439
  if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
440
    lwsl_wsi_ext(wsi, "no client extensions allowed by server");
441
    goto check_accept;
442
  }
443
444
  /*
445
   * break down the list of server accepted extensions
446
   * and go through matching them or identifying bogons
447
   */
448
449
  if (lws_hdr_copy(wsi, sb, (int)context->pt_serv_buf_size,
450
       WSI_TOKEN_EXTENSIONS) < 0) {
451
    lwsl_wsi_warn(wsi, "ext list from server failed to copy");
452
    *cce = "HS: EXT: list too big";
453
    goto bail2;
454
  }
455
456
  c = sb;
457
  n = 0;
458
  ignore = 0;
459
  a = NULL;
460
  while (more) {
461
462
    if (*c && (*c != ',' && *c != '\t')) {
463
      if (*c == ';') {
464
        ignore = 1;
465
        if (!a)
466
          a = c + 1;
467
      }
468
      if (ignore || *c == ' ') {
469
        c++;
470
        continue;
471
      }
472
473
      ext_name[n] = *c++;
474
      if (n < (int)sizeof(ext_name) - 1)
475
        n++;
476
      continue;
477
    }
478
    ext_name[n] = '\0';
479
    ignore = 0;
480
    if (!*c)
481
      more = 0;
482
    else {
483
      c++;
484
      if (!n)
485
        continue;
486
    }
487
488
    /* check we actually support it */
489
490
    lwsl_wsi_notice(wsi, "checking client ext %s", ext_name);
491
492
    n = 0;
493
    ext = wsi->a.vhost->ws.extensions;
494
    while (ext && ext->callback) {
495
      if (strcmp(ext_name, ext->name)) {
496
        ext++;
497
        continue;
498
      }
499
500
      n = 1;
501
      lwsl_wsi_notice(wsi, "instantiating client ext %s", ext_name);
502
503
      /* instantiate the extension on this conn */
504
505
      wsi->ws->active_extensions[wsi->ws->count_act_ext] = ext;
506
507
      /* allow him to construct his ext instance */
508
509
      if (ext->callback(lws_get_context(wsi), ext, wsi,
510
           LWS_EXT_CB_CLIENT_CONSTRUCT,
511
           (void *)&wsi->ws->act_ext_user[
512
                                wsi->ws->count_act_ext],
513
           (void *)&opts, 0)) {
514
        lwsl_wsi_info(wsi, " ext %s failed construction",
515
            ext_name);
516
        ext++;
517
        continue;
518
      }
519
520
      /*
521
       * allow the user code to override ext defaults if it
522
       * wants to
523
       */
524
      ext_name[0] = '\0';
525
      if (user_callback_handle_rxflow(wsi->a.protocol->callback,
526
          wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
527
          (char *)ext->name, ext_name,
528
          sizeof(ext_name))) {
529
        *cce = "HS: EXT: failed setting defaults";
530
        goto bail2;
531
      }
532
533
      if (ext_name[0] &&
534
          lws_ext_parse_options(ext, wsi,
535
                    wsi->ws->act_ext_user[
536
                    wsi->ws->count_act_ext],
537
                    opts, ext_name,
538
              (int)strlen(ext_name))) {
539
        lwsl_wsi_err(wsi, "unable to parse user defaults '%s'",
540
               ext_name);
541
        *cce = "HS: EXT: failed parsing defaults";
542
        goto bail2;
543
      }
544
545
      /*
546
       * give the extension the server options
547
       */
548
      if (a && lws_ext_parse_options(ext, wsi,
549
          wsi->ws->act_ext_user[
550
                          wsi->ws->count_act_ext],
551
          opts, a, lws_ptr_diff(c, a))) {
552
        lwsl_wsi_err(wsi, "unable to parse remote def '%s'", a);
553
        *cce = "HS: EXT: failed parsing options";
554
        goto bail2;
555
      }
556
557
      if (ext->callback(lws_get_context(wsi), ext, wsi,
558
          LWS_EXT_CB_OPTION_CONFIRM,
559
              wsi->ws->act_ext_user[wsi->ws->count_act_ext],
560
              NULL, 0)) {
561
        lwsl_wsi_err(wsi, "ext %s rejects server options %s",
562
               ext->name, a);
563
        *cce = "HS: EXT: Rejects server options";
564
        goto bail2;
565
      }
566
567
      wsi->ws->count_act_ext++;
568
569
      ext++;
570
    }
571
572
    if (n == 0) {
573
      lwsl_wsi_warn(wsi, "Unknown ext '%s'!", ext_name);
574
      *cce = "HS: EXT: unknown ext";
575
      goto bail2;
576
    }
577
578
    a = NULL;
579
    n = 0;
580
  }
581
582
check_accept:
583
#endif
584
585
  /*
586
   * Confirm his accept token is the one we precomputed
587
   */
588
589
0
  p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
590
0
  if (strcmp(p, wsi->http.ah->initial_handshake_hash_base64)) {
591
0
    lwsl_wsi_warn(wsi, "lws_client_int_s_hs: accept '%s' wrong vs '%s'", p,
592
0
          wsi->http.ah->initial_handshake_hash_base64);
593
0
    *cce = "HS: Accept hash wrong";
594
0
    goto bail2;
595
0
  }
596
597
  /* allocate the per-connection user memory (if any) */
598
0
  if (lws_ensure_user_space(wsi)) {
599
0
    lwsl_wsi_err(wsi, "Problem allocating wsi user mem");
600
0
    *cce = "HS: OOM";
601
0
    goto bail2;
602
0
  }
603
604
  /*
605
   * we seem to be good to go, give client last chance to check
606
   * headers and OK it
607
   */
608
0
  if (wsi->a.protocol->callback(wsi,
609
0
            LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
610
0
            wsi->user_space, NULL, 0)) {
611
0
    *cce = "HS: Rejected by filter cb";
612
0
    goto bail2;
613
0
  }
614
615
  /* clear his proxy connection timeout */
616
0
  lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
617
618
  /* free up his parsing allocations */
619
0
  lws_header_table_detach(wsi, 0);
620
621
0
  lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, &role_ops_ws);
622
0
  lws_validity_confirmed(wsi);
623
624
0
  wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
625
626
  /*
627
   * create the frame buffer for this connection according to the
628
   * size mentioned in the protocol definition.  If 0 there, then
629
   * use a big default for compatibility
630
   */
631
0
  n = (int)wsi->a.protocol->rx_buffer_size;
632
0
  if (!n)
633
0
    n = (int)context->pt_serv_buf_size;
634
0
  n += LWS_PRE;
635
0
  wsi->ws->rx_ubuf = lws_malloc((unsigned int)n + 4 /* 0x0000ffff zlib */,
636
0
        "client frame buffer");
637
0
  if (!wsi->ws->rx_ubuf) {
638
0
    lwsl_wsi_err(wsi, "OOM allocating rx buffer %d", n);
639
0
    *cce = "HS: OOM";
640
0
    goto bail2;
641
0
  }
642
0
  wsi->ws->rx_ubuf_alloc = (unsigned int)n;
643
644
0
  lwsl_wsi_debug(wsi, "handshake OK for protocol %s", wsi->a.protocol->name);
645
646
  /* call him back to inform him he is up */
647
648
0
  if (wsi->a.protocol->callback(wsi, LWS_CALLBACK_CLIENT_ESTABLISHED,
649
0
            wsi->user_space, NULL, 0)) {
650
0
    *cce = "HS: Rejected at CLIENT_ESTABLISHED";
651
0
    goto bail3;
652
0
  }
653
654
0
  return 0;
655
656
0
bail3:
657
0
  return 3;
658
659
0
bail2:
660
0
  return 2;
661
0
}