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