Coverage Report

Created: 2026-02-26 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/roles/http/client/client-http.c
Line
Count
Source
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2025 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
void
28
lws_client_http_body_pending(struct lws *wsi, int something_left_to_send)
29
0
{
30
0
  wsi->client_http_body_pending = !!something_left_to_send;
31
0
}
32
33
/*
34
 * Returns 0 for wsi survived OK, or LWS_HPI_RET_WSI_ALREADY_DIED
35
 * meaning the wsi was destroyed by us before return.
36
 */
37
  
38
int
39
lws_http_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
40
0
{
41
0
  struct lws_context *context = wsi->a.context;
42
0
  struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
43
0
  char *p = (char *)&pt->serv_buf[0], *end = p + wsi->a.context->pt_serv_buf_size;
44
0
#if defined(LWS_WITH_TLS)
45
0
  char ebuf[128];
46
0
#endif
47
0
  const char *cce = NULL;
48
0
  char *sb = p;
49
0
  int n = 0;
50
51
0
  switch (lwsi_state(wsi)) {
52
53
0
  case LRS_WAITING_DNS:
54
    /*
55
     * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
56
     * timeout protection set in client-handshake.c
57
     */
58
0
    lwsl_err("%s: %s: WAITING_DNS\n", __func__, lws_wsi_tag(wsi));
59
0
    if (!lws_client_connect_2_dnsreq_MAY_CLOSE_WSI(wsi)) {
60
      /* closed */
61
0
      lwsl_client("closed\n");
62
0
      return LWS_HPI_RET_WSI_ALREADY_DIED;
63
0
    }
64
65
    /* either still pending connection, or changed mode */
66
0
    return 0;
67
68
0
  case LRS_WAITING_CONNECT:
69
70
    /*
71
     * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
72
     * timeout protection set in client-handshake.c
73
     */
74
0
    if (pollfd->revents & LWS_POLLOUT)
75
0
      if (lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL) == NULL) {
76
0
        lwsl_client("closed\n");
77
0
        return LWS_HPI_RET_WSI_ALREADY_DIED;
78
0
      }
79
0
    break;
80
81
#if defined(LWS_WITH_SOCKS5)
82
  /* SOCKS Greeting Reply */
83
  case LRS_WAITING_SOCKS_GREETING_REPLY:
84
  case LRS_WAITING_SOCKS_AUTH_REPLY:
85
  case LRS_WAITING_SOCKS_CONNECT_REPLY:
86
87
    switch (lws_socks5c_handle_state(wsi, pollfd, &cce)) {
88
    case LW5CHS_RET_RET0:
89
      return 0;
90
    case LW5CHS_RET_BAIL3:
91
      goto bail3;
92
    case LW5CHS_RET_STARTHS:
93
      goto start_ws_handshake;
94
    default:
95
      break;
96
    }
97
    break;
98
#endif
99
100
0
#if defined(LWS_CLIENT_HTTP_PROXYING) && (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))
101
102
0
  case LRS_WAITING_PROXY_REPLY:
103
104
    /* handle proxy hung up on us */
105
106
0
    if (pollfd->revents & LWS_POLLHUP) {
107
108
0
      lwsl_warn("Proxy conn %s (fd=%d) dead\n",
109
0
          lws_wsi_tag(wsi), pollfd->fd);
110
111
0
      cce = "proxy conn dead";
112
0
      goto bail3;
113
0
    }
114
115
0
    n = (int)recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0);
116
0
    if (n < 0) {
117
0
      if (LWS_ERRNO == LWS_EAGAIN) {
118
0
        lwsl_debug("Proxy read EAGAIN... retrying\n");
119
0
        return 0;
120
0
      }
121
0
      lwsl_err("ERROR reading from proxy socket\n");
122
0
      cce = "proxy read err";
123
0
      goto bail3;
124
0
    }
125
126
    /* sanity check what we were sent... */
127
128
0
    pt->serv_buf[13] = '\0';
129
0
    if (n < 13 || strncmp(sb, "HTTP/1.", 7) ||
130
0
            (sb[7] != '0' && sb[7] != '1') || sb[8] != ' ') {
131
      /* lwsl_hexdump_notice(sb, n); */
132
0
      cce = "http_proxy fail";
133
0
      goto bail3;
134
0
    }
135
136
    /* it's h1 alright... what's his logical response code? */
137
0
    n = atoi(&sb[9]);
138
0
    if (n != 200) {
139
0
      lws_snprintf(sb, 20, "http_proxy -> %u",
140
0
             (unsigned int)n);
141
0
      cce = sb;
142
0
      goto bail3;
143
0
    }
144
145
0
    lwsl_info("%s: proxy connection established\n", __func__);
146
147
    /* clear his proxy connection timeout */
148
149
0
    lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
150
151
               /* fallthru */
152
153
0
#endif
154
155
               /* dummy fallthru to satisfy compiler */
156
               /* fallthru */
157
0
  case LRS_H1C_ISSUE_HANDSHAKE:
158
159
0
    lwsl_debug("%s: LRS_H1C_ISSUE_HANDSHAKE\n", __func__);
160
161
    /*
162
     * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
163
     * timeout protection set in client-handshake.c
164
     *
165
     * take care of our lws_callback_on_writable
166
     * happening at a time when there's no real connection yet
167
     */
168
#if defined(LWS_WITH_SOCKS5)
169
start_ws_handshake:
170
#endif
171
0
    if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
172
0
      cce = "unable to clear POLLOUT";
173
      /* turn whatever went wrong into a clean close */
174
0
      goto bail3;
175
0
    }
176
177
0
#if defined(LWS_ROLE_H2) || defined(LWS_WITH_TLS)
178
0
    if (
179
0
#if defined(LWS_WITH_TLS)
180
0
        !(wsi->tls.use_ssl & LCCSCF_USE_SSL)
181
0
#endif
182
0
#if defined(LWS_ROLE_H2) && defined(LWS_WITH_TLS)
183
0
        &&
184
0
#endif
185
0
#if defined(LWS_ROLE_H2)
186
0
        !(wsi->flags & LCCSCF_H2_PRIOR_KNOWLEDGE)
187
0
#endif
188
0
        )
189
0
      goto hs2;
190
0
#endif
191
192
0
#if defined(LWS_WITH_TLS)
193
0
    n = lws_client_create_tls(wsi, &cce, 1);
194
0
    if (n == CCTLS_RETURN_ERROR)
195
0
      goto bail3;
196
0
    if (n == CCTLS_RETURN_RETRY)
197
0
      return 0;
198
199
    /*
200
     * lws_client_create_tls() can already have done the
201
     * whole tls setup and preface send... if so he set our state
202
     * to LRS_H1C_ISSUE_HANDSHAKE2... let's proceed but be prepared
203
     * to notice our state and not resend the preface...
204
     */
205
206
0
    lwsl_debug("%s: LRS_H1C_ISSUE_HANDSHAKE fallthru\n", __func__);
207
208
    /* fallthru */
209
210
0
  case LRS_WAITING_SSL:
211
212
0
    if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
213
0
      n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf));
214
0
      if (!n)
215
0
        return 0;
216
0
      if (n < 0) {
217
0
        cce = ebuf;
218
0
        goto bail3;
219
0
      }
220
0
    } else {
221
0
      wsi->tls.ssl = NULL;
222
0
      if (wsi->flags & LCCSCF_H2_PRIOR_KNOWLEDGE) {
223
0
        lwsl_info("h2 prior knowledge\n");
224
0
        lws_role_call_alpn_negotiated(wsi, "h2");
225
0
      }
226
0
    }
227
0
#endif
228
229
0
#if defined (LWS_WITH_HTTP2)
230
0
    if (wsi->client_h2_alpn //&&
231
        //lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2
232
0
        ) {
233
      /*
234
       * We connected to the server and set up tls and
235
       * negotiated "h2" or connected as clear text
236
       * with http/2 prior knowledge.
237
       *
238
       * So this is it, we are an h2 nwsi client connection
239
       * now, not an h1 client connection.
240
       */
241
242
0
      lwsl_info("%s: doing h2 hello path\n", __func__);
243
244
      /*
245
       * send the H2 preface to legitimize the connection
246
       *
247
       * transitions us to LRS_H2_WAITING_TO_SEND_HEADERS
248
       */
249
0
      if (wsi->client_h2_alpn)
250
0
        if (lws_h2_issue_preface(wsi)) {
251
0
          cce = "error sending h2 preface";
252
0
          goto bail3;
253
0
        }
254
255
    //  lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
256
0
      lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
257
0
          (int)context->timeout_secs);
258
259
0
      break;
260
0
    }
261
0
#endif
262
263
    /* fallthru */
264
265
0
  case LRS_H1C_ISSUE_HANDSHAKE2:
266
267
0
#if defined(LWS_ROLE_H2) || defined(LWS_WITH_TLS)
268
0
hs2:
269
0
#endif
270
271
0
    p = lws_generate_client_handshake(wsi, p,
272
0
              lws_ptr_diff_size_t(end, p));
273
0
    if (p == NULL) {
274
0
      if (wsi->role_ops == &role_ops_raw_skt
275
0
#if defined(LWS_ROLE_RAW_FILE)
276
0
        || wsi->role_ops == &role_ops_raw_file
277
0
#endif
278
0
          )
279
0
        return 0;
280
281
0
      lwsl_err("Failed to generate handshake for client\n");
282
0
      lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
283
0
             "chs");
284
0
      return -1;
285
0
    }
286
287
    /* send our request to the server */
288
289
0
    lwsl_info("%s: HANDSHAKE2: %s: sending headers "
290
0
        "(wsistate 0x%lx), w sock %d\n",
291
0
        __func__, lws_wsi_tag(wsi),
292
0
        (unsigned long)wsi->wsistate, wsi->desc.sockfd);
293
294
0
    n = lws_ssl_capable_write(wsi, (unsigned char *)sb, lws_ptr_diff_size_t(p, sb));
295
0
    switch (n) {
296
0
    case LWS_SSL_CAPABLE_ERROR:
297
0
      lwsl_debug("ERROR writing to client socket\n");
298
0
      lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
299
0
             "cws");
300
0
      return LWS_HPI_RET_WSI_ALREADY_DIED;
301
0
    case LWS_SSL_CAPABLE_MORE_SERVICE:
302
0
      lws_callback_on_writable(wsi);
303
0
      break;
304
0
    }
305
306
0
    if (wsi->client_http_body_pending || lws_has_buffered_out(wsi)) {
307
0
      lwsl_debug("body pending\n");
308
0
      lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY);
309
0
      lws_set_timeout(wsi,
310
0
          PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
311
0
          (int)context->timeout_secs);
312
313
0
      if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED)
314
0
        lws_callback_on_writable(wsi);
315
#if defined(LWS_WITH_HTTP_PROXY)
316
      if (wsi->http.proxy_clientside && wsi->parent &&
317
          wsi->parent->http.buflist_post_body)
318
        lws_callback_on_writable(wsi);
319
#endif
320
      /* user code must ask for writable callback */
321
0
      break;
322
0
    }
323
324
0
    lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
325
0
    wsi->hdr_parsing_completed = 0;
326
327
0
    if (lwsi_state(wsi) == LRS_IDLING) {
328
0
      lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
329
0
      wsi->hdr_parsing_completed = 0;
330
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
331
0
      wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
332
0
      wsi->http.ah->lextable_pos = 0;
333
0
      wsi->http.ah->unk_pos = 0;
334
      /* If we're (re)starting on hdr, need other implied init */
335
0
      wsi->http.ah->ues = URIES_IDLE;
336
0
#endif
337
0
    }
338
339
0
    lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
340
0
        (int)wsi->a.context->timeout_secs);
341
342
0
    lws_callback_on_writable(wsi);
343
344
0
    goto client_http_body_sent;
345
346
0
  case LRS_ISSUE_HTTP_BODY:
347
#if defined(LWS_WITH_HTTP_PROXY)
348
      if (wsi->http.proxy_clientside && wsi->parent &&
349
          wsi->parent->http.buflist_post_body)
350
        lws_callback_on_writable(wsi);
351
#endif
352
0
    if (wsi->client_http_body_pending || lws_has_buffered_out(wsi)) {
353
      //lws_set_timeout(wsi,
354
      //    PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
355
      //    context->timeout_secs);
356
      /* user code must ask for writable callback */
357
0
      break;
358
0
    }
359
0
client_http_body_sent:
360
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
361
    /* prepare ourselves to do the parsing */
362
0
    wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
363
0
    wsi->http.ah->lextable_pos = 0;
364
0
    wsi->http.ah->unk_pos = 0;
365
0
#endif
366
0
    lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
367
0
    lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
368
0
        (int)context->timeout_secs);
369
0
    break;
370
371
0
  case LRS_WAITING_SERVER_REPLY:
372
    /*
373
     * handle server hanging up on us...
374
     * but if there is POLLIN waiting, handle that first
375
     */
376
0
    if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
377
0
                LWS_POLLHUP) {
378
379
0
      if (lws_buflist_total_len(&wsi->buflist))
380
0
        lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 3);
381
0
      else {
382
0
        lwsl_debug("Server conn %s (fd=%d) dead\n",
383
0
            lws_wsi_tag(wsi), pollfd->fd);
384
0
        cce = "Peer hung up";
385
0
        goto bail3;
386
0
      }
387
0
    }
388
389
0
    if (pollfd->revents & LWS_POLLOUT)
390
0
      if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
391
0
        cce = "Unable to clear POLLOUT";
392
0
        goto bail3;
393
0
      }
394
395
0
    if (!(pollfd->revents & LWS_POLLIN))
396
0
      break;
397
398
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
399
    /* interpret the server response
400
     *
401
     *  HTTP/1.1 101 Switching Protocols
402
     *  Upgrade: websocket
403
     *  Connection: Upgrade
404
     *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
405
     *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
406
     *  Sec-WebSocket-Protocol: chat
407
     *
408
     * we have to take some care here to only take from the
409
     * socket bytewise.  The browser may (and has been seen to
410
     * in the case that onopen() performs websocket traffic)
411
     * coalesce both handshake response and websocket traffic
412
     * in one packet, since at that point the connection is
413
     * definitively ready from browser pov.
414
     */
415
0
    while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE) {
416
0
      struct lws_tokens eb;
417
0
      int n, m, buffered;
418
419
0
      eb.token = NULL;
420
0
      eb.len = 0;
421
0
      buffered = lws_buflist_aware_read(pt, wsi, &eb, 0, __func__);
422
0
      lwsl_debug("%s: buflist-aware-read %d %d\n", __func__,
423
0
          buffered, eb.len);
424
0
      if (eb.len == LWS_SSL_CAPABLE_MORE_SERVICE)
425
0
        return 0;
426
0
      if (buffered < 0 || eb.len < 0) {
427
0
        cce = "read failed";
428
0
        goto bail3;
429
0
      }
430
0
      if (!eb.len)
431
0
        return 0;
432
433
0
      n = eb.len;
434
0
      if (lws_parse(wsi, eb.token, &n)) {
435
0
        lwsl_warn("problems parsing header\n");
436
0
        cce = "problems parsing header";
437
0
        goto bail3;
438
0
      }
439
440
0
      m = eb.len - n;
441
#if defined(LWS_WITH_SECURE_STREAMS_BUFFER_DUMP)
442
      do {
443
        lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
444
        if (!h)
445
          break;
446
447
        if (h->info.dump) {
448
          h->info.dump(ss_to_userobj(h),
449
            (const uint8_t *)eb.token,
450
            (size_t)m,
451
            (wsi->http.ah->parser_state ==
452
             WSI_PARSING_COMPLETE) ? 1 : 0);
453
        }
454
      } while (0);
455
#endif
456
0
      if (lws_buflist_aware_finished_consuming(wsi, &eb, m,
457
0
                 buffered,
458
0
                 __func__))
459
0
              goto bail3;
460
461
      /*
462
       * coverity: uncomment if extended
463
       *
464
       * eb.token += m;
465
       * eb.len -= m;
466
       */
467
468
0
      if (n) {
469
0
        assert(wsi->http.ah->parser_state ==
470
0
            WSI_PARSING_COMPLETE);
471
472
0
        break;
473
0
      }
474
0
    }
475
476
    /*
477
     * hs may also be coming in multiple packets, there is a 5-sec
478
     * libwebsocket timeout still active here too, so if parsing did
479
     * not complete just wait for next packet coming in this state
480
     */
481
0
    if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
482
0
      break;
483
0
#endif
484
485
    /*
486
     * otherwise deal with the handshake.  If there's any
487
     * packet traffic already arrived we'll trigger poll() again
488
     * right away and deal with it that way
489
     */
490
0
    return lws_client_interpret_server_handshake(wsi);
491
492
0
bail3:
493
0
    lwsl_info("%s: closing conn at LWS_CONNMODE...SERVER_REPLY, %s, state 0x%x\n",
494
0
        __func__, lws_wsi_tag(wsi), lwsi_state(wsi));
495
0
    if (cce)
496
0
      lwsl_info("reason: %s\n", cce);
497
0
    else
498
0
      cce = "unknown";
499
0
    lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce));
500
501
0
    lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
502
0
    return LWS_HPI_RET_WSI_ALREADY_DIED;
503
504
0
  default:
505
0
    break;
506
0
  }
507
508
0
  return 0;
509
0
}
510
511
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
512
513
int LWS_WARN_UNUSED_RESULT
514
lws_http_transaction_completed_client(struct lws *wsi)
515
0
{
516
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
517
0
  int n;
518
519
0
  lwsl_info("%s: %s (%s)\n", __func__, lws_wsi_tag(wsi),
520
0
      wsi->a.protocol->name);
521
522
  // if (wsi->http.ah && wsi->http.ah->http_response)
523
  /* we're only judging if any (200, or 500 etc) http txn completed */
524
0
  lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
525
526
0
  if (user_callback_handle_rxflow(wsi->a.protocol->callback, wsi,
527
0
          LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
528
0
          wsi->user_space, NULL, 0)) {
529
0
    lwsl_debug("%s: Completed call returned nonzero (role 0x%lx)\n",
530
0
         __func__, (unsigned long)lwsi_role(wsi));
531
0
    return -1;
532
0
  }
533
534
0
  wsi->http.rx_content_length = 0;
535
536
  /*
537
   * For h1, wsi may pass some assets on to a queued child and be
538
   * destroyed during this.
539
   */
540
0
  lws_pt_lock(pt, __func__);
541
0
  n = _lws_generic_transaction_completed_active_conn(&wsi, 1);
542
0
  lws_pt_unlock(pt);
543
544
0
  if (wsi->http.ah) {
545
0
    if (wsi->client_mux_substream)
546
      /*
547
       * As an h2 client, once we did our transaction, that is
548
       * it for us.  Further transactions will happen as new
549
       * SIDs on the connection.
550
       */
551
0
      __lws_header_table_detach(wsi, 0);
552
0
    else
553
0
      if (!n)
554
0
        _lws_header_table_reset(wsi->http.ah);
555
0
  }
556
557
0
  if (!n || !wsi->http.ah)
558
0
    return 0;
559
560
  /*
561
   * H1: we can serialize the queued guys into the same ah
562
   * H2: everybody needs their own ah until their own STREAM_END
563
   */
564
565
  /* otherwise set ourselves up ready to go again */
566
0
  lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
567
568
0
  wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
569
0
  wsi->http.ah->lextable_pos = 0;
570
0
  wsi->http.ah->unk_pos = 0;
571
572
0
  lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
573
0
      (int)wsi->a.context->timeout_secs);
574
575
  /* If we're (re)starting on headers, need other implied init */
576
0
  wsi->http.ah->ues = URIES_IDLE;
577
0
  lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
578
579
0
  lwsl_info("%s: %s: new queued transaction\n", __func__, lws_wsi_tag(wsi));
580
0
  lws_callback_on_writable(wsi);
581
582
0
  return 0;
583
0
}
584
585
unsigned int
586
lws_http_client_http_response(struct lws *wsi)
587
0
{
588
0
  if (wsi->http.ah && wsi->http.ah->http_response)
589
0
    return wsi->http.ah->http_response;
590
591
0
  return 0;
592
0
}
593
#endif
594
595
596
#if defined(LWS_WITH_HTTP_DIGEST_AUTH) && defined(LWS_WITH_TLS)
597
598
static const char *digest_toks[] = {
599
  "Digest", // 1 <<  0
600
  "username", // 1 <<  1
601
  "realm",  // 1 <<  2
602
  "nonce",  // 1 <<  3
603
  "uri",    // 1 <<  4 optional
604
  "response", // 1 <<  5
605
  "opaque", // 1 <<  6
606
  "qop",    // 1 <<  7
607
  "algorithm",  // 1 <<  8
608
  "nc",   // 1 <<  9
609
  "cnonce", // 1 << 10
610
  "domain", // 1 << 11
611
};
612
613
0
#define PEND_NAME_EQ -1
614
0
#define PEND_DELIM -2
615
616
enum lws_check_basic_auth_results
617
lws_http_digest_auth(struct lws* wsi)
618
0
{
619
0
  uint8_t nonce[256], response[LWS_GENHASH_LARGEST], qop[32];
620
0
  int seen = 0, n, pend = -1;
621
0
  char *tmp_digest = NULL;
622
0
  struct lws_tokenize ts;
623
0
  char resp_username[32];
624
0
  lws_tokenize_elem e;
625
0
  char realm[64];
626
0
  char b64[512];
627
0
  int m, ml, fi;
628
629
  /* Did he send auth? */
630
0
  ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_WWW_AUTHENTICATE);
631
0
  if (!ml)
632
0
    return LCBA_FAILED_AUTH;
633
634
  /* Disallow fragmentation monkey business */
635
636
0
  fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_WWW_AUTHENTICATE];
637
0
  if (wsi->http.ah->frags[fi].nfrag) {
638
0
    lwsl_wsi_err(wsi, "fragmented http auth header not allowed\n");
639
0
    return LCBA_FAILED_AUTH;
640
0
  }
641
642
0
  m = lws_hdr_copy(wsi, b64, sizeof(b64), WSI_TOKEN_HTTP_WWW_AUTHENTICATE);
643
0
  if (m < 7) {
644
0
    lwsl_wsi_err(wsi, "HTTP auth length bad\n");
645
0
    return LCBA_END_TRANSACTION;
646
0
  }
647
648
  /*
649
   * We are expecting AUTHORIZATION to have something like this
650
   *
651
   * Authorization: Digest
652
   *   username="Mufasa",
653
   *   realm="testrealm@host.com",
654
   *   nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
655
   *   uri="/dir/index.html",
656
   *   response="e966c932a9242554e42c8ee200cec7f6",
657
   *   opaque="5ccc069c403ebaf9f0171e9517f40e41"
658
   *
659
   * but the order, whitespace etc is quite open.  uri is optional
660
   */
661
0
  lws_tokenize_init(&ts,b64, LWS_TOKENIZE_F_MINUS_NONTERM |
662
0
           LWS_TOKENIZE_F_NO_INTEGERS |
663
0
           LWS_TOKENIZE_F_RFC7230_DELIMS);
664
665
0
  do {
666
0
    e = lws_tokenize(&ts);
667
0
    switch (e) {
668
0
    case LWS_TOKZE_TOKEN:
669
0
      if (pend == 8) {
670
        /* algorithm name */
671
672
0
        if (!strncasecmp(ts.token, "MD5", ts.token_len)) {
673
0
          lwsl_wsi_err(wsi, "wrong alg %.*s\n",
674
0
              (int)ts.token_len,
675
0
              ts.token);
676
0
          return LCBA_END_TRANSACTION;
677
0
        }
678
0
        pend = PEND_DELIM;
679
0
        break;
680
0
      }
681
0
      if (!strncasecmp(ts.token, "Digest", ts.token_len)) {
682
0
        seen |= 1 << 0;
683
0
        break;
684
0
      }
685
0
      if (seen) /* we must be first and one time */
686
0
        return LCBA_END_TRANSACTION;
687
688
0
      seen |= 1 << 15;
689
0
      pend = PEND_NAME_EQ;
690
0
      break;
691
692
0
    case LWS_TOKZE_TOKEN_NAME_EQUALS:
693
0
      if ((seen & (1 << 15)) == (1 << 15) || pend != -1)
694
        /* no auth type token or disordered */
695
0
        return LCBA_END_TRANSACTION;
696
697
0
      for (n = 0; n < (int)LWS_ARRAY_SIZE(digest_toks); n++)
698
0
        if (!strncmp(ts.token, digest_toks[n], ts.token_len))
699
0
          break;
700
701
0
      if (n == LWS_ARRAY_SIZE(digest_toks)) {
702
0
        lwsl_wsi_notice(wsi, "c: '%.*s'\n",
703
0
            (int)ts.token_len,
704
0
            ts.token);
705
706
0
        return LCBA_END_TRANSACTION;
707
0
      }
708
709
0
      if (seen & (1 << n) || (seen & (1 << 15)) == (1 << 15))
710
        /* dup or no auth type token */
711
0
        return LCBA_END_TRANSACTION;
712
713
0
      seen |= 1 << n;
714
0
      pend = n;
715
0
      break;
716
717
0
    case LWS_TOKZE_QUOTED_STRING:
718
0
      if (pend < 0)
719
0
        return LCBA_END_TRANSACTION;
720
721
0
      switch (pend) {
722
0
      case 1: /* username */
723
0
        if (ts.token_len >= (int)sizeof(resp_username))
724
0
          return LCBA_END_TRANSACTION;
725
726
0
        strncpy(resp_username, ts.token, ts.token_len);
727
0
        break;
728
0
      case 2: /* realm */
729
0
        if (ts.token_len >= (int)sizeof(realm))
730
0
          return LCBA_END_TRANSACTION;
731
732
0
        strncpy(realm, ts.token, ts.token_len);
733
0
        realm[ts.token_len] = 0;
734
0
        break;
735
0
      case 3: /* nonce */
736
0
        if (ts.token_len >= (int)sizeof(nonce))
737
0
          return LCBA_END_TRANSACTION;
738
739
0
        strncpy((char *)nonce, ts.token, ts.token_len);
740
0
        nonce[ts.token_len] = 0;
741
0
        break;
742
0
      case 4: /* uri */
743
0
        break;
744
0
      case 5: /* response */
745
0
        if (ts.token_len !=
746
0
          lws_genhash_size(LWS_GENHASH_TYPE_MD5) * 2)
747
0
          return LCBA_END_TRANSACTION;
748
749
0
        if (lws_hex_len_to_byte_array(ts.token, ts.token_len,
750
0
                response,
751
0
                sizeof(response)) < 0)
752
0
          return LCBA_END_TRANSACTION;
753
0
        break;
754
0
      case 6: /* opaque */
755
0
        break;
756
0
      case 7: /* qop */
757
0
        if (strncmp(ts.token, "auth", ts.token_len))
758
0
          return LCBA_END_TRANSACTION;
759
760
0
        strncpy((char *)qop, ts.token, ts.token_len);
761
0
        qop[ts.token_len] = 0;
762
0
        break;
763
0
      }
764
0
      pend = PEND_DELIM;
765
0
      break;
766
767
0
      case LWS_TOKZE_DELIMITER:
768
0
        if (*ts.token == ',') {
769
0
          if (pend != PEND_DELIM)
770
0
            return LCBA_END_TRANSACTION;
771
772
0
          pend = PEND_NAME_EQ;
773
0
          break;
774
0
        }
775
0
        if (*ts.token == ';') {
776
          /* it's the end */
777
0
          e = LWS_TOKZE_ENDED;
778
0
          break;
779
0
        }
780
0
        break;
781
782
0
      case LWS_TOKZE_ENDED:
783
0
        break;
784
785
0
      default:
786
0
        lwsl_wsi_notice(wsi, "unexpected token %d\n", e);
787
0
        return LCBA_END_TRANSACTION;
788
0
    }
789
790
0
  } while (e > 0);
791
792
  /* we got all the parts we care about? Realm + Nonce... */
793
794
0
  if ((seen & 0xc) != 0xc) {
795
0
    lwsl_wsi_err(wsi,
796
0
        "%s: Not all digest auth tokens found! "
797
0
        "m: 0x%x\nServer sent: %s",
798
0
        __func__, seen & 0x81ef, b64);
799
800
0
    return LCBA_END_TRANSACTION;
801
0
  }
802
803
0
  lwsl_wsi_info(wsi, "HTTP digest auth realm %s nonce %s\n", realm, nonce);
804
805
0
  if (wsi->stash &&
806
0
      wsi->stash->cis[CIS_PATH]) {
807
0
    char *username =  wsi->stash->cis[CIS_USERNAME];
808
0
    char *password = wsi->stash->cis[CIS_PASSWORD];
809
0
    uint8_t digest[LWS_GENHASH_LARGEST * 2 + 1];
810
0
    char *uri = wsi->stash->cis[CIS_PATH];
811
0
    char a1[LWS_GENHASH_LARGEST * 2 + 1];
812
0
    char a2[LWS_GENHASH_LARGEST * 2 + 1];
813
0
    char nc[sizeof(int) * 2 + 1];
814
0
    struct lws_genhash_ctx hc;
815
0
    int ncount = 1, ssl;
816
0
    const char *a, *p;
817
0
    struct lws *nwsi;
818
0
    char cnonce[256];
819
0
    size_t l;
820
821
0
    l = sizeof(a1) + sizeof(a2) + sizeof(nonce) +
822
0
      (sizeof(ncount) *2) + sizeof(response) +
823
0
      sizeof(cnonce) + sizeof(qop) + strlen(uri) +
824
0
      strlen(username) + strlen(password) +
825
0
      strlen(realm) + 111;
826
827
0
    tmp_digest = lws_malloc(l, __func__);
828
0
    if (!tmp_digest)
829
0
      return LCBA_FAILED_AUTH;
830
831
0
    n = lws_snprintf(tmp_digest, l, "%s:%s:%s",
832
0
         username, realm, password);
833
834
0
    if (lws_genhash_init(&hc, LWS_GENHASH_TYPE_MD5) ||
835
0
        lws_genhash_update(&hc,
836
0
               tmp_digest,
837
0
              (size_t)n) ||
838
0
        lws_genhash_destroy(&hc, digest)) {
839
0
      lws_genhash_destroy(&hc, NULL);
840
841
0
      goto bail;
842
0
    }
843
844
0
    lws_hex_from_byte_array(digest,
845
0
          lws_genhash_size(LWS_GENHASH_TYPE_MD5),
846
0
          a1, sizeof(a1));
847
0
    lwsl_debug("A1: %s:%s:%s = %s\n", username, realm, password, a1);
848
849
    /*
850
     * In case of Websocket upgrade, method is NULL
851
     * we assume it is a GET
852
    */
853
854
0
    n = lws_snprintf(tmp_digest, l, "%s:%s",
855
0
           wsi->stash->cis[CIS_METHOD] ?
856
0
           wsi->stash->cis[CIS_METHOD] : "GET", uri);
857
858
0
    if (lws_genhash_init(&hc, LWS_GENHASH_TYPE_MD5) ||
859
0
             lws_genhash_update(&hc,
860
0
                tmp_digest,
861
0
                (size_t)n) ||
862
0
             lws_genhash_destroy(&hc, digest)) {
863
0
      lws_genhash_destroy(&hc, NULL);
864
0
      lwsl_err("%s: hash failed\n", __func__);
865
866
0
      goto bail;
867
0
    }
868
0
    lws_hex_from_byte_array(digest,
869
0
          lws_genhash_size(LWS_GENHASH_TYPE_MD5),
870
0
          a2, sizeof(a2));
871
0
    lwsl_debug("A2: %s:%s = %s\n", wsi->stash->cis[CIS_METHOD],
872
0
        uri, a2);
873
874
0
    lws_hex_random(lws_get_context(wsi), cnonce, sizeof(cnonce));
875
0
    lws_hex_from_byte_array((const uint8_t *)&ncount,
876
0
          sizeof(ncount), nc, sizeof(nc));
877
878
0
    n = lws_snprintf(tmp_digest, l, "%s:%s:%08x:%s:%s:%s", a1,
879
0
        nonce, ncount, cnonce, qop, a2);
880
881
0
    lwsl_wsi_debug(wsi, "digest response: %s\n", tmp_digest);
882
883
884
0
    if (lws_genhash_init(&hc, LWS_GENHASH_TYPE_MD5) ||
885
0
        lws_genhash_update(&hc, tmp_digest, (size_t)n) ||
886
0
        lws_genhash_destroy(&hc, digest)) {
887
0
      lws_genhash_destroy(&hc, NULL);
888
0
      lwsl_wsi_err(wsi, "hash failed\n");
889
890
0
      goto bail;
891
0
    }
892
0
    lws_hex_from_byte_array(digest,
893
0
          lws_genhash_size(LWS_GENHASH_TYPE_MD5),
894
0
          (char *)response,
895
0
          lws_genhash_size(LWS_GENHASH_TYPE_MD5) * 2 + 1);
896
897
0
    n = lws_snprintf(tmp_digest, l,
898
0
         "Digest username=\"%s\", realm=\"%s\", "
899
0
         "nonce=\"%s\", uri=\"%s\", qop=%s, nc=%08x, "
900
0
         "cnonce=\"%s\", response=\"%s\", "
901
0
         "algorithm=\"MD5\"",
902
0
         username, realm, nonce, uri, qop, ncount,
903
0
         cnonce, response);
904
905
0
    lwsl_hexdump(tmp_digest, l);
906
907
0
    if (lws_hdr_simple_create(wsi, WSI_TOKEN_HTTP_AUTHORIZATION,
908
0
                tmp_digest)) {
909
0
      lwsl_wsi_err(wsi, "Failed to add Digest auth header");
910
0
      goto bail;
911
0
    }
912
913
0
    nwsi = lws_get_network_wsi(wsi);
914
0
    ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL;
915
916
0
    a = wsi->stash->cis[CIS_ADDRESS];
917
0
    p = &wsi->stash->cis[CIS_PATH][1];
918
919
    /*
920
     * This prevents connection pipelining when two
921
     * HTTP connection use the same tcp socket.
922
     */
923
0
    wsi->keepalive_rejected = 1;
924
925
0
    if (!lws_client_reset(&wsi, ssl, a, wsi->c_port, p, a, 1)) {
926
0
      lwsl_wsi_err(wsi, "Failed to reset WSI for Digest auth");
927
928
0
      goto bail;
929
0
    }
930
931
    /*
932
     * Keep track of digest auth to send it at next attempt, lws_client_reset will free it
933
    */
934
935
0
    wsi->http.digest_auth_hdr = tmp_digest;
936
0
    wsi->client_pipeline = 0;
937
0
  }
938
939
0
  return 0;
940
941
0
bail:
942
0
  lws_free(tmp_digest);
943
944
0
  return LCBA_FAILED_AUTH;
945
0
}
946
#endif
947
948
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
949
950
int
951
lws_http_is_redirected_to_get(struct lws *wsi)
952
0
{
953
0
  return wsi->redirected_to_get;
954
0
}
955
956
int
957
lws_client_interpret_server_handshake(struct lws *wsi)
958
0
{
959
0
  int n, port = 0, ssl = 0;
960
0
  int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
961
0
  const char *prot, *ads = NULL, *path, *cce = NULL;
962
0
  struct allocated_headers *ah, *ah1;
963
0
  struct lws *nwsi = lws_get_network_wsi(wsi);
964
0
  char *p = NULL, *q, *simp;
965
0
  char new_path[300];
966
0
  void *opaque;
967
968
  // lws_free_set_NULL(wsi->stash);
969
970
0
#if defined(LWS_WITH_CONMON)
971
0
  wsi->conmon.ciu_txn_resp = (lws_conmon_interval_us_t)
972
0
          (lws_now_usecs() - wsi->conmon_datum);
973
0
#endif
974
  // lws_free_set_NULL(wsi->stash);
975
976
0
  ah = wsi->http.ah;
977
0
  if (!wsi->do_ws) {
978
    /* we are being an http client...
979
     */
980
0
#if defined(LWS_ROLE_H2)
981
0
    if (wsi->client_h2_alpn || wsi->client_mux_substream) {
982
0
      lwsl_debug("%s: %s: transitioning to h2 client\n",
983
0
           __func__, lws_wsi_tag(wsi));
984
0
      lws_role_transition(wsi, LWSIFR_CLIENT,
985
0
              LRS_ESTABLISHED, &role_ops_h2);
986
0
    } else
987
0
#endif
988
0
    {
989
0
#if defined(LWS_ROLE_H1)
990
0
      {
991
0
      lwsl_debug("%s: %s: transitioning to h1 client\n",
992
0
           __func__, lws_wsi_tag(wsi));
993
0
      lws_role_transition(wsi, LWSIFR_CLIENT,
994
0
              LRS_ESTABLISHED, &role_ops_h1);
995
0
      }
996
#else
997
      cce = "h1 not built";
998
      goto bail3;
999
#endif
1000
0
    }
1001
1002
0
    wsi->http.ah = ah;
1003
0
    ah->http_response = 0;
1004
0
  }
1005
1006
0
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
1007
1008
0
  if ((wsi->flags & LCCSCF_CACHE_COOKIES) &&
1009
0
      lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_SET_COOKIE))
1010
0
    lws_parse_set_cookie(wsi);
1011
1012
0
#endif
1013
  /*
1014
   * well, what the server sent looked reasonable for syntax.
1015
   * Now let's confirm it sent all the necessary headers
1016
   *
1017
   * http (non-ws) client will expect something like this
1018
   *
1019
   * HTTP/1.0.200
1020
   * server:.libwebsockets
1021
   * content-type:.text/html
1022
   * content-length:.17703
1023
   * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
1024
   */
1025
1026
0
  wsi->http.conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1027
0
  if (!wsi->client_mux_substream) {
1028
0
    p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
1029
    /*
1030
    if (wsi->do_ws && !p) {
1031
      lwsl_info("no URI\n");
1032
      cce = "HS: URI missing";
1033
      goto bail3;
1034
    }
1035
    */
1036
0
    if (!p) {
1037
0
      p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
1038
0
      wsi->http.conn_type = HTTP_CONNECTION_CLOSE;
1039
0
    }
1040
0
    if (!p) {
1041
0
      cce = "HS: URI missing";
1042
0
      lwsl_info("no URI\n");
1043
0
      goto bail3;
1044
0
    }
1045
0
#if defined(LWS_ROLE_H2)
1046
0
  } else {
1047
0
    p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_STATUS);
1048
0
    if (!p) {
1049
0
      cce = "HS: :status missing";
1050
0
      lwsl_info("no status\n");
1051
0
      goto bail3;
1052
0
    }
1053
0
#endif
1054
0
  }
1055
#if !defined(LWS_ROLE_H2)
1056
  if (!p) {
1057
    cce = "HS: :status missing";
1058
    lwsl_info("no status\n");
1059
    goto bail3;
1060
  }
1061
#endif
1062
0
  n = atoi(p);
1063
1064
0
#if defined(LWS_WITH_HTTP_DIGEST_AUTH) && defined(LWS_WITH_TLS)
1065
0
  if (n == 401 && lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_WWW_AUTHENTICATE)) {
1066
0
    if (!(wsi->stash && wsi->stash->cis[CIS_USERNAME] &&
1067
0
                        wsi->stash->cis[CIS_PASSWORD])) {
1068
0
      lwsl_err("Digest auth requested by server but no credentials provided by user\n");
1069
      
1070
0
      return LCBA_FAILED_AUTH;
1071
0
    }
1072
1073
0
    if (lws_http_digest_auth(wsi))
1074
0
      goto bail3;
1075
1076
0
    opaque = wsi->a.opaque_user_data;
1077
0
    lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "digest_auth_step2");
1078
0
    wsi->a.opaque_user_data = opaque;
1079
1080
0
    return -1;
1081
0
  }
1082
1083
0
    ah = wsi->http.ah;
1084
0
#endif
1085
0
  if (ah)
1086
0
    ah->http_response = (unsigned int)n;
1087
1088
0
  if (!wsi->client_no_follow_redirect &&
1089
#if defined(LWS_WITH_HTTP_PROXY)
1090
      !wsi->http.proxy_clientside &&
1091
#endif
1092
0
      (n == 301 || n == 302 || n == 303 || n == 307 || n == 308)) {
1093
0
    p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION);
1094
0
    if (!p) {
1095
0
      cce = "HS: Redirect code but no Location";
1096
0
      goto bail3;
1097
0
    }
1098
1099
0
#if defined(LWS_WITH_CONMON)
1100
0
    if (wsi->conmon.pcol == LWSCONMON_PCOL_NONE) {
1101
0
      wsi->conmon.pcol = LWSCONMON_PCOL_HTTP;
1102
0
      wsi->conmon.protocol_specific.http.response = n;
1103
0
    }
1104
1105
0
#if defined(LWS_WITH_SECURE_STREAMS)
1106
0
    if (wsi->for_ss
1107
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
1108
        && !wsi->client_bound_sspc
1109
#endif
1110
0
       ) {
1111
  
1112
0
      lws_ss_handle_t *h = (lws_ss_handle_t *)lws_get_opaque_user_data(wsi);
1113
1114
0
      if (h)
1115
0
        lws_conmon_ss_json(h);
1116
0
    }
1117
0
#endif
1118
0
#endif
1119
1120
    /* let's let the user code know, if he cares */
1121
1122
0
    if (wsi->a.protocol->callback(wsi,
1123
0
          LWS_CALLBACK_CLIENT_HTTP_REDIRECT,
1124
0
          wsi->user_space, p, (unsigned int)n)) {
1125
0
      cce = "HS: user code rejected redirect";
1126
0
      goto bail3;
1127
0
    }
1128
1129
    /* Relative reference absolute path */
1130
0
    if (p[0] == '/' || !strchr(p, ':')) {
1131
0
#if defined(LWS_WITH_TLS)
1132
0
      ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL;
1133
0
#endif
1134
0
      ads = lws_hdr_simple_ptr(wsi,
1135
0
             _WSI_TOKEN_CLIENT_PEER_ADDRESS);
1136
0
      port = nwsi->c_port;
1137
0
      path = p;
1138
      /* lws_client_reset expects leading / omitted */
1139
0
      if (*path == '/')
1140
0
        path++;
1141
0
    }
1142
    /* Absolute (Full) URI */
1143
0
    else if (strchr(p, ':')) {
1144
0
      if (lws_parse_uri(p, &prot, &ads, &port, &path)) {
1145
0
        cce = "HS: URI did not parse";
1146
0
        goto bail3;
1147
0
      }
1148
1149
0
      if (!strcmp(prot, "wss") || !strcmp(prot, "https"))
1150
0
        ssl = LCCSCF_USE_SSL;
1151
0
    }
1152
    /* Relative reference relative path */
1153
0
    else {
1154
      /* This doesn't try to calculate an absolute path,
1155
       * that will be left to the server */
1156
0
#if defined(LWS_WITH_TLS)
1157
0
      ssl = nwsi->tls.use_ssl & LCCSCF_USE_SSL;
1158
0
#endif
1159
0
      ads = lws_hdr_simple_ptr(wsi,
1160
0
             _WSI_TOKEN_CLIENT_PEER_ADDRESS);
1161
0
      port = wsi->c_port;
1162
      /* +1 as lws_client_reset expects leading / omitted */
1163
0
      path = new_path + 1;
1164
0
      if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI))
1165
0
        lws_strncpy(new_path, lws_hdr_simple_ptr(wsi,
1166
0
           _WSI_TOKEN_CLIENT_URI), sizeof(new_path));
1167
0
      else {
1168
0
        new_path[0] = '/';
1169
0
        new_path[1] = '\0';
1170
0
      }
1171
0
      q = strrchr(new_path, '/');
1172
0
      if (q)
1173
0
        lws_strncpy(q + 1, p, sizeof(new_path) -
1174
0
              (unsigned int)(q - new_path) - 1);
1175
0
      else
1176
0
        path = p;
1177
0
    }
1178
1179
    /*
1180
     * Some redirect codes imply we have to change the method
1181
     * used for the subsequent transaction.
1182
     *
1183
     * ugh... https://peterdaugaardrasmussen.com/2020/05/09/how-to-redirect-http-put-or-post-requests/
1184
     * says only 307 or 308 mean keep POST or other method
1185
     */
1186
1187
0
    if (n != 307 && n != 308) {
1188
0
      char *mp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
1189
0
      int ml = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_METHOD);
1190
0
      uint16_t pl = (uint16_t)strlen(path);
1191
1192
0
      if (ml >= 3 && mp) {
1193
0
        lwsl_info("%s: 303 switching to GET\n", __func__);
1194
0
        memcpy(mp, "GET", 4);
1195
0
        wsi->redirected_to_get = 1;
1196
0
        wsi->http.ah->frags[wsi->http.ah->frag_index[
1197
0
          _WSI_TOKEN_CLIENT_METHOD]].len = 3;
1198
0
      }
1199
0
            if (wsi->stash)
1200
0
                    wsi->stash->cis[CIS_METHOD] = "GET";
1201
1202
0
      mp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
1203
0
      ml = lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI);
1204
1205
0
      if (wsi->http.ah->pos + pl + 1 >= wsi->http.ah->data_length) {
1206
0
        lwsl_warn("%s: redirect path exceeds ah size\n", __func__);
1207
0
        goto bail3;
1208
0
      }
1209
0
      memcpy(wsi->http.ah->data + wsi->http.ah->pos + 1, path, pl + 1u);
1210
0
      wsi->http.ah->data[wsi->http.ah->pos] = '/';
1211
0
      wsi->http.ah->frags[wsi->http.ah->frag_index[_WSI_TOKEN_CLIENT_URI]].offset = wsi->http.ah->pos;
1212
0
      wsi->http.ah->frags[wsi->http.ah->frag_index[_WSI_TOKEN_CLIENT_URI]].len = (uint16_t)(pl + 1u);
1213
1214
0
      if (wsi->stash)
1215
0
        wsi->stash->cis[CIS_PATH] = wsi->http.ah->data + wsi->http.ah->pos;
1216
1217
0
      wsi->http.ah->pos += pl + 1u;
1218
0
    }
1219
1220
1221
0
#if defined(LWS_WITH_TLS)
1222
0
    if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !ssl &&
1223
0
         !(wsi->flags & LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS)) {
1224
0
      cce = "HS: Redirect attempted SSL downgrade";
1225
0
      goto bail3;
1226
0
    }
1227
0
#endif
1228
1229
0
    if (!ads) /* make coverity happy */ {
1230
0
      cce = "no ads";
1231
0
      goto bail3;
1232
0
    }
1233
1234
0
    if (!lws_client_reset(&wsi, ssl, ads, port, path, ads, 1)) {
1235
0
      lwsl_err("Redirect failed\n");
1236
0
      cce = "HS: Redirect failed";
1237
0
      goto bail3;
1238
0
    }
1239
1240
    /*
1241
     * We are redirecting, let's close in order to extricate
1242
     * ourselves from the current wsi usage, eg, h2 mux cleanly.
1243
     *
1244
     * We will notice close_is_redirect and switch to redirect
1245
     * flow late in the close action.
1246
     */
1247
1248
0
    opaque = wsi->a.opaque_user_data;
1249
0
    lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "redir");
1250
0
    wsi->a.opaque_user_data = opaque;
1251
1252
0
    return LWS_HPI_RET_WSI_ALREADY_DIED;
1253
0
  }
1254
1255
  /* if h1 KA is allowed, enable the queued pipeline guys */
1256
1257
0
  if (!wsi->client_h2_alpn && !wsi->client_mux_substream) {
1258
    /* ie, coming to this for the first time */
1259
0
    if (wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
1260
0
      wsi->keepalive_active = 1;
1261
0
    else {
1262
      /*
1263
       * Ugh... now the main http connection has seen
1264
       * both sides, we learn the server doesn't
1265
       * support keepalive.
1266
       *
1267
       * That means any guys queued on us are going
1268
       * to have to be restarted from connect2 with
1269
       * their own connections.
1270
       */
1271
1272
      /*
1273
       * stick around telling any new guys they can't
1274
       * pipeline to this server
1275
       */
1276
0
      wsi->keepalive_rejected = 1;
1277
1278
0
      lws_vhost_lock(wsi->a.vhost);
1279
0
      lws_start_foreach_dll_safe(struct lws_dll2 *,
1280
0
               d, d1,
1281
0
        wsi->dll2_cli_txn_queue_owner.head) {
1282
0
        struct lws *ww = lws_container_of(d,
1283
0
          struct lws,
1284
0
          dll2_cli_txn_queue);
1285
1286
        /* remove him from our queue */
1287
0
        lws_dll2_remove(&ww->dll2_cli_txn_queue);
1288
        /* give up on pipelining */
1289
0
        ww->client_pipeline = 0;
1290
1291
        /* go back to "trying to connect" state */
1292
0
        lws_role_transition(ww, LWSIFR_CLIENT,
1293
0
                LRS_UNCONNECTED,
1294
0
#if defined(LWS_ROLE_H1)
1295
0
                &role_ops_h1);
1296
#else
1297
#if defined (LWS_ROLE_H2)
1298
                &role_ops_h2);
1299
#else
1300
                &role_ops_raw);
1301
#endif
1302
#endif
1303
0
        ww->user_space = NULL;
1304
0
      } lws_end_foreach_dll_safe(d, d1);
1305
0
      lws_vhost_unlock(wsi->a.vhost);
1306
0
    }
1307
0
  }
1308
1309
#ifdef LWS_WITH_HTTP_PROXY
1310
  wsi->http.perform_rewrite = 0;
1311
  if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
1312
    if (!strncmp(lws_hdr_simple_ptr(wsi,
1313
          WSI_TOKEN_HTTP_CONTENT_TYPE),
1314
          "text/html", 9))
1315
      wsi->http.perform_rewrite = 0;
1316
  }
1317
#endif
1318
1319
  /* he may choose to send us stuff in chunked transfer-coding */
1320
0
  wsi->chunked = 0;
1321
0
  wsi->chunk_remaining = 0; /* ie, next thing is chunk size */
1322
0
  if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING)) {
1323
0
    simp = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING);
1324
1325
    /* cannot be NULL, since it has nonzero length... coverity */
1326
0
    if (!simp)
1327
0
      goto bail2;
1328
0
    wsi->chunked = !strcmp(simp, "chunked");
1329
    /* first thing is hex, after payload there is crlf */
1330
0
    wsi->chunk_parser = ELCP_HEX;
1331
0
  }
1332
1333
0
  wsi->http.content_length_given = 0;
1334
0
  if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
1335
0
    simp = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH);
1336
1337
    /* cannot be NULL, since it has nonzero length... coverity */
1338
0
    if (!simp)
1339
0
      goto bail2;
1340
1341
0
    wsi->http.rx_content_length = (lws_filepos_t)atoll(simp);
1342
0
    lwsl_info("%s: incoming content length %llu\n",
1343
0
          __func__, (unsigned long long)
1344
0
            wsi->http.rx_content_length);
1345
0
    wsi->http.rx_content_remain =
1346
0
        wsi->http.rx_content_length;
1347
0
    wsi->http.content_length_given = 1;
1348
0
  } else { /* can't do 1.1 without a content length or chunked */
1349
0
    if (!wsi->chunked)
1350
0
      wsi->http.conn_type = HTTP_CONNECTION_CLOSE;
1351
0
    lwsl_debug("%s: no content length\n", __func__);
1352
0
  }
1353
1354
0
  if (wsi->do_ws) {
1355
    /*
1356
     * Give one last opportunity to ws protocols to inspect server reply
1357
     * before the ws upgrade code discard it. ie: download reply body in case
1358
     * of any other response code than 101.
1359
     */
1360
0
    if (wsi->a.protocol->callback(wsi,
1361
0
            LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
1362
0
            wsi->user_space, NULL, 0)) {
1363
1364
0
      cce = "HS: disallowed by client filter";
1365
0
      goto bail2;
1366
0
    }
1367
0
  } else {
1368
    /* allocate the per-connection user memory (if any) */
1369
0
    if (lws_ensure_user_space(wsi)) {
1370
0
      lwsl_err("Problem allocating wsi user mem\n");
1371
0
      cce = "HS: OOM";
1372
0
      goto bail2;
1373
0
    }
1374
1375
1376
    /*
1377
     * we seem to be good to go, give client last chance to check
1378
     * headers and OK it
1379
     */
1380
0
    ah1 = wsi->http.ah;
1381
0
    wsi->http.ah = ah;
1382
0
    if (wsi->a.protocol->callback(wsi,
1383
0
        LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
1384
0
              wsi->user_space, NULL, 0)) {
1385
0
      wsi->http.ah = ah1;
1386
0
      cce = "HS: disallowed by client filter";
1387
0
      goto bail2;
1388
0
    }
1389
1390
    /* clear his proxy connection timeout */
1391
0
    lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1392
1393
0
    wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
1394
1395
    /* call him back to inform him he is up */
1396
0
    if (wsi->a.protocol->callback(wsi,
1397
0
              LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP,
1398
0
              wsi->user_space, NULL, 0)) {
1399
0
      wsi->http.ah = ah1;
1400
0
      cce = "HS: disallowed at ESTABLISHED";
1401
0
      goto bail3;
1402
0
    }
1403
1404
0
    wsi->http.ah = ah1;
1405
1406
0
    lwsl_info("%s: %s: client conn up\n", __func__, lws_wsi_tag(wsi));
1407
1408
    /*
1409
     * Did we get a response from the server with an explicit
1410
     * content-length of zero?  If so, and it's not H2 which will
1411
     * notice it via END_STREAM, this transaction is already
1412
     * completed at the end of the header processing...
1413
     * We also completed it if the request method is HEAD which as
1414
     * no content leftover.
1415
     * Or if the response status code is 204 : No Content
1416
     */
1417
0
    simp = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
1418
0
    if (!wsi->mux_substream &&
1419
0
        !wsi->client_mux_substream &&
1420
0
      (204 == lws_http_client_http_response(wsi) ||
1421
0
       (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
1422
0
        (!wsi->http.rx_content_length ||
1423
0
        (simp && !strcmp(simp,"HEAD"))))))
1424
0
        return !!lws_http_transaction_completed_client(wsi);
1425
1426
    /*
1427
     * We can also get a case where it's http/1 and there's no
1428
     * content-length at all, so anything that comes is the body
1429
     * until it hangs up on us.  With that situation, hanging up
1430
     * on us past this point should generate a valid
1431
     * LWS_CALLBACK_COMPLETED_CLIENT_HTTP.
1432
     *
1433
     * In that situation, he can't pipeline because in h1 there's
1434
     * no post-header in-band way to signal the end of the
1435
     * transaction except hangup.
1436
     *
1437
     * lws_http_transaction_completed_client() is the right guy to
1438
     * issue it when we see the peer has hung up on us.
1439
     */
1440
1441
0
    return 0;
1442
0
  }
1443
1444
0
#if defined(LWS_ROLE_WS)
1445
0
  switch (lws_client_ws_upgrade(wsi, &cce)) {
1446
0
  case 2:
1447
0
    goto bail2;
1448
0
  case 3:
1449
0
    goto bail3;
1450
0
  }
1451
1452
0
  return 0;
1453
0
#endif
1454
1455
0
bail3:
1456
0
  close_reason = LWS_CLOSE_STATUS_NOSTATUS;
1457
1458
0
bail2:
1459
0
  if (wsi->a.protocol) {
1460
0
    n = 0;
1461
0
    if (cce)
1462
0
      n = (int)strlen(cce);
1463
1464
0
    lws_inform_client_conn_fail(wsi, (void *)cce, (unsigned int)n);
1465
0
  }
1466
1467
0
  lwsl_info("closing connection (prot %s) "
1468
0
      "due to bail2 connection error: %s\n", wsi->a.protocol ?
1469
0
          wsi->a.protocol->name : "unknown", cce);
1470
1471
  /* closing will free up his parsing allocations */
1472
0
  lws_close_free_wsi(wsi, (enum lws_close_status)close_reason, "c hs interp");
1473
1474
0
  return LWS_HPI_RET_WSI_ALREADY_DIED;
1475
0
}
1476
#endif
1477
1478
/*
1479
 * set the boundary string and the content-type for client multipart mime
1480
 */
1481
1482
uint8_t *
1483
lws_http_multipart_headers(struct lws *wsi, uint8_t *p)
1484
0
{
1485
0
  char buf[10], arg[48];
1486
0
  int n;
1487
1488
0
  if (lws_get_random(wsi->a.context, (uint8_t *)buf, sizeof(buf)) !=
1489
0
      sizeof(buf))
1490
0
    return NULL;
1491
1492
0
  lws_b64_encode_string(buf, sizeof(buf),
1493
0
             wsi->http.multipart_boundary,
1494
0
             sizeof(wsi->http.multipart_boundary));
1495
1496
0
  n = lws_snprintf(arg, sizeof(arg), "multipart/form-data; boundary=\"%s\"",
1497
0
       wsi->http.multipart_boundary);
1498
1499
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
1500
0
           (uint8_t *)arg, n, &p, p + 100))
1501
0
    return NULL;
1502
1503
0
  wsi->http.multipart = wsi->http.multipart_issue_boundary = 1;
1504
0
  lws_client_http_body_pending(wsi, 1);
1505
1506
0
  return p;
1507
0
}
1508
1509
int
1510
lws_client_http_multipart(struct lws *wsi, const char *name,
1511
        const char *filename, const char *content_type,
1512
        char **p, char *end)
1513
0
{
1514
  /*
1515
   * Client conn must have been created with LCCSCF_HTTP_MULTIPART_MIME
1516
   * flag to use this api
1517
   */
1518
0
  assert(wsi->http.multipart);
1519
1520
0
  if (!name) {
1521
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1522
0
          "\xd\xa--%s--\xd\xa",
1523
0
          wsi->http.multipart_boundary);
1524
1525
0
    return 0;
1526
0
  }
1527
1528
0
  if (wsi->client_subsequent_mime_part)
1529
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "\xd\xa");
1530
0
  wsi->client_subsequent_mime_part = 1;
1531
1532
0
  *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "--%s\xd\xa"
1533
0
            "Content-Disposition: form-data; "
1534
0
              "name=\"%s\"",
1535
0
              wsi->http.multipart_boundary, name);
1536
0
  if (filename)
1537
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1538
0
           "; filename=\"%s\"", filename);
1539
1540
0
  if (content_type)
1541
0
    *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "\xd\xa"
1542
0
        "Content-Type: %s", content_type);
1543
1544
0
  *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "\xd\xa\xd\xa");
1545
1546
0
  return *p == end;
1547
0
}
1548
1549
/*
1550
 * replacement multipart state machine
1551
 *
1552
 * We want it to emit this kind of thing:
1553
 *
1554
 * POST /builds?project=warmcat%2Flibwebsockets HTTP/1.1
1555
 * Host: 127.0.0.1
1556
 * User-Agent: lws
1557
 * Accept: * / *
1558
 * Content-Length: 698
1559
 * Content-Type: multipart/form-data; boundary=------------------------dbe229171d826cc3
1560
 *
1561
 * --------------------------dbe229171d826cc3
1562
 * Content-Disposition: form-data; name="file"; filename="xxx.bin"
1563
 * Content-Type: application/octet-stream
1564
 *
1565
 * #!/bin/bash -x
1566
 * xxx
1567
 * exit $?
1568
 *
1569
 * --------------------------dbe229171d826cc3
1570
 * Content-Disposition: form-data; name="version"
1571
 *
1572
 * f2dcc4ea
1573
 * --------------------------dbe229171d826cc3
1574
 * Content-Disposition: form-data; name="description"
1575
 * 
1576
 * lws qa
1577
 * --------------------------dbe229171d826cc3
1578
 * Content-Disposition: form-data; name="token"
1579
 *
1580
 * mytoken
1581
 * --------------------------dbe229171d826cc3
1582
 * Content-Disposition: form-data; name="email"
1583
 *
1584
 * my@email.com
1585
 * --------------------------dbe229171d826cc3--
1586
 *
1587
 */
1588
1589
typedef enum {
1590
  LWS_POST_STATE__NEXT,
1591
  LWS_POST_STATE__FILE,
1592
  LWS_POST_STATE__DATA,
1593
} post_state;
1594
1595
typedef struct lws_http_mp_sm {
1596
  struct lws_context  *cx;
1597
  lws_http_mp_sm_cb_t cb;
1598
  char      boundary[24 + 16 + 1];
1599
  char      ft[4096];
1600
  char      *eq;
1601
  int     fd;
1602
  lws_filepos_t   pos;
1603
  lws_filepos_t   total;
1604
  const char    *a; /* last hit */
1605
  post_state    ps;
1606
} lws_http_mp_sm_t;
1607
1608
struct lws_http_mp_sm *
1609
lws_http_mp_sm_init(struct lws *wsi, lws_http_mp_sm_cb_t cb, uint8_t **p, uint8_t *end)
1610
0
{
1611
0
  struct lws_http_mp_sm *phms;
1612
0
  char cla[512 + sizeof(phms->boundary)], ft[256], *eq;
1613
0
  uint64_t cl = 0;
1614
0
  struct stat s;
1615
0
  int n;
1616
1617
0
  phms = lws_malloc(sizeof(*phms), __func__);
1618
0
  if (!phms)
1619
0
    return NULL;
1620
0
  phms->cb = cb;
1621
0
  phms->cx = lws_get_context(wsi);
1622
1623
0
  for (n = 0; n < 24; n++)
1624
0
    phms->boundary[n] = '-';
1625
0
  lws_hex_random(phms->cx, phms->boundary + 24, 16);
1626
0
  phms->boundary[24 + 16] = '\0';
1627
1628
0
  n = lws_snprintf(cla, sizeof(cla), "multipart/form-data; boundary=%s", phms->boundary);
1629
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
1630
0
         (const uint8_t *)cla, n, p, end)) {
1631
0
    lwsl_warn("%s: failed to set content_type\n", __func__);
1632
0
    goto bail;
1633
0
  }
1634
1635
  /*
1636
   * We have to now add together the length of everything we will put in
1637
   * the body, in order to know the content-length now at header-time.
1638
   *
1639
   * That includes the multipart boundaries, headers, and CRLF delimiters.
1640
   */
1641
1642
0
  phms->a = NULL;
1643
0
  do {
1644
    /* The cb will a) use cla / len as a scratchpad and
1645
     * b) provide a string formelem=@name or formelem=name */
1646
1647
0
    n = phms->cb(lws_get_context(wsi), ft, sizeof(ft), &phms->a);
1648
0
    if (n < 0)
1649
0
      goto bail;
1650
0
    if (n)
1651
0
      break;
1652
0
    eq = strchr(ft, '=');
1653
0
    if (eq) {
1654
0
      *eq = '\0';
1655
0
      eq++;
1656
0
    } /* ft contains the lhs of the = (now NUL) and eq the rhs sz */
1657
1658
0
    cl += 2 /* -- */ + strlen(phms->boundary) + 2 /* CRLF */;
1659
0
    if (eq && *eq == '@') { /* ie, form file contents */
1660
0
      cl += (unsigned int)lws_snprintf(cla, sizeof(cla),
1661
0
             "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\x0d\x0a"
1662
0
             "Content-Type: application/octet-stream\x0d\x0a\x0d\x0a",
1663
0
             ft, eq + 1);
1664
0
      if (stat(eq + 1, &s)) {
1665
0
        lwsl_warn("%s: failed to stat %s\n", __func__, eq + 1);
1666
0
        goto bail;
1667
0
      }
1668
1669
0
      cl += (uint64_t)s.st_size + 2 /* ending CRLF */;
1670
0
      continue;
1671
0
    }
1672
1673
    /* form data */
1674
1675
0
    cl += (unsigned int)lws_snprintf(cla, sizeof(cla),
1676
0
           "Content-Disposition: form-data; name=\"%s\"\x0d\x0a\x0d\x0a", ft);
1677
0
    if (eq)
1678
0
      cl += strlen(eq) + 2 /* CRLF */;
1679
1680
0
  } while (1);
1681
1682
0
  cl += 2 /* -- */ + strlen(phms->boundary) + 2 /* -- */ + 2 /* CRLF */;
1683
1684
  // lwsl_warn("%s: going with content length 0x%x\n", __func__, (unsigned int)cl);
1685
1686
0
  if (lws_add_http_header_content_length(wsi, cl, p, end))
1687
0
    goto bail;
1688
1689
0
  phms->a   = NULL;
1690
0
  phms->pos = 0;
1691
0
  phms->total = 0;
1692
0
  phms->ps  = LWS_POST_STATE__NEXT;
1693
1694
  /*
1695
   * Tell lws we are going to send the body next...
1696
   */
1697
1698
0
  return phms;
1699
1700
0
bail:
1701
0
  free(phms);
1702
1703
0
  return NULL;
1704
0
}
1705
1706
void
1707
lws_http_mp_sm_destroy(struct lws_http_mp_sm **pphms)
1708
0
{
1709
0
  if (*pphms) {
1710
0
    lws_free(*pphms);
1711
0
    *pphms = NULL;
1712
0
  }
1713
0
}
1714
1715
int
1716
lws_http_mp_sm_fill(struct lws_http_mp_sm *phms, uint8_t **p, uint8_t *end)
1717
0
{
1718
0
  int n;
1719
1720
0
  assert(phms);
1721
1722
0
  do {
1723
0
    switch (phms->ps) {
1724
0
    case LWS_POST_STATE__NEXT:
1725
1726
0
      if (lws_ptr_diff(end, *p) < 300)
1727
0
        return 1;
1728
1729
0
      n = phms->cb(phms->cx, phms->ft, sizeof(phms->ft), &phms->a);
1730
0
      if (n < 0) { /* error */
1731
0
        return -1;
1732
0
      }
1733
0
      if (n) { /* no more form elements */
1734
0
        *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "--%s--\x0d\x0a", phms->boundary);
1735
1736
0
        return 0; /* finished then */
1737
0
      }
1738
1739
0
      phms->eq = strchr(phms->ft, '=');
1740
0
      if (phms->eq) {
1741
0
        *phms->eq = '\0';
1742
0
        phms->eq++;
1743
0
      } /* phms->ft contains the lhs of the = (now NUL) and eq the rhs sz */
1744
1745
0
      *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "--%s\x0d\x0a", phms->boundary);
1746
1747
0
      if (phms->eq && *phms->eq == '@') { /* ie, form file contents */
1748
0
        struct stat s;
1749
1750
0
        *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1751
0
               "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\x0d\x0a"
1752
0
               "Content-Type: application/octet-stream\x0d\x0a\x0d\x0a",
1753
0
               phms->ft, phms->eq + 1);
1754
0
        phms->fd = open(phms->eq + 1, O_RDONLY);
1755
0
        if (phms->fd == -1) {
1756
0
          lwsl_warn("%s: unable to open '%s'\n", __func__, phms->eq + 1);
1757
0
          return -1; /* failed */
1758
0
        }
1759
0
        if (fstat(phms->fd, &s)) {
1760
0
          lwsl_warn("%s: failed to stat %s\n", __func__, phms->eq + 1);
1761
0
          return -1; /* failed */
1762
0
        }
1763
0
        phms->pos = 0;
1764
0
        phms->total = (lws_filepos_t)s.st_size;
1765
0
        phms->ps = LWS_POST_STATE__FILE;
1766
0
        continue;
1767
0
      }
1768
1769
      /* form data */
1770
1771
0
      *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p),
1772
0
           "Content-Disposition: form-data; name=\"%s\"\x0d\x0a\x0d\x0a", phms->ft);
1773
0
      phms->ps = LWS_POST_STATE__DATA;
1774
0
      break;
1775
1776
0
    case LWS_POST_STATE__FILE: {
1777
0
      size_t chunk = lws_ptr_diff_size_t(end, *p) - 2;
1778
0
      ssize_t r;
1779
1780
0
      if (lws_ptr_diff(end, *p) < 100)
1781
0
                                return 1;
1782
1783
0
      r = read(phms->fd, *p, LWS_POSIX_LENGTH_CAST(chunk));
1784
0
      if (r < 0) {
1785
0
        close(phms->fd);
1786
0
        lwsl_warn("%s: unable to read\n", __func__);
1787
0
        return -1; /* failed */
1788
0
      }
1789
1790
0
      *p += r;
1791
0
      phms->pos += (uint64_t)r;
1792
0
      if (phms->pos == phms->total) {
1793
0
        **p = '\x0d';
1794
0
        *p += 1;
1795
0
        **p = '\x0a';
1796
0
        *p += 1;
1797
0
        close(phms->fd);
1798
0
        phms->ps = LWS_POST_STATE__NEXT;
1799
0
      }
1800
0
      break;
1801
0
    }
1802
0
    case LWS_POST_STATE__DATA:
1803
0
      if (lws_ptr_diff(end, *p) < 300)
1804
0
        return 1;
1805
1806
0
      *p += lws_snprintf((char *)(*p), lws_ptr_diff_size_t(end, *p), "%s\x0d\x0a", phms->eq);
1807
0
      phms->ps = LWS_POST_STATE__NEXT;
1808
0
      break;
1809
1810
0
    } /* switch */
1811
0
  } while (lws_ptr_diff(end, *p) > 100);
1812
1813
0
  return 1; /* more to do */
1814
0
}
1815
1816
1817
char *
1818
lws_generate_client_handshake(struct lws *wsi, char *pkt, size_t pkt_len)
1819
0
{
1820
0
  const char *meth, *pp = lws_hdr_simple_ptr(wsi,
1821
0
        _WSI_TOKEN_CLIENT_SENT_PROTOCOLS), *path;
1822
0
  char *p = pkt, *p1, *end = p + pkt_len;
1823
1824
0
  meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
1825
0
  if (!meth) {
1826
0
    meth = "GET";
1827
0
    wsi->do_ws = 1;
1828
0
  } else {
1829
0
    wsi->do_ws = 0;
1830
0
  }
1831
1832
0
  if (!strcmp(meth, "RAW")) {
1833
0
    lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1834
0
    lwsl_notice("client transition to raw\n");
1835
1836
0
    if (pp) {
1837
0
      const struct lws_protocols *pr;
1838
1839
0
      pr = lws_vhost_name_to_protocol(wsi->a.vhost, pp);
1840
1841
0
      if (!pr) {
1842
0
        lwsl_err("protocol %s not enabled on vhost\n",
1843
0
           pp);
1844
0
        return NULL;
1845
0
      }
1846
1847
0
      lws_bind_protocol(wsi, pr, __func__);
1848
0
    }
1849
1850
0
    if ((wsi->a.protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT,
1851
0
                wsi->user_space, NULL, 0))
1852
0
      return NULL;
1853
1854
0
    lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
1855
0
            &role_ops_raw_skt);
1856
0
    lws_header_table_detach(wsi, 1);
1857
1858
0
    return NULL;
1859
0
  }
1860
1861
  /*
1862
   * 04 example client handshake
1863
   *
1864
   * GET /chat HTTP/1.1
1865
   * Host: server.example.com
1866
   * Upgrade: websocket
1867
   * Connection: Upgrade
1868
   * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
1869
   * Sec-WebSocket-Origin: http://example.com
1870
   * Sec-WebSocket-Protocol: chat, superchat
1871
   * Sec-WebSocket-Version: 4
1872
   */
1873
1874
0
  path = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
1875
0
  if (!path) {
1876
0
    if (wsi->stash && wsi->stash->cis[CIS_PATH] &&
1877
0
      wsi->stash->cis[CIS_PATH][0])
1878
0
      path = wsi->stash->cis[CIS_PATH];
1879
0
    else
1880
0
      path = "/";
1881
0
  }
1882
1883
0
  p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
1884
0
        "%s %s HTTP/1.1\x0d\x0a", meth, path);
1885
1886
0
  if (!(wsi->flags & LCCSCF_HTTP_NO_CACHE_CONTROL))
1887
0
    p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
1888
0
          "Pragma: no-cache\x0d\x0a"
1889
0
          "Cache-Control: no-cache\x0d\x0a");
1890
1891
0
  p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
1892
0
        "Host: %s\x0d\x0a",
1893
0
        lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
1894
1895
0
  if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
1896
0
    if (lws_check_opt(wsi->a.context->options,
1897
0
          LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
1898
0
      p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
1899
0
            "Origin: %s\x0d\x0a",
1900
0
            lws_hdr_simple_ptr(wsi,
1901
0
                 _WSI_TOKEN_CLIENT_ORIGIN));
1902
0
    else
1903
0
      p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p),
1904
0
            "Origin: %s://%s\x0d\x0a",
1905
0
            wsi->flags & LCCSCF_USE_SSL ?
1906
0
               "https" : "http",
1907
0
            lws_hdr_simple_ptr(wsi,
1908
0
                 _WSI_TOKEN_CLIENT_ORIGIN));
1909
0
  }
1910
1911
0
  if (wsi->flags & LCCSCF_HTTP_MULTIPART_MIME) {
1912
0
    p1 = (char *)lws_http_multipart_headers(wsi, (uint8_t *)p);
1913
0
    if (!p1)
1914
0
      return NULL;
1915
0
    p = p1;
1916
0
  }
1917
1918
#if defined(LWS_WITH_HTTP_PROXY)
1919
  if (wsi->parent &&
1920
      lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
1921
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
1922
          "Content-Length: %s\x0d\x0a",
1923
      lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH));
1924
    if (atoi(lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)))
1925
      wsi->client_http_body_pending = 1;
1926
  }
1927
  if (wsi->parent &&
1928
      lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION)) {
1929
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
1930
          "Authorization: %s\x0d\x0a",
1931
      lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION));
1932
  }
1933
  if (wsi->parent &&
1934
      lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
1935
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
1936
          "Content-Type: %s\x0d\x0a",
1937
      lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE));
1938
  }
1939
1940
  if (wsi->parent && wsi->parent->http.extra_onward_headers) {
1941
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s",
1942
          wsi->parent->http.extra_onward_headers);
1943
  }
1944
#endif
1945
1946
0
#if defined(LWS_WITH_HTTP_DIGEST_AUTH)
1947
0
    if (wsi->http.digest_auth_hdr) {
1948
0
        p += lws_snprintf(p, 1024, "Authorization: %s\x0d\x0a",
1949
0
                          wsi->http.digest_auth_hdr);
1950
0
        lws_free(wsi->http.digest_auth_hdr);
1951
0
        wsi->http.digest_auth_hdr = NULL;
1952
0
    }
1953
0
#endif
1954
1955
0
#if defined(LWS_ROLE_WS)
1956
0
  if (wsi->do_ws) {
1957
0
    const char *conn1 = "";
1958
  //  if (!wsi->client_pipeline)
1959
  //    conn1 = "close, ";
1960
0
    p = lws_generate_client_ws_handshake(wsi, p, conn1,
1961
0
                 lws_ptr_diff_size_t(end, p));
1962
0
                if (!p)
1963
0
                    return NULL;
1964
0
  } else
1965
0
#endif
1966
0
  {
1967
0
    if (!wsi->client_pipeline)
1968
0
      p += lws_snprintf(p, 64, "connection: close\x0d\x0a");
1969
0
  }
1970
1971
  /* give userland a chance to append, eg, cookies */
1972
1973
0
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
1974
0
  if (wsi->flags & LCCSCF_CACHE_COOKIES)
1975
0
    lws_cookie_send_cookies(wsi, &p, end);
1976
0
#endif
1977
1978
0
  if (wsi->a.protocol->callback(wsi,
1979
0
      LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
1980
0
      wsi->user_space, &p,
1981
0
      (unsigned int)((pkt + wsi->a.context->pt_serv_buf_size) - p - 12)))
1982
0
    return NULL;
1983
1984
0
  if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED) {
1985
0
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "Content-Type: application/x-www-form-urlencoded\x0d\x0a");
1986
0
    p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p), "Content-Length: %lu\x0d\x0a", wsi->http.writeable_len);
1987
0
    lws_client_http_body_pending(wsi, 1);
1988
0
  }
1989
1990
0
  p += lws_snprintf(p,  lws_ptr_diff_size_t(end, p), "\x0d\x0a");
1991
1992
0
  if (wsi->client_http_body_pending || lws_has_buffered_out(wsi))
1993
0
    lws_callback_on_writable(wsi);
1994
1995
0
  lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_http_txn);
1996
0
#if defined(LWS_WITH_CONMON)
1997
0
  wsi->conmon_datum = lws_now_usecs();
1998
0
#endif
1999
2000
  // puts(pkt);
2001
2002
0
  return p;
2003
0
}
2004
2005
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
2006
#if defined(LWS_WITH_HTTP_BASIC_AUTH)
2007
2008
int
2009
lws_http_basic_auth_gen2(const char *user, const void *pw, size_t pwd_len,
2010
       char *buf, size_t len)
2011
0
{
2012
0
  size_t n = strlen(user), m = pwd_len;
2013
0
  char b[128];
2014
2015
0
  if (len < 6 + ((4 * (n + m + 1)) / 3) + 1)
2016
0
    return 1;
2017
2018
0
  memcpy(buf, "Basic ", 6);
2019
2020
0
  n = (unsigned int)lws_snprintf(b, sizeof(b), "%s:", user);
2021
0
  if ((n + pwd_len) >= sizeof(b) - 2)
2022
0
    return 2;
2023
2024
0
  memcpy(&b[n], pw, pwd_len);
2025
0
  n += pwd_len;
2026
2027
0
  lws_b64_encode_string(b, (int)n, buf + 6, (int)len - 6);
2028
0
  buf[len - 1] = '\0';
2029
2030
0
  return 0;
2031
0
}
2032
2033
int lws_http_basic_auth_gen(const char *user, const char *pw, char *buf, size_t len)
2034
0
{
2035
0
  return lws_http_basic_auth_gen2(user, pw, strlen(pw), buf, len);
2036
0
}
2037
2038
#endif
2039
2040
int
2041
lws_http_client_read(struct lws *wsi, char **buf, int *len)
2042
0
{
2043
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
2044
0
  struct lws_tokens eb;
2045
0
  int buffered, n, consumed = 0;
2046
2047
  /*
2048
   * If the caller provided a non-NULL *buf and nonzero *len, we should
2049
   * use that as the buffer for the read action, limititing it to *len
2050
   * (actual payload will be less if chunked headers inside).
2051
   *
2052
   * If it's NULL / 0 length, buflist_aware_read will use the pt_serv_buf
2053
   */
2054
2055
0
  eb.token = (unsigned char *)*buf;
2056
0
  eb.len = *len;
2057
2058
0
  buffered = lws_buflist_aware_read(pt, wsi, &eb, 0, __func__);
2059
0
  *buf = (char *)eb.token; /* may be pointing to buflist or pt_serv_buf */
2060
0
  *len = 0;
2061
2062
  /*
2063
   * we're taking on responsibility for handling used / unused eb
2064
   * when we leave, via lws_buflist_aware_finished_consuming()
2065
   */
2066
2067
//  lwsl_notice("%s: eb.len %d ENTRY chunk remaining %d\n", __func__, eb.len,
2068
//      wsi->chunk_remaining);
2069
2070
  /* allow the source to signal he has data again next time */
2071
0
  if (lws_change_pollfd(wsi, 0, LWS_POLLIN))
2072
0
    return -1;
2073
2074
0
  if (buffered < 0) {
2075
0
    lwsl_debug("%s: SSL capable error\n", __func__);
2076
2077
0
    if (wsi->http.ah &&
2078
0
        wsi->http.ah->parser_state == WSI_PARSING_COMPLETE &&
2079
0
        !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
2080
      /*
2081
       * We had the headers from this stream, but as there
2082
       * was no content-length: we had to wait until the
2083
       * stream ended to inform the user code the transaction
2084
       * has completed to the best of our knowledge
2085
       */
2086
0
      if (lws_http_transaction_completed_client(wsi))
2087
        /*
2088
         * We're going to close anyway, but that api has
2089
         * warn_unused_result
2090
         */
2091
0
        return -1;
2092
2093
0
    return -1;
2094
0
  }
2095
2096
0
  if (eb.len <= 0)
2097
0
    return 0;
2098
2099
0
  *len = eb.len;
2100
0
  wsi->client_rx_avail = 0;
2101
2102
  /*
2103
   * server may insist on transfer-encoding: chunked,
2104
   * so http client must deal with it
2105
   */
2106
0
spin_chunks:
2107
  //lwsl_notice("%s: len %d SPIN chunk remaining %d\n", __func__, *len,
2108
  //    wsi->chunk_remaining);
2109
0
  while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) {
2110
0
    switch (wsi->chunk_parser) {
2111
0
    case ELCP_HEX:
2112
0
      if ((*buf)[0] == '\x0d') {
2113
0
        wsi->chunk_parser = ELCP_CR;
2114
0
        break;
2115
0
      }
2116
0
      n = char_to_hex((*buf)[0]);
2117
0
      if (n < 0) {
2118
0
        lwsl_err("%s: chunking failure A\n", __func__);
2119
0
        return -1;
2120
0
      }
2121
0
      if (wsi->chunk_remaining > (INT_MAX - 15) / 16) {
2122
0
        lwsl_err("%s: chunk size overflow\n", __func__);
2123
0
        return -1;
2124
0
      }
2125
0
      wsi->chunk_remaining <<= 4;
2126
0
      wsi->chunk_remaining |= n;
2127
0
      break;
2128
0
    case ELCP_CR:
2129
0
      if ((*buf)[0] != '\x0a') {
2130
0
        lwsl_err("%s: chunking failure B\n", __func__);
2131
0
        return -1;
2132
0
      }
2133
0
      if (wsi->chunk_remaining) {
2134
0
        wsi->chunk_parser = ELCP_CONTENT;
2135
        //lwsl_notice("starting chunk size %d (block rem %d)\n",
2136
        //    wsi->chunk_remaining, *len);
2137
0
        break;
2138
0
      }
2139
2140
0
      wsi->chunk_parser = ELCP_TRAILER_CR;
2141
0
      break;
2142
2143
0
    case ELCP_CONTENT:
2144
0
      break;
2145
2146
0
    case ELCP_POST_CR:
2147
0
      if ((*buf)[0] != '\x0d') {
2148
0
        lwsl_err("%s: chunking failure C\n", __func__);
2149
0
        lwsl_hexdump_err(*buf, (unsigned int)*len);
2150
2151
0
        return -1;
2152
0
      }
2153
2154
0
      wsi->chunk_parser = ELCP_POST_LF;
2155
0
      break;
2156
2157
0
    case ELCP_POST_LF:
2158
0
      if ((*buf)[0] != '\x0a') {
2159
0
        lwsl_err("%s: chunking failure D\n", __func__);
2160
2161
0
        return -1;
2162
0
      }
2163
2164
0
      wsi->chunk_parser = ELCP_HEX;
2165
0
      wsi->chunk_remaining = 0;
2166
0
      break;
2167
2168
0
    case ELCP_TRAILER_CR:
2169
0
      if ((*buf)[0] != '\x0d') {
2170
0
        lwsl_err("%s: chunking failure F\n", __func__);
2171
0
        lwsl_hexdump_err(*buf, (unsigned int)*len);
2172
2173
0
        return -1;
2174
0
      }
2175
2176
0
      wsi->chunk_parser = ELCP_TRAILER_LF;
2177
0
      break;
2178
2179
0
    case ELCP_TRAILER_LF:
2180
0
      if ((*buf)[0] != '\x0a') {
2181
0
        lwsl_err("%s: chunking failure F\n", __func__);
2182
0
        lwsl_hexdump_err(*buf, (unsigned int)*len);
2183
2184
0
        return -1;
2185
0
      }
2186
2187
0
      (*buf)++;
2188
0
      (*len)--;
2189
0
      consumed++;
2190
2191
0
      lwsl_info("final chunk\n");
2192
0
      goto completed;
2193
0
    }
2194
0
    (*buf)++;
2195
0
    (*len)--;
2196
0
    consumed++;
2197
0
  }
2198
2199
0
  if (wsi->chunked && !wsi->chunk_remaining)
2200
0
    goto account_and_ret;
2201
2202
0
  if (wsi->http.rx_content_remain &&
2203
0
      wsi->http.rx_content_remain < (unsigned int)*len)
2204
0
    n = (int)wsi->http.rx_content_remain;
2205
0
  else
2206
0
    n = *len;
2207
2208
0
  if (wsi->chunked && wsi->chunk_remaining &&
2209
0
      wsi->chunk_remaining < n)
2210
0
    n = wsi->chunk_remaining;
2211
2212
#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB)
2213
  /* hubbub */
2214
  if (wsi->http.perform_rewrite)
2215
    lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n);
2216
  else
2217
#endif
2218
0
  {
2219
0
    if (
2220
#if defined(LWS_WITH_HTTP_PROXY)
2221
        !wsi->protocol_bind_balance ==
2222
        !!wsi->http.proxy_clientside
2223
#else
2224
0
        !!wsi->protocol_bind_balance
2225
0
#endif
2226
0
      ) {
2227
0
      int q;
2228
2229
0
      q = user_callback_handle_rxflow(wsi->a.protocol->callback,
2230
0
        wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
2231
0
        wsi->user_space, *buf, (unsigned int)n);
2232
0
      if (q) {
2233
0
        lwsl_info("%s: RECEIVE_CLIENT_HTTP_READ returned %d\n",
2234
0
            __func__, q);
2235
2236
0
        return q;
2237
0
      }
2238
0
    } else
2239
0
      lwsl_notice("%s: swallowed read (%d)\n", __func__, n);
2240
0
  }
2241
2242
0
  (*buf) += n;
2243
0
  *len -= n;
2244
0
  if (wsi->chunked && wsi->chunk_remaining)
2245
0
    wsi->chunk_remaining -= n;
2246
2247
  //lwsl_notice("chunk_remaining <- %d, block remaining %d\n",
2248
  //    wsi->chunk_remaining, *len);
2249
2250
0
  consumed += n;
2251
  //eb.token += n;
2252
  //eb.len -= n;
2253
2254
0
  if (wsi->chunked && !wsi->chunk_remaining)
2255
0
    wsi->chunk_parser = ELCP_POST_CR;
2256
2257
0
  if (wsi->chunked && *len)
2258
0
    goto spin_chunks;
2259
2260
0
  if (wsi->chunked)
2261
0
    goto account_and_ret;
2262
2263
  /* if we know the content length, decrement the content remaining */
2264
0
  if (wsi->http.rx_content_length > 0)
2265
0
    wsi->http.rx_content_remain -= (unsigned int)n;
2266
2267
  // lwsl_notice("rx_content_remain %lld, rx_content_length %lld, giv %d\n",
2268
  //      wsi->http.rx_content_remain, wsi->http.rx_content_length,
2269
  //      wsi->http.content_length_given);
2270
2271
0
  if (wsi->http.rx_content_remain || !wsi->http.content_length_given)
2272
0
    goto account_and_ret;
2273
2274
0
completed:
2275
2276
0
  if (lws_http_transaction_completed_client(wsi)) {
2277
0
    lwsl_info("%s: transaction completed says -1\n", __func__);
2278
0
    return -1;
2279
0
  }
2280
2281
0
account_and_ret:
2282
//  lwsl_warn("%s: on way out, consuming %d / %d\n", __func__, consumed, eb.len);
2283
0
  if (lws_buflist_aware_finished_consuming(wsi, &eb, consumed, buffered,
2284
0
              __func__))
2285
0
    return -1;
2286
2287
0
  return 0;
2288
0
}
2289
2290
#endif
2291
2292
static uint8_t hnames2[] = {
2293
  _WSI_TOKEN_CLIENT_ORIGIN,
2294
  _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
2295
  _WSI_TOKEN_CLIENT_METHOD,
2296
  _WSI_TOKEN_CLIENT_IFACE
2297
};
2298
2299
/**
2300
 * lws_client_reset() - retarget a connected wsi to start over with a new
2301
 *      connection (ie, redirect)
2302
 *      this only works if still in HTTP, ie, not upgraded yet
2303
 * wsi:   connection to reset
2304
 * address: network address of the new server
2305
 * port:  port to connect to
2306
 * path:  uri path to connect to on the new server
2307
 * host:  host header to send to the new server
2308
 */
2309
struct lws *
2310
lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
2311
    const char *path, const char *host, char weak)
2312
0
{
2313
0
  struct lws_context_per_thread *pt;
2314
0
#if defined(LWS_ROLE_WS)
2315
0
  struct _lws_websocket_related *ws;
2316
0
#endif
2317
0
  const char *cisin[CIS_COUNT];
2318
0
  struct lws *wsi;
2319
0
  size_t o;
2320
0
  int n, r;
2321
2322
0
  if (!pwsi)
2323
0
    return NULL;
2324
2325
0
  wsi = *pwsi;
2326
0
  pt = &wsi->a.context->pt[(int)wsi->tsi];
2327
2328
0
  lwsl_debug("%s: %s: redir %d: %s\n", __func__, lws_wsi_tag(wsi),
2329
0
      wsi->redirects, address);
2330
2331
0
  if (wsi->redirects == 4) {
2332
0
    lwsl_err("%s: Too many redirects\n", __func__);
2333
0
    return NULL;
2334
0
  }
2335
0
  wsi->redirects++;
2336
2337
  /*
2338
   * goal is to close our role part, close the sockfd, detach the ah
2339
   * but leave our wsi extant and still bound to whatever vhost it was
2340
   */
2341
2342
0
  o = path[0] == '/' && path[1] == '/';
2343
2344
0
  memset((char *)cisin, 0, sizeof(cisin));
2345
2346
0
  cisin[CIS_ADDRESS]  = address;
2347
0
  cisin[CIS_PATH]   = path + o;
2348
0
  cisin[CIS_HOST]   = host;
2349
2350
0
  for (n = 0; n < (int)LWS_ARRAY_SIZE(hnames2); n++)
2351
0
    cisin[n + 3] = lws_hdr_simple_ptr(wsi, hnames2[n]);
2352
2353
0
  r = (int)wsi->http.ah->http_response;
2354
2355
0
#if defined(LWS_WITH_TLS)
2356
0
  cisin[CIS_ALPN]   = wsi->alpn;
2357
0
#endif
2358
2359
0
  if (!wsi->stash && lws_client_stash_create(wsi, cisin))
2360
0
    return NULL;
2361
2362
0
  if (!port) {
2363
0
    lwsl_info("%s: forcing port 443\n", __func__);
2364
2365
0
    port = 443;
2366
0
    ssl = 1;
2367
0
  }
2368
2369
0
  wsi->c_port = (uint16_t)port;
2370
2371
0
  wsi->flags = (wsi->flags & (~LCCSCF_USE_SSL)) |
2372
0
    (ssl ? LCCSCF_USE_SSL : 0);
2373
2374
0
  if (!cisin[CIS_ALPN] || !cisin[CIS_ALPN][0])
2375
0
#if defined(LWS_ROLE_H2)
2376
0
    cisin[CIS_ALPN] = "h2,http/1.1";
2377
#else
2378
  cisin[CIS_ALPN] = "http/1.1";
2379
#endif
2380
2381
0
  lwsl_notice("%s: REDIRECT %d: %s %s:%d, path='%s', ssl = %d, alpn='%s'\n",
2382
0
        __func__, r, cisin[CIS_METHOD], address,
2383
0
        port, path, ssl, cisin[CIS_ALPN]);
2384
2385
0
  lws_pt_lock(pt, __func__);
2386
0
  __remove_wsi_socket_from_fds(wsi);
2387
0
  lws_pt_unlock(pt);
2388
2389
0
#if defined(LWS_ROLE_WS)
2390
0
  if (weak) {
2391
0
    ws = wsi->ws;
2392
0
    wsi->ws = NULL;
2393
0
  }
2394
0
#endif
2395
2396
  /*
2397
   * After this point we can't trust the incoming strings like address,
2398
   * path any more, since they may have been pointing into the old ah.
2399
   *
2400
   * We must use the copies in the wsi->stash instead if we want them.
2401
   */
2402
2403
0
  __lws_reset_wsi(wsi); /* detaches ah here */
2404
0
#if defined(LWS_ROLE_WS)
2405
0
  if (weak)
2406
0
    wsi->ws = ws;
2407
0
#endif
2408
0
  wsi->client_pipeline = 1;
2409
2410
  /*
2411
   * We could be a redirect before, or after the POST was done.
2412
   * Http's hack around this is 307 / 308 keep the method, ie,
2413
   * it's pre and they have to repeat the body.  Other 3xx
2414
   * turn it into a GET.
2415
   */
2416
2417
0
  if ((r / 100) == 3 && r != 307 && r != 308)
2418
0
    wsi->redirected_to_get = 1;
2419
2420
  /*
2421
   * Will complete at close flow
2422
   */
2423
2424
0
  wsi->close_is_redirect = 1;
2425
2426
0
  return *pwsi;
2427
0
}