quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

cf-h1-proxy.c (23121B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
     28 
     29 #include <curl/curl.h>
     30 #include "urldata.h"
     31 #include "curlx/dynbuf.h"
     32 #include "sendf.h"
     33 #include "http.h"
     34 #include "http1.h"
     35 #include "http_proxy.h"
     36 #include "url.h"
     37 #include "select.h"
     38 #include "progress.h"
     39 #include "cfilters.h"
     40 #include "cf-h1-proxy.h"
     41 #include "connect.h"
     42 #include "curl_trc.h"
     43 #include "strcase.h"
     44 #include "vtls/vtls.h"
     45 #include "transfer.h"
     46 #include "multiif.h"
     47 #include "curlx/strparse.h"
     48 
     49 /* The last 3 #include files should be in this order */
     50 #include "curl_printf.h"
     51 #include "curl_memory.h"
     52 #include "memdebug.h"
     53 
     54 
     55 typedef enum {
     56     H1_TUNNEL_INIT,     /* init/default/no tunnel state */
     57     H1_TUNNEL_CONNECT,  /* CONNECT request is being send */
     58     H1_TUNNEL_RECEIVE,  /* CONNECT answer is being received */
     59     H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
     60     H1_TUNNEL_ESTABLISHED,
     61     H1_TUNNEL_FAILED
     62 } h1_tunnel_state;
     63 
     64 /* struct for HTTP CONNECT tunneling */
     65 struct h1_tunnel_state {
     66   struct dynbuf rcvbuf;
     67   struct dynbuf request_data;
     68   size_t nsent;
     69   size_t headerlines;
     70   struct Curl_chunker ch;
     71   enum keeponval {
     72     KEEPON_DONE,
     73     KEEPON_CONNECT,
     74     KEEPON_IGNORE
     75   } keepon;
     76   curl_off_t cl; /* size of content to read and ignore */
     77   h1_tunnel_state tunnel_state;
     78   BIT(chunked_encoding);
     79   BIT(close_connection);
     80 };
     81 
     82 
     83 static bool tunnel_is_established(struct h1_tunnel_state *ts)
     84 {
     85   return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
     86 }
     87 
     88 static bool tunnel_is_failed(struct h1_tunnel_state *ts)
     89 {
     90   return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
     91 }
     92 
     93 static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
     94                               struct Curl_easy *data,
     95                               struct h1_tunnel_state *ts)
     96 {
     97   (void)data;
     98   (void)cf;
     99   DEBUGASSERT(ts);
    100   curlx_dyn_reset(&ts->rcvbuf);
    101   curlx_dyn_reset(&ts->request_data);
    102   ts->tunnel_state = H1_TUNNEL_INIT;
    103   ts->keepon = KEEPON_CONNECT;
    104   ts->cl = 0;
    105   ts->close_connection = FALSE;
    106   return CURLE_OK;
    107 }
    108 
    109 static CURLcode tunnel_init(struct Curl_cfilter *cf,
    110                             struct Curl_easy *data,
    111                             struct h1_tunnel_state **pts)
    112 {
    113   struct h1_tunnel_state *ts;
    114 
    115   if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
    116     failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
    117     return CURLE_UNSUPPORTED_PROTOCOL;
    118   }
    119 
    120   ts = calloc(1, sizeof(*ts));
    121   if(!ts)
    122     return CURLE_OUT_OF_MEMORY;
    123 
    124   infof(data, "allocate connect buffer");
    125 
    126   curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
    127   curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
    128   Curl_httpchunk_init(data, &ts->ch, TRUE);
    129 
    130   *pts =  ts;
    131   connkeep(cf->conn, "HTTP proxy CONNECT");
    132   return tunnel_reinit(cf, data, ts);
    133 }
    134 
    135 static void h1_tunnel_go_state(struct Curl_cfilter *cf,
    136                                struct h1_tunnel_state *ts,
    137                                h1_tunnel_state new_state,
    138                                struct Curl_easy *data)
    139 {
    140   if(ts->tunnel_state == new_state)
    141     return;
    142   /* entering this one */
    143   switch(new_state) {
    144   case H1_TUNNEL_INIT:
    145     CURL_TRC_CF(data, cf, "new tunnel state 'init'");
    146     tunnel_reinit(cf, data, ts);
    147     break;
    148 
    149   case H1_TUNNEL_CONNECT:
    150     CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
    151     ts->tunnel_state = H1_TUNNEL_CONNECT;
    152     ts->keepon = KEEPON_CONNECT;
    153     curlx_dyn_reset(&ts->rcvbuf);
    154     break;
    155 
    156   case H1_TUNNEL_RECEIVE:
    157     CURL_TRC_CF(data, cf, "new tunnel state 'receive'");
    158     ts->tunnel_state = H1_TUNNEL_RECEIVE;
    159     break;
    160 
    161   case H1_TUNNEL_RESPONSE:
    162     CURL_TRC_CF(data, cf, "new tunnel state 'response'");
    163     ts->tunnel_state = H1_TUNNEL_RESPONSE;
    164     break;
    165 
    166   case H1_TUNNEL_ESTABLISHED:
    167     CURL_TRC_CF(data, cf, "new tunnel state 'established'");
    168     infof(data, "CONNECT phase completed");
    169     data->state.authproxy.done = TRUE;
    170     data->state.authproxy.multipass = FALSE;
    171     FALLTHROUGH();
    172   case H1_TUNNEL_FAILED:
    173     if(new_state == H1_TUNNEL_FAILED)
    174       CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
    175     ts->tunnel_state = new_state;
    176     curlx_dyn_reset(&ts->rcvbuf);
    177     curlx_dyn_reset(&ts->request_data);
    178     /* restore the protocol pointer */
    179     data->info.httpcode = 0; /* clear it as it might've been used for the
    180                                 proxy */
    181     /* If a proxy-authorization header was used for the proxy, then we should
    182        make sure that it is not accidentally used for the document request
    183        after we have connected. So let's free and clear it here. */
    184     Curl_safefree(data->state.aptr.proxyuserpwd);
    185     break;
    186   }
    187 }
    188 
    189 static void tunnel_free(struct Curl_cfilter *cf,
    190                         struct Curl_easy *data)
    191 {
    192   if(cf) {
    193     struct h1_tunnel_state *ts = cf->ctx;
    194     if(ts) {
    195       h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
    196       curlx_dyn_free(&ts->rcvbuf);
    197       curlx_dyn_free(&ts->request_data);
    198       Curl_httpchunk_free(data, &ts->ch);
    199       free(ts);
    200       cf->ctx = NULL;
    201     }
    202   }
    203 }
    204 
    205 static bool tunnel_want_send(struct h1_tunnel_state *ts)
    206 {
    207   return ts->tunnel_state == H1_TUNNEL_CONNECT;
    208 }
    209 
    210 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
    211                               struct Curl_easy *data,
    212                               struct h1_tunnel_state *ts)
    213 {
    214   struct httpreq *req = NULL;
    215   int http_minor;
    216   CURLcode result;
    217 
    218     /* This only happens if we have looped here due to authentication
    219        reasons, and we do not really use the newly cloned URL here
    220        then. Just free() it. */
    221   Curl_safefree(data->req.newurl);
    222 
    223   result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
    224   if(result)
    225     goto out;
    226 
    227   infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
    228 
    229   curlx_dyn_reset(&ts->request_data);
    230   ts->nsent = 0;
    231   ts->headerlines = 0;
    232   http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
    233 
    234   result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
    235   if(!result)
    236     result = Curl_creader_set_null(data);
    237 
    238 out:
    239   if(result)
    240     failf(data, "Failed sending CONNECT to proxy");
    241   if(req)
    242     Curl_http_req_free(req);
    243   return result;
    244 }
    245 
    246 static CURLcode send_CONNECT(struct Curl_cfilter *cf,
    247                              struct Curl_easy *data,
    248                              struct h1_tunnel_state *ts,
    249                              bool *done)
    250 {
    251   char *buf = curlx_dyn_ptr(&ts->request_data);
    252   size_t request_len = curlx_dyn_len(&ts->request_data);
    253   size_t blen = request_len;
    254   CURLcode result = CURLE_OK;
    255   size_t nwritten;
    256 
    257   if(blen <= ts->nsent)
    258     goto out;  /* we are done */
    259 
    260   blen -= ts->nsent;
    261   buf += ts->nsent;
    262 
    263   result = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &nwritten);
    264   if(result) {
    265     if(result == CURLE_AGAIN)
    266       result = CURLE_OK;
    267     goto out;
    268   }
    269 
    270   DEBUGASSERT(blen >= nwritten);
    271   ts->nsent += nwritten;
    272   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
    273 
    274 out:
    275   if(result)
    276     failf(data, "Failed sending CONNECT to proxy");
    277   *done = (!result && (ts->nsent >= request_len));
    278   return result;
    279 }
    280 
    281 static CURLcode on_resp_header(struct Curl_cfilter *cf,
    282                                struct Curl_easy *data,
    283                                struct h1_tunnel_state *ts,
    284                                const char *header)
    285 {
    286   CURLcode result = CURLE_OK;
    287   struct SingleRequest *k = &data->req;
    288   (void)cf;
    289 
    290   if((checkprefix("WWW-Authenticate:", header) &&
    291       (401 == k->httpcode)) ||
    292      (checkprefix("Proxy-authenticate:", header) &&
    293       (407 == k->httpcode))) {
    294 
    295     bool proxy = (k->httpcode == 407);
    296     char *auth = Curl_copy_header_value(header);
    297     if(!auth)
    298       return CURLE_OUT_OF_MEMORY;
    299 
    300     CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
    301     result = Curl_http_input_auth(data, proxy, auth);
    302 
    303     free(auth);
    304 
    305     if(result)
    306       return result;
    307   }
    308   else if(checkprefix("Content-Length:", header)) {
    309     if(k->httpcode/100 == 2) {
    310       /* A client MUST ignore any Content-Length or Transfer-Encoding
    311          header fields received in a successful response to CONNECT.
    312          "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
    313       infof(data, "Ignoring Content-Length in CONNECT %03d response",
    314             k->httpcode);
    315     }
    316     else {
    317       const char *p = header + strlen("Content-Length:");
    318       if(curlx_str_numblanks(&p, &ts->cl)) {
    319         failf(data, "Unsupported Content-Length value");
    320         return CURLE_WEIRD_SERVER_REPLY;
    321       }
    322     }
    323   }
    324   else if(Curl_compareheader(header,
    325                              STRCONST("Connection:"), STRCONST("close")))
    326     ts->close_connection = TRUE;
    327   else if(checkprefix("Transfer-Encoding:", header)) {
    328     if(k->httpcode/100 == 2) {
    329       /* A client MUST ignore any Content-Length or Transfer-Encoding
    330          header fields received in a successful response to CONNECT.
    331          "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
    332       infof(data, "Ignoring Transfer-Encoding in "
    333             "CONNECT %03d response", k->httpcode);
    334     }
    335     else if(Curl_compareheader(header,
    336                                STRCONST("Transfer-Encoding:"),
    337                                STRCONST("chunked"))) {
    338       infof(data, "CONNECT responded chunked");
    339       ts->chunked_encoding = TRUE;
    340       /* reset our chunky engine */
    341       Curl_httpchunk_reset(data, &ts->ch, TRUE);
    342     }
    343   }
    344   else if(Curl_compareheader(header,
    345                              STRCONST("Proxy-Connection:"),
    346                              STRCONST("close")))
    347     ts->close_connection = TRUE;
    348   else if(!strncmp(header, "HTTP/1.", 7) &&
    349           ((header[7] == '0') || (header[7] == '1')) &&
    350           (header[8] == ' ') &&
    351           ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
    352           !ISDIGIT(header[12])) {
    353     /* store the HTTP code from the proxy */
    354     data->info.httpproxycode =  k->httpcode = (header[9] - '0') * 100 +
    355       (header[10] - '0') * 10 + (header[11] - '0');
    356   }
    357   return result;
    358 }
    359 
    360 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
    361                                   struct Curl_easy *data,
    362                                   struct h1_tunnel_state *ts,
    363                                   bool *done)
    364 {
    365   CURLcode result = CURLE_OK;
    366   struct SingleRequest *k = &data->req;
    367   char *linep;
    368   size_t line_len;
    369   int error, writetype;
    370 
    371 #define SELECT_OK      0
    372 #define SELECT_ERROR   1
    373 
    374   error = SELECT_OK;
    375   *done = FALSE;
    376 
    377   while(ts->keepon) {
    378     size_t nread;
    379     char byte;
    380 
    381     /* Read one byte at a time to avoid a race condition. Wait at most one
    382        second before looping to ensure continuous pgrsUpdates. */
    383     result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread);
    384     if(result == CURLE_AGAIN)
    385       /* socket buffer drained, return */
    386       return CURLE_OK;
    387 
    388     if(Curl_pgrsUpdate(data))
    389       return CURLE_ABORTED_BY_CALLBACK;
    390 
    391     if(result) {
    392       ts->keepon = KEEPON_DONE;
    393       break;
    394     }
    395 
    396     if(!nread) {
    397       if(data->set.proxyauth && data->state.authproxy.avail &&
    398          data->state.aptr.proxyuserpwd) {
    399         /* proxy auth was requested and there was proxy auth available,
    400            then deem this as "mere" proxy disconnect */
    401         ts->close_connection = TRUE;
    402         infof(data, "Proxy CONNECT connection closed");
    403       }
    404       else {
    405         error = SELECT_ERROR;
    406         failf(data, "Proxy CONNECT aborted");
    407       }
    408       ts->keepon = KEEPON_DONE;
    409       break;
    410     }
    411 
    412     if(ts->keepon == KEEPON_IGNORE) {
    413       /* This means we are currently ignoring a response-body */
    414 
    415       if(ts->cl) {
    416         /* A Content-Length based body: simply count down the counter
    417            and make sure to break out of the loop when we are done! */
    418         ts->cl--;
    419         if(ts->cl <= 0) {
    420           ts->keepon = KEEPON_DONE;
    421           break;
    422         }
    423       }
    424       else if(ts->chunked_encoding) {
    425         /* chunked-encoded body, so we need to do the chunked dance
    426            properly to know when the end of the body is reached */
    427         size_t consumed = 0;
    428 
    429         /* now parse the chunked piece of data so that we can
    430            properly tell when the stream ends */
    431         result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
    432         if(result)
    433           return result;
    434         if(Curl_httpchunk_is_done(data, &ts->ch)) {
    435           /* we are done reading chunks! */
    436           infof(data, "chunk reading DONE");
    437           ts->keepon = KEEPON_DONE;
    438         }
    439       }
    440       continue;
    441     }
    442 
    443     if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) {
    444       failf(data, "CONNECT response too large");
    445       return CURLE_RECV_ERROR;
    446     }
    447 
    448     /* if this is not the end of a header line then continue */
    449     if(byte != 0x0a)
    450       continue;
    451 
    452     ts->headerlines++;
    453     linep = curlx_dyn_ptr(&ts->rcvbuf);
    454     line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
    455 
    456     /* output debug if that is requested */
    457     Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
    458 
    459     /* send the header to the callback */
    460     writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
    461       (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
    462     result = Curl_client_write(data, writetype, linep, line_len);
    463     if(result)
    464       return result;
    465 
    466     result = Curl_bump_headersize(data, line_len, TRUE);
    467     if(result)
    468       return result;
    469 
    470     /* Newlines are CRLF, so the CR is ignored as the line is not
    471        really terminated until the LF comes. Treat a following CR
    472        as end-of-headers as well.*/
    473 
    474     if(('\r' == linep[0]) ||
    475        ('\n' == linep[0])) {
    476       /* end of response-headers from the proxy */
    477 
    478       if((407 == k->httpcode) && !data->state.authproblem) {
    479         /* If we get a 407 response code with content length
    480            when we have no auth problem, we must ignore the
    481            whole response-body */
    482         ts->keepon = KEEPON_IGNORE;
    483 
    484         if(ts->cl) {
    485           infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl);
    486         }
    487         else if(ts->chunked_encoding) {
    488           infof(data, "Ignore chunked response-body");
    489         }
    490         else {
    491           /* without content-length or chunked encoding, we
    492              cannot keep the connection alive since the close is
    493              the end signal so we bail out at once instead */
    494           CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
    495           ts->keepon = KEEPON_DONE;
    496         }
    497       }
    498       else {
    499         ts->keepon = KEEPON_DONE;
    500       }
    501 
    502       DEBUGASSERT(ts->keepon == KEEPON_IGNORE
    503                   || ts->keepon == KEEPON_DONE);
    504       continue;
    505     }
    506 
    507     result = on_resp_header(cf, data, ts, linep);
    508     if(result)
    509       return result;
    510 
    511     curlx_dyn_reset(&ts->rcvbuf);
    512   } /* while there is buffer left and loop is requested */
    513 
    514   if(error)
    515     result = CURLE_RECV_ERROR;
    516   *done = (ts->keepon == KEEPON_DONE);
    517   if(!result && *done && data->info.httpproxycode/100 != 2) {
    518     /* Deal with the possibly already received authenticate
    519        headers. 'newurl' is set to a new URL if we must loop. */
    520     result = Curl_http_auth_act(data);
    521   }
    522   return result;
    523 }
    524 
    525 static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
    526                            struct Curl_easy *data,
    527                            struct h1_tunnel_state *ts)
    528 {
    529   struct connectdata *conn = cf->conn;
    530   CURLcode result;
    531   bool done;
    532 
    533   if(tunnel_is_established(ts))
    534     return CURLE_OK;
    535   if(tunnel_is_failed(ts))
    536     return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
    537 
    538   do {
    539     timediff_t check;
    540 
    541     check = Curl_timeleft(data, NULL, TRUE);
    542     if(check <= 0) {
    543       failf(data, "Proxy CONNECT aborted due to timeout");
    544       result = CURLE_OPERATION_TIMEDOUT;
    545       goto out;
    546     }
    547 
    548     switch(ts->tunnel_state) {
    549     case H1_TUNNEL_INIT:
    550       /* Prepare the CONNECT request and make a first attempt to send. */
    551       CURL_TRC_CF(data, cf, "CONNECT start");
    552       result = start_CONNECT(cf, data, ts);
    553       if(result)
    554         goto out;
    555       h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
    556       FALLTHROUGH();
    557 
    558     case H1_TUNNEL_CONNECT:
    559       /* see that the request is completely sent */
    560       CURL_TRC_CF(data, cf, "CONNECT send");
    561       result = send_CONNECT(cf, data, ts, &done);
    562       if(result || !done)
    563         goto out;
    564       h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
    565       FALLTHROUGH();
    566 
    567     case H1_TUNNEL_RECEIVE:
    568       /* read what is there */
    569       CURL_TRC_CF(data, cf, "CONNECT receive");
    570       result = recv_CONNECT_resp(cf, data, ts, &done);
    571       if(Curl_pgrsUpdate(data)) {
    572         result = CURLE_ABORTED_BY_CALLBACK;
    573         goto out;
    574       }
    575       /* error or not complete yet. return for more multi-multi */
    576       if(result || !done)
    577         goto out;
    578       /* got it */
    579       h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
    580       FALLTHROUGH();
    581 
    582     case H1_TUNNEL_RESPONSE:
    583       CURL_TRC_CF(data, cf, "CONNECT response");
    584       if(data->req.newurl) {
    585         /* not the "final" response, we need to do a follow up request.
    586          * If the other side indicated a connection close, or if someone
    587          * else told us to close this connection, do so now.
    588          */
    589         Curl_req_soft_reset(&data->req, data);
    590         if(ts->close_connection || conn->bits.close) {
    591           /* Close this filter and the sub-chain, re-connect the
    592            * sub-chain and continue. Closing this filter will
    593            * reset our tunnel state. To avoid recursion, we return
    594            * and expect to be called again.
    595            */
    596           CURL_TRC_CF(data, cf, "CONNECT need to close+open");
    597           infof(data, "Connect me again please");
    598           Curl_conn_cf_close(cf, data);
    599           connkeep(conn, "HTTP proxy CONNECT");
    600           result = Curl_conn_cf_connect(cf->next, data, &done);
    601           goto out;
    602         }
    603         else {
    604           /* staying on this connection, reset state */
    605           h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
    606         }
    607       }
    608       break;
    609 
    610     default:
    611       break;
    612     }
    613 
    614   } while(data->req.newurl);
    615 
    616   DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
    617   if(data->info.httpproxycode/100 != 2) {
    618     /* a non-2xx response and we have no next URL to try. */
    619     Curl_safefree(data->req.newurl);
    620     /* failure, close this connection to avoid reuse */
    621     streamclose(conn, "proxy CONNECT failure");
    622     h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
    623     failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
    624     return CURLE_RECV_ERROR;
    625   }
    626   /* 2xx response, SUCCESS! */
    627   h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
    628   infof(data, "CONNECT tunnel established, response %d",
    629         data->info.httpproxycode);
    630   result = CURLE_OK;
    631 
    632 out:
    633   if(result)
    634     h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
    635   return result;
    636 }
    637 
    638 static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
    639                                     struct Curl_easy *data,
    640                                     bool *done)
    641 {
    642   CURLcode result;
    643   struct h1_tunnel_state *ts = cf->ctx;
    644 
    645   if(cf->connected) {
    646     *done = TRUE;
    647     return CURLE_OK;
    648   }
    649 
    650   CURL_TRC_CF(data, cf, "connect");
    651   result = cf->next->cft->do_connect(cf->next, data, done);
    652   if(result || !*done)
    653     return result;
    654 
    655   *done = FALSE;
    656   if(!ts) {
    657     result = tunnel_init(cf, data, &ts);
    658     if(result)
    659       return result;
    660     cf->ctx = ts;
    661   }
    662 
    663   /* We want "seamless" operations through HTTP proxy tunnel */
    664 
    665   result = H1_CONNECT(cf, data, ts);
    666   if(result)
    667     goto out;
    668   Curl_safefree(data->state.aptr.proxyuserpwd);
    669 
    670 out:
    671   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
    672   if(*done) {
    673     cf->connected = TRUE;
    674     /* The real request will follow the CONNECT, reset request partially */
    675     Curl_req_soft_reset(&data->req, data);
    676     Curl_client_reset(data);
    677     Curl_pgrsSetUploadCounter(data, 0);
    678     Curl_pgrsSetDownloadCounter(data, 0);
    679 
    680     tunnel_free(cf, data);
    681   }
    682   return result;
    683 }
    684 
    685 static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
    686                                        struct Curl_easy *data,
    687                                        struct easy_pollset *ps)
    688 {
    689   struct h1_tunnel_state *ts = cf->ctx;
    690 
    691   if(!cf->connected) {
    692     /* If we are not connected, but the filter "below" is
    693      * and not waiting on something, we are tunneling. */
    694     curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
    695     if(ts) {
    696       /* when we have sent a CONNECT to a proxy, we should rather either
    697          wait for the socket to become readable to be able to get the
    698          response headers or if we are still sending the request, wait
    699          for write. */
    700       if(tunnel_want_send(ts))
    701         Curl_pollset_set_out_only(data, ps, sock);
    702       else
    703         Curl_pollset_set_in_only(data, ps, sock);
    704     }
    705     else
    706       Curl_pollset_set_out_only(data, ps, sock);
    707   }
    708 }
    709 
    710 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
    711                                 struct Curl_easy *data)
    712 {
    713   CURL_TRC_CF(data, cf, "destroy");
    714   tunnel_free(cf, data);
    715 }
    716 
    717 static void cf_h1_proxy_close(struct Curl_cfilter *cf,
    718                               struct Curl_easy *data)
    719 {
    720   CURL_TRC_CF(data, cf, "close");
    721   if(cf) {
    722     cf->connected = FALSE;
    723     if(cf->ctx) {
    724       h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
    725     }
    726     if(cf->next)
    727       cf->next->cft->do_close(cf->next, data);
    728   }
    729 }
    730 
    731 
    732 struct Curl_cftype Curl_cft_h1_proxy = {
    733   "H1-PROXY",
    734   CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
    735   0,
    736   cf_h1_proxy_destroy,
    737   cf_h1_proxy_connect,
    738   cf_h1_proxy_close,
    739   Curl_cf_def_shutdown,
    740   cf_h1_proxy_adjust_pollset,
    741   Curl_cf_def_data_pending,
    742   Curl_cf_def_send,
    743   Curl_cf_def_recv,
    744   Curl_cf_def_cntrl,
    745   Curl_cf_def_conn_is_alive,
    746   Curl_cf_def_conn_keep_alive,
    747   Curl_cf_http_proxy_query,
    748 };
    749 
    750 CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
    751                                        struct Curl_easy *data)
    752 {
    753   struct Curl_cfilter *cf;
    754   CURLcode result;
    755 
    756   (void)data;
    757   result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
    758   if(!result)
    759     Curl_conn_cf_insert_after(cf_at, cf);
    760   return result;
    761 }
    762 
    763 #endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */