xref: /curl/lib/cf-h1-proxy.c (revision fc3e1cbc)
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 "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 "curlx.h"
44 #include "vtls/vtls.h"
45 #include "transfer.h"
46 #include "multiif.h"
47 
48 /* The last 3 #include files should be in this order */
49 #include "curl_printf.h"
50 #include "curl_memory.h"
51 #include "memdebug.h"
52 
53 
54 typedef enum {
55     H1_TUNNEL_INIT,     /* init/default/no tunnel state */
56     H1_TUNNEL_CONNECT,  /* CONNECT request is being send */
57     H1_TUNNEL_RECEIVE,  /* CONNECT answer is being received */
58     H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
59     H1_TUNNEL_ESTABLISHED,
60     H1_TUNNEL_FAILED
61 } h1_tunnel_state;
62 
63 /* struct for HTTP CONNECT tunneling */
64 struct h1_tunnel_state {
65   struct dynbuf rcvbuf;
66   struct dynbuf request_data;
67   size_t nsent;
68   size_t headerlines;
69   struct Curl_chunker ch;
70   enum keeponval {
71     KEEPON_DONE,
72     KEEPON_CONNECT,
73     KEEPON_IGNORE
74   } keepon;
75   curl_off_t cl; /* size of content to read and ignore */
76   h1_tunnel_state tunnel_state;
77   BIT(chunked_encoding);
78   BIT(close_connection);
79 };
80 
81 
tunnel_is_established(struct h1_tunnel_state * ts)82 static bool tunnel_is_established(struct h1_tunnel_state *ts)
83 {
84   return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
85 }
86 
tunnel_is_failed(struct h1_tunnel_state * ts)87 static bool tunnel_is_failed(struct h1_tunnel_state *ts)
88 {
89   return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
90 }
91 
tunnel_reinit(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)92 static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
93                               struct Curl_easy *data,
94                               struct h1_tunnel_state *ts)
95 {
96   (void)data;
97   (void)cf;
98   DEBUGASSERT(ts);
99   Curl_dyn_reset(&ts->rcvbuf);
100   Curl_dyn_reset(&ts->request_data);
101   ts->tunnel_state = H1_TUNNEL_INIT;
102   ts->keepon = KEEPON_CONNECT;
103   ts->cl = 0;
104   ts->close_connection = FALSE;
105   return CURLE_OK;
106 }
107 
tunnel_init(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state ** pts)108 static CURLcode tunnel_init(struct Curl_cfilter *cf,
109                             struct Curl_easy *data,
110                             struct h1_tunnel_state **pts)
111 {
112   struct h1_tunnel_state *ts;
113 
114   if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
115     failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
116     return CURLE_UNSUPPORTED_PROTOCOL;
117   }
118 
119   ts = calloc(1, sizeof(*ts));
120   if(!ts)
121     return CURLE_OUT_OF_MEMORY;
122 
123   infof(data, "allocate connect buffer");
124 
125   Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
126   Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
127   Curl_httpchunk_init(data, &ts->ch, TRUE);
128 
129   *pts =  ts;
130   connkeep(cf->conn, "HTTP proxy CONNECT");
131   return tunnel_reinit(cf, data, ts);
132 }
133 
h1_tunnel_go_state(struct Curl_cfilter * cf,struct h1_tunnel_state * ts,h1_tunnel_state new_state,struct Curl_easy * data)134 static void h1_tunnel_go_state(struct Curl_cfilter *cf,
135                                struct h1_tunnel_state *ts,
136                                h1_tunnel_state new_state,
137                                struct Curl_easy *data)
138 {
139   if(ts->tunnel_state == new_state)
140     return;
141   /* entering this one */
142   switch(new_state) {
143   case H1_TUNNEL_INIT:
144     CURL_TRC_CF(data, cf, "new tunnel state 'init'");
145     tunnel_reinit(cf, data, ts);
146     break;
147 
148   case H1_TUNNEL_CONNECT:
149     CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
150     ts->tunnel_state = H1_TUNNEL_CONNECT;
151     ts->keepon = KEEPON_CONNECT;
152     Curl_dyn_reset(&ts->rcvbuf);
153     break;
154 
155   case H1_TUNNEL_RECEIVE:
156     CURL_TRC_CF(data, cf, "new tunnel state 'receive'");
157     ts->tunnel_state = H1_TUNNEL_RECEIVE;
158     break;
159 
160   case H1_TUNNEL_RESPONSE:
161     CURL_TRC_CF(data, cf, "new tunnel state 'response'");
162     ts->tunnel_state = H1_TUNNEL_RESPONSE;
163     break;
164 
165   case H1_TUNNEL_ESTABLISHED:
166     CURL_TRC_CF(data, cf, "new tunnel state 'established'");
167     infof(data, "CONNECT phase completed");
168     data->state.authproxy.done = TRUE;
169     data->state.authproxy.multipass = FALSE;
170     FALLTHROUGH();
171   case H1_TUNNEL_FAILED:
172     if(new_state == H1_TUNNEL_FAILED)
173       CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
174     ts->tunnel_state = new_state;
175     Curl_dyn_reset(&ts->rcvbuf);
176     Curl_dyn_reset(&ts->request_data);
177     /* restore the protocol pointer */
178     data->info.httpcode = 0; /* clear it as it might've been used for the
179                                 proxy */
180     /* If a proxy-authorization header was used for the proxy, then we should
181        make sure that it is not accidentally used for the document request
182        after we have connected. So let's free and clear it here. */
183     Curl_safefree(data->state.aptr.proxyuserpwd);
184     break;
185   }
186 }
187 
tunnel_free(struct Curl_cfilter * cf,struct Curl_easy * data)188 static void tunnel_free(struct Curl_cfilter *cf,
189                         struct Curl_easy *data)
190 {
191   if(cf) {
192     struct h1_tunnel_state *ts = cf->ctx;
193     if(ts) {
194       h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
195       Curl_dyn_free(&ts->rcvbuf);
196       Curl_dyn_free(&ts->request_data);
197       Curl_httpchunk_free(data, &ts->ch);
198       free(ts);
199       cf->ctx = NULL;
200     }
201   }
202 }
203 
tunnel_want_send(struct h1_tunnel_state * ts)204 static bool tunnel_want_send(struct h1_tunnel_state *ts)
205 {
206   return (ts->tunnel_state == H1_TUNNEL_CONNECT);
207 }
208 
start_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)209 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
210                               struct Curl_easy *data,
211                               struct h1_tunnel_state *ts)
212 {
213   struct httpreq *req = NULL;
214   int http_minor;
215   CURLcode result;
216 
217     /* This only happens if we have looped here due to authentication
218        reasons, and we do not really use the newly cloned URL here
219        then. Just free() it. */
220   Curl_safefree(data->req.newurl);
221 
222   result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
223   if(result)
224     goto out;
225 
226   infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
227 
228   Curl_dyn_reset(&ts->request_data);
229   ts->nsent = 0;
230   ts->headerlines = 0;
231   http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
232 
233   result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
234   if(!result)
235     result = Curl_creader_set_null(data);
236 
237 out:
238   if(result)
239     failf(data, "Failed sending CONNECT to proxy");
240   if(req)
241     Curl_http_req_free(req);
242   return result;
243 }
244 
send_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)245 static CURLcode send_CONNECT(struct Curl_cfilter *cf,
246                              struct Curl_easy *data,
247                              struct h1_tunnel_state *ts,
248                              bool *done)
249 {
250   char *buf = Curl_dyn_ptr(&ts->request_data);
251   size_t request_len = Curl_dyn_len(&ts->request_data);
252   size_t blen = request_len;
253   CURLcode result = CURLE_OK;
254   ssize_t nwritten;
255 
256   if(blen <= ts->nsent)
257     goto out;  /* we are done */
258 
259   blen -= ts->nsent;
260   buf += ts->nsent;
261 
262   nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &result);
263   if(nwritten < 0) {
264     if(result == CURLE_AGAIN) {
265       result = CURLE_OK;
266     }
267     goto out;
268   }
269 
270   DEBUGASSERT(blen >= (size_t)nwritten);
271   ts->nsent += (size_t)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 
on_resp_header(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,const char * header)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       (void)curlx_strtoofft(header + strlen("Content-Length:"),
318                             NULL, 10, &ts->cl);
319     }
320   }
321   else if(Curl_compareheader(header,
322                              STRCONST("Connection:"), STRCONST("close")))
323     ts->close_connection = TRUE;
324   else if(checkprefix("Transfer-Encoding:", header)) {
325     if(k->httpcode/100 == 2) {
326       /* A client MUST ignore any Content-Length or Transfer-Encoding
327          header fields received in a successful response to CONNECT.
328          "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
329       infof(data, "Ignoring Transfer-Encoding in "
330             "CONNECT %03d response", k->httpcode);
331     }
332     else if(Curl_compareheader(header,
333                                STRCONST("Transfer-Encoding:"),
334                                STRCONST("chunked"))) {
335       infof(data, "CONNECT responded chunked");
336       ts->chunked_encoding = TRUE;
337       /* reset our chunky engine */
338       Curl_httpchunk_reset(data, &ts->ch, TRUE);
339     }
340   }
341   else if(Curl_compareheader(header,
342                              STRCONST("Proxy-Connection:"),
343                              STRCONST("close")))
344     ts->close_connection = TRUE;
345   else if(!strncmp(header, "HTTP/1.", 7) &&
346           ((header[7] == '0') || (header[7] == '1')) &&
347           (header[8] == ' ') &&
348           ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
349           !ISDIGIT(header[12])) {
350     /* store the HTTP code from the proxy */
351     data->info.httpproxycode =  k->httpcode = (header[9] - '0') * 100 +
352       (header[10] - '0') * 10 + (header[11] - '0');
353   }
354   return result;
355 }
356 
recv_CONNECT_resp(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)357 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
358                                   struct Curl_easy *data,
359                                   struct h1_tunnel_state *ts,
360                                   bool *done)
361 {
362   CURLcode result = CURLE_OK;
363   struct SingleRequest *k = &data->req;
364   char *linep;
365   size_t line_len;
366   int error, writetype;
367 
368 #define SELECT_OK      0
369 #define SELECT_ERROR   1
370 
371   error = SELECT_OK;
372   *done = FALSE;
373 
374   if(!Curl_conn_data_pending(data, cf->sockindex))
375     return CURLE_OK;
376 
377   while(ts->keepon) {
378     ssize_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 <= 0) {
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(Curl_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 = Curl_dyn_ptr(&ts->rcvbuf);
454     line_len = Curl_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     Curl_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 
H1_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)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, FALSE, &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 
cf_h1_proxy_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)638 static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
639                                     struct Curl_easy *data,
640                                     bool blocking, 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, blocking, 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   /* TODO: can we do blocking? */
664   /* We want "seamless" operations through HTTP proxy tunnel */
665 
666   result = H1_CONNECT(cf, data, ts);
667   if(result)
668     goto out;
669   Curl_safefree(data->state.aptr.proxyuserpwd);
670 
671 out:
672   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
673   if(*done) {
674     cf->connected = TRUE;
675     /* The real request will follow the CONNECT, reset request partially */
676     Curl_req_soft_reset(&data->req, data);
677     Curl_client_reset(data);
678     Curl_pgrsSetUploadCounter(data, 0);
679     Curl_pgrsSetDownloadCounter(data, 0);
680 
681     tunnel_free(cf, data);
682   }
683   return result;
684 }
685 
cf_h1_proxy_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)686 static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
687                                         struct Curl_easy *data,
688                                         struct easy_pollset *ps)
689 {
690   struct h1_tunnel_state *ts = cf->ctx;
691 
692   if(!cf->connected) {
693     /* If we are not connected, but the filter "below" is
694      * and not waiting on something, we are tunneling. */
695     curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
696     if(ts) {
697       /* when we have sent a CONNECT to a proxy, we should rather either
698          wait for the socket to become readable to be able to get the
699          response headers or if we are still sending the request, wait
700          for write. */
701       if(tunnel_want_send(ts))
702         Curl_pollset_set_out_only(data, ps, sock);
703       else
704         Curl_pollset_set_in_only(data, ps, sock);
705     }
706     else
707       Curl_pollset_set_out_only(data, ps, sock);
708   }
709 }
710 
cf_h1_proxy_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)711 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
712                                 struct Curl_easy *data)
713 {
714   CURL_TRC_CF(data, cf, "destroy");
715   tunnel_free(cf, data);
716 }
717 
cf_h1_proxy_close(struct Curl_cfilter * cf,struct Curl_easy * data)718 static void cf_h1_proxy_close(struct Curl_cfilter *cf,
719                               struct Curl_easy *data)
720 {
721   CURL_TRC_CF(data, cf, "close");
722   if(cf) {
723     cf->connected = FALSE;
724     if(cf->ctx) {
725       h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
726     }
727     if(cf->next)
728       cf->next->cft->do_close(cf->next, data);
729   }
730 }
731 
732 
733 struct Curl_cftype Curl_cft_h1_proxy = {
734   "H1-PROXY",
735   CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
736   0,
737   cf_h1_proxy_destroy,
738   cf_h1_proxy_connect,
739   cf_h1_proxy_close,
740   Curl_cf_def_shutdown,
741   Curl_cf_http_proxy_get_host,
742   cf_h1_proxy_adjust_pollset,
743   Curl_cf_def_data_pending,
744   Curl_cf_def_send,
745   Curl_cf_def_recv,
746   Curl_cf_def_cntrl,
747   Curl_cf_def_conn_is_alive,
748   Curl_cf_def_conn_keep_alive,
749   Curl_cf_def_query,
750 };
751 
Curl_cf_h1_proxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)752 CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
753                                        struct Curl_easy *data)
754 {
755   struct Curl_cfilter *cf;
756   CURLcode result;
757 
758   (void)data;
759   result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
760   if(!result)
761     Curl_conn_cf_insert_after(cf_at, cf);
762   return result;
763 }
764 
765 #endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */
766