Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmcurl/lib/cf-h1-proxy.c
3153 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
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 */
764
765