1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
11
#include "iostream-ssl.h"
12
#include "http-response-parser.h"
14
#include "http-client-private.h"
21
http_client_peer_debug(struct http_client_peer *peer,
22
const char *format, ...) ATTR_FORMAT(2, 3);
25
http_client_peer_debug(struct http_client_peer *peer,
26
const char *format, ...)
30
if (peer->client->set.debug) {
31
va_start(args, format);
32
i_debug("http-client: peer %s:%u: %s",
33
net_ip2addr(&peer->addr.ip), peer->addr.port,
34
t_strdup_vprintf(format, args));
43
unsigned int http_client_peer_addr_hash
44
(const struct http_client_peer_addr *peer)
46
return net_ip_hash(&peer->ip) + peer->port +
47
(peer->https_name == NULL ? 0 : str_hash(peer->https_name));
50
int http_client_peer_addr_cmp
51
(const struct http_client_peer_addr *peer1,
52
const struct http_client_peer_addr *peer2)
56
if ((ret=net_ip_cmp(&peer1->ip, &peer2->ip)) != 0)
58
if (peer1->port != peer2->port)
59
return (peer1->port > peer2->port ? 1 : -1);
60
return null_strcmp(peer1->https_name, peer2->https_name);
68
http_client_peer_connect(struct http_client_peer *peer, unsigned int count)
72
for (i = 0; i < count; i++) {
73
http_client_peer_debug(peer, "Making new connection %u of %u", i+1, count);
74
(void)http_client_connection_create(peer);
79
http_client_peer_requests_pending(struct http_client_peer *peer,
80
unsigned int *num_urgent_r)
82
struct http_client_host *const *host;
83
unsigned int num_requests = 0, num_urgent = 0, requests, urgent;
85
array_foreach(&peer->hosts, host) {
86
requests = http_client_host_requests_pending(*host, &peer->addr, &urgent);
88
num_requests += requests;
91
*num_urgent_r = num_urgent;
96
http_client_peer_next_request(struct http_client_peer *peer,
97
bool *created_connections)
99
struct http_client_connection *const *conn_idx;
100
struct http_client_connection *conn = NULL;
101
unsigned int connecting = 0, closing = 0, min_waiting = UINT_MAX;
102
unsigned int num_urgent, new_connections, working_conn_count;
104
if (http_client_peer_requests_pending(peer, &num_urgent) == 0)
107
/* find the least busy connection */
108
array_foreach(&peer->conns, conn_idx) {
109
if (http_client_connection_is_ready(*conn_idx)) {
110
unsigned int waiting = http_client_connection_count_pending(*conn_idx);
112
if (waiting < min_waiting) {
113
min_waiting = waiting;
115
if (min_waiting == 0) {
116
/* found idle connection, use it now */
121
/* count the number of connecting and closing connections */
122
if ((*conn_idx)->closing)
124
else if (!(*conn_idx)->connected)
127
working_conn_count = array_count(&peer->conns) - closing;
129
/* did we find an idle connection? */
130
if (conn != NULL && min_waiting == 0) {
132
return http_client_connection_next_request(conn);
135
/* no, but can we create a new connection? */
136
if (num_urgent == 0 &&
137
working_conn_count >= peer->client->set.max_parallel_connections) {
140
http_client_peer_debug(peer,
141
"Only non-urgent requests, and we already have "
142
"%u pending connections", working_conn_count);
146
return http_client_connection_next_request(conn);
149
/* yes, determine how many connections to set up */
150
if (peer->last_connect_failed && working_conn_count > 0 &&
151
working_conn_count == connecting) {
152
/* don't create new connections until the existing ones have
153
finished connecting successfully. */
155
} else if (num_urgent == 0) {
156
new_connections = connecting == 0 ? 1 : 0;
158
new_connections = (num_urgent > connecting ? num_urgent - connecting : 0);
160
http_client_peer_debug(peer,
161
"Creating %u new connections to handle requests "
162
"(already %u usable, connecting to %u, closing %u)",
163
new_connections, working_conn_count - connecting,
164
connecting, closing);
165
if (new_connections > 0) {
166
*created_connections = TRUE;
167
http_client_peer_connect(peer, new_connections);
170
/* now we wait until it is connected */
174
bool http_client_peer_handle_requests(struct http_client_peer *peer)
176
bool created_connections = FALSE;
178
while (http_client_peer_next_request(peer, &created_connections)) ;
179
return created_connections;
182
static struct http_client_peer *
183
http_client_peer_create(struct http_client *client,
184
const struct http_client_peer_addr *addr)
186
struct http_client_peer *peer;
188
i_assert(addr->https_name == NULL || client->ssl_ctx != NULL);
190
peer = i_new(struct http_client_peer, 1);
191
peer->client = client;
193
peer->addr.https_name = i_strdup(addr->https_name);
194
i_array_init(&peer->hosts, 16);
195
i_array_init(&peer->conns, 16);
198
(client->peers, (const struct http_client_peer_addr *)&peer->addr, peer);
199
DLLIST_PREPEND(&client->peers_list, peer);
201
http_client_peer_debug(peer, "Peer created");
202
http_client_peer_connect(peer, 1);
206
void http_client_peer_free(struct http_client_peer **_peer)
208
struct http_client_peer *peer = *_peer;
209
struct http_client_connection **conn;
210
ARRAY_TYPE(http_client_connection) conns;
214
peer->destroyed = TRUE;
216
http_client_peer_debug(peer, "Peer destroy");
218
/* make a copy of the connection array; freed connections modify it */
219
t_array_init(&conns, array_count(&peer->conns));
220
array_copy(&conns.arr, 0, &peer->conns.arr, 0, array_count(&peer->conns));
222
array_foreach_modifiable(&conns, conn) {
223
http_client_connection_unref(conn);
226
i_assert(array_count(&peer->conns) == 0);
227
array_free(&peer->conns);
228
array_free(&peer->hosts);
231
(peer->client->peers, (const struct http_client_peer_addr *)&peer->addr);
232
DLLIST_REMOVE(&peer->client->peers_list, peer);
234
i_free(peer->addr.https_name);
239
struct http_client_peer *
240
http_client_peer_get(struct http_client *client,
241
const struct http_client_peer_addr *addr)
243
struct http_client_peer *peer;
245
peer = hash_table_lookup(client->peers, addr);
247
peer = http_client_peer_create(client, addr);
252
bool http_client_peer_have_host(struct http_client_peer *peer,
253
struct http_client_host *host)
255
struct http_client_host *const *host_idx;
257
array_foreach(&peer->hosts, host_idx) {
258
if (*host_idx == host)
264
void http_client_peer_add_host(struct http_client_peer *peer,
265
struct http_client_host *host)
267
if (!http_client_peer_have_host(peer, host))
268
array_append(&peer->hosts, &host, 1);
271
struct http_client_request *
272
http_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent)
274
struct http_client_host *const *host_idx;
275
struct http_client_request *req;
277
array_foreach(&peer->hosts, host_idx) {
278
if ((req=http_client_host_claim_request
279
(*host_idx, &peer->addr, no_urgent)) != NULL) {
288
void http_client_peer_connection_success(struct http_client_peer *peer)
290
struct http_client_host *const *host;
292
peer->last_connect_failed = FALSE;
294
array_foreach(&peer->hosts, host) {
295
http_client_host_connection_success(*host, &peer->addr);
299
void http_client_peer_connection_failure(struct http_client_peer *peer,
302
struct http_client_host *const *host;
303
unsigned int num_urgent;
305
i_assert(array_count(&peer->conns) > 0);
307
http_client_peer_debug(peer, "Failed to make connection");
309
peer->last_connect_failed = TRUE;
310
if (array_count(&peer->conns) > 1) {
311
/* if there are other connections attempting to connect, wait
312
for them before failing the requests. remember that we had
313
trouble with connecting so in future we don't try to create
314
more than one connection until connects work again. */
316
/* this was the only/last connection and connecting to it
317
failed. a second connect will probably also fail, so just
318
abort all requests. */
319
array_foreach(&peer->hosts, host) {
320
http_client_host_connection_failure(*host, &peer->addr, reason);
323
if (array_count(&peer->conns) == 0 &&
324
http_client_peer_requests_pending(peer, &num_urgent) == 0)
325
http_client_peer_free(&peer);
328
void http_client_peer_connection_lost(struct http_client_peer *peer)
330
unsigned int num_urgent;
332
/* we get here when an already connected connection fails. if the
333
connect itself fails, http_client_peer_connection_failure() is
339
http_client_peer_debug(peer, "Lost a connection (%d connections left)",
340
array_count(&peer->conns));
342
/* if there are pending requests for this peer, create a new connection
344
http_client_peer_handle_requests(peer);
346
if (array_count(&peer->conns) == 0 &&
347
http_client_peer_requests_pending(peer, &num_urgent) == 0)
348
http_client_peer_free(&peer);
351
unsigned int http_client_peer_idle_connections(struct http_client_peer *peer)
353
struct http_client_connection *const *conn_idx;
354
unsigned int idle = 0;
356
/* find idle connections */
357
array_foreach(&peer->conns, conn_idx) {
358
if (http_client_connection_is_idle(*conn_idx))