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 #include "urldata.h"
28 #include "strerror.h"
29 #include "cfilters.h"
30 #include "connect.h"
31 #include "url.h" /* for Curl_safefree() */
32 #include "sendf.h"
33 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
34 #include "multiif.h"
35 #include "progress.h"
36 #include "warnless.h"
37
38 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
41 #include "memdebug.h"
42
43 #ifndef ARRAYSIZE
44 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
45 #endif
46
47 #ifdef DEBUGBUILD
48 /* used by unit2600.c */
Curl_cf_def_close(struct Curl_cfilter * cf,struct Curl_easy * data)49 void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
50 {
51 cf->connected = FALSE;
52 if(cf->next)
53 cf->next->cft->do_close(cf->next, data);
54 }
55 #endif
56
57 static void conn_report_connect_stats(struct Curl_easy *data,
58 struct connectdata *conn);
59
Curl_cf_def_get_host(struct Curl_cfilter * cf,struct Curl_easy * data,const char ** phost,const char ** pdisplay_host,int * pport)60 void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
61 const char **phost, const char **pdisplay_host,
62 int *pport)
63 {
64 if(cf->next)
65 cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
66 else {
67 *phost = cf->conn->host.name;
68 *pdisplay_host = cf->conn->host.dispname;
69 *pport = cf->conn->port;
70 }
71 }
72
Curl_cf_def_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)73 int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
74 struct Curl_easy *data,
75 curl_socket_t *socks)
76 {
77 return cf->next?
78 cf->next->cft->get_select_socks(cf->next, data, socks) : 0;
79 }
80
Curl_cf_def_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)81 bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
82 const struct Curl_easy *data)
83 {
84 return cf->next?
85 cf->next->cft->has_data_pending(cf->next, data) : FALSE;
86 }
87
Curl_cf_def_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)88 ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
89 const void *buf, size_t len, CURLcode *err)
90 {
91 return cf->next?
92 cf->next->cft->do_send(cf->next, data, buf, len, err) :
93 CURLE_RECV_ERROR;
94 }
95
Curl_cf_def_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)96 ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
97 char *buf, size_t len, CURLcode *err)
98 {
99 return cf->next?
100 cf->next->cft->do_recv(cf->next, data, buf, len, err) :
101 CURLE_SEND_ERROR;
102 }
103
Curl_cf_def_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)104 bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
105 struct Curl_easy *data,
106 bool *input_pending)
107 {
108 return cf->next?
109 cf->next->cft->is_alive(cf->next, data, input_pending) :
110 FALSE; /* pessimistic in absence of data */
111 }
112
Curl_cf_def_conn_keep_alive(struct Curl_cfilter * cf,struct Curl_easy * data)113 CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
114 struct Curl_easy *data)
115 {
116 return cf->next?
117 cf->next->cft->keep_alive(cf->next, data) :
118 CURLE_OK;
119 }
120
Curl_cf_def_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)121 CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
122 struct Curl_easy *data,
123 int query, int *pres1, void *pres2)
124 {
125 return cf->next?
126 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
127 CURLE_UNKNOWN_OPTION;
128 }
129
Curl_conn_cf_discard_chain(struct Curl_cfilter ** pcf,struct Curl_easy * data)130 void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
131 struct Curl_easy *data)
132 {
133 struct Curl_cfilter *cfn, *cf = *pcf;
134
135 if(cf) {
136 *pcf = NULL;
137 while(cf) {
138 cfn = cf->next;
139 /* prevent destroying filter to mess with its sub-chain, since
140 * we have the reference now and will call destroy on it.
141 */
142 cf->next = NULL;
143 cf->cft->destroy(cf, data);
144 free(cf);
145 cf = cfn;
146 }
147 }
148 }
149
Curl_conn_cf_discard_all(struct Curl_easy * data,struct connectdata * conn,int index)150 void Curl_conn_cf_discard_all(struct Curl_easy *data,
151 struct connectdata *conn, int index)
152 {
153 Curl_conn_cf_discard_chain(&conn->cfilter[index], data);
154 }
155
Curl_conn_close(struct Curl_easy * data,int index)156 void Curl_conn_close(struct Curl_easy *data, int index)
157 {
158 struct Curl_cfilter *cf;
159
160 DEBUGASSERT(data->conn);
161 /* it is valid to call that without filters being present */
162 cf = data->conn->cfilter[index];
163 if(cf) {
164 cf->cft->do_close(cf, data);
165 }
166 }
167
Curl_conn_recv(struct Curl_easy * data,int num,char * buf,size_t len,CURLcode * code)168 ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
169 size_t len, CURLcode *code)
170 {
171 struct Curl_cfilter *cf;
172
173 DEBUGASSERT(data);
174 DEBUGASSERT(data->conn);
175 cf = data->conn->cfilter[num];
176 while(cf && !cf->connected) {
177 cf = cf->next;
178 }
179 if(cf) {
180 return cf->cft->do_recv(cf, data, buf, len, code);
181 }
182 failf(data, "recv: no filter connected");
183 *code = CURLE_FAILED_INIT;
184 return -1;
185 }
186
Curl_conn_send(struct Curl_easy * data,int num,const void * mem,size_t len,CURLcode * code)187 ssize_t Curl_conn_send(struct Curl_easy *data, int num,
188 const void *mem, size_t len, CURLcode *code)
189 {
190 struct Curl_cfilter *cf;
191
192 DEBUGASSERT(data);
193 DEBUGASSERT(data->conn);
194 cf = data->conn->cfilter[num];
195 while(cf && !cf->connected) {
196 cf = cf->next;
197 }
198 if(cf) {
199 return cf->cft->do_send(cf, data, mem, len, code);
200 }
201 failf(data, "send: no filter connected");
202 DEBUGASSERT(0);
203 *code = CURLE_FAILED_INIT;
204 return -1;
205 }
206
Curl_cf_create(struct Curl_cfilter ** pcf,const struct Curl_cftype * cft,void * ctx)207 CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
208 const struct Curl_cftype *cft,
209 void *ctx)
210 {
211 struct Curl_cfilter *cf;
212 CURLcode result = CURLE_OUT_OF_MEMORY;
213
214 DEBUGASSERT(cft);
215 cf = calloc(sizeof(*cf), 1);
216 if(!cf)
217 goto out;
218
219 cf->cft = cft;
220 cf->ctx = ctx;
221 result = CURLE_OK;
222 out:
223 *pcf = cf;
224 return result;
225 }
226
Curl_conn_cf_add(struct Curl_easy * data,struct connectdata * conn,int index,struct Curl_cfilter * cf)227 void Curl_conn_cf_add(struct Curl_easy *data,
228 struct connectdata *conn,
229 int index,
230 struct Curl_cfilter *cf)
231 {
232 (void)data;
233 DEBUGASSERT(conn);
234 DEBUGASSERT(!cf->conn);
235 DEBUGASSERT(!cf->next);
236
237 cf->next = conn->cfilter[index];
238 cf->conn = conn;
239 cf->sockindex = index;
240 conn->cfilter[index] = cf;
241 CURL_TRC_CF(data, cf, "added");
242 }
243
Curl_conn_cf_insert_after(struct Curl_cfilter * cf_at,struct Curl_cfilter * cf_new)244 void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
245 struct Curl_cfilter *cf_new)
246 {
247 struct Curl_cfilter *tail, **pnext;
248
249 DEBUGASSERT(cf_at);
250 DEBUGASSERT(cf_new);
251 DEBUGASSERT(!cf_new->conn);
252
253 tail = cf_at->next;
254 cf_at->next = cf_new;
255 do {
256 cf_new->conn = cf_at->conn;
257 cf_new->sockindex = cf_at->sockindex;
258 pnext = &cf_new->next;
259 cf_new = cf_new->next;
260 } while(cf_new);
261 *pnext = tail;
262 }
263
Curl_conn_cf_discard_sub(struct Curl_cfilter * cf,struct Curl_cfilter * discard,struct Curl_easy * data,bool destroy_always)264 bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf,
265 struct Curl_cfilter *discard,
266 struct Curl_easy *data,
267 bool destroy_always)
268 {
269 struct Curl_cfilter **pprev = &cf->next;
270 bool found = FALSE;
271
272 /* remove from sub-chain and destroy */
273 DEBUGASSERT(cf);
274 while(*pprev) {
275 if(*pprev == cf) {
276 *pprev = discard->next;
277 discard->next = NULL;
278 found = TRUE;
279 break;
280 }
281 pprev = &((*pprev)->next);
282 }
283 if(found || destroy_always) {
284 discard->next = NULL;
285 discard->cft->destroy(discard, data);
286 free(discard);
287 }
288 return found;
289 }
290
Curl_conn_cf_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)291 CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
292 struct Curl_easy *data,
293 bool blocking, bool *done)
294 {
295 if(cf)
296 return cf->cft->do_connect(cf, data, blocking, done);
297 return CURLE_FAILED_INIT;
298 }
299
Curl_conn_cf_close(struct Curl_cfilter * cf,struct Curl_easy * data)300 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
301 {
302 if(cf)
303 cf->cft->do_close(cf, data);
304 }
305
Curl_conn_cf_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)306 int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
307 struct Curl_easy *data,
308 curl_socket_t *socks)
309 {
310 if(cf)
311 return cf->cft->get_select_socks(cf, data, socks);
312 return 0;
313 }
314
Curl_conn_cf_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)315 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
316 const void *buf, size_t len, CURLcode *err)
317 {
318 if(cf)
319 return cf->cft->do_send(cf, data, buf, len, err);
320 *err = CURLE_SEND_ERROR;
321 return -1;
322 }
323
Curl_conn_cf_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)324 ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
325 char *buf, size_t len, CURLcode *err)
326 {
327 if(cf)
328 return cf->cft->do_recv(cf, data, buf, len, err);
329 *err = CURLE_RECV_ERROR;
330 return -1;
331 }
332
Curl_conn_connect(struct Curl_easy * data,int sockindex,bool blocking,bool * done)333 CURLcode Curl_conn_connect(struct Curl_easy *data,
334 int sockindex,
335 bool blocking,
336 bool *done)
337 {
338 struct Curl_cfilter *cf;
339 CURLcode result = CURLE_OK;
340
341 DEBUGASSERT(data);
342 DEBUGASSERT(data->conn);
343
344 cf = data->conn->cfilter[sockindex];
345 DEBUGASSERT(cf);
346 if(!cf)
347 return CURLE_FAILED_INIT;
348
349 *done = cf->connected;
350 if(!*done) {
351 result = cf->cft->do_connect(cf, data, blocking, done);
352 if(!result && *done) {
353 Curl_conn_ev_update_info(data, data->conn);
354 conn_report_connect_stats(data, data->conn);
355 data->conn->keepalive = Curl_now();
356 }
357 else if(result) {
358 conn_report_connect_stats(data, data->conn);
359 }
360 }
361
362 return result;
363 }
364
Curl_conn_is_connected(struct connectdata * conn,int sockindex)365 bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
366 {
367 struct Curl_cfilter *cf;
368
369 cf = conn->cfilter[sockindex];
370 return cf && cf->connected;
371 }
372
Curl_conn_is_ip_connected(struct Curl_easy * data,int sockindex)373 bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
374 {
375 struct Curl_cfilter *cf;
376
377 cf = data->conn->cfilter[sockindex];
378 while(cf) {
379 if(cf->connected)
380 return TRUE;
381 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
382 return FALSE;
383 cf = cf->next;
384 }
385 return FALSE;
386 }
387
Curl_conn_cf_is_ssl(struct Curl_cfilter * cf)388 bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
389 {
390 for(; cf; cf = cf->next) {
391 if(cf->cft->flags & CF_TYPE_SSL)
392 return TRUE;
393 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
394 return FALSE;
395 }
396 return FALSE;
397 }
398
Curl_conn_is_ssl(struct connectdata * conn,int sockindex)399 bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
400 {
401 return conn? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
402 }
403
Curl_conn_is_multiplex(struct connectdata * conn,int sockindex)404 bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
405 {
406 struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
407
408 for(; cf; cf = cf->next) {
409 if(cf->cft->flags & CF_TYPE_MULTIPLEX)
410 return TRUE;
411 if(cf->cft->flags & CF_TYPE_IP_CONNECT
412 || cf->cft->flags & CF_TYPE_SSL)
413 return FALSE;
414 }
415 return FALSE;
416 }
417
Curl_conn_data_pending(struct Curl_easy * data,int sockindex)418 bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
419 {
420 struct Curl_cfilter *cf;
421
422 (void)data;
423 DEBUGASSERT(data);
424 DEBUGASSERT(data->conn);
425
426 cf = data->conn->cfilter[sockindex];
427 while(cf && !cf->connected) {
428 cf = cf->next;
429 }
430 if(cf) {
431 return cf->cft->has_data_pending(cf, data);
432 }
433 return FALSE;
434 }
435
Curl_conn_get_select_socks(struct Curl_easy * data,int sockindex,curl_socket_t * socks)436 int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
437 curl_socket_t *socks)
438 {
439 struct Curl_cfilter *cf;
440
441 DEBUGASSERT(data);
442 DEBUGASSERT(data->conn);
443 cf = data->conn->cfilter[sockindex];
444
445 /* if the next one is not yet connected, that's the one we want */
446 while(cf && cf->next && !cf->next->connected)
447 cf = cf->next;
448 if(cf) {
449 return cf->cft->get_select_socks(cf, data, socks);
450 }
451 return GETSOCK_BLANK;
452 }
453
Curl_conn_get_host(struct Curl_easy * data,int sockindex,const char ** phost,const char ** pdisplay_host,int * pport)454 void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
455 const char **phost, const char **pdisplay_host,
456 int *pport)
457 {
458 struct Curl_cfilter *cf;
459
460 DEBUGASSERT(data->conn);
461 cf = data->conn->cfilter[sockindex];
462 if(cf) {
463 cf->cft->get_host(cf, data, phost, pdisplay_host, pport);
464 }
465 else {
466 /* Some filter ask during shutdown for this, mainly for debugging
467 * purposes. We hand out the defaults, however this is not always
468 * accurate, as the connection might be tunneled, etc. But all that
469 * state is already gone here. */
470 *phost = data->conn->host.name;
471 *pdisplay_host = data->conn->host.dispname;
472 *pport = data->conn->remote_port;
473 }
474 }
475
Curl_cf_def_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)476 CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
477 struct Curl_easy *data,
478 int event, int arg1, void *arg2)
479 {
480 (void)cf;
481 (void)data;
482 (void)event;
483 (void)arg1;
484 (void)arg2;
485 return CURLE_OK;
486 }
487
Curl_conn_cf_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,bool ignore_result,int event,int arg1,void * arg2)488 CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
489 struct Curl_easy *data,
490 bool ignore_result,
491 int event, int arg1, void *arg2)
492 {
493 CURLcode result = CURLE_OK;
494
495 for(; cf; cf = cf->next) {
496 if(Curl_cf_def_cntrl == cf->cft->cntrl)
497 continue;
498 result = cf->cft->cntrl(cf, data, event, arg1, arg2);
499 if(!ignore_result && result)
500 break;
501 }
502 return result;
503 }
504
Curl_conn_cf_get_socket(struct Curl_cfilter * cf,struct Curl_easy * data)505 curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
506 struct Curl_easy *data)
507 {
508 curl_socket_t sock;
509 if(cf && !cf->cft->query(cf, data, CF_QUERY_SOCKET, NULL, &sock))
510 return sock;
511 return CURL_SOCKET_BAD;
512 }
513
Curl_conn_get_socket(struct Curl_easy * data,int sockindex)514 curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
515 {
516 struct Curl_cfilter *cf;
517
518 cf = data->conn? data->conn->cfilter[sockindex] : NULL;
519 /* if the top filter has not connected, ask it (and its sub-filters)
520 * for the socket. Otherwise conn->sock[sockindex] should have it.
521 */
522 if(cf && !cf->connected)
523 return Curl_conn_cf_get_socket(cf, data);
524 return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
525 }
526
cf_cntrl_all(struct connectdata * conn,struct Curl_easy * data,bool ignore_result,int event,int arg1,void * arg2)527 static CURLcode cf_cntrl_all(struct connectdata *conn,
528 struct Curl_easy *data,
529 bool ignore_result,
530 int event, int arg1, void *arg2)
531 {
532 CURLcode result = CURLE_OK;
533 size_t i;
534
535 for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
536 result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
537 event, arg1, arg2);
538 if(!ignore_result && result)
539 break;
540 }
541 return result;
542 }
543
Curl_conn_ev_data_attach(struct connectdata * conn,struct Curl_easy * data)544 void Curl_conn_ev_data_attach(struct connectdata *conn,
545 struct Curl_easy *data)
546 {
547 cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL);
548 }
549
Curl_conn_ev_data_detach(struct connectdata * conn,struct Curl_easy * data)550 void Curl_conn_ev_data_detach(struct connectdata *conn,
551 struct Curl_easy *data)
552 {
553 cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL);
554 }
555
Curl_conn_ev_data_setup(struct Curl_easy * data)556 CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
557 {
558 return cf_cntrl_all(data->conn, data, FALSE,
559 CF_CTRL_DATA_SETUP, 0, NULL);
560 }
561
Curl_conn_ev_data_idle(struct Curl_easy * data)562 CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
563 {
564 return cf_cntrl_all(data->conn, data, FALSE,
565 CF_CTRL_DATA_IDLE, 0, NULL);
566 }
567
568 /**
569 * Notify connection filters that the transfer represented by `data`
570 * is donw with sending data (e.g. has uploaded everything).
571 */
Curl_conn_ev_data_done_send(struct Curl_easy * data)572 void Curl_conn_ev_data_done_send(struct Curl_easy *data)
573 {
574 cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, 0, NULL);
575 }
576
577 /**
578 * Notify connection filters that the transfer represented by `data`
579 * is finished - eventually premature, e.g. before being complete.
580 */
Curl_conn_ev_data_done(struct Curl_easy * data,bool premature)581 void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature)
582 {
583 cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE, premature, NULL);
584 }
585
Curl_conn_ev_data_pause(struct Curl_easy * data,bool do_pause)586 CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
587 {
588 return cf_cntrl_all(data->conn, data, FALSE,
589 CF_CTRL_DATA_PAUSE, do_pause, NULL);
590 }
591
Curl_conn_ev_update_info(struct Curl_easy * data,struct connectdata * conn)592 void Curl_conn_ev_update_info(struct Curl_easy *data,
593 struct connectdata *conn)
594 {
595 cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
596 }
597
598 /**
599 * Update connection statistics
600 */
conn_report_connect_stats(struct Curl_easy * data,struct connectdata * conn)601 static void conn_report_connect_stats(struct Curl_easy *data,
602 struct connectdata *conn)
603 {
604 struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
605 if(cf) {
606 struct curltime connected;
607 struct curltime appconnected;
608
609 memset(&connected, 0, sizeof(connected));
610 cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
611 if(connected.tv_sec || connected.tv_usec)
612 Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
613
614 memset(&appconnected, 0, sizeof(appconnected));
615 cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
616 if(appconnected.tv_sec || appconnected.tv_usec)
617 Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
618 }
619 }
620
Curl_conn_is_alive(struct Curl_easy * data,struct connectdata * conn,bool * input_pending)621 bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
622 bool *input_pending)
623 {
624 struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
625 return cf && !cf->conn->bits.close &&
626 cf->cft->is_alive(cf, data, input_pending);
627 }
628
Curl_conn_keep_alive(struct Curl_easy * data,struct connectdata * conn,int sockindex)629 CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
630 struct connectdata *conn,
631 int sockindex)
632 {
633 struct Curl_cfilter *cf = conn->cfilter[sockindex];
634 return cf? cf->cft->keep_alive(cf, data) : CURLE_OK;
635 }
636
Curl_conn_get_max_concurrent(struct Curl_easy * data,struct connectdata * conn,int sockindex)637 size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
638 struct connectdata *conn,
639 int sockindex)
640 {
641 CURLcode result;
642 int n = 0;
643
644 struct Curl_cfilter *cf = conn->cfilter[sockindex];
645 result = cf? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT,
646 &n, NULL) : CURLE_UNKNOWN_OPTION;
647 return (result || n <= 0)? 1 : (size_t)n;
648 }
649