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