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