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: %s",
33
http_client_peer_label(peer), t_strdup_vprintf(format, args));
42
unsigned int http_client_peer_addr_hash
43
(const struct http_client_peer_addr *peer)
46
case HTTP_CLIENT_PEER_ADDR_RAW:
47
return net_ip_hash(&peer->ip) + peer->port + 1;
48
case HTTP_CLIENT_PEER_ADDR_HTTP:
49
return net_ip_hash(&peer->ip) + peer->port;
50
case HTTP_CLIENT_PEER_ADDR_HTTPS:
51
case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
52
return net_ip_hash(&peer->ip) + peer->port +
53
(peer->https_name == NULL ? 0 : str_hash(peer->https_name));
59
int http_client_peer_addr_cmp
60
(const struct http_client_peer_addr *peer1,
61
const struct http_client_peer_addr *peer2)
65
if (peer1->type != peer2->type)
66
return (peer1->type > peer2->type ? 1 : -1);
67
if ((ret=net_ip_cmp(&peer1->ip, &peer2->ip)) != 0)
69
if (peer1->port != peer2->port)
70
return (peer1->port > peer2->port ? 1 : -1);
71
if (peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS)
73
return null_strcmp(peer1->https_name, peer2->https_name);
81
http_client_peer_connect(struct http_client_peer *peer, unsigned int count)
85
for (i = 0; i < count; i++) {
86
http_client_peer_debug(peer,
87
"Making new connection %u of %u", i+1, count);
88
(void)http_client_connection_create(peer);
92
bool http_client_peer_is_connected(struct http_client_peer *peer)
94
struct http_client_connection *const *conn_idx;
96
array_foreach(&peer->conns, conn_idx) {
97
if ((*conn_idx)->connected)
104
static void http_client_peer_check_idle(struct http_client_peer *peer)
106
struct http_client_connection *const *conn_idx;
108
array_foreach(&peer->conns, conn_idx) {
109
http_client_connection_check_idle(*conn_idx);
114
http_client_peer_requests_pending(struct http_client_peer *peer,
115
unsigned int *num_urgent_r)
117
struct http_client_queue *const *queue;
118
unsigned int num_requests = 0, num_urgent = 0, requests, urgent;
120
array_foreach(&peer->queues, queue) {
121
requests = http_client_queue_requests_pending(*queue, &urgent);
123
num_requests += requests;
124
num_urgent += urgent;
126
*num_urgent_r = num_urgent;
131
http_client_peer_handle_requests_real(struct http_client_peer *peer)
133
struct _conn_available {
134
struct http_client_connection *conn;
135
unsigned int pending_requests;
137
struct http_client_connection *const *conn_idx;
138
ARRAY(struct _conn_available) conns_avail;
139
struct _conn_available *conn_avail_idx;
140
unsigned int connecting, closing, idle;
141
unsigned int num_pending, num_urgent, new_connections, working_conn_count;
142
bool statistics_dirty = TRUE;
144
/* FIXME: limit the number of requests handled in one run to prevent
147
/* don't do anything unless we have pending requests */
148
num_pending = http_client_peer_requests_pending(peer, &num_urgent);
149
if (num_pending == 0) {
150
http_client_peer_check_idle(peer);
154
t_array_init(&conns_avail, array_count(&peer->conns));
156
array_clear(&conns_avail);
157
connecting = closing = idle = 0;
159
/* gather connection statistics */
160
array_foreach(&peer->conns, conn_idx) {
161
if (http_client_connection_is_ready(*conn_idx)) {
162
struct _conn_available *conn_avail;
163
unsigned int insert_idx, pending_requests;
165
/* compile sorted availability list */
166
pending_requests = http_client_connection_count_pending(*conn_idx);
167
if (array_count(&conns_avail) == 0) {
170
insert_idx = array_count(&conns_avail);
171
array_foreach_modifiable(&conns_avail, conn_avail_idx) {
172
if (conn_avail_idx->pending_requests > pending_requests) {
173
insert_idx = array_foreach_idx(&conns_avail, conn_avail_idx);
178
conn_avail = array_insert_space(&conns_avail, insert_idx);
179
conn_avail->conn = *conn_idx;
180
conn_avail->pending_requests = pending_requests;
181
if (pending_requests == 0)
184
/* count the number of connecting and closing connections */
185
if ((*conn_idx)->closing)
187
else if (!(*conn_idx)->connected)
191
working_conn_count = array_count(&peer->conns) - closing;
192
statistics_dirty = FALSE;
194
/* use idle connections right away */
196
http_client_peer_debug(peer,
197
"Using %u idle connections to handle %u requests "
198
"(%u total connections ready)",
199
idle, num_pending > idle ? idle : num_pending,
200
array_count(&conns_avail));
202
array_foreach_modifiable(&conns_avail, conn_avail_idx) {
203
if (num_pending == 0 || conn_avail_idx->pending_requests > 0)
206
if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) {
207
/* no longer available (probably connection error/closed) */
208
statistics_dirty = TRUE;
209
conn_avail_idx->conn = NULL;
211
/* update statistics */
212
conn_avail_idx->pending_requests++;
220
/* don't continue unless we have more pending requests */
221
num_pending = http_client_peer_requests_pending(peer, &num_urgent);
222
if (num_pending == 0) {
223
http_client_peer_check_idle(peer);
226
} while (statistics_dirty);
230
/* determine how many new connections we can set up */
231
if (peer->last_connect_failed && working_conn_count > 0 &&
232
working_conn_count == connecting) {
233
/* don't create new connections until the existing ones have
234
finished connecting successfully. */
237
if (working_conn_count - connecting + num_urgent >=
238
peer->client->set.max_parallel_connections) {
239
/* only create connections for urgent requests */
240
new_connections = (num_urgent > connecting ? num_urgent - connecting : 0);
241
} else if (num_pending <= connecting) {
242
/* there are already enough connections being made */
244
} else if (working_conn_count == connecting) {
245
/* no connections succeeded so far, don't hammer the server with more
246
than one connection attempt unless its urgent */
247
if (num_urgent > 0) {
249
(num_urgent > connecting ? num_urgent - connecting : 0);
251
new_connections = (connecting == 0 ? 1 : 0);
253
} else if (num_pending - connecting >
254
peer->client->set.max_parallel_connections - working_conn_count) {
255
/* create maximum allowed connections */
257
peer->client->set.max_parallel_connections - working_conn_count;
259
/* create as many connections as we need */
260
new_connections = num_pending - connecting;
264
/* create connections */
265
if (new_connections > 0) {
266
http_client_peer_debug(peer,
267
"Creating %u new connections to handle requests "
268
"(already %u usable, connecting to %u, closing %u)",
269
new_connections, working_conn_count - connecting,
270
connecting, closing);
271
http_client_peer_connect(peer, new_connections);
275
/* cannot create new connections for normal request; attempt pipelining */
276
if (working_conn_count - connecting >=
277
peer->client->set.max_parallel_connections) {
278
unsigned int pipeline_level = 0, total_handled = 0, handled;
280
if (!peer->allows_pipelining) {
281
http_client_peer_debug(peer,
282
"Will not pipeline until peer has shown support");
289
/* fill smallest pipelines first,
290
until all pipelines are filled to the same level */
291
array_foreach_modifiable(&conns_avail, conn_avail_idx) {
292
if (conn_avail_idx->conn == NULL)
294
if (pipeline_level == 0) {
295
pipeline_level = conn_avail_idx->pending_requests;
296
} else if (conn_avail_idx->pending_requests > pipeline_level) {
297
pipeline_level = conn_avail_idx->pending_requests;
298
break; /* restart from least busy connection */
301
if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) {
302
/* connection now unavailable */
303
conn_avail_idx->conn = NULL;
305
/* successfully pipelined */
306
conn_avail_idx->pending_requests++;
312
total_handled += handled;
313
} while (num_pending > num_urgent && handled > 0);
315
http_client_peer_debug(peer,
316
"Pipelined %u requests (filled pipelines up to %u requests)",
317
total_handled, pipeline_level);
321
/* still waiting for connections to finish */
322
http_client_peer_debug(peer,
323
"No request handled; waiting for new connections");
327
static void http_client_peer_handle_requests(struct http_client_peer *peer)
329
if (peer->to_req_handling != NULL)
330
timeout_remove(&peer->to_req_handling);
333
http_client_peer_handle_requests_real(peer);
337
void http_client_peer_trigger_request_handler(struct http_client_peer *peer)
339
/* trigger request handling through timeout */
340
if (peer->to_req_handling == NULL) {
341
peer->to_req_handling =
342
timeout_add_short(0, http_client_peer_handle_requests, peer);
346
static struct http_client_peer *
347
http_client_peer_create(struct http_client *client,
348
const struct http_client_peer_addr *addr)
350
struct http_client_peer *peer;
352
i_assert(addr->https_name == NULL || client->ssl_ctx != NULL);
354
peer = i_new(struct http_client_peer, 1);
355
peer->client = client;
357
peer->https_name = i_strdup(addr->https_name);
358
peer->addr.https_name = peer->https_name;
359
i_array_init(&peer->queues, 16);
360
i_array_init(&peer->conns, 16);
363
(client->peers, (const struct http_client_peer_addr *)&peer->addr, peer);
364
DLLIST_PREPEND(&client->peers_list, peer);
366
http_client_peer_debug(peer, "Peer created");
370
void http_client_peer_free(struct http_client_peer **_peer)
372
struct http_client_peer *peer = *_peer;
373
struct http_client_connection **conn;
374
ARRAY_TYPE(http_client_connection) conns;
378
peer->destroyed = TRUE;
380
http_client_peer_debug(peer, "Peer destroy");
382
if (peer->to_req_handling != NULL)
383
timeout_remove(&peer->to_req_handling);
385
/* make a copy of the connection array; freed connections modify it */
386
t_array_init(&conns, array_count(&peer->conns));
387
array_copy(&conns.arr, 0, &peer->conns.arr, 0, array_count(&peer->conns));
388
array_foreach_modifiable(&conns, conn) {
389
http_client_connection_unref(conn);
392
i_assert(array_count(&peer->conns) == 0);
393
array_free(&peer->conns);
394
array_free(&peer->queues);
397
(peer->client->peers, (const struct http_client_peer_addr *)&peer->addr);
398
DLLIST_REMOVE(&peer->client->peers_list, peer);
400
i_free(peer->https_name);
405
struct http_client_peer *
406
http_client_peer_get(struct http_client *client,
407
const struct http_client_peer_addr *addr)
409
struct http_client_peer *peer;
411
peer = hash_table_lookup(client->peers, addr);
413
peer = http_client_peer_create(client, addr);
418
bool http_client_peer_have_queue(struct http_client_peer *peer,
419
struct http_client_queue *queue)
421
struct http_client_queue *const *queue_idx;
423
array_foreach(&peer->queues, queue_idx) {
424
if (*queue_idx == queue)
430
void http_client_peer_link_queue(struct http_client_peer *peer,
431
struct http_client_queue *queue)
433
if (!http_client_peer_have_queue(peer, queue))
434
array_append(&peer->queues, &queue, 1);
437
void http_client_peer_unlink_queue(struct http_client_peer *peer,
438
struct http_client_queue *queue)
440
struct http_client_queue *const *queue_idx;
442
array_foreach(&peer->queues, queue_idx) {
443
if (*queue_idx == queue) {
444
array_delete(&peer->queues,
445
array_foreach_idx(&peer->queues, queue_idx), 1);
446
if (array_count(&peer->queues) == 0)
447
http_client_peer_free(&peer);
453
struct http_client_request *
454
http_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent)
456
struct http_client_queue *const *queue_idx;
457
struct http_client_request *req;
459
array_foreach(&peer->queues, queue_idx) {
460
if ((req=http_client_queue_claim_request
461
(*queue_idx, &peer->addr, no_urgent)) != NULL) {
470
void http_client_peer_connection_success(struct http_client_peer *peer)
472
struct http_client_queue *const *queue;
474
peer->last_connect_failed = FALSE;
476
array_foreach(&peer->queues, queue) {
477
http_client_queue_connection_success(*queue, &peer->addr);
480
http_client_peer_trigger_request_handler(peer);
483
void http_client_peer_connection_failure(struct http_client_peer *peer,
486
struct http_client_queue *const *queue;
487
unsigned int num_urgent;
489
i_assert(array_count(&peer->conns) > 0);
491
http_client_peer_debug(peer, "Failed to make connection");
493
peer->last_connect_failed = TRUE;
494
if (array_count(&peer->conns) > 1) {
495
/* if there are other connections attempting to connect, wait
496
for them before failing the requests. remember that we had
497
trouble with connecting so in future we don't try to create
498
more than one connection until connects work again. */
500
/* this was the only/last connection and connecting to it
501
failed. a second connect will probably also fail, so just
502
try another IP for the hosts(s) or abort all requests if this
503
was the only/last option. */
504
array_foreach(&peer->queues, queue) {
505
http_client_queue_connection_failure(*queue, &peer->addr, reason);
508
if (array_count(&peer->conns) == 0 &&
509
http_client_peer_requests_pending(peer, &num_urgent) == 0)
510
http_client_peer_free(&peer);
513
void http_client_peer_connection_lost(struct http_client_peer *peer)
515
unsigned int num_urgent;
517
/* we get here when an already connected connection fails. if the
518
connect itself fails, http_client_peer_connection_failure() is
524
http_client_peer_debug(peer, "Lost a connection (%d connections left)",
525
array_count(&peer->conns));
527
/* if there are pending requests for this peer, create a new connection
529
http_client_peer_trigger_request_handler(peer);
531
if (array_count(&peer->conns) == 0 &&
532
http_client_peer_requests_pending(peer, &num_urgent) == 0)
533
http_client_peer_free(&peer);
536
unsigned int http_client_peer_idle_connections(struct http_client_peer *peer)
538
struct http_client_connection *const *conn_idx;
539
unsigned int idle = 0;
541
/* find idle connections */
542
array_foreach(&peer->conns, conn_idx) {
543
if (http_client_connection_is_idle(*conn_idx))
550
void http_client_peer_switch_ioloop(struct http_client_peer *peer)
552
if (peer->to_req_handling != NULL) {
553
peer->to_req_handling =
554
io_loop_move_timeout(&peer->to_req_handling);