/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "net.h"
#include "str.h"
#include "hash.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "llist.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "time-util.h"
#include "dns-lookup.h"
#include "http-response-parser.h"
#include "http-client-private.h"
static void
static void
static void
/*
* Queue object
*/
static struct http_client_queue *
const struct http_client_peer_addr *addr)
{
return queue;
}
return NULL;
}
static struct http_client_queue *
const struct http_client_peer_addr *addr)
{
break;
break;
break;
break;
default:
i_unreached();
}
queue->ips_connect_idx = 0;
return queue;
}
struct http_client_queue *
const struct http_client_peer_addr *addr)
{
return queue;
}
{
/* currently only called when peer is freed, so there is no need to
unlink from the peer */
/* unlink all peers */
}
/* abort all requests */
/* cancel timeouts */
/* free */
}
/*
* Error handling
*/
static void
{
struct http_client_request **req_idx;
unsigned int retained = 0;
/* abort requests */
if (queued_only &&
retained++;
else
}
/* all queues should be empty now... unless new requests were submitted
from the callback. this invariant captures it all: */
}
static void
{
}
/*
* Connection management
*/
static bool
{
/* if a maximum connect attempts > 1 is set, enforce it directly */
return TRUE;
/* otherwise, we'll always go through all the IPs. we don't necessarily
start connecting from the first IP, so we'll need to treat the IPs as
a ring buffer where we automatically wrap back to the first IP
when necessary. */
}
static void
{
unsigned int ip_idx;
return;
}
/* continue with current peer */
} else {
/* reset connect attempts */
}
}
static void
{
const char *https_name;
/* no more IPs to try */
return;
}
/* if our our previous connection attempt takes longer than the
soft_connect_timeout, we start a connection attempt to the next IP in
parallel */
"starting parallel connection attempt to next IP",
/* next IP */
/* setup connection to new peer (can start new soft timeout) */
}
static struct http_client_peer *
{
unsigned int num_requests =
int ret;
if (num_requests == 0)
return NULL;
/* check whether host IPs are still up-to-date */
/* performing asynchronous lookup */
return NULL;
}
if (ret > 0) {
/* new lookup performed */
}
/* update our peer address */
}
/* already got a peer? */
/* is it still the one we want? */
/* is it still connected? */
/* yes */
"Using existing connection to %s%s "
"(%u requests pending)",
/* handle requests; */
}
/* no */
} else {
/* peer is not relevant to this queue anymore */
}
}
/* create provisional link between queue and peer */
if (http_client_peer_is_connected(peer)) {
/* drop any pending peers */
}
}
} else {
unsigned int msecs;
/* not already connected, wait for connections */
/* we may be waiting for this peer already */
break;
}
}
if (new_peer) {
if (queue->connect_attempts++ == 0)
}
/* start soft connect time-out (but only if we have another IP left) */
}
}
}
return peer;
}
{
}
unsigned int
{
unsigned int reqs_pending =
if (reqs_pending > 0)
return reqs_pending;
}
{
}
void
struct http_client_peer *peer)
{
if (http_client_host_ready(host) &&
/* we achieved at least one connection the the addr->ip */
/* list of IPs changed during connect */
queue->ips_connect_start_idx = 0;
}
}
/* reset attempt counter */
queue->connect_attempts = 0;
/* stop soft connect time-out */
/* drop all other attempts to the hport. note that we get here whenever
a connection is successfully created, so pending_peers array
may be empty. */
/* don't drop any connections to the successfully
connected peer, even if some of the connections
are pending. they may be intended for urgent
requests. */
continue;
}
peer will be freed, closing all connections.
*/
}
}
}
void
{
"Failed to set up connection to %s%s: %s "
"(%u peers pending, %u requests pending)",
} else {
/* we're still doing the initial connections to this hport. if
we're also doing parallel connections with soft timeouts
(pending_peer_count>1), wait for them to finish
first. */
break;
}
}
"Waiting for remaining pending peers.");
return;
}
/* one of the connections failed. if we're not using soft timeouts,
we need to try to connect to the next IP. if we are using soft
timeouts, we've already tried all of the IPs by now. */
return;
}
}
/* all IPs failed, but retry all of them again if we have more
connect attempts left or on the next request. */
"Failed to set up any connection; failing all queued requests");
unsigned int total_msecs =
}
queue->connect_attempts = 0;
return;
}
} else {
}
}
return;
}
void
struct http_client_peer *peer)
{
return;
}
break;
}
}
}
/*
* Main request queue
*/
void
struct http_client_request *req)
{
unsigned int count, i;
/* drop from queue */
for (i = 0; i < count; i++) {
break;
}
}
} else {
for (i = 0; i < count; i++) {
break;
}
}
}
/* drop from delay queue */
for (i = 0; i < count; i++) {
break;
}
if (i < count) {
if (i == 0) {
if (count > 1) {
}
}
}
}
}
/* drop from main request list */
for (i = 0; i < count; i++) {
break;
}
if (i == 0) {
}
}
return;
}
static void
{
unsigned int count, i;
/* collect failed requests */
for (i = 0; i < count; i++) {
&ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) {
break;
}
}
/* update timeout */
if (i < count)
/* abort all failed request */
for (i = 0; i < count; i++) {
"Absolute timeout expired for request %s (%s)",
"Absolute request timeout expired (%s)",
}
}
}
static void
{
"Set request timeout to %s.%03lu (now: %s.%03lu)",
/* set timer */
}
static int
struct http_client_request *const *req2)
{
int ret;
/* 0 means no timeout */
/* sort by age */
return ret;
} else {
return 1;
}
return -1;
/* sort by timeout */
} else if
return ret;
}
/* sort by minimum attempts for fairness */
}
struct http_client_request *req)
{
else
/* enqueue */
/* no timeout; enqueue at end */
/* pretty much already timed out; don't bother */
return;
} else {
unsigned int insert_idx;
/* keep transmission queue sorted earliest timeout first */
}
}
/*
* Delayed request queue
*/
static void
{
finished = 0;
for (i = 0; i < count; i++) {
&ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) {
break;
}
finished++;
}
if (i < count) {
}
}
static void
{
int msecs;
/* round up to nearest microsecond */
/* set timer */
}
static int
struct http_client_request *const *req2)
{
}
/*
* Request submission
*/
struct http_client_request *req)
{
unsigned int insert_idx;
/* check delay vs timeout */
/* release time is later than absolute timeout */
/* timeout rightaway */
"Delayed request %s%s already timed out",
}
/* add to main request list */
/* no timeout; just append */
} else {
unsigned int insert_idx;
/* keep main request list sorted earliest timeout first */
/* now first in queue; update timer */
if (insert_idx == 0)
}
/* handle delay */
&ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) {
"Delayed request %s%s submitted (time remaining: %d msecs)",
if (insert_idx == 0)
return;
}
}
}
/*
* Request retrieval
*/
struct http_client_request *
{
unsigned int i, count;
count = 0;
if (!no_urgent)
if (count == 0)
if (count == 0)
return NULL;
i = 0;
else
"Connection to peer %s claimed request %s %s",
return req;
}
unsigned int
unsigned int *num_urgent_r)
{
if (num_urgent_r != NULL)
}
unsigned int
{
}
/*
* ioloop
*/
{
}