Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/lib/cf-h1-proxy.c
2065 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
ssize_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
nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &result);
264
if(nwritten < 0) {
265
if(result == CURLE_AGAIN) {
266
result = CURLE_OK;
267
}
268
goto out;
269
}
270
271
DEBUGASSERT(blen >= (size_t)nwritten);
272
ts->nsent += (size_t)nwritten;
273
Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
274
275
out:
276
if(result)
277
failf(data, "Failed sending CONNECT to proxy");
278
*done = (!result && (ts->nsent >= request_len));
279
return result;
280
}
281
282
static CURLcode on_resp_header(struct Curl_cfilter *cf,
283
struct Curl_easy *data,
284
struct h1_tunnel_state *ts,
285
const char *header)
286
{
287
CURLcode result = CURLE_OK;
288
struct SingleRequest *k = &data->req;
289
(void)cf;
290
291
if((checkprefix("WWW-Authenticate:", header) &&
292
(401 == k->httpcode)) ||
293
(checkprefix("Proxy-authenticate:", header) &&
294
(407 == k->httpcode))) {
295
296
bool proxy = (k->httpcode == 407);
297
char *auth = Curl_copy_header_value(header);
298
if(!auth)
299
return CURLE_OUT_OF_MEMORY;
300
301
CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
302
result = Curl_http_input_auth(data, proxy, auth);
303
304
free(auth);
305
306
if(result)
307
return result;
308
}
309
else if(checkprefix("Content-Length:", header)) {
310
if(k->httpcode/100 == 2) {
311
/* A client MUST ignore any Content-Length or Transfer-Encoding
312
header fields received in a successful response to CONNECT.
313
"Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
314
infof(data, "Ignoring Content-Length in CONNECT %03d response",
315
k->httpcode);
316
}
317
else {
318
const char *p = header + strlen("Content-Length:");
319
if(curlx_str_numblanks(&p, &ts->cl)) {
320
failf(data, "Unsupported Content-Length value");
321
return CURLE_WEIRD_SERVER_REPLY;
322
}
323
}
324
}
325
else if(Curl_compareheader(header,
326
STRCONST("Connection:"), STRCONST("close")))
327
ts->close_connection = TRUE;
328
else if(checkprefix("Transfer-Encoding:", header)) {
329
if(k->httpcode/100 == 2) {
330
/* A client MUST ignore any Content-Length or Transfer-Encoding
331
header fields received in a successful response to CONNECT.
332
"Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
333
infof(data, "Ignoring Transfer-Encoding in "
334
"CONNECT %03d response", k->httpcode);
335
}
336
else if(Curl_compareheader(header,
337
STRCONST("Transfer-Encoding:"),
338
STRCONST("chunked"))) {
339
infof(data, "CONNECT responded chunked");
340
ts->chunked_encoding = TRUE;
341
/* reset our chunky engine */
342
Curl_httpchunk_reset(data, &ts->ch, TRUE);
343
}
344
}
345
else if(Curl_compareheader(header,
346
STRCONST("Proxy-Connection:"),
347
STRCONST("close")))
348
ts->close_connection = TRUE;
349
else if(!strncmp(header, "HTTP/1.", 7) &&
350
((header[7] == '0') || (header[7] == '1')) &&
351
(header[8] == ' ') &&
352
ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
353
!ISDIGIT(header[12])) {
354
/* store the HTTP code from the proxy */
355
data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 +
356
(header[10] - '0') * 10 + (header[11] - '0');
357
}
358
return result;
359
}
360
361
static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
362
struct Curl_easy *data,
363
struct h1_tunnel_state *ts,
364
bool *done)
365
{
366
CURLcode result = CURLE_OK;
367
struct SingleRequest *k = &data->req;
368
char *linep;
369
size_t line_len;
370
int error, writetype;
371
372
#define SELECT_OK 0
373
#define SELECT_ERROR 1
374
375
error = SELECT_OK;
376
*done = FALSE;
377
378
if(!Curl_conn_data_pending(data, cf->sockindex))
379
return CURLE_OK;
380
381
while(ts->keepon) {
382
ssize_t nread;
383
char byte;
384
385
/* Read one byte at a time to avoid a race condition. Wait at most one
386
second before looping to ensure continuous pgrsUpdates. */
387
result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread);
388
if(result == CURLE_AGAIN)
389
/* socket buffer drained, return */
390
return CURLE_OK;
391
392
if(Curl_pgrsUpdate(data))
393
return CURLE_ABORTED_BY_CALLBACK;
394
395
if(result) {
396
ts->keepon = KEEPON_DONE;
397
break;
398
}
399
400
if(nread <= 0) {
401
if(data->set.proxyauth && data->state.authproxy.avail &&
402
data->state.aptr.proxyuserpwd) {
403
/* proxy auth was requested and there was proxy auth available,
404
then deem this as "mere" proxy disconnect */
405
ts->close_connection = TRUE;
406
infof(data, "Proxy CONNECT connection closed");
407
}
408
else {
409
error = SELECT_ERROR;
410
failf(data, "Proxy CONNECT aborted");
411
}
412
ts->keepon = KEEPON_DONE;
413
break;
414
}
415
416
if(ts->keepon == KEEPON_IGNORE) {
417
/* This means we are currently ignoring a response-body */
418
419
if(ts->cl) {
420
/* A Content-Length based body: simply count down the counter
421
and make sure to break out of the loop when we are done! */
422
ts->cl--;
423
if(ts->cl <= 0) {
424
ts->keepon = KEEPON_DONE;
425
break;
426
}
427
}
428
else if(ts->chunked_encoding) {
429
/* chunked-encoded body, so we need to do the chunked dance
430
properly to know when the end of the body is reached */
431
size_t consumed = 0;
432
433
/* now parse the chunked piece of data so that we can
434
properly tell when the stream ends */
435
result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
436
if(result)
437
return result;
438
if(Curl_httpchunk_is_done(data, &ts->ch)) {
439
/* we are done reading chunks! */
440
infof(data, "chunk reading DONE");
441
ts->keepon = KEEPON_DONE;
442
}
443
}
444
continue;
445
}
446
447
if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) {
448
failf(data, "CONNECT response too large");
449
return CURLE_RECV_ERROR;
450
}
451
452
/* if this is not the end of a header line then continue */
453
if(byte != 0x0a)
454
continue;
455
456
ts->headerlines++;
457
linep = curlx_dyn_ptr(&ts->rcvbuf);
458
line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
459
460
/* output debug if that is requested */
461
Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
462
463
/* send the header to the callback */
464
writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
465
(ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
466
result = Curl_client_write(data, writetype, linep, line_len);
467
if(result)
468
return result;
469
470
result = Curl_bump_headersize(data, line_len, TRUE);
471
if(result)
472
return result;
473
474
/* Newlines are CRLF, so the CR is ignored as the line is not
475
really terminated until the LF comes. Treat a following CR
476
as end-of-headers as well.*/
477
478
if(('\r' == linep[0]) ||
479
('\n' == linep[0])) {
480
/* end of response-headers from the proxy */
481
482
if((407 == k->httpcode) && !data->state.authproblem) {
483
/* If we get a 407 response code with content length
484
when we have no auth problem, we must ignore the
485
whole response-body */
486
ts->keepon = KEEPON_IGNORE;
487
488
if(ts->cl) {
489
infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl);
490
}
491
else if(ts->chunked_encoding) {
492
infof(data, "Ignore chunked response-body");
493
}
494
else {
495
/* without content-length or chunked encoding, we
496
cannot keep the connection alive since the close is
497
the end signal so we bail out at once instead */
498
CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
499
ts->keepon = KEEPON_DONE;
500
}
501
}
502
else {
503
ts->keepon = KEEPON_DONE;
504
}
505
506
DEBUGASSERT(ts->keepon == KEEPON_IGNORE
507
|| ts->keepon == KEEPON_DONE);
508
continue;
509
}
510
511
result = on_resp_header(cf, data, ts, linep);
512
if(result)
513
return result;
514
515
curlx_dyn_reset(&ts->rcvbuf);
516
} /* while there is buffer left and loop is requested */
517
518
if(error)
519
result = CURLE_RECV_ERROR;
520
*done = (ts->keepon == KEEPON_DONE);
521
if(!result && *done && data->info.httpproxycode/100 != 2) {
522
/* Deal with the possibly already received authenticate
523
headers. 'newurl' is set to a new URL if we must loop. */
524
result = Curl_http_auth_act(data);
525
}
526
return result;
527
}
528
529
static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
530
struct Curl_easy *data,
531
struct h1_tunnel_state *ts)
532
{
533
struct connectdata *conn = cf->conn;
534
CURLcode result;
535
bool done;
536
537
if(tunnel_is_established(ts))
538
return CURLE_OK;
539
if(tunnel_is_failed(ts))
540
return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
541
542
do {
543
timediff_t check;
544
545
check = Curl_timeleft(data, NULL, TRUE);
546
if(check <= 0) {
547
failf(data, "Proxy CONNECT aborted due to timeout");
548
result = CURLE_OPERATION_TIMEDOUT;
549
goto out;
550
}
551
552
switch(ts->tunnel_state) {
553
case H1_TUNNEL_INIT:
554
/* Prepare the CONNECT request and make a first attempt to send. */
555
CURL_TRC_CF(data, cf, "CONNECT start");
556
result = start_CONNECT(cf, data, ts);
557
if(result)
558
goto out;
559
h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
560
FALLTHROUGH();
561
562
case H1_TUNNEL_CONNECT:
563
/* see that the request is completely sent */
564
CURL_TRC_CF(data, cf, "CONNECT send");
565
result = send_CONNECT(cf, data, ts, &done);
566
if(result || !done)
567
goto out;
568
h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
569
FALLTHROUGH();
570
571
case H1_TUNNEL_RECEIVE:
572
/* read what is there */
573
CURL_TRC_CF(data, cf, "CONNECT receive");
574
result = recv_CONNECT_resp(cf, data, ts, &done);
575
if(Curl_pgrsUpdate(data)) {
576
result = CURLE_ABORTED_BY_CALLBACK;
577
goto out;
578
}
579
/* error or not complete yet. return for more multi-multi */
580
if(result || !done)
581
goto out;
582
/* got it */
583
h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
584
FALLTHROUGH();
585
586
case H1_TUNNEL_RESPONSE:
587
CURL_TRC_CF(data, cf, "CONNECT response");
588
if(data->req.newurl) {
589
/* not the "final" response, we need to do a follow up request.
590
* If the other side indicated a connection close, or if someone
591
* else told us to close this connection, do so now.
592
*/
593
Curl_req_soft_reset(&data->req, data);
594
if(ts->close_connection || conn->bits.close) {
595
/* Close this filter and the sub-chain, re-connect the
596
* sub-chain and continue. Closing this filter will
597
* reset our tunnel state. To avoid recursion, we return
598
* and expect to be called again.
599
*/
600
CURL_TRC_CF(data, cf, "CONNECT need to close+open");
601
infof(data, "Connect me again please");
602
Curl_conn_cf_close(cf, data);
603
connkeep(conn, "HTTP proxy CONNECT");
604
result = Curl_conn_cf_connect(cf->next, data, &done);
605
goto out;
606
}
607
else {
608
/* staying on this connection, reset state */
609
h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
610
}
611
}
612
break;
613
614
default:
615
break;
616
}
617
618
} while(data->req.newurl);
619
620
DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
621
if(data->info.httpproxycode/100 != 2) {
622
/* a non-2xx response and we have no next URL to try. */
623
Curl_safefree(data->req.newurl);
624
/* failure, close this connection to avoid reuse */
625
streamclose(conn, "proxy CONNECT failure");
626
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
627
failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
628
return CURLE_RECV_ERROR;
629
}
630
/* 2xx response, SUCCESS! */
631
h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
632
infof(data, "CONNECT tunnel established, response %d",
633
data->info.httpproxycode);
634
result = CURLE_OK;
635
636
out:
637
if(result)
638
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
639
return result;
640
}
641
642
static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
643
struct Curl_easy *data,
644
bool *done)
645
{
646
CURLcode result;
647
struct h1_tunnel_state *ts = cf->ctx;
648
649
if(cf->connected) {
650
*done = TRUE;
651
return CURLE_OK;
652
}
653
654
CURL_TRC_CF(data, cf, "connect");
655
result = cf->next->cft->do_connect(cf->next, data, done);
656
if(result || !*done)
657
return result;
658
659
*done = FALSE;
660
if(!ts) {
661
result = tunnel_init(cf, data, &ts);
662
if(result)
663
return result;
664
cf->ctx = ts;
665
}
666
667
/* We want "seamless" operations through HTTP proxy tunnel */
668
669
result = H1_CONNECT(cf, data, ts);
670
if(result)
671
goto out;
672
Curl_safefree(data->state.aptr.proxyuserpwd);
673
674
out:
675
*done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
676
if(*done) {
677
cf->connected = TRUE;
678
/* The real request will follow the CONNECT, reset request partially */
679
Curl_req_soft_reset(&data->req, data);
680
Curl_client_reset(data);
681
Curl_pgrsSetUploadCounter(data, 0);
682
Curl_pgrsSetDownloadCounter(data, 0);
683
684
tunnel_free(cf, data);
685
}
686
return result;
687
}
688
689
static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
690
struct Curl_easy *data,
691
struct easy_pollset *ps)
692
{
693
struct h1_tunnel_state *ts = cf->ctx;
694
695
if(!cf->connected) {
696
/* If we are not connected, but the filter "below" is
697
* and not waiting on something, we are tunneling. */
698
curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
699
if(ts) {
700
/* when we have sent a CONNECT to a proxy, we should rather either
701
wait for the socket to become readable to be able to get the
702
response headers or if we are still sending the request, wait
703
for write. */
704
if(tunnel_want_send(ts))
705
Curl_pollset_set_out_only(data, ps, sock);
706
else
707
Curl_pollset_set_in_only(data, ps, sock);
708
}
709
else
710
Curl_pollset_set_out_only(data, ps, sock);
711
}
712
}
713
714
static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
715
struct Curl_easy *data)
716
{
717
CURL_TRC_CF(data, cf, "destroy");
718
tunnel_free(cf, data);
719
}
720
721
static void cf_h1_proxy_close(struct Curl_cfilter *cf,
722
struct Curl_easy *data)
723
{
724
CURL_TRC_CF(data, cf, "close");
725
if(cf) {
726
cf->connected = FALSE;
727
if(cf->ctx) {
728
h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
729
}
730
if(cf->next)
731
cf->next->cft->do_close(cf->next, data);
732
}
733
}
734
735
736
struct Curl_cftype Curl_cft_h1_proxy = {
737
"H1-PROXY",
738
CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
739
0,
740
cf_h1_proxy_destroy,
741
cf_h1_proxy_connect,
742
cf_h1_proxy_close,
743
Curl_cf_def_shutdown,
744
Curl_cf_http_proxy_get_host,
745
cf_h1_proxy_adjust_pollset,
746
Curl_cf_def_data_pending,
747
Curl_cf_def_send,
748
Curl_cf_def_recv,
749
Curl_cf_def_cntrl,
750
Curl_cf_def_conn_is_alive,
751
Curl_cf_def_conn_keep_alive,
752
Curl_cf_def_query,
753
};
754
755
CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
756
struct Curl_easy *data)
757
{
758
struct Curl_cfilter *cf;
759
CURLcode result;
760
761
(void)data;
762
result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
763
if(!result)
764
Curl_conn_cf_insert_after(cf_at, cf);
765
return result;
766
}
767
768
#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */
769
770