cfilters.c 18 KB

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