xref: /third_party/curl/lib/cf-h1-proxy.c (revision 13498266)
113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include <curl/curl.h>
3013498266Sopenharmony_ci#ifdef USE_HYPER
3113498266Sopenharmony_ci#include <hyper.h>
3213498266Sopenharmony_ci#endif
3313498266Sopenharmony_ci#include "urldata.h"
3413498266Sopenharmony_ci#include "dynbuf.h"
3513498266Sopenharmony_ci#include "sendf.h"
3613498266Sopenharmony_ci#include "http.h"
3713498266Sopenharmony_ci#include "http1.h"
3813498266Sopenharmony_ci#include "http_proxy.h"
3913498266Sopenharmony_ci#include "url.h"
4013498266Sopenharmony_ci#include "select.h"
4113498266Sopenharmony_ci#include "progress.h"
4213498266Sopenharmony_ci#include "cfilters.h"
4313498266Sopenharmony_ci#include "cf-h1-proxy.h"
4413498266Sopenharmony_ci#include "connect.h"
4513498266Sopenharmony_ci#include "curl_trc.h"
4613498266Sopenharmony_ci#include "curlx.h"
4713498266Sopenharmony_ci#include "vtls/vtls.h"
4813498266Sopenharmony_ci#include "transfer.h"
4913498266Sopenharmony_ci#include "multiif.h"
5013498266Sopenharmony_ci
5113498266Sopenharmony_ci/* The last 3 #include files should be in this order */
5213498266Sopenharmony_ci#include "curl_printf.h"
5313498266Sopenharmony_ci#include "curl_memory.h"
5413498266Sopenharmony_ci#include "memdebug.h"
5513498266Sopenharmony_ci
5613498266Sopenharmony_ci
5713498266Sopenharmony_citypedef enum {
5813498266Sopenharmony_ci    H1_TUNNEL_INIT,     /* init/default/no tunnel state */
5913498266Sopenharmony_ci    H1_TUNNEL_CONNECT,  /* CONNECT request is being send */
6013498266Sopenharmony_ci    H1_TUNNEL_RECEIVE,  /* CONNECT answer is being received */
6113498266Sopenharmony_ci    H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
6213498266Sopenharmony_ci    H1_TUNNEL_ESTABLISHED,
6313498266Sopenharmony_ci    H1_TUNNEL_FAILED
6413498266Sopenharmony_ci} h1_tunnel_state;
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci/* struct for HTTP CONNECT tunneling */
6713498266Sopenharmony_cistruct h1_tunnel_state {
6813498266Sopenharmony_ci  struct HTTP CONNECT;
6913498266Sopenharmony_ci  struct dynbuf rcvbuf;
7013498266Sopenharmony_ci  struct dynbuf request_data;
7113498266Sopenharmony_ci  size_t nsent;
7213498266Sopenharmony_ci  size_t headerlines;
7313498266Sopenharmony_ci  struct Curl_chunker ch;
7413498266Sopenharmony_ci  enum keeponval {
7513498266Sopenharmony_ci    KEEPON_DONE,
7613498266Sopenharmony_ci    KEEPON_CONNECT,
7713498266Sopenharmony_ci    KEEPON_IGNORE
7813498266Sopenharmony_ci  } keepon;
7913498266Sopenharmony_ci  curl_off_t cl; /* size of content to read and ignore */
8013498266Sopenharmony_ci  h1_tunnel_state tunnel_state;
8113498266Sopenharmony_ci  BIT(chunked_encoding);
8213498266Sopenharmony_ci  BIT(close_connection);
8313498266Sopenharmony_ci};
8413498266Sopenharmony_ci
8513498266Sopenharmony_ci
8613498266Sopenharmony_cistatic bool tunnel_is_established(struct h1_tunnel_state *ts)
8713498266Sopenharmony_ci{
8813498266Sopenharmony_ci  return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
8913498266Sopenharmony_ci}
9013498266Sopenharmony_ci
9113498266Sopenharmony_cistatic bool tunnel_is_failed(struct h1_tunnel_state *ts)
9213498266Sopenharmony_ci{
9313498266Sopenharmony_ci  return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
9413498266Sopenharmony_ci}
9513498266Sopenharmony_ci
9613498266Sopenharmony_cistatic CURLcode tunnel_reinit(struct Curl_cfilter *cf,
9713498266Sopenharmony_ci                              struct Curl_easy *data,
9813498266Sopenharmony_ci                              struct h1_tunnel_state *ts)
9913498266Sopenharmony_ci{
10013498266Sopenharmony_ci  (void)data;
10113498266Sopenharmony_ci  (void)cf;
10213498266Sopenharmony_ci  DEBUGASSERT(ts);
10313498266Sopenharmony_ci  Curl_dyn_reset(&ts->rcvbuf);
10413498266Sopenharmony_ci  Curl_dyn_reset(&ts->request_data);
10513498266Sopenharmony_ci  ts->tunnel_state = H1_TUNNEL_INIT;
10613498266Sopenharmony_ci  ts->keepon = KEEPON_CONNECT;
10713498266Sopenharmony_ci  ts->cl = 0;
10813498266Sopenharmony_ci  ts->close_connection = FALSE;
10913498266Sopenharmony_ci  return CURLE_OK;
11013498266Sopenharmony_ci}
11113498266Sopenharmony_ci
11213498266Sopenharmony_cistatic CURLcode tunnel_init(struct Curl_cfilter *cf,
11313498266Sopenharmony_ci                            struct Curl_easy *data,
11413498266Sopenharmony_ci                            struct h1_tunnel_state **pts)
11513498266Sopenharmony_ci{
11613498266Sopenharmony_ci  struct h1_tunnel_state *ts;
11713498266Sopenharmony_ci  CURLcode result;
11813498266Sopenharmony_ci
11913498266Sopenharmony_ci  if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
12013498266Sopenharmony_ci    failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
12113498266Sopenharmony_ci    return CURLE_UNSUPPORTED_PROTOCOL;
12213498266Sopenharmony_ci  }
12313498266Sopenharmony_ci
12413498266Sopenharmony_ci  /* we might need the upload buffer for streaming a partial request */
12513498266Sopenharmony_ci  result = Curl_get_upload_buffer(data);
12613498266Sopenharmony_ci  if(result)
12713498266Sopenharmony_ci    return result;
12813498266Sopenharmony_ci
12913498266Sopenharmony_ci  ts = calloc(1, sizeof(*ts));
13013498266Sopenharmony_ci  if(!ts)
13113498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
13213498266Sopenharmony_ci
13313498266Sopenharmony_ci  infof(data, "allocate connect buffer");
13413498266Sopenharmony_ci
13513498266Sopenharmony_ci  Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
13613498266Sopenharmony_ci  Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
13713498266Sopenharmony_ci  Curl_httpchunk_init(data, &ts->ch, TRUE);
13813498266Sopenharmony_ci
13913498266Sopenharmony_ci  *pts =  ts;
14013498266Sopenharmony_ci  connkeep(cf->conn, "HTTP proxy CONNECT");
14113498266Sopenharmony_ci  return tunnel_reinit(cf, data, ts);
14213498266Sopenharmony_ci}
14313498266Sopenharmony_ci
14413498266Sopenharmony_cistatic void h1_tunnel_go_state(struct Curl_cfilter *cf,
14513498266Sopenharmony_ci                               struct h1_tunnel_state *ts,
14613498266Sopenharmony_ci                               h1_tunnel_state new_state,
14713498266Sopenharmony_ci                               struct Curl_easy *data)
14813498266Sopenharmony_ci{
14913498266Sopenharmony_ci  if(ts->tunnel_state == new_state)
15013498266Sopenharmony_ci    return;
15113498266Sopenharmony_ci  /* entering this one */
15213498266Sopenharmony_ci  switch(new_state) {
15313498266Sopenharmony_ci  case H1_TUNNEL_INIT:
15413498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "new tunnel state 'init'");
15513498266Sopenharmony_ci    tunnel_reinit(cf, data, ts);
15613498266Sopenharmony_ci    break;
15713498266Sopenharmony_ci
15813498266Sopenharmony_ci  case H1_TUNNEL_CONNECT:
15913498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
16013498266Sopenharmony_ci    ts->tunnel_state = H1_TUNNEL_CONNECT;
16113498266Sopenharmony_ci    ts->keepon = KEEPON_CONNECT;
16213498266Sopenharmony_ci    Curl_dyn_reset(&ts->rcvbuf);
16313498266Sopenharmony_ci    break;
16413498266Sopenharmony_ci
16513498266Sopenharmony_ci  case H1_TUNNEL_RECEIVE:
16613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "new tunnel state 'receive'");
16713498266Sopenharmony_ci    ts->tunnel_state = H1_TUNNEL_RECEIVE;
16813498266Sopenharmony_ci    break;
16913498266Sopenharmony_ci
17013498266Sopenharmony_ci  case H1_TUNNEL_RESPONSE:
17113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "new tunnel state 'response'");
17213498266Sopenharmony_ci    ts->tunnel_state = H1_TUNNEL_RESPONSE;
17313498266Sopenharmony_ci    break;
17413498266Sopenharmony_ci
17513498266Sopenharmony_ci  case H1_TUNNEL_ESTABLISHED:
17613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "new tunnel state 'established'");
17713498266Sopenharmony_ci    infof(data, "CONNECT phase completed");
17813498266Sopenharmony_ci    data->state.authproxy.done = TRUE;
17913498266Sopenharmony_ci    data->state.authproxy.multipass = FALSE;
18013498266Sopenharmony_ci    FALLTHROUGH();
18113498266Sopenharmony_ci  case H1_TUNNEL_FAILED:
18213498266Sopenharmony_ci    if(new_state == H1_TUNNEL_FAILED)
18313498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
18413498266Sopenharmony_ci    ts->tunnel_state = new_state;
18513498266Sopenharmony_ci    Curl_dyn_reset(&ts->rcvbuf);
18613498266Sopenharmony_ci    Curl_dyn_reset(&ts->request_data);
18713498266Sopenharmony_ci    /* restore the protocol pointer */
18813498266Sopenharmony_ci    data->info.httpcode = 0; /* clear it as it might've been used for the
18913498266Sopenharmony_ci                                proxy */
19013498266Sopenharmony_ci    /* If a proxy-authorization header was used for the proxy, then we should
19113498266Sopenharmony_ci       make sure that it isn't accidentally used for the document request
19213498266Sopenharmony_ci       after we've connected. So let's free and clear it here. */
19313498266Sopenharmony_ci    Curl_safefree(data->state.aptr.proxyuserpwd);
19413498266Sopenharmony_ci#ifdef USE_HYPER
19513498266Sopenharmony_ci    data->state.hconnect = FALSE;
19613498266Sopenharmony_ci#endif
19713498266Sopenharmony_ci    break;
19813498266Sopenharmony_ci  }
19913498266Sopenharmony_ci}
20013498266Sopenharmony_ci
20113498266Sopenharmony_cistatic void tunnel_free(struct Curl_cfilter *cf,
20213498266Sopenharmony_ci                        struct Curl_easy *data)
20313498266Sopenharmony_ci{
20413498266Sopenharmony_ci  struct h1_tunnel_state *ts = cf->ctx;
20513498266Sopenharmony_ci  if(ts) {
20613498266Sopenharmony_ci    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
20713498266Sopenharmony_ci    Curl_dyn_free(&ts->rcvbuf);
20813498266Sopenharmony_ci    Curl_dyn_free(&ts->request_data);
20913498266Sopenharmony_ci    Curl_httpchunk_free(data, &ts->ch);
21013498266Sopenharmony_ci    free(ts);
21113498266Sopenharmony_ci    cf->ctx = NULL;
21213498266Sopenharmony_ci  }
21313498266Sopenharmony_ci}
21413498266Sopenharmony_ci
21513498266Sopenharmony_ci#ifndef USE_HYPER
21613498266Sopenharmony_cistatic CURLcode start_CONNECT(struct Curl_cfilter *cf,
21713498266Sopenharmony_ci                              struct Curl_easy *data,
21813498266Sopenharmony_ci                              struct h1_tunnel_state *ts)
21913498266Sopenharmony_ci{
22013498266Sopenharmony_ci  struct httpreq *req = NULL;
22113498266Sopenharmony_ci  int http_minor;
22213498266Sopenharmony_ci  CURLcode result;
22313498266Sopenharmony_ci
22413498266Sopenharmony_ci    /* This only happens if we've looped here due to authentication
22513498266Sopenharmony_ci       reasons, and we don't really use the newly cloned URL here
22613498266Sopenharmony_ci       then. Just free() it. */
22713498266Sopenharmony_ci  Curl_safefree(data->req.newurl);
22813498266Sopenharmony_ci
22913498266Sopenharmony_ci  result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
23013498266Sopenharmony_ci  if(result)
23113498266Sopenharmony_ci    goto out;
23213498266Sopenharmony_ci
23313498266Sopenharmony_ci  infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
23413498266Sopenharmony_ci
23513498266Sopenharmony_ci  Curl_dyn_reset(&ts->request_data);
23613498266Sopenharmony_ci  ts->nsent = 0;
23713498266Sopenharmony_ci  ts->headerlines = 0;
23813498266Sopenharmony_ci  http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
23913498266Sopenharmony_ci
24013498266Sopenharmony_ci  result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
24113498266Sopenharmony_ci
24213498266Sopenharmony_ciout:
24313498266Sopenharmony_ci  if(result)
24413498266Sopenharmony_ci    failf(data, "Failed sending CONNECT to proxy");
24513498266Sopenharmony_ci  if(req)
24613498266Sopenharmony_ci    Curl_http_req_free(req);
24713498266Sopenharmony_ci  return result;
24813498266Sopenharmony_ci}
24913498266Sopenharmony_ci
25013498266Sopenharmony_cistatic CURLcode send_CONNECT(struct Curl_cfilter *cf,
25113498266Sopenharmony_ci                             struct Curl_easy *data,
25213498266Sopenharmony_ci                             struct h1_tunnel_state *ts,
25313498266Sopenharmony_ci                             bool *done)
25413498266Sopenharmony_ci{
25513498266Sopenharmony_ci  char *buf = Curl_dyn_ptr(&ts->request_data);
25613498266Sopenharmony_ci  size_t request_len = Curl_dyn_len(&ts->request_data);
25713498266Sopenharmony_ci  size_t blen = request_len;
25813498266Sopenharmony_ci  CURLcode result = CURLE_OK;
25913498266Sopenharmony_ci  ssize_t nwritten;
26013498266Sopenharmony_ci
26113498266Sopenharmony_ci  if(blen <= ts->nsent)
26213498266Sopenharmony_ci    goto out;  /* we are done */
26313498266Sopenharmony_ci
26413498266Sopenharmony_ci  blen -= ts->nsent;
26513498266Sopenharmony_ci  buf += ts->nsent;
26613498266Sopenharmony_ci
26713498266Sopenharmony_ci  nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result);
26813498266Sopenharmony_ci  if(nwritten < 0) {
26913498266Sopenharmony_ci    if(result == CURLE_AGAIN) {
27013498266Sopenharmony_ci      result = CURLE_OK;
27113498266Sopenharmony_ci    }
27213498266Sopenharmony_ci    goto out;
27313498266Sopenharmony_ci  }
27413498266Sopenharmony_ci
27513498266Sopenharmony_ci  DEBUGASSERT(blen >= (size_t)nwritten);
27613498266Sopenharmony_ci  ts->nsent += (size_t)nwritten;
27713498266Sopenharmony_ci  Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
27813498266Sopenharmony_ci
27913498266Sopenharmony_ciout:
28013498266Sopenharmony_ci  if(result)
28113498266Sopenharmony_ci    failf(data, "Failed sending CONNECT to proxy");
28213498266Sopenharmony_ci  *done = (!result && (ts->nsent >= request_len));
28313498266Sopenharmony_ci  return result;
28413498266Sopenharmony_ci}
28513498266Sopenharmony_ci
28613498266Sopenharmony_cistatic CURLcode on_resp_header(struct Curl_cfilter *cf,
28713498266Sopenharmony_ci                               struct Curl_easy *data,
28813498266Sopenharmony_ci                               struct h1_tunnel_state *ts,
28913498266Sopenharmony_ci                               const char *header)
29013498266Sopenharmony_ci{
29113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
29213498266Sopenharmony_ci  struct SingleRequest *k = &data->req;
29313498266Sopenharmony_ci  (void)cf;
29413498266Sopenharmony_ci
29513498266Sopenharmony_ci  if((checkprefix("WWW-Authenticate:", header) &&
29613498266Sopenharmony_ci      (401 == k->httpcode)) ||
29713498266Sopenharmony_ci     (checkprefix("Proxy-authenticate:", header) &&
29813498266Sopenharmony_ci      (407 == k->httpcode))) {
29913498266Sopenharmony_ci
30013498266Sopenharmony_ci    bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
30113498266Sopenharmony_ci    char *auth = Curl_copy_header_value(header);
30213498266Sopenharmony_ci    if(!auth)
30313498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
30413498266Sopenharmony_ci
30513498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
30613498266Sopenharmony_ci    result = Curl_http_input_auth(data, proxy, auth);
30713498266Sopenharmony_ci
30813498266Sopenharmony_ci    free(auth);
30913498266Sopenharmony_ci
31013498266Sopenharmony_ci    if(result)
31113498266Sopenharmony_ci      return result;
31213498266Sopenharmony_ci  }
31313498266Sopenharmony_ci  else if(checkprefix("Content-Length:", header)) {
31413498266Sopenharmony_ci    if(k->httpcode/100 == 2) {
31513498266Sopenharmony_ci      /* A client MUST ignore any Content-Length or Transfer-Encoding
31613498266Sopenharmony_ci         header fields received in a successful response to CONNECT.
31713498266Sopenharmony_ci         "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
31813498266Sopenharmony_ci      infof(data, "Ignoring Content-Length in CONNECT %03d response",
31913498266Sopenharmony_ci            k->httpcode);
32013498266Sopenharmony_ci    }
32113498266Sopenharmony_ci    else {
32213498266Sopenharmony_ci      (void)curlx_strtoofft(header + strlen("Content-Length:"),
32313498266Sopenharmony_ci                            NULL, 10, &ts->cl);
32413498266Sopenharmony_ci    }
32513498266Sopenharmony_ci  }
32613498266Sopenharmony_ci  else if(Curl_compareheader(header,
32713498266Sopenharmony_ci                             STRCONST("Connection:"), STRCONST("close")))
32813498266Sopenharmony_ci    ts->close_connection = TRUE;
32913498266Sopenharmony_ci  else if(checkprefix("Transfer-Encoding:", header)) {
33013498266Sopenharmony_ci    if(k->httpcode/100 == 2) {
33113498266Sopenharmony_ci      /* A client MUST ignore any Content-Length or Transfer-Encoding
33213498266Sopenharmony_ci         header fields received in a successful response to CONNECT.
33313498266Sopenharmony_ci         "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
33413498266Sopenharmony_ci      infof(data, "Ignoring Transfer-Encoding in "
33513498266Sopenharmony_ci            "CONNECT %03d response", k->httpcode);
33613498266Sopenharmony_ci    }
33713498266Sopenharmony_ci    else if(Curl_compareheader(header,
33813498266Sopenharmony_ci                               STRCONST("Transfer-Encoding:"),
33913498266Sopenharmony_ci                               STRCONST("chunked"))) {
34013498266Sopenharmony_ci      infof(data, "CONNECT responded chunked");
34113498266Sopenharmony_ci      ts->chunked_encoding = TRUE;
34213498266Sopenharmony_ci      /* reset our chunky engine */
34313498266Sopenharmony_ci      Curl_httpchunk_reset(data, &ts->ch, TRUE);
34413498266Sopenharmony_ci    }
34513498266Sopenharmony_ci  }
34613498266Sopenharmony_ci  else if(Curl_compareheader(header,
34713498266Sopenharmony_ci                             STRCONST("Proxy-Connection:"),
34813498266Sopenharmony_ci                             STRCONST("close")))
34913498266Sopenharmony_ci    ts->close_connection = TRUE;
35013498266Sopenharmony_ci  else if(!strncmp(header, "HTTP/1.", 7) &&
35113498266Sopenharmony_ci          ((header[7] == '0') || (header[7] == '1')) &&
35213498266Sopenharmony_ci          (header[8] == ' ') &&
35313498266Sopenharmony_ci          ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
35413498266Sopenharmony_ci          !ISDIGIT(header[12])) {
35513498266Sopenharmony_ci    /* store the HTTP code from the proxy */
35613498266Sopenharmony_ci    data->info.httpproxycode =  k->httpcode = (header[9] - '0') * 100 +
35713498266Sopenharmony_ci      (header[10] - '0') * 10 + (header[11] - '0');
35813498266Sopenharmony_ci  }
35913498266Sopenharmony_ci  return result;
36013498266Sopenharmony_ci}
36113498266Sopenharmony_ci
36213498266Sopenharmony_cistatic CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
36313498266Sopenharmony_ci                                  struct Curl_easy *data,
36413498266Sopenharmony_ci                                  struct h1_tunnel_state *ts,
36513498266Sopenharmony_ci                                  bool *done)
36613498266Sopenharmony_ci{
36713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
36813498266Sopenharmony_ci  struct SingleRequest *k = &data->req;
36913498266Sopenharmony_ci  curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
37013498266Sopenharmony_ci  char *linep;
37113498266Sopenharmony_ci  size_t line_len;
37213498266Sopenharmony_ci  int error, writetype;
37313498266Sopenharmony_ci
37413498266Sopenharmony_ci#define SELECT_OK      0
37513498266Sopenharmony_ci#define SELECT_ERROR   1
37613498266Sopenharmony_ci
37713498266Sopenharmony_ci  error = SELECT_OK;
37813498266Sopenharmony_ci  *done = FALSE;
37913498266Sopenharmony_ci
38013498266Sopenharmony_ci  if(!Curl_conn_data_pending(data, cf->sockindex))
38113498266Sopenharmony_ci    return CURLE_OK;
38213498266Sopenharmony_ci
38313498266Sopenharmony_ci  while(ts->keepon) {
38413498266Sopenharmony_ci    ssize_t nread;
38513498266Sopenharmony_ci    char byte;
38613498266Sopenharmony_ci
38713498266Sopenharmony_ci    /* Read one byte at a time to avoid a race condition. Wait at most one
38813498266Sopenharmony_ci       second before looping to ensure continuous pgrsUpdates. */
38913498266Sopenharmony_ci    result = Curl_read(data, tunnelsocket, &byte, 1, &nread);
39013498266Sopenharmony_ci    if(result == CURLE_AGAIN)
39113498266Sopenharmony_ci      /* socket buffer drained, return */
39213498266Sopenharmony_ci      return CURLE_OK;
39313498266Sopenharmony_ci
39413498266Sopenharmony_ci    if(Curl_pgrsUpdate(data))
39513498266Sopenharmony_ci      return CURLE_ABORTED_BY_CALLBACK;
39613498266Sopenharmony_ci
39713498266Sopenharmony_ci    if(result) {
39813498266Sopenharmony_ci      ts->keepon = KEEPON_DONE;
39913498266Sopenharmony_ci      break;
40013498266Sopenharmony_ci    }
40113498266Sopenharmony_ci
40213498266Sopenharmony_ci    if(nread <= 0) {
40313498266Sopenharmony_ci      if(data->set.proxyauth && data->state.authproxy.avail &&
40413498266Sopenharmony_ci         data->state.aptr.proxyuserpwd) {
40513498266Sopenharmony_ci        /* proxy auth was requested and there was proxy auth available,
40613498266Sopenharmony_ci           then deem this as "mere" proxy disconnect */
40713498266Sopenharmony_ci        ts->close_connection = TRUE;
40813498266Sopenharmony_ci        infof(data, "Proxy CONNECT connection closed");
40913498266Sopenharmony_ci      }
41013498266Sopenharmony_ci      else {
41113498266Sopenharmony_ci        error = SELECT_ERROR;
41213498266Sopenharmony_ci        failf(data, "Proxy CONNECT aborted");
41313498266Sopenharmony_ci      }
41413498266Sopenharmony_ci      ts->keepon = KEEPON_DONE;
41513498266Sopenharmony_ci      break;
41613498266Sopenharmony_ci    }
41713498266Sopenharmony_ci
41813498266Sopenharmony_ci    if(ts->keepon == KEEPON_IGNORE) {
41913498266Sopenharmony_ci      /* This means we are currently ignoring a response-body */
42013498266Sopenharmony_ci
42113498266Sopenharmony_ci      if(ts->cl) {
42213498266Sopenharmony_ci        /* A Content-Length based body: simply count down the counter
42313498266Sopenharmony_ci           and make sure to break out of the loop when we're done! */
42413498266Sopenharmony_ci        ts->cl--;
42513498266Sopenharmony_ci        if(ts->cl <= 0) {
42613498266Sopenharmony_ci          ts->keepon = KEEPON_DONE;
42713498266Sopenharmony_ci          break;
42813498266Sopenharmony_ci        }
42913498266Sopenharmony_ci      }
43013498266Sopenharmony_ci      else if(ts->chunked_encoding) {
43113498266Sopenharmony_ci        /* chunked-encoded body, so we need to do the chunked dance
43213498266Sopenharmony_ci           properly to know when the end of the body is reached */
43313498266Sopenharmony_ci        size_t consumed = 0;
43413498266Sopenharmony_ci
43513498266Sopenharmony_ci        /* now parse the chunked piece of data so that we can
43613498266Sopenharmony_ci           properly tell when the stream ends */
43713498266Sopenharmony_ci        result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
43813498266Sopenharmony_ci        if(result)
43913498266Sopenharmony_ci          return result;
44013498266Sopenharmony_ci        if(Curl_httpchunk_is_done(data, &ts->ch)) {
44113498266Sopenharmony_ci          /* we're done reading chunks! */
44213498266Sopenharmony_ci          infof(data, "chunk reading DONE");
44313498266Sopenharmony_ci          ts->keepon = KEEPON_DONE;
44413498266Sopenharmony_ci        }
44513498266Sopenharmony_ci      }
44613498266Sopenharmony_ci      continue;
44713498266Sopenharmony_ci    }
44813498266Sopenharmony_ci
44913498266Sopenharmony_ci    if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
45013498266Sopenharmony_ci      failf(data, "CONNECT response too large");
45113498266Sopenharmony_ci      return CURLE_RECV_ERROR;
45213498266Sopenharmony_ci    }
45313498266Sopenharmony_ci
45413498266Sopenharmony_ci    /* if this is not the end of a header line then continue */
45513498266Sopenharmony_ci    if(byte != 0x0a)
45613498266Sopenharmony_ci      continue;
45713498266Sopenharmony_ci
45813498266Sopenharmony_ci    ts->headerlines++;
45913498266Sopenharmony_ci    linep = Curl_dyn_ptr(&ts->rcvbuf);
46013498266Sopenharmony_ci    line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
46113498266Sopenharmony_ci
46213498266Sopenharmony_ci    /* output debug if that is requested */
46313498266Sopenharmony_ci    Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
46413498266Sopenharmony_ci
46513498266Sopenharmony_ci    /* send the header to the callback */
46613498266Sopenharmony_ci    writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
46713498266Sopenharmony_ci      (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
46813498266Sopenharmony_ci    result = Curl_client_write(data, writetype, linep, line_len);
46913498266Sopenharmony_ci    if(result)
47013498266Sopenharmony_ci      return result;
47113498266Sopenharmony_ci
47213498266Sopenharmony_ci    result = Curl_bump_headersize(data, line_len, TRUE);
47313498266Sopenharmony_ci    if(result)
47413498266Sopenharmony_ci      return result;
47513498266Sopenharmony_ci
47613498266Sopenharmony_ci    /* Newlines are CRLF, so the CR is ignored as the line isn't
47713498266Sopenharmony_ci       really terminated until the LF comes. Treat a following CR
47813498266Sopenharmony_ci       as end-of-headers as well.*/
47913498266Sopenharmony_ci
48013498266Sopenharmony_ci    if(('\r' == linep[0]) ||
48113498266Sopenharmony_ci       ('\n' == linep[0])) {
48213498266Sopenharmony_ci      /* end of response-headers from the proxy */
48313498266Sopenharmony_ci
48413498266Sopenharmony_ci      if((407 == k->httpcode) && !data->state.authproblem) {
48513498266Sopenharmony_ci        /* If we get a 407 response code with content length
48613498266Sopenharmony_ci           when we have no auth problem, we must ignore the
48713498266Sopenharmony_ci           whole response-body */
48813498266Sopenharmony_ci        ts->keepon = KEEPON_IGNORE;
48913498266Sopenharmony_ci
49013498266Sopenharmony_ci        if(ts->cl) {
49113498266Sopenharmony_ci          infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
49213498266Sopenharmony_ci                " bytes of response-body", ts->cl);
49313498266Sopenharmony_ci        }
49413498266Sopenharmony_ci        else if(ts->chunked_encoding) {
49513498266Sopenharmony_ci          infof(data, "Ignore chunked response-body");
49613498266Sopenharmony_ci        }
49713498266Sopenharmony_ci        else {
49813498266Sopenharmony_ci          /* without content-length or chunked encoding, we
49913498266Sopenharmony_ci             can't keep the connection alive since the close is
50013498266Sopenharmony_ci             the end signal so we bail out at once instead */
50113498266Sopenharmony_ci          CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
50213498266Sopenharmony_ci          ts->keepon = KEEPON_DONE;
50313498266Sopenharmony_ci        }
50413498266Sopenharmony_ci      }
50513498266Sopenharmony_ci      else {
50613498266Sopenharmony_ci        ts->keepon = KEEPON_DONE;
50713498266Sopenharmony_ci      }
50813498266Sopenharmony_ci
50913498266Sopenharmony_ci      DEBUGASSERT(ts->keepon == KEEPON_IGNORE
51013498266Sopenharmony_ci                  || ts->keepon == KEEPON_DONE);
51113498266Sopenharmony_ci      continue;
51213498266Sopenharmony_ci    }
51313498266Sopenharmony_ci
51413498266Sopenharmony_ci    result = on_resp_header(cf, data, ts, linep);
51513498266Sopenharmony_ci    if(result)
51613498266Sopenharmony_ci      return result;
51713498266Sopenharmony_ci
51813498266Sopenharmony_ci    Curl_dyn_reset(&ts->rcvbuf);
51913498266Sopenharmony_ci  } /* while there's buffer left and loop is requested */
52013498266Sopenharmony_ci
52113498266Sopenharmony_ci  if(error)
52213498266Sopenharmony_ci    result = CURLE_RECV_ERROR;
52313498266Sopenharmony_ci  *done = (ts->keepon == KEEPON_DONE);
52413498266Sopenharmony_ci  if(!result && *done && data->info.httpproxycode/100 != 2) {
52513498266Sopenharmony_ci    /* Deal with the possibly already received authenticate
52613498266Sopenharmony_ci       headers. 'newurl' is set to a new URL if we must loop. */
52713498266Sopenharmony_ci    result = Curl_http_auth_act(data);
52813498266Sopenharmony_ci  }
52913498266Sopenharmony_ci  return result;
53013498266Sopenharmony_ci}
53113498266Sopenharmony_ci
53213498266Sopenharmony_ci#else /* USE_HYPER */
53313498266Sopenharmony_ci
53413498266Sopenharmony_cistatic CURLcode CONNECT_host(struct Curl_cfilter *cf,
53513498266Sopenharmony_ci                             struct Curl_easy *data,
53613498266Sopenharmony_ci                             char **pauthority,
53713498266Sopenharmony_ci                             char **phost_header)
53813498266Sopenharmony_ci{
53913498266Sopenharmony_ci  const char *hostname;
54013498266Sopenharmony_ci  int port;
54113498266Sopenharmony_ci  bool ipv6_ip;
54213498266Sopenharmony_ci  CURLcode result;
54313498266Sopenharmony_ci  char *authority; /* for CONNECT, the destination host + port */
54413498266Sopenharmony_ci  char *host_header = NULL; /* Host: authority */
54513498266Sopenharmony_ci
54613498266Sopenharmony_ci  result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
54713498266Sopenharmony_ci  if(result)
54813498266Sopenharmony_ci    return result;
54913498266Sopenharmony_ci
55013498266Sopenharmony_ci  authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
55113498266Sopenharmony_ci                      port);
55213498266Sopenharmony_ci  if(!authority)
55313498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
55413498266Sopenharmony_ci
55513498266Sopenharmony_ci  /* If user is not overriding the Host header later */
55613498266Sopenharmony_ci  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
55713498266Sopenharmony_ci    host_header = aprintf("Host: %s\r\n", authority);
55813498266Sopenharmony_ci    if(!host_header) {
55913498266Sopenharmony_ci      free(authority);
56013498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
56113498266Sopenharmony_ci    }
56213498266Sopenharmony_ci  }
56313498266Sopenharmony_ci  *pauthority = authority;
56413498266Sopenharmony_ci  *phost_header = host_header;
56513498266Sopenharmony_ci  return CURLE_OK;
56613498266Sopenharmony_ci}
56713498266Sopenharmony_ci
56813498266Sopenharmony_ci/* The Hyper version of CONNECT */
56913498266Sopenharmony_cistatic CURLcode start_CONNECT(struct Curl_cfilter *cf,
57013498266Sopenharmony_ci                              struct Curl_easy *data,
57113498266Sopenharmony_ci                              struct h1_tunnel_state *ts)
57213498266Sopenharmony_ci{
57313498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
57413498266Sopenharmony_ci  struct hyptransfer *h = &data->hyp;
57513498266Sopenharmony_ci  curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
57613498266Sopenharmony_ci  hyper_io *io = NULL;
57713498266Sopenharmony_ci  hyper_request *req = NULL;
57813498266Sopenharmony_ci  hyper_headers *headers = NULL;
57913498266Sopenharmony_ci  hyper_clientconn_options *options = NULL;
58013498266Sopenharmony_ci  hyper_task *handshake = NULL;
58113498266Sopenharmony_ci  hyper_task *task = NULL; /* for the handshake */
58213498266Sopenharmony_ci  hyper_clientconn *client = NULL;
58313498266Sopenharmony_ci  hyper_task *sendtask = NULL; /* for the send */
58413498266Sopenharmony_ci  char *authority = NULL; /* for CONNECT */
58513498266Sopenharmony_ci  char *host_header = NULL; /* Host: */
58613498266Sopenharmony_ci  CURLcode result = CURLE_OUT_OF_MEMORY;
58713498266Sopenharmony_ci  (void)ts;
58813498266Sopenharmony_ci
58913498266Sopenharmony_ci  io = hyper_io_new();
59013498266Sopenharmony_ci  if(!io) {
59113498266Sopenharmony_ci    failf(data, "Couldn't create hyper IO");
59213498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
59313498266Sopenharmony_ci    goto error;
59413498266Sopenharmony_ci  }
59513498266Sopenharmony_ci  /* tell Hyper how to read/write network data */
59613498266Sopenharmony_ci  hyper_io_set_userdata(io, data);
59713498266Sopenharmony_ci  hyper_io_set_read(io, Curl_hyper_recv);
59813498266Sopenharmony_ci  hyper_io_set_write(io, Curl_hyper_send);
59913498266Sopenharmony_ci  conn->sockfd = tunnelsocket;
60013498266Sopenharmony_ci
60113498266Sopenharmony_ci  data->state.hconnect = TRUE;
60213498266Sopenharmony_ci
60313498266Sopenharmony_ci  /* create an executor to poll futures */
60413498266Sopenharmony_ci  if(!h->exec) {
60513498266Sopenharmony_ci    h->exec = hyper_executor_new();
60613498266Sopenharmony_ci    if(!h->exec) {
60713498266Sopenharmony_ci      failf(data, "Couldn't create hyper executor");
60813498266Sopenharmony_ci      result = CURLE_OUT_OF_MEMORY;
60913498266Sopenharmony_ci      goto error;
61013498266Sopenharmony_ci    }
61113498266Sopenharmony_ci  }
61213498266Sopenharmony_ci
61313498266Sopenharmony_ci  options = hyper_clientconn_options_new();
61413498266Sopenharmony_ci  if(!options) {
61513498266Sopenharmony_ci    failf(data, "Couldn't create hyper client options");
61613498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
61713498266Sopenharmony_ci    goto error;
61813498266Sopenharmony_ci  }
61913498266Sopenharmony_ci  hyper_clientconn_options_set_preserve_header_case(options, 1);
62013498266Sopenharmony_ci  hyper_clientconn_options_set_preserve_header_order(options, 1);
62113498266Sopenharmony_ci
62213498266Sopenharmony_ci  hyper_clientconn_options_exec(options, h->exec);
62313498266Sopenharmony_ci
62413498266Sopenharmony_ci  /* "Both the `io` and the `options` are consumed in this function
62513498266Sopenharmony_ci     call" */
62613498266Sopenharmony_ci  handshake = hyper_clientconn_handshake(io, options);
62713498266Sopenharmony_ci  if(!handshake) {
62813498266Sopenharmony_ci    failf(data, "Couldn't create hyper client handshake");
62913498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
63013498266Sopenharmony_ci    goto error;
63113498266Sopenharmony_ci  }
63213498266Sopenharmony_ci  io = NULL;
63313498266Sopenharmony_ci  options = NULL;
63413498266Sopenharmony_ci
63513498266Sopenharmony_ci  if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
63613498266Sopenharmony_ci    failf(data, "Couldn't hyper_executor_push the handshake");
63713498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
63813498266Sopenharmony_ci    goto error;
63913498266Sopenharmony_ci  }
64013498266Sopenharmony_ci  handshake = NULL; /* ownership passed on */
64113498266Sopenharmony_ci
64213498266Sopenharmony_ci  task = hyper_executor_poll(h->exec);
64313498266Sopenharmony_ci  if(!task) {
64413498266Sopenharmony_ci    failf(data, "Couldn't hyper_executor_poll the handshake");
64513498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
64613498266Sopenharmony_ci    goto error;
64713498266Sopenharmony_ci  }
64813498266Sopenharmony_ci
64913498266Sopenharmony_ci  client = hyper_task_value(task);
65013498266Sopenharmony_ci  hyper_task_free(task);
65113498266Sopenharmony_ci
65213498266Sopenharmony_ci  req = hyper_request_new();
65313498266Sopenharmony_ci  if(!req) {
65413498266Sopenharmony_ci    failf(data, "Couldn't hyper_request_new");
65513498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
65613498266Sopenharmony_ci    goto error;
65713498266Sopenharmony_ci  }
65813498266Sopenharmony_ci  if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
65913498266Sopenharmony_ci                              strlen("CONNECT"))) {
66013498266Sopenharmony_ci    failf(data, "error setting method");
66113498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
66213498266Sopenharmony_ci    goto error;
66313498266Sopenharmony_ci  }
66413498266Sopenharmony_ci
66513498266Sopenharmony_ci    /* This only happens if we've looped here due to authentication
66613498266Sopenharmony_ci       reasons, and we don't really use the newly cloned URL here
66713498266Sopenharmony_ci       then. Just free() it. */
66813498266Sopenharmony_ci  Curl_safefree(data->req.newurl);
66913498266Sopenharmony_ci
67013498266Sopenharmony_ci  result = CONNECT_host(cf, data, &authority, &host_header);
67113498266Sopenharmony_ci  if(result)
67213498266Sopenharmony_ci    goto error;
67313498266Sopenharmony_ci
67413498266Sopenharmony_ci  infof(data, "Establish HTTP proxy tunnel to %s", authority);
67513498266Sopenharmony_ci
67613498266Sopenharmony_ci  if(hyper_request_set_uri(req, (uint8_t *)authority,
67713498266Sopenharmony_ci                           strlen(authority))) {
67813498266Sopenharmony_ci    failf(data, "error setting path");
67913498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
68013498266Sopenharmony_ci    goto error;
68113498266Sopenharmony_ci  }
68213498266Sopenharmony_ci  if(data->set.verbose) {
68313498266Sopenharmony_ci    char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority);
68413498266Sopenharmony_ci    if(!se) {
68513498266Sopenharmony_ci      result = CURLE_OUT_OF_MEMORY;
68613498266Sopenharmony_ci      goto error;
68713498266Sopenharmony_ci    }
68813498266Sopenharmony_ci    Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
68913498266Sopenharmony_ci    free(se);
69013498266Sopenharmony_ci  }
69113498266Sopenharmony_ci  /* Setup the proxy-authorization header, if any */
69213498266Sopenharmony_ci  result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
69313498266Sopenharmony_ci                                 authority, TRUE);
69413498266Sopenharmony_ci  if(result)
69513498266Sopenharmony_ci    goto error;
69613498266Sopenharmony_ci  Curl_safefree(authority);
69713498266Sopenharmony_ci
69813498266Sopenharmony_ci  /* default is 1.1 */
69913498266Sopenharmony_ci  if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
70013498266Sopenharmony_ci     (HYPERE_OK != hyper_request_set_version(req,
70113498266Sopenharmony_ci                                             HYPER_HTTP_VERSION_1_0))) {
70213498266Sopenharmony_ci    failf(data, "error setting HTTP version");
70313498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
70413498266Sopenharmony_ci    goto error;
70513498266Sopenharmony_ci  }
70613498266Sopenharmony_ci
70713498266Sopenharmony_ci  headers = hyper_request_headers(req);
70813498266Sopenharmony_ci  if(!headers) {
70913498266Sopenharmony_ci    failf(data, "hyper_request_headers");
71013498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
71113498266Sopenharmony_ci    goto error;
71213498266Sopenharmony_ci  }
71313498266Sopenharmony_ci  if(host_header) {
71413498266Sopenharmony_ci    result = Curl_hyper_header(data, headers, host_header);
71513498266Sopenharmony_ci    if(result)
71613498266Sopenharmony_ci      goto error;
71713498266Sopenharmony_ci    Curl_safefree(host_header);
71813498266Sopenharmony_ci  }
71913498266Sopenharmony_ci
72013498266Sopenharmony_ci  if(data->state.aptr.proxyuserpwd) {
72113498266Sopenharmony_ci    result = Curl_hyper_header(data, headers,
72213498266Sopenharmony_ci                               data->state.aptr.proxyuserpwd);
72313498266Sopenharmony_ci    if(result)
72413498266Sopenharmony_ci      goto error;
72513498266Sopenharmony_ci  }
72613498266Sopenharmony_ci
72713498266Sopenharmony_ci  if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
72813498266Sopenharmony_ci     data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
72913498266Sopenharmony_ci    struct dynbuf ua;
73013498266Sopenharmony_ci    Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
73113498266Sopenharmony_ci    result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
73213498266Sopenharmony_ci                           data->set.str[STRING_USERAGENT]);
73313498266Sopenharmony_ci    if(result)
73413498266Sopenharmony_ci      goto error;
73513498266Sopenharmony_ci    result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
73613498266Sopenharmony_ci    if(result)
73713498266Sopenharmony_ci      goto error;
73813498266Sopenharmony_ci    Curl_dyn_free(&ua);
73913498266Sopenharmony_ci  }
74013498266Sopenharmony_ci
74113498266Sopenharmony_ci  if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
74213498266Sopenharmony_ci    result = Curl_hyper_header(data, headers,
74313498266Sopenharmony_ci                               "Proxy-Connection: Keep-Alive");
74413498266Sopenharmony_ci    if(result)
74513498266Sopenharmony_ci      goto error;
74613498266Sopenharmony_ci  }
74713498266Sopenharmony_ci
74813498266Sopenharmony_ci  result = Curl_add_custom_headers(data, TRUE, headers);
74913498266Sopenharmony_ci  if(result)
75013498266Sopenharmony_ci    goto error;
75113498266Sopenharmony_ci
75213498266Sopenharmony_ci  sendtask = hyper_clientconn_send(client, req);
75313498266Sopenharmony_ci  if(!sendtask) {
75413498266Sopenharmony_ci    failf(data, "hyper_clientconn_send");
75513498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
75613498266Sopenharmony_ci    goto error;
75713498266Sopenharmony_ci  }
75813498266Sopenharmony_ci  req = NULL;
75913498266Sopenharmony_ci
76013498266Sopenharmony_ci  if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
76113498266Sopenharmony_ci    failf(data, "Couldn't hyper_executor_push the send");
76213498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
76313498266Sopenharmony_ci    goto error;
76413498266Sopenharmony_ci  }
76513498266Sopenharmony_ci  sendtask = NULL; /* ownership passed on */
76613498266Sopenharmony_ci
76713498266Sopenharmony_ci  hyper_clientconn_free(client);
76813498266Sopenharmony_ci  client = NULL;
76913498266Sopenharmony_ci
77013498266Sopenharmony_cierror:
77113498266Sopenharmony_ci  free(host_header);
77213498266Sopenharmony_ci  free(authority);
77313498266Sopenharmony_ci  if(io)
77413498266Sopenharmony_ci    hyper_io_free(io);
77513498266Sopenharmony_ci  if(options)
77613498266Sopenharmony_ci    hyper_clientconn_options_free(options);
77713498266Sopenharmony_ci  if(handshake)
77813498266Sopenharmony_ci    hyper_task_free(handshake);
77913498266Sopenharmony_ci  if(client)
78013498266Sopenharmony_ci    hyper_clientconn_free(client);
78113498266Sopenharmony_ci  if(req)
78213498266Sopenharmony_ci    hyper_request_free(req);
78313498266Sopenharmony_ci
78413498266Sopenharmony_ci  return result;
78513498266Sopenharmony_ci}
78613498266Sopenharmony_ci
78713498266Sopenharmony_cistatic CURLcode send_CONNECT(struct Curl_cfilter *cf,
78813498266Sopenharmony_ci                             struct Curl_easy *data,
78913498266Sopenharmony_ci                             struct h1_tunnel_state *ts,
79013498266Sopenharmony_ci                             bool *done)
79113498266Sopenharmony_ci{
79213498266Sopenharmony_ci  struct hyptransfer *h = &data->hyp;
79313498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
79413498266Sopenharmony_ci  hyper_task *task = NULL;
79513498266Sopenharmony_ci  hyper_error *hypererr = NULL;
79613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
79713498266Sopenharmony_ci
79813498266Sopenharmony_ci  (void)ts;
79913498266Sopenharmony_ci  (void)conn;
80013498266Sopenharmony_ci  do {
80113498266Sopenharmony_ci    task = hyper_executor_poll(h->exec);
80213498266Sopenharmony_ci    if(task) {
80313498266Sopenharmony_ci      bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
80413498266Sopenharmony_ci      if(error)
80513498266Sopenharmony_ci        hypererr = hyper_task_value(task);
80613498266Sopenharmony_ci      hyper_task_free(task);
80713498266Sopenharmony_ci      if(error) {
80813498266Sopenharmony_ci        /* this could probably use a better error code? */
80913498266Sopenharmony_ci        result = CURLE_OUT_OF_MEMORY;
81013498266Sopenharmony_ci        goto error;
81113498266Sopenharmony_ci      }
81213498266Sopenharmony_ci    }
81313498266Sopenharmony_ci  } while(task);
81413498266Sopenharmony_cierror:
81513498266Sopenharmony_ci  *done = (result == CURLE_OK);
81613498266Sopenharmony_ci  if(hypererr) {
81713498266Sopenharmony_ci    uint8_t errbuf[256];
81813498266Sopenharmony_ci    size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
81913498266Sopenharmony_ci    failf(data, "Hyper: %.*s", (int)errlen, errbuf);
82013498266Sopenharmony_ci    hyper_error_free(hypererr);
82113498266Sopenharmony_ci  }
82213498266Sopenharmony_ci  return result;
82313498266Sopenharmony_ci}
82413498266Sopenharmony_ci
82513498266Sopenharmony_cistatic CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
82613498266Sopenharmony_ci                                  struct Curl_easy *data,
82713498266Sopenharmony_ci                                  struct h1_tunnel_state *ts,
82813498266Sopenharmony_ci                                  bool *done)
82913498266Sopenharmony_ci{
83013498266Sopenharmony_ci  struct hyptransfer *h = &data->hyp;
83113498266Sopenharmony_ci  CURLcode result;
83213498266Sopenharmony_ci  int didwhat;
83313498266Sopenharmony_ci
83413498266Sopenharmony_ci  (void)ts;
83513498266Sopenharmony_ci  *done = FALSE;
83613498266Sopenharmony_ci  result = Curl_hyper_stream(data, cf->conn, &didwhat, done,
83713498266Sopenharmony_ci                             CURL_CSELECT_IN | CURL_CSELECT_OUT);
83813498266Sopenharmony_ci  if(result || !*done)
83913498266Sopenharmony_ci    return result;
84013498266Sopenharmony_ci  if(h->exec) {
84113498266Sopenharmony_ci    hyper_executor_free(h->exec);
84213498266Sopenharmony_ci    h->exec = NULL;
84313498266Sopenharmony_ci  }
84413498266Sopenharmony_ci  if(h->read_waker) {
84513498266Sopenharmony_ci    hyper_waker_free(h->read_waker);
84613498266Sopenharmony_ci    h->read_waker = NULL;
84713498266Sopenharmony_ci  }
84813498266Sopenharmony_ci  if(h->write_waker) {
84913498266Sopenharmony_ci    hyper_waker_free(h->write_waker);
85013498266Sopenharmony_ci    h->write_waker = NULL;
85113498266Sopenharmony_ci  }
85213498266Sopenharmony_ci  return result;
85313498266Sopenharmony_ci}
85413498266Sopenharmony_ci
85513498266Sopenharmony_ci#endif /* USE_HYPER */
85613498266Sopenharmony_ci
85713498266Sopenharmony_cistatic CURLcode H1_CONNECT(struct Curl_cfilter *cf,
85813498266Sopenharmony_ci                           struct Curl_easy *data,
85913498266Sopenharmony_ci                           struct h1_tunnel_state *ts)
86013498266Sopenharmony_ci{
86113498266Sopenharmony_ci  struct connectdata *conn = cf->conn;
86213498266Sopenharmony_ci  CURLcode result;
86313498266Sopenharmony_ci  bool done;
86413498266Sopenharmony_ci
86513498266Sopenharmony_ci  if(tunnel_is_established(ts))
86613498266Sopenharmony_ci    return CURLE_OK;
86713498266Sopenharmony_ci  if(tunnel_is_failed(ts))
86813498266Sopenharmony_ci    return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
86913498266Sopenharmony_ci
87013498266Sopenharmony_ci  do {
87113498266Sopenharmony_ci    timediff_t check;
87213498266Sopenharmony_ci
87313498266Sopenharmony_ci    check = Curl_timeleft(data, NULL, TRUE);
87413498266Sopenharmony_ci    if(check <= 0) {
87513498266Sopenharmony_ci      failf(data, "Proxy CONNECT aborted due to timeout");
87613498266Sopenharmony_ci      result = CURLE_OPERATION_TIMEDOUT;
87713498266Sopenharmony_ci      goto out;
87813498266Sopenharmony_ci    }
87913498266Sopenharmony_ci
88013498266Sopenharmony_ci    switch(ts->tunnel_state) {
88113498266Sopenharmony_ci    case H1_TUNNEL_INIT:
88213498266Sopenharmony_ci      /* Prepare the CONNECT request and make a first attempt to send. */
88313498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "CONNECT start");
88413498266Sopenharmony_ci      result = start_CONNECT(cf, data, ts);
88513498266Sopenharmony_ci      if(result)
88613498266Sopenharmony_ci        goto out;
88713498266Sopenharmony_ci      h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
88813498266Sopenharmony_ci      FALLTHROUGH();
88913498266Sopenharmony_ci
89013498266Sopenharmony_ci    case H1_TUNNEL_CONNECT:
89113498266Sopenharmony_ci      /* see that the request is completely sent */
89213498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "CONNECT send");
89313498266Sopenharmony_ci      result = send_CONNECT(cf, data, ts, &done);
89413498266Sopenharmony_ci      if(result || !done)
89513498266Sopenharmony_ci        goto out;
89613498266Sopenharmony_ci      h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
89713498266Sopenharmony_ci      FALLTHROUGH();
89813498266Sopenharmony_ci
89913498266Sopenharmony_ci    case H1_TUNNEL_RECEIVE:
90013498266Sopenharmony_ci      /* read what is there */
90113498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "CONNECT receive");
90213498266Sopenharmony_ci      result = recv_CONNECT_resp(cf, data, ts, &done);
90313498266Sopenharmony_ci      if(Curl_pgrsUpdate(data)) {
90413498266Sopenharmony_ci        result = CURLE_ABORTED_BY_CALLBACK;
90513498266Sopenharmony_ci        goto out;
90613498266Sopenharmony_ci      }
90713498266Sopenharmony_ci      /* error or not complete yet. return for more multi-multi */
90813498266Sopenharmony_ci      if(result || !done)
90913498266Sopenharmony_ci        goto out;
91013498266Sopenharmony_ci      /* got it */
91113498266Sopenharmony_ci      h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
91213498266Sopenharmony_ci      FALLTHROUGH();
91313498266Sopenharmony_ci
91413498266Sopenharmony_ci    case H1_TUNNEL_RESPONSE:
91513498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "CONNECT response");
91613498266Sopenharmony_ci      if(data->req.newurl) {
91713498266Sopenharmony_ci        /* not the "final" response, we need to do a follow up request.
91813498266Sopenharmony_ci         * If the other side indicated a connection close, or if someone
91913498266Sopenharmony_ci         * else told us to close this connection, do so now.
92013498266Sopenharmony_ci         */
92113498266Sopenharmony_ci        if(ts->close_connection || conn->bits.close) {
92213498266Sopenharmony_ci          /* Close this filter and the sub-chain, re-connect the
92313498266Sopenharmony_ci           * sub-chain and continue. Closing this filter will
92413498266Sopenharmony_ci           * reset our tunnel state. To avoid recursion, we return
92513498266Sopenharmony_ci           * and expect to be called again.
92613498266Sopenharmony_ci           */
92713498266Sopenharmony_ci          CURL_TRC_CF(data, cf, "CONNECT need to close+open");
92813498266Sopenharmony_ci          infof(data, "Connect me again please");
92913498266Sopenharmony_ci          Curl_conn_cf_close(cf, data);
93013498266Sopenharmony_ci          connkeep(conn, "HTTP proxy CONNECT");
93113498266Sopenharmony_ci          result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
93213498266Sopenharmony_ci          goto out;
93313498266Sopenharmony_ci        }
93413498266Sopenharmony_ci        else {
93513498266Sopenharmony_ci          /* staying on this connection, reset state */
93613498266Sopenharmony_ci          h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
93713498266Sopenharmony_ci        }
93813498266Sopenharmony_ci      }
93913498266Sopenharmony_ci      break;
94013498266Sopenharmony_ci
94113498266Sopenharmony_ci    default:
94213498266Sopenharmony_ci      break;
94313498266Sopenharmony_ci    }
94413498266Sopenharmony_ci
94513498266Sopenharmony_ci  } while(data->req.newurl);
94613498266Sopenharmony_ci
94713498266Sopenharmony_ci  DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
94813498266Sopenharmony_ci  if(data->info.httpproxycode/100 != 2) {
94913498266Sopenharmony_ci    /* a non-2xx response and we have no next url to try. */
95013498266Sopenharmony_ci    Curl_safefree(data->req.newurl);
95113498266Sopenharmony_ci    /* failure, close this connection to avoid reuse */
95213498266Sopenharmony_ci    streamclose(conn, "proxy CONNECT failure");
95313498266Sopenharmony_ci    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
95413498266Sopenharmony_ci    failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
95513498266Sopenharmony_ci    return CURLE_RECV_ERROR;
95613498266Sopenharmony_ci  }
95713498266Sopenharmony_ci  /* 2xx response, SUCCESS! */
95813498266Sopenharmony_ci  h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
95913498266Sopenharmony_ci  infof(data, "CONNECT tunnel established, response %d",
96013498266Sopenharmony_ci        data->info.httpproxycode);
96113498266Sopenharmony_ci  result = CURLE_OK;
96213498266Sopenharmony_ci
96313498266Sopenharmony_ciout:
96413498266Sopenharmony_ci  if(result)
96513498266Sopenharmony_ci    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
96613498266Sopenharmony_ci  return result;
96713498266Sopenharmony_ci}
96813498266Sopenharmony_ci
96913498266Sopenharmony_cistatic CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
97013498266Sopenharmony_ci                                    struct Curl_easy *data,
97113498266Sopenharmony_ci                                    bool blocking, bool *done)
97213498266Sopenharmony_ci{
97313498266Sopenharmony_ci  CURLcode result;
97413498266Sopenharmony_ci  struct h1_tunnel_state *ts = cf->ctx;
97513498266Sopenharmony_ci
97613498266Sopenharmony_ci  if(cf->connected) {
97713498266Sopenharmony_ci    *done = TRUE;
97813498266Sopenharmony_ci    return CURLE_OK;
97913498266Sopenharmony_ci  }
98013498266Sopenharmony_ci
98113498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "connect");
98213498266Sopenharmony_ci  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
98313498266Sopenharmony_ci  if(result || !*done)
98413498266Sopenharmony_ci    return result;
98513498266Sopenharmony_ci
98613498266Sopenharmony_ci  *done = FALSE;
98713498266Sopenharmony_ci  if(!ts) {
98813498266Sopenharmony_ci    result = tunnel_init(cf, data, &ts);
98913498266Sopenharmony_ci    if(result)
99013498266Sopenharmony_ci      return result;
99113498266Sopenharmony_ci    cf->ctx = ts;
99213498266Sopenharmony_ci  }
99313498266Sopenharmony_ci
99413498266Sopenharmony_ci  /* TODO: can we do blocking? */
99513498266Sopenharmony_ci  /* We want "seamless" operations through HTTP proxy tunnel */
99613498266Sopenharmony_ci
99713498266Sopenharmony_ci  result = H1_CONNECT(cf, data, ts);
99813498266Sopenharmony_ci  if(result)
99913498266Sopenharmony_ci    goto out;
100013498266Sopenharmony_ci  Curl_safefree(data->state.aptr.proxyuserpwd);
100113498266Sopenharmony_ci
100213498266Sopenharmony_ciout:
100313498266Sopenharmony_ci  *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
100413498266Sopenharmony_ci  if(*done) {
100513498266Sopenharmony_ci    cf->connected = TRUE;
100613498266Sopenharmony_ci    /* Restore `data->req` fields that may habe been touched */
100713498266Sopenharmony_ci    data->req.header = TRUE; /* assume header */
100813498266Sopenharmony_ci    data->req.bytecount = 0;
100913498266Sopenharmony_ci    data->req.ignorebody = FALSE;
101013498266Sopenharmony_ci    Curl_client_cleanup(data);
101113498266Sopenharmony_ci    Curl_pgrsSetUploadCounter(data, 0);
101213498266Sopenharmony_ci    Curl_pgrsSetDownloadCounter(data, 0);
101313498266Sopenharmony_ci
101413498266Sopenharmony_ci    tunnel_free(cf, data);
101513498266Sopenharmony_ci  }
101613498266Sopenharmony_ci  return result;
101713498266Sopenharmony_ci}
101813498266Sopenharmony_ci
101913498266Sopenharmony_cistatic void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
102013498266Sopenharmony_ci                                        struct Curl_easy *data,
102113498266Sopenharmony_ci                                        struct easy_pollset *ps)
102213498266Sopenharmony_ci{
102313498266Sopenharmony_ci  struct h1_tunnel_state *ts = cf->ctx;
102413498266Sopenharmony_ci
102513498266Sopenharmony_ci  if(!cf->connected) {
102613498266Sopenharmony_ci    /* If we are not connected, but the filter "below" is
102713498266Sopenharmony_ci     * and not waiting on something, we are tunneling. */
102813498266Sopenharmony_ci    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
102913498266Sopenharmony_ci    if(ts) {
103013498266Sopenharmony_ci      /* when we've sent a CONNECT to a proxy, we should rather either
103113498266Sopenharmony_ci         wait for the socket to become readable to be able to get the
103213498266Sopenharmony_ci         response headers or if we're still sending the request, wait
103313498266Sopenharmony_ci         for write. */
103413498266Sopenharmony_ci      if(ts->CONNECT.sending == HTTPSEND_REQUEST)
103513498266Sopenharmony_ci        Curl_pollset_set_out_only(data, ps, sock);
103613498266Sopenharmony_ci      else
103713498266Sopenharmony_ci        Curl_pollset_set_in_only(data, ps, sock);
103813498266Sopenharmony_ci    }
103913498266Sopenharmony_ci    else
104013498266Sopenharmony_ci      Curl_pollset_set_out_only(data, ps, sock);
104113498266Sopenharmony_ci  }
104213498266Sopenharmony_ci}
104313498266Sopenharmony_ci
104413498266Sopenharmony_cistatic void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
104513498266Sopenharmony_ci                                struct Curl_easy *data)
104613498266Sopenharmony_ci{
104713498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "destroy");
104813498266Sopenharmony_ci  tunnel_free(cf, data);
104913498266Sopenharmony_ci}
105013498266Sopenharmony_ci
105113498266Sopenharmony_cistatic void cf_h1_proxy_close(struct Curl_cfilter *cf,
105213498266Sopenharmony_ci                              struct Curl_easy *data)
105313498266Sopenharmony_ci{
105413498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "close");
105513498266Sopenharmony_ci  cf->connected = FALSE;
105613498266Sopenharmony_ci  if(cf->ctx) {
105713498266Sopenharmony_ci    h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
105813498266Sopenharmony_ci  }
105913498266Sopenharmony_ci  if(cf->next)
106013498266Sopenharmony_ci    cf->next->cft->do_close(cf->next, data);
106113498266Sopenharmony_ci}
106213498266Sopenharmony_ci
106313498266Sopenharmony_ci
106413498266Sopenharmony_cistruct Curl_cftype Curl_cft_h1_proxy = {
106513498266Sopenharmony_ci  "H1-PROXY",
106613498266Sopenharmony_ci  CF_TYPE_IP_CONNECT,
106713498266Sopenharmony_ci  0,
106813498266Sopenharmony_ci  cf_h1_proxy_destroy,
106913498266Sopenharmony_ci  cf_h1_proxy_connect,
107013498266Sopenharmony_ci  cf_h1_proxy_close,
107113498266Sopenharmony_ci  Curl_cf_http_proxy_get_host,
107213498266Sopenharmony_ci  cf_h1_proxy_adjust_pollset,
107313498266Sopenharmony_ci  Curl_cf_def_data_pending,
107413498266Sopenharmony_ci  Curl_cf_def_send,
107513498266Sopenharmony_ci  Curl_cf_def_recv,
107613498266Sopenharmony_ci  Curl_cf_def_cntrl,
107713498266Sopenharmony_ci  Curl_cf_def_conn_is_alive,
107813498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
107913498266Sopenharmony_ci  Curl_cf_def_query,
108013498266Sopenharmony_ci};
108113498266Sopenharmony_ci
108213498266Sopenharmony_ciCURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
108313498266Sopenharmony_ci                                       struct Curl_easy *data)
108413498266Sopenharmony_ci{
108513498266Sopenharmony_ci  struct Curl_cfilter *cf;
108613498266Sopenharmony_ci  CURLcode result;
108713498266Sopenharmony_ci
108813498266Sopenharmony_ci  (void)data;
108913498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
109013498266Sopenharmony_ci  if(!result)
109113498266Sopenharmony_ci    Curl_conn_cf_insert_after(cf_at, cf);
109213498266Sopenharmony_ci  return result;
109313498266Sopenharmony_ci}
109413498266Sopenharmony_ci
109513498266Sopenharmony_ci#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */
1096