1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "net.h"
5 #include "time-util.h"
6 #include "str.h"
7 #include "hash.h"
8 #include "array.h"
9 #include "llist.h"
10 #include "istream.h"
11 #include "ostream.h"
12 #include "iostream-ssl.h"
13 #include "http-response-parser.h"
14
15 #include "http-client-private.h"
16
17 static void
18 http_client_peer_connect_backoff(struct http_client_peer *peer);
19
20 static void
21 http_client_peer_shared_connection_success(
22 struct http_client_peer_shared *pshared);
23 static void
24 http_client_peer_shared_connection_failure(
25 struct http_client_peer_shared *pshared);
26 static void
27 http_client_peer_connection_succeeded_pool(struct http_client_peer *peer);
28 static void
29 http_client_peer_connection_failed_pool(struct http_client_peer *peer,
30 const char *reason);
31
32 /*
33 * Peer address
34 */
35
36 unsigned int ATTR_NO_SANITIZE_INTEGER
http_client_peer_addr_hash(const struct http_client_peer_addr * peer)37 http_client_peer_addr_hash(const struct http_client_peer_addr *peer)
38 {
39 unsigned int hash = (unsigned int)peer->type;
40
41 switch (peer->type) {
42 case HTTP_CLIENT_PEER_ADDR_HTTPS:
43 case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
44 if (peer->a.tcp.https_name != NULL)
45 hash += str_hash(peer->a.tcp.https_name);
46 /* fall through */
47 case HTTP_CLIENT_PEER_ADDR_RAW:
48 case HTTP_CLIENT_PEER_ADDR_HTTP:
49 if (peer->a.tcp.ip.family != 0)
50 hash += net_ip_hash(&peer->a.tcp.ip);
51 hash += peer->a.tcp.port;
52 break;
53 case HTTP_CLIENT_PEER_ADDR_UNIX:
54 hash += str_hash(peer->a.un.path);
55 break;
56 }
57
58 return hash;
59 }
60
http_client_peer_addr_cmp(const struct http_client_peer_addr * peer1,const struct http_client_peer_addr * peer2)61 int http_client_peer_addr_cmp(const struct http_client_peer_addr *peer1,
62 const struct http_client_peer_addr *peer2)
63 {
64 int ret;
65
66 if (peer1->type != peer2->type)
67 return (peer1->type > peer2->type ? 1 : -1);
68 switch (peer1->type) {
69 case HTTP_CLIENT_PEER_ADDR_RAW:
70 case HTTP_CLIENT_PEER_ADDR_HTTP:
71 case HTTP_CLIENT_PEER_ADDR_HTTPS:
72 case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
73 /* Queues are created with peer addresses that have an
74 uninitialized IP value, because that is assigned later when
75 the host lookup completes. In all other other contexts, the
76 IP is always initialized, so we do not compare IPs when one
77 of them is unassigned. */
78 if (peer1->a.tcp.ip.family != 0 &&
79 peer2->a.tcp.ip.family != 0 &&
80 (ret = net_ip_cmp(&peer1->a.tcp.ip, &peer2->a.tcp.ip)) != 0)
81 return ret;
82 if (peer1->a.tcp.port != peer2->a.tcp.port)
83 return (peer1->a.tcp.port > peer2->a.tcp.port ? 1 : -1);
84 if (peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS &&
85 peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL)
86 return 0;
87 return null_strcmp(peer1->a.tcp.https_name,
88 peer2->a.tcp.https_name);
89 case HTTP_CLIENT_PEER_ADDR_UNIX:
90 return null_strcmp(peer1->a.un.path, peer2->a.un.path);
91 }
92 i_unreached();
93 }
94
95 /*
96 * Peer pool
97 */
98
99 static struct http_client_peer_pool *
http_client_peer_pool_create(struct http_client_peer_shared * pshared,struct ssl_iostream_context * ssl_ctx,const char * rawlog_dir)100 http_client_peer_pool_create(struct http_client_peer_shared *pshared,
101 struct ssl_iostream_context *ssl_ctx,
102 const char *rawlog_dir)
103 {
104 struct http_client_peer_pool *ppool;
105
106 ppool = i_new(struct http_client_peer_pool, 1);
107 ppool->refcount = 1;
108 ppool->peer = pshared;
109 ppool->event = event_create(pshared->cctx->event);
110 event_set_append_log_prefix(
111 ppool->event,
112 t_strdup_printf("peer %s: ",
113 http_client_peer_shared_label(pshared)));
114
115 http_client_peer_shared_ref(pshared);
116
117 i_array_init(&ppool->conns, 16);
118 i_array_init(&ppool->pending_conns, 16);
119 i_array_init(&ppool->idle_conns, 16);
120
121 DLLIST_PREPEND(&pshared->pools_list, ppool);
122
123 ppool->ssl_ctx = ssl_ctx;
124 ppool->rawlog_dir = i_strdup(rawlog_dir);
125
126 e_debug(ppool->event, "Peer pool created");
127
128 return ppool;
129 }
130
http_client_peer_pool_ref(struct http_client_peer_pool * ppool)131 void http_client_peer_pool_ref(struct http_client_peer_pool *ppool)
132 {
133 if (ppool->destroyed)
134 return;
135 ppool->refcount++;
136 }
137
http_client_peer_pool_close(struct http_client_peer_pool ** _ppool)138 void http_client_peer_pool_close(struct http_client_peer_pool **_ppool)
139 {
140 struct http_client_peer_pool *ppool = *_ppool;
141 struct http_client_connection *conn;
142 ARRAY_TYPE(http_client_connection) conns;
143
144 http_client_peer_pool_ref(ppool);
145
146 /* Make a copy of the connection array; freed connections modify it */
147 t_array_init(&conns, array_count(&ppool->conns));
148 array_copy(&conns.arr, 0, &ppool->conns.arr, 0,
149 array_count(&ppool->conns));
150 array_foreach_elem(&conns, conn)
151 http_client_connection_unref(&conn);
152 i_assert(array_count(&ppool->idle_conns) == 0);
153 i_assert(array_count(&ppool->pending_conns) == 0);
154 i_assert(array_count(&ppool->conns) == 0);
155
156 http_client_peer_pool_unref(_ppool);
157 }
158
http_client_peer_pool_unref(struct http_client_peer_pool ** _ppool)159 void http_client_peer_pool_unref(struct http_client_peer_pool **_ppool)
160 {
161 struct http_client_peer_pool *ppool = *_ppool;
162 struct http_client_peer_shared *pshared = ppool->peer;
163
164 *_ppool = NULL;
165
166 if (ppool->destroyed)
167 return;
168
169 i_assert(ppool->refcount > 0);
170 if (--ppool->refcount > 0)
171 return;
172
173 e_debug(ppool->event, "Peer pool destroy");
174 ppool->destroyed = TRUE;
175
176 i_assert(array_count(&ppool->idle_conns) == 0);
177 i_assert(array_count(&ppool->conns) == 0);
178 array_free(&ppool->idle_conns);
179 array_free(&ppool->pending_conns);
180 array_free(&ppool->conns);
181
182 DLLIST_REMOVE(&pshared->pools_list, ppool);
183
184 event_unref(&ppool->event);
185 i_free(ppool->rawlog_dir);
186 i_free(ppool);
187 http_client_peer_shared_unref(&pshared);
188 }
189
190 static struct http_client_peer_pool *
http_client_peer_pool_get(struct http_client_peer_shared * pshared,struct http_client * client)191 http_client_peer_pool_get(struct http_client_peer_shared *pshared,
192 struct http_client *client)
193 {
194 struct http_client_peer_pool *ppool;
195 struct ssl_iostream_context *ssl_ctx = client->ssl_ctx;
196 const char *rawlog_dir = client->set.rawlog_dir;
197
198 i_assert(!http_client_peer_addr_is_https(&pshared->addr) ||
199 ssl_ctx != NULL);
200
201 ppool = pshared->pools_list;
202 while (ppool != NULL) {
203 if (ppool->ssl_ctx == ssl_ctx &&
204 null_strcmp(ppool->rawlog_dir, rawlog_dir) == 0)
205 break;
206 ppool = ppool->next;
207 }
208
209 if (ppool == NULL) {
210 ppool = http_client_peer_pool_create(pshared, ssl_ctx,
211 rawlog_dir);
212 } else {
213 e_debug(ppool->event, "Peer pool reused");
214 http_client_peer_pool_ref(ppool);
215 }
216
217 return ppool;
218 }
219
220 static void
http_client_peer_pool_connection_success(struct http_client_peer_pool * ppool)221 http_client_peer_pool_connection_success(struct http_client_peer_pool *ppool)
222 {
223 e_debug(ppool->event, "Successfully connected "
224 "(%u connections exist, %u pending)",
225 array_count(&ppool->conns), array_count(&ppool->pending_conns));
226
227 http_client_peer_shared_connection_success(ppool->peer);
228
229 if (array_count(&ppool->pending_conns) > 0) {
230 /* If there are other connections attempting to connect, wait
231 for them before notifying other peer objects about the
232 success (which may be premature). */
233 } else {
234 struct http_client_peer *peer;
235
236 /* This was the only/last connection and connecting to it
237 succeeded. notify all interested peers in this pool about the
238 success */
239 peer = ppool->peer->peers_list;
240 while (peer != NULL) {
241 struct http_client_peer *peer_next = peer->shared_next;
242 if (peer->ppool == ppool)
243 http_client_peer_connection_succeeded_pool(peer);
244 peer = peer_next;
245 }
246 }
247 }
248
249 static void
http_client_peer_pool_connection_failure(struct http_client_peer_pool * ppool,const char * reason)250 http_client_peer_pool_connection_failure(struct http_client_peer_pool *ppool,
251 const char *reason)
252 {
253 e_debug(ppool->event,
254 "Failed to make connection "
255 "(%u connections exist, %u pending)",
256 array_count(&ppool->conns), array_count(&ppool->pending_conns));
257
258 http_client_peer_shared_connection_failure(ppool->peer);
259
260 if (array_count(&ppool->pending_conns) > 0) {
261 /* If there are other connections attempting to connect, wait
262 for them before failing the requests. remember that we had
263 trouble with connecting so in future we don't try to create
264 more than one connection until connects work again. */
265 } else {
266 struct http_client_peer *peer;
267
268 /* This was the only/last connection and connecting to it
269 failed. notify all interested peers in this pool about the
270 failure */
271 peer = ppool->peer->peers_list;
272 while (peer != NULL) {
273 struct http_client_peer *peer_next = peer->shared_next;
274 if (peer->ppool == ppool)
275 http_client_peer_connection_failed_pool(peer, reason);
276 peer = peer_next;
277 }
278 }
279 }
280
281 /*
282 * Peer (shared)
283 */
284
285 static struct http_client_peer_shared *
http_client_peer_shared_create(struct http_client_context * cctx,const struct http_client_peer_addr * addr)286 http_client_peer_shared_create(struct http_client_context *cctx,
287 const struct http_client_peer_addr *addr)
288 {
289 struct http_client_peer_shared *pshared;
290
291 pshared = i_new(struct http_client_peer_shared, 1);
292 pshared->refcount = 1;
293 pshared->cctx = cctx;
294
295 pshared->addr = *addr;
296 switch (addr->type) {
297 case HTTP_CLIENT_PEER_ADDR_RAW:
298 case HTTP_CLIENT_PEER_ADDR_HTTP:
299 i_assert(pshared->addr.a.tcp.ip.family != 0);
300 break;
301 case HTTP_CLIENT_PEER_ADDR_HTTPS:
302 case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
303 i_assert(pshared->addr.a.tcp.ip.family != 0);
304 pshared->addr_name = i_strdup(addr->a.tcp.https_name);
305 pshared->addr.a.tcp.https_name = pshared->addr_name;
306 break;
307 case HTTP_CLIENT_PEER_ADDR_UNIX:
308 pshared->addr_name = i_strdup(addr->a.un.path);
309 pshared->addr.a.un.path = pshared->addr_name;
310 break;
311 default:
312 break;
313 }
314 pshared->event = event_create(cctx->event);
315 event_set_append_log_prefix(
316 pshared->event,
317 t_strdup_printf("peer %s (shared): ",
318 http_client_peer_shared_label(pshared)));
319
320 hash_table_insert(cctx->peers,
321 (const struct http_client_peer_addr *)&pshared->addr,
322 pshared);
323 DLLIST_PREPEND(&cctx->peers_list, pshared);
324
325 pshared->backoff_initial_time_msecs =
326 cctx->set.connect_backoff_time_msecs;
327 pshared->backoff_max_time_msecs =
328 cctx->set.connect_backoff_max_time_msecs;
329
330 e_debug(pshared->event, "Peer created");
331 return pshared;
332 }
333
http_client_peer_shared_ref(struct http_client_peer_shared * pshared)334 void http_client_peer_shared_ref(struct http_client_peer_shared *pshared)
335 {
336 pshared->refcount++;
337 }
338
http_client_peer_shared_unref(struct http_client_peer_shared ** _pshared)339 void http_client_peer_shared_unref(struct http_client_peer_shared **_pshared)
340 {
341 struct http_client_peer_shared *pshared = *_pshared;
342
343 *_pshared = NULL;
344
345 i_assert(pshared->refcount > 0);
346 if (--pshared->refcount > 0)
347 return;
348
349 e_debug(pshared->event, "Peer destroy");
350
351 i_assert(pshared->pools_list == NULL);
352
353 /* Unlist in client */
354 hash_table_remove(pshared->cctx->peers,
355 (const struct http_client_peer_addr *)&pshared->addr);
356 DLLIST_REMOVE(&pshared->cctx->peers_list, pshared);
357
358 timeout_remove(&pshared->to_backoff);
359
360 event_unref(&pshared->event);
361 i_free(pshared->addr_name);
362 i_free(pshared->label);
363 i_free(pshared);
364 }
365
366 static struct http_client_peer_shared *
http_client_peer_shared_get(struct http_client_context * cctx,const struct http_client_peer_addr * addr)367 http_client_peer_shared_get(struct http_client_context *cctx,
368 const struct http_client_peer_addr *addr)
369 {
370 struct http_client_peer_shared *pshared;
371
372 pshared = hash_table_lookup(cctx->peers, addr);
373 if (pshared == NULL) {
374 pshared = http_client_peer_shared_create(cctx, addr);
375 } else {
376 e_debug(pshared->event, "Peer reused");
377 http_client_peer_shared_ref(pshared);
378 }
379
380 return pshared;
381 }
382
http_client_peer_shared_close(struct http_client_peer_shared ** _pshared)383 void http_client_peer_shared_close(struct http_client_peer_shared **_pshared)
384 {
385 struct http_client_peer_shared *pshared = *_pshared;
386 struct http_client_peer_pool *pool, *next;
387
388 http_client_peer_shared_ref(pshared);
389 pool = pshared->pools_list;
390 while (pool != NULL) {
391 next = pool->next;
392 http_client_peer_pool_close(&pool);
393 pool = next;
394 }
395 http_client_peer_shared_unref(_pshared);
396 }
397
398 const char *
http_client_peer_shared_label(struct http_client_peer_shared * pshared)399 http_client_peer_shared_label(struct http_client_peer_shared *pshared)
400 {
401 if (pshared->label == NULL) {
402 switch (pshared->addr.type) {
403 case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
404 pshared->label = i_strconcat(
405 http_client_peer_addr2str(&pshared->addr),
406 " (tunnel)", NULL);
407 break;
408 default:
409 pshared->label = i_strdup(
410 http_client_peer_addr2str(&pshared->addr));
411 }
412 }
413 return pshared->label;
414 }
415
416 static void
http_client_peer_shared_connect_backoff(struct http_client_peer_shared * pshared)417 http_client_peer_shared_connect_backoff(struct http_client_peer_shared *pshared)
418 {
419 struct http_client_peer *peer;
420
421 i_assert(pshared->to_backoff != NULL);
422
423 e_debug(pshared->event, "Backoff timer expired");
424
425 timeout_remove(&pshared->to_backoff);
426
427 peer = pshared->peers_list;
428 while (peer != NULL) {
429 struct http_client_peer *peer_next = peer->shared_next;
430
431 http_client_peer_connect_backoff(peer);
432 peer = peer_next;
433 }
434 }
435
436 static bool
http_client_peer_shared_start_backoff_timer(struct http_client_peer_shared * pshared)437 http_client_peer_shared_start_backoff_timer(
438 struct http_client_peer_shared *pshared)
439 {
440 if (pshared->to_backoff != NULL)
441 return TRUE;
442
443 if (pshared->last_failure.tv_sec > 0) {
444 int backoff_time_spent =
445 timeval_diff_msecs(&ioloop_timeval,
446 &pshared->last_failure);
447
448 if (backoff_time_spent < (int)pshared->backoff_current_time_msecs) {
449 unsigned int new_time = (unsigned int)
450 (pshared->backoff_current_time_msecs -
451 backoff_time_spent);
452 e_debug(pshared->event,
453 "Starting backoff timer for %d msecs", new_time);
454 pshared->to_backoff = timeout_add_to(
455 pshared->cctx->ioloop, new_time,
456 http_client_peer_shared_connect_backoff, pshared);
457 return TRUE;
458 }
459
460 e_debug(pshared->event,
461 "Backoff time already exceeded by %d msecs",
462 (backoff_time_spent -
463 pshared->backoff_current_time_msecs));
464 }
465 return FALSE;
466 }
467
468 static void
http_client_peer_shared_increase_backoff_timer(struct http_client_peer_shared * pshared)469 http_client_peer_shared_increase_backoff_timer(
470 struct http_client_peer_shared *pshared)
471 {
472 if (pshared->backoff_current_time_msecs == 0) {
473 pshared->backoff_current_time_msecs =
474 pshared->backoff_initial_time_msecs;
475 } else {
476 pshared->backoff_current_time_msecs *= 2;
477 }
478 if (pshared->backoff_current_time_msecs >
479 pshared->backoff_max_time_msecs) {
480 pshared->backoff_current_time_msecs =
481 pshared->backoff_max_time_msecs;
482 }
483 }
484
485 static void
http_client_peer_shared_reset_backoff_timer(struct http_client_peer_shared * pshared)486 http_client_peer_shared_reset_backoff_timer(
487 struct http_client_peer_shared *pshared)
488 {
489 pshared->backoff_current_time_msecs = 0;
490
491 timeout_remove(&pshared->to_backoff);
492 }
493
494 static void
http_client_peer_shared_connection_success(struct http_client_peer_shared * pshared)495 http_client_peer_shared_connection_success(
496 struct http_client_peer_shared *pshared)
497 {
498 pshared->last_failure.tv_sec = pshared->last_failure.tv_usec = 0;
499 http_client_peer_shared_reset_backoff_timer(pshared);
500 }
501
502 static void
http_client_peer_shared_connection_failure(struct http_client_peer_shared * pshared)503 http_client_peer_shared_connection_failure(
504 struct http_client_peer_shared *pshared)
505 {
506 struct http_client_peer_pool *ppool;
507 unsigned int pending = 0;
508
509 /* Determine the number of connections still pending */
510 ppool = pshared->pools_list;
511 while (ppool != NULL) {
512 pending += array_count(&ppool->pending_conns);
513 ppool = ppool->next;
514 }
515
516 pshared->last_failure = ioloop_timeval;
517
518 /* Manage backoff timer only when this was the only attempt */
519 if (pending == 0)
520 http_client_peer_shared_increase_backoff_timer(pshared);
521 }
522
523 static void
http_client_peer_shared_connection_lost(struct http_client_peer_shared * pshared,bool premature)524 http_client_peer_shared_connection_lost(struct http_client_peer_shared *pshared,
525 bool premature)
526 {
527 /* Update backoff timer if the connection was lost prematurely. this
528 prevents reconnecting immediately to a server that is misbehaving by
529 disconnecting before sending a response.
530 */
531 if (premature) {
532 pshared->last_failure = ioloop_timeval;
533 http_client_peer_shared_increase_backoff_timer(pshared);
534 }
535 }
536
http_client_peer_shared_switch_ioloop(struct http_client_peer_shared * pshared)537 void http_client_peer_shared_switch_ioloop(
538 struct http_client_peer_shared *pshared)
539 {
540 if (pshared->to_backoff != NULL) {
541 pshared->to_backoff =
542 io_loop_move_timeout(&pshared->to_backoff);
543 }
544 }
545
546 unsigned int
http_client_peer_shared_max_connections(struct http_client_peer_shared * pshared)547 http_client_peer_shared_max_connections(struct http_client_peer_shared *pshared)
548 {
549 struct http_client_peer *peer;
550 unsigned int max_conns = 0;
551
552 peer = pshared->peers_list;
553 while (peer != NULL) {
554 const struct http_client_settings *set = &peer->client->set;
555 unsigned int client_max_conns = set->max_parallel_connections;
556
557 if ((UINT_MAX - max_conns) <= client_max_conns)
558 return UINT_MAX;
559 max_conns += client_max_conns;
560 peer = peer->shared_next;
561 }
562
563 return max_conns;
564 }
565
566 /*
567 * Peer
568 */
569
570 static void http_client_peer_drop(struct http_client_peer **_peer);
571
572 static struct http_client_peer *
http_client_peer_create(struct http_client * client,struct http_client_peer_shared * pshared)573 http_client_peer_create(struct http_client *client,
574 struct http_client_peer_shared *pshared)
575 {
576 struct http_client_peer *peer;
577
578 peer = i_new(struct http_client_peer, 1);
579 peer->refcount = 1;
580 peer->client = client;
581 peer->shared = pshared;
582
583 peer->event = event_create(client->event);
584 event_set_append_log_prefix(peer->event, t_strdup_printf(
585 "peer %s: ", http_client_peer_shared_label(pshared)));
586
587 i_array_init(&peer->queues, 16);
588 i_array_init(&peer->conns, 16);
589 i_array_init(&peer->pending_conns, 16);
590
591 DLLIST_PREPEND_FULL(&client->peers_list, peer,
592 client_prev, client_next);
593 DLLIST_PREPEND_FULL(&pshared->peers_list, peer,
594 shared_prev, shared_next);
595 pshared->peers_count++;
596
597 http_client_peer_shared_ref(pshared);
598 peer->ppool = http_client_peer_pool_get(pshared, client);
599
600 /* Choose backoff times */
601 if (pshared->peers_list == NULL ||
602 (client->set.connect_backoff_time_msecs <
603 pshared->backoff_initial_time_msecs)) {
604 pshared->backoff_initial_time_msecs =
605 client->set.connect_backoff_time_msecs;
606 }
607 if (pshared->peers_list == NULL ||
608 (client->set.connect_backoff_max_time_msecs >
609 pshared->backoff_max_time_msecs)) {
610 pshared->backoff_max_time_msecs =
611 client->set.connect_backoff_max_time_msecs;
612 }
613
614 e_debug(peer->event, "Peer created");
615 return peer;
616 }
617
http_client_peer_ref(struct http_client_peer * peer)618 void http_client_peer_ref(struct http_client_peer *peer)
619 {
620 peer->refcount++;
621 }
622
http_client_peer_disconnect(struct http_client_peer * peer)623 static void http_client_peer_disconnect(struct http_client_peer *peer)
624 {
625 struct http_client_queue *queue;
626 struct http_client *client = peer->client;
627 struct http_client_peer_shared *pshared = peer->shared;
628 struct http_client_connection *conn;
629 ARRAY_TYPE(http_client_connection) conns;
630
631 if (peer->disconnected)
632 return;
633 peer->disconnected = TRUE;
634
635 e_debug(peer->event, "Peer disconnect");
636
637 /* Make a copy of the connection array; freed connections modify it */
638 t_array_init(&conns, array_count(&peer->conns));
639 array_copy(&conns.arr, 0, &peer->conns.arr, 0,
640 array_count(&peer->conns));
641 array_foreach_elem(&conns, conn)
642 http_client_connection_lost_peer(conn);
643 i_assert(array_count(&peer->conns) == 0);
644 array_clear(&peer->pending_conns);
645
646 timeout_remove(&peer->to_req_handling);
647
648 /* Unlist in client */
649 DLLIST_REMOVE_FULL(&client->peers_list, peer,
650 client_prev, client_next);
651 /* Unlist in peer */
652 DLLIST_REMOVE_FULL(&pshared->peers_list, peer,
653 shared_prev, shared_next);
654 pshared->peers_count--;
655
656 /* Unlink all queues */
657 array_foreach_elem(&peer->queues, queue)
658 http_client_queue_peer_disconnected(queue, peer);
659 array_clear(&peer->queues);
660 }
661
http_client_peer_unref(struct http_client_peer ** _peer)662 bool http_client_peer_unref(struct http_client_peer **_peer)
663 {
664 struct http_client_peer *peer = *_peer;
665 struct http_client_peer_pool *ppool = peer->ppool;
666 struct http_client_peer_shared *pshared = peer->shared;
667
668 *_peer = NULL;
669
670 i_assert(peer->refcount > 0);
671 if (--peer->refcount > 0)
672 return TRUE;
673
674 e_debug(peer->event, "Peer destroy");
675
676 http_client_peer_disconnect(peer);
677
678 i_assert(array_count(&peer->queues) == 0);
679
680 event_unref(&peer->event);
681 array_free(&peer->conns);
682 array_free(&peer->pending_conns);
683 array_free(&peer->queues);
684 i_free(peer);
685
686 /* Choose new backoff times */
687 peer = pshared->peers_list;
688 while (peer != NULL) {
689 struct http_client *client = peer->client;
690
691 if (client->set.connect_backoff_time_msecs <
692 pshared->backoff_initial_time_msecs) {
693 pshared->backoff_initial_time_msecs =
694 client->set.connect_backoff_time_msecs;
695 }
696 if (client->set.connect_backoff_max_time_msecs >
697 pshared->backoff_max_time_msecs) {
698 pshared->backoff_max_time_msecs =
699 client->set.connect_backoff_max_time_msecs;
700 }
701 peer = peer->shared_next;
702 }
703
704 http_client_peer_pool_unref(&ppool);
705 http_client_peer_shared_unref(&pshared);
706 return FALSE;
707 }
708
http_client_peer_close(struct http_client_peer ** _peer)709 void http_client_peer_close(struct http_client_peer **_peer)
710 {
711 struct http_client_peer *peer = *_peer;
712
713 e_debug(peer->event, "Peer close");
714
715 http_client_peer_disconnect(peer);
716
717 (void)http_client_peer_unref(_peer);
718 }
719
http_client_peer_drop(struct http_client_peer ** _peer)720 static void http_client_peer_drop(struct http_client_peer **_peer)
721 {
722 struct http_client_peer *peer = *_peer;
723 struct http_client_peer_shared *pshared = peer->shared;
724 unsigned int conns_active =
725 http_client_peer_active_connections(peer);
726
727 if (conns_active > 0) {
728 e_debug(peer->event,
729 "Not dropping peer (%d connections active)",
730 conns_active);
731 return;
732 }
733
734 if (pshared->to_backoff != NULL)
735 return;
736
737 if (http_client_peer_shared_start_backoff_timer(pshared)) {
738 e_debug(peer->event,
739 "Dropping peer (waiting for backof timeout)");
740
741 /* Will disconnect any pending connections */
742 http_client_peer_trigger_request_handler(peer);
743 } else {
744 e_debug(peer->event, "Dropping peer now");
745 /* Drop peer immediately */
746 http_client_peer_close(_peer);
747 }
748 }
749
750 struct http_client_peer *
http_client_peer_get(struct http_client * client,const struct http_client_peer_addr * addr)751 http_client_peer_get(struct http_client *client,
752 const struct http_client_peer_addr *addr)
753 {
754 struct http_client_peer *peer;
755 struct http_client_peer_shared *pshared;
756
757 pshared = http_client_peer_shared_get(client->cctx, addr);
758
759 peer = pshared->peers_list;
760 while (peer != NULL) {
761 if (peer->client == client)
762 break;
763 peer = peer->shared_next;
764 }
765
766 if (peer == NULL)
767 peer = http_client_peer_create(client, pshared);
768
769 http_client_peer_shared_unref(&pshared);
770 return peer;
771 }
772
773 static void
http_client_peer_do_connect(struct http_client_peer * peer,unsigned int count)774 http_client_peer_do_connect(struct http_client_peer *peer, unsigned int count)
775 {
776 struct http_client_peer_pool *ppool = peer->ppool;
777 struct http_client_connection *const *idle_conns;
778 unsigned int i, idle_count;
779 bool claimed_existing = FALSE;
780
781 if (count == 0)
782 return;
783
784 idle_conns = array_get(&ppool->idle_conns, &idle_count);
785 for (i = 0; i < count && i < idle_count; i++) {
786 http_client_connection_claim_idle(idle_conns[i], peer);
787 claimed_existing = TRUE;
788
789 e_debug(peer->event,
790 "Claimed idle connection "
791 "(%u connections exist, %u pending)",
792 array_count(&peer->conns),
793 array_count(&peer->pending_conns));
794 }
795
796 for (; i < count; i++) {
797 e_debug(peer->event,
798 "Making new connection %u of %u "
799 "(%u connections exist, %u pending)",
800 i+1, count, array_count(&peer->conns),
801 array_count(&peer->pending_conns));
802
803 (void)http_client_connection_create(peer);
804 }
805
806 if (claimed_existing)
807 http_client_peer_connection_success(peer);
808 }
809
http_client_peer_connect_backoff(struct http_client_peer * peer)810 static void http_client_peer_connect_backoff(struct http_client_peer *peer)
811 {
812 if (peer->connect_backoff && array_count(&peer->queues) == 0) {
813 http_client_peer_close(&peer);
814 return;
815 }
816
817 http_client_peer_do_connect(peer, 1);
818 peer->connect_backoff = FALSE;
819 }
820
821 static void
http_client_peer_connect(struct http_client_peer * peer,unsigned int count)822 http_client_peer_connect(struct http_client_peer *peer, unsigned int count)
823 {
824 if (http_client_peer_shared_start_backoff_timer(peer->shared)) {
825 peer->connect_backoff = TRUE;
826 return;
827 }
828
829 http_client_peer_do_connect(peer, count);
830 }
831
http_client_peer_is_connected(struct http_client_peer * peer)832 bool http_client_peer_is_connected(struct http_client_peer *peer)
833 {
834 struct http_client_connection *conn;
835
836 if (array_count(&peer->ppool->idle_conns) > 0)
837 return TRUE;
838
839 array_foreach_elem(&peer->conns, conn) {
840 if (conn->connected)
841 return TRUE;
842 }
843
844 return FALSE;
845 }
846
847 static void
http_client_peer_cancel(struct http_client_peer * peer)848 http_client_peer_cancel(struct http_client_peer *peer)
849 {
850 struct http_client_connection *conn;
851 ARRAY_TYPE(http_client_connection) conns;
852
853 e_debug(peer->event, "Peer cancel");
854
855 /* Make a copy of the connection array; freed connections modify it */
856 t_array_init(&conns, array_count(&peer->conns));
857 array_copy(&conns.arr, 0, &peer->conns.arr, 0,
858 array_count(&peer->conns));
859 array_foreach_elem(&conns, conn) {
860 if (!http_client_connection_is_active(conn))
861 http_client_connection_close(&conn);
862 }
863 i_assert(array_count(&peer->pending_conns) == 0);
864 }
865
866 static unsigned int
http_client_peer_requests_pending(struct http_client_peer * peer,unsigned int * num_urgent_r)867 http_client_peer_requests_pending(struct http_client_peer *peer,
868 unsigned int *num_urgent_r)
869 {
870 struct http_client_queue *queue;
871 unsigned int num_requests = 0, num_urgent = 0, requests, urgent;
872
873 array_foreach_elem(&peer->queues, queue) {
874 requests = http_client_queue_requests_pending(queue, &urgent);
875
876 num_requests += requests;
877 num_urgent += urgent;
878 }
879 *num_urgent_r = num_urgent;
880 return num_requests;
881 }
882
http_client_peer_check_idle(struct http_client_peer * peer)883 static void http_client_peer_check_idle(struct http_client_peer *peer)
884 {
885 struct http_client_connection *conn;
886 unsigned int num_urgent = 0;
887
888 if (array_count(&peer->conns) == 0 &&
889 http_client_peer_requests_pending(peer, &num_urgent) == 0) {
890 /* No connections or pending requests; disconnect immediately */
891 http_client_peer_drop(&peer);
892 return;
893 }
894
895 /* Check all connections for idle status */
896 array_foreach_elem(&peer->conns, conn)
897 http_client_connection_check_idle(conn);
898 }
899
900 static void
http_client_peer_handle_requests_real(struct http_client_peer * peer)901 http_client_peer_handle_requests_real(struct http_client_peer *peer)
902 {
903 struct _conn_available {
904 struct http_client_connection *conn;
905 unsigned int pending_requests;
906 };
907 struct http_client_connection *const *conn_idx;
908 ARRAY(struct _conn_available) conns_avail;
909 struct _conn_available *conn_avail_idx;
910 struct http_client_peer_shared *pshared = peer->shared;
911 unsigned int connecting, closing, idle;
912 unsigned int num_pending, num_urgent, new_connections;
913 unsigned int working_conn_count;
914 struct http_client_peer *tmp_peer;
915 bool statistics_dirty = TRUE;
916
917 /* FIXME: limit the number of requests handled in one run to prevent
918 I/O starvation. */
919
920 /* Disconnect pending connections if we're not linked to any queue
921 anymore */
922 if (array_count(&peer->queues) == 0) {
923 if (array_count(&peer->conns) == 0 &&
924 pshared->to_backoff == NULL) {
925 /* Peer is completely unused and inactive; drop it
926 immediately */
927 http_client_peer_drop(&peer);
928 return;
929 }
930 e_debug(peer->event,
931 "Peer no longer used; will now cancel pending connections "
932 "(%u connections exist, %u pending)",
933 array_count(&peer->conns),
934 array_count(&peer->pending_conns));
935
936 http_client_peer_cancel(peer);
937 return;
938 }
939
940 /* Don't do anything unless we have pending requests */
941 num_pending = http_client_peer_requests_pending(peer, &num_urgent);
942 if (num_pending == 0) {
943 e_debug(peer->event,
944 "No requests to service for this peer "
945 "(%u connections exist, %u pending)",
946 array_count(&peer->conns),
947 array_count(&peer->pending_conns));
948 http_client_peer_check_idle(peer);
949 return;
950 }
951
952 http_client_peer_ref(peer);
953 peer->handling_requests = TRUE;
954 t_array_init(&conns_avail, array_count(&peer->conns));
955 do {
956 bool conn_lost = FALSE;
957
958 array_clear(&conns_avail);
959 closing = idle = 0;
960
961 /* Gather connection statistics */
962 array_foreach(&peer->conns, conn_idx) {
963 struct http_client_connection *conn = *conn_idx;
964 int ret;
965
966 ret = http_client_connection_check_ready(conn);
967 if (ret < 0) {
968 conn_lost = TRUE;
969 break;
970 } else if (ret > 0) {
971 struct _conn_available *conn_avail;
972 unsigned int insert_idx, pending_requests;
973
974 /* Compile sorted availability list */
975 pending_requests = http_client_connection_count_pending(conn);
976 if (array_count(&conns_avail) == 0) {
977 insert_idx = 0;
978 } else {
979 insert_idx = array_count(&conns_avail);
980 array_foreach_modifiable(&conns_avail, conn_avail_idx) {
981 if (conn_avail_idx->pending_requests > pending_requests) {
982 insert_idx = array_foreach_idx(&conns_avail, conn_avail_idx);
983 break;
984 }
985 }
986 }
987 conn_avail = array_insert_space(&conns_avail, insert_idx);
988 conn_avail->conn = conn;
989 conn_avail->pending_requests = pending_requests;
990 if (pending_requests == 0)
991 idle++;
992 }
993 /* Count the number of connecting and closing connections */
994 if (conn->closing)
995 closing++;
996 }
997
998 if (conn_lost) {
999 /* Connection array changed while iterating; retry */
1000 continue;
1001 }
1002
1003 working_conn_count = array_count(&peer->conns) - closing;
1004 statistics_dirty = FALSE;
1005
1006 /* Use idle connections right away */
1007 if (idle > 0) {
1008 e_debug(peer->event,
1009 "Using %u idle connections to handle %u requests "
1010 "(%u total connections ready)",
1011 idle, num_pending > idle ? idle : num_pending,
1012 array_count(&conns_avail));
1013
1014 array_foreach_modifiable(&conns_avail, conn_avail_idx) {
1015 if (num_pending == 0 ||
1016 conn_avail_idx->pending_requests > 0)
1017 break;
1018 idle--;
1019 if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) {
1020 /* No longer available (probably connection error/closed) */
1021 statistics_dirty = TRUE;
1022 conn_avail_idx->conn = NULL;
1023 } else {
1024 /* Update statistics */
1025 conn_avail_idx->pending_requests++;
1026 if (num_urgent > 0)
1027 num_urgent--;
1028 num_pending--;
1029 }
1030 }
1031 }
1032
1033 /* Don't continue unless we have more pending requests */
1034 num_pending = http_client_peer_requests_pending(peer, &num_urgent);
1035 if (num_pending == 0) {
1036 e_debug(peer->event,
1037 "No more requests to service for this peer "
1038 "(%u connections exist, %u pending)",
1039 array_count(&peer->conns),
1040 array_count(&peer->pending_conns));
1041 http_client_peer_check_idle(peer);
1042 break;
1043 }
1044 } while (statistics_dirty);
1045
1046 tmp_peer = peer;
1047 if (!http_client_peer_unref(&tmp_peer))
1048 return;
1049 peer->handling_requests = FALSE;
1050
1051 if (num_pending == 0)
1052 return;
1053
1054 i_assert(idle == 0);
1055 connecting = array_count(&peer->pending_conns);
1056
1057 /* Determine how many new connections we can set up */
1058 if (pshared->last_failure.tv_sec > 0 && working_conn_count > 0 &&
1059 working_conn_count == connecting) {
1060 /* Don't create new connections until the existing ones have
1061 finished connecting successfully. */
1062 new_connections = 0;
1063 } else {
1064 if ((working_conn_count - connecting + num_urgent) >=
1065 peer->client->set.max_parallel_connections) {
1066 /* Only create connections for urgent requests */
1067 new_connections = (num_urgent > connecting ?
1068 num_urgent - connecting : 0);
1069 } else if (num_pending <= connecting) {
1070 /* There are already enough connections being made */
1071 new_connections = 0;
1072 } else if (working_conn_count == connecting) {
1073 /* No connections succeeded so far, don't hammer the
1074 server with more than one connection attempt unless
1075 its urgent */
1076 if (num_urgent > 0) {
1077 new_connections = (num_urgent > connecting ?
1078 num_urgent - connecting : 0);
1079 } else {
1080 new_connections = (connecting == 0 ? 1 : 0);
1081 }
1082 } else if ((num_pending - connecting) >
1083 (peer->client->set.max_parallel_connections -
1084 working_conn_count)) {
1085 /* Create maximum allowed connections */
1086 new_connections =
1087 (peer->client->set.max_parallel_connections -
1088 working_conn_count);
1089 } else {
1090 /* Create as many connections as we need */
1091 new_connections = num_pending - connecting;
1092 }
1093 }
1094
1095 /* Create connections */
1096 if (new_connections > 0) {
1097 e_debug(peer->event,
1098 "Creating %u new connections to handle requests "
1099 "(already %u usable, connecting to %u, closing %u)",
1100 new_connections, working_conn_count - connecting,
1101 connecting, closing);
1102 http_client_peer_connect(peer, new_connections);
1103 return;
1104 }
1105
1106 /* Cannot create new connections for normal request; attempt pipelining
1107 */
1108 if ((working_conn_count - connecting) >=
1109 peer->client->set.max_parallel_connections) {
1110 unsigned int pipeline_level = 0, total_handled = 0, handled;
1111
1112 if (!pshared->allows_pipelining) {
1113 e_debug(peer->event,
1114 "Will not pipeline until peer has shown support");
1115 return;
1116 }
1117
1118 /* Fill pipelines */
1119 do {
1120 handled = 0;
1121 /* Fill smallest pipelines first,
1122 until all pipelines are filled to the same level */
1123 array_foreach_modifiable(&conns_avail, conn_avail_idx) {
1124 if (conn_avail_idx->conn == NULL)
1125 continue;
1126 if (pipeline_level == 0) {
1127 pipeline_level = conn_avail_idx->pending_requests;
1128 } else if (conn_avail_idx->pending_requests > pipeline_level) {
1129 pipeline_level = conn_avail_idx->pending_requests;
1130 break; /* restart from least busy connection */
1131 }
1132 /* Pipeline it */
1133 if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) {
1134 /* connection now unavailable */
1135 conn_avail_idx->conn = NULL;
1136 } else {
1137 /* Successfully pipelined */
1138 conn_avail_idx->pending_requests++;
1139 num_pending--;
1140 handled++;
1141 }
1142 }
1143
1144 total_handled += handled;
1145 } while (num_pending > num_urgent && handled > 0);
1146
1147 e_debug(peer->event,
1148 "Pipelined %u requests "
1149 "(filled pipelines up to %u requests)",
1150 total_handled, pipeline_level);
1151 return;
1152 }
1153
1154 /* Still waiting for connections to finish */
1155 e_debug(peer->event, "No request handled; "
1156 "waiting for pending connections "
1157 "(%u connections exist, %u pending)",
1158 array_count(&peer->conns), connecting);
1159 return;
1160 }
1161
http_client_peer_handle_requests(struct http_client_peer * peer)1162 static void http_client_peer_handle_requests(struct http_client_peer *peer)
1163 {
1164 timeout_remove(&peer->to_req_handling);
1165
1166 T_BEGIN {
1167 http_client_peer_handle_requests_real(peer);
1168 } T_END;
1169 }
1170
http_client_peer_trigger_request_handler(struct http_client_peer * peer)1171 void http_client_peer_trigger_request_handler(struct http_client_peer *peer)
1172 {
1173 /* Trigger request handling through timeout */
1174 if (peer->to_req_handling == NULL) {
1175 peer->to_req_handling = timeout_add_short_to(
1176 peer->client->ioloop, 0,
1177 http_client_peer_handle_requests, peer);
1178 }
1179 }
1180
http_client_peer_have_queue(struct http_client_peer * peer,struct http_client_queue * queue)1181 bool http_client_peer_have_queue(struct http_client_peer *peer,
1182 struct http_client_queue *queue)
1183 {
1184 struct http_client_queue *const *queue_idx;
1185
1186 array_foreach(&peer->queues, queue_idx) {
1187 if (*queue_idx == queue)
1188 return TRUE;
1189 }
1190 return FALSE;
1191 }
1192
http_client_peer_link_queue(struct http_client_peer * peer,struct http_client_queue * queue)1193 void http_client_peer_link_queue(struct http_client_peer *peer,
1194 struct http_client_queue *queue)
1195 {
1196 if (!http_client_peer_have_queue(peer, queue)) {
1197 array_push_back(&peer->queues, &queue);
1198
1199 e_debug(peer->event, "Linked queue %s (%d queues linked)",
1200 queue->name, array_count(&peer->queues));
1201 }
1202 }
1203
http_client_peer_unlink_queue(struct http_client_peer * peer,struct http_client_queue * queue)1204 void http_client_peer_unlink_queue(struct http_client_peer *peer,
1205 struct http_client_queue *queue)
1206 {
1207 struct http_client_queue *const *queue_idx;
1208
1209 array_foreach(&peer->queues, queue_idx) {
1210 if (*queue_idx == queue) {
1211 array_delete(&peer->queues,
1212 array_foreach_idx(&peer->queues, queue_idx), 1);
1213
1214 e_debug(peer->event,
1215 "Unlinked queue %s (%d queues linked)",
1216 queue->name, array_count(&peer->queues));
1217
1218 if (array_count(&peer->queues) == 0)
1219 http_client_peer_check_idle(peer);
1220 return;
1221 }
1222 }
1223 }
1224
1225 struct http_client_request *
http_client_peer_claim_request(struct http_client_peer * peer,bool no_urgent)1226 http_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent)
1227 {
1228 struct http_client_queue *queue;
1229 struct http_client_request *req;
1230
1231 array_foreach_elem(&peer->queues, queue) {
1232 req = http_client_queue_claim_request(
1233 queue, &peer->shared->addr, no_urgent);
1234 if (req != NULL) {
1235 req->peer = peer;
1236 return req;
1237 }
1238 }
1239
1240 return NULL;
1241 }
1242
http_client_peer_connection_success(struct http_client_peer * peer)1243 void http_client_peer_connection_success(struct http_client_peer *peer)
1244 {
1245 struct http_client_peer_pool *ppool = peer->ppool;
1246 struct http_client_queue *queue;
1247
1248 e_debug(peer->event, "Successfully connected "
1249 "(%u connections exist, %u pending)",
1250 array_count(&peer->conns), array_count(&peer->pending_conns));
1251
1252 http_client_peer_pool_connection_success(ppool);
1253
1254 array_foreach_elem(&peer->queues, queue)
1255 http_client_queue_connection_success(queue, peer);
1256
1257 http_client_peer_trigger_request_handler(peer);
1258 }
1259
http_client_peer_connection_failure(struct http_client_peer * peer,const char * reason)1260 void http_client_peer_connection_failure(struct http_client_peer *peer,
1261 const char *reason)
1262 {
1263 struct http_client_peer_pool *ppool = peer->ppool;
1264
1265 e_debug(peer->event, "Connection failed "
1266 "(%u connections exist, %u pending)",
1267 array_count(&peer->conns), array_count(&peer->pending_conns));
1268
1269 http_client_peer_pool_connection_failure(ppool, reason);
1270
1271 peer->connect_failed = TRUE;
1272 }
1273
1274 static void
http_client_peer_connection_succeeded_pool(struct http_client_peer * peer)1275 http_client_peer_connection_succeeded_pool(struct http_client_peer *peer)
1276 {
1277 if (!peer->connect_failed)
1278 return;
1279 peer->connect_failed = FALSE;
1280
1281 e_debug(peer->event,
1282 "A connection succeeded within our peer pool, "
1283 "so this peer can retry connecting as well if needed "
1284 "(%u connections exist, %u pending)",
1285 array_count(&peer->conns), array_count(&peer->pending_conns));
1286
1287 /* If there are pending requests for this peer, try creating a new
1288 connection for them. if not, this peer will wind itself down. */
1289 http_client_peer_trigger_request_handler(peer);
1290 }
1291
1292 static void
http_client_peer_connection_failed_pool(struct http_client_peer * peer,const char * reason)1293 http_client_peer_connection_failed_pool(struct http_client_peer *peer,
1294 const char *reason)
1295 {
1296 struct http_client_queue *queue;
1297 ARRAY_TYPE(http_client_queue) queues;
1298
1299 e_debug(peer->event,
1300 "Failed to establish any connection within our peer pool: %s "
1301 "(%u connections exist, %u pending)", reason,
1302 array_count(&peer->conns), array_count(&peer->pending_conns));
1303
1304 peer->connect_failed = TRUE;
1305
1306 /* Make a copy of the queue array; queues get linked/unlinged while the
1307 connection failure is handled */
1308 t_array_init(&queues, array_count(&peer->queues));
1309 array_copy(&queues.arr, 0, &peer->queues.arr, 0,
1310 array_count(&peer->queues));
1311
1312 /* Failed to make any connection. a second connect will probably also
1313 fail, so just try another IP for the hosts(s) or abort all requests
1314 if this was the only/last option. */
1315 array_foreach_elem(&queues, queue)
1316 http_client_queue_connection_failure(queue, peer, reason);
1317 }
1318
http_client_peer_connection_lost(struct http_client_peer * peer,bool premature)1319 void http_client_peer_connection_lost(struct http_client_peer *peer,
1320 bool premature)
1321 {
1322 unsigned int num_pending, num_urgent;
1323
1324 /* We get here when an already connected connection fails. if the
1325 connect itself fails, http_client_peer_shared_connection_failure() is
1326 called instead. */
1327
1328 if (peer->disconnected)
1329 return;
1330
1331 http_client_peer_shared_connection_lost(peer->shared, premature);
1332
1333 num_pending = http_client_peer_requests_pending(peer, &num_urgent);
1334
1335 e_debug(peer->event,
1336 "Lost a connection%s "
1337 "(%u queues linked, %u connections left, "
1338 "%u connections pending, %u requests pending, "
1339 "%u requests urgent)",
1340 (premature ? " prematurely" : ""),
1341 array_count(&peer->queues), array_count(&peer->conns),
1342 array_count(&peer->pending_conns), num_pending, num_urgent);
1343
1344 if (peer->handling_requests) {
1345 /* We got here from the request handler loop */
1346 e_debug(peer->event,
1347 "Lost a connection while handling requests");
1348 return;
1349 }
1350
1351 /* If there are pending requests for this peer, create a new connection
1352 for them. if not, this peer will wind itself down. */
1353 http_client_peer_trigger_request_handler(peer);
1354 }
1355
1356 unsigned int
http_client_peer_active_connections(struct http_client_peer * peer)1357 http_client_peer_active_connections(struct http_client_peer *peer)
1358 {
1359 struct http_client_connection *conn;
1360 unsigned int active = 0;
1361
1362 /* Find idle connections */
1363 array_foreach_elem(&peer->conns, conn) {
1364 if (http_client_connection_is_active(conn))
1365 active++;
1366 }
1367
1368 return active;
1369 }
1370
1371 unsigned int
http_client_peer_pending_connections(struct http_client_peer * peer)1372 http_client_peer_pending_connections(struct http_client_peer *peer)
1373 {
1374 return array_count(&peer->pending_conns);
1375 }
1376
http_client_peer_switch_ioloop(struct http_client_peer * peer)1377 void http_client_peer_switch_ioloop(struct http_client_peer *peer)
1378 {
1379 if (peer->to_req_handling != NULL) {
1380 peer->to_req_handling =
1381 io_loop_move_timeout(&peer->to_req_handling);
1382 }
1383 }
1384