/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "net.h"
#include "str.h"
#include "hash.h"
#include "llist.h"
#include "array.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "connection.h"
#include "dns-lookup.h"
#include "iostream-rawlog.h"
#include "iostream-ssl.h"
#include "http-url.h"
#include "http-client-private.h"
/* Structure:
http_client_context:
Shared context between multiple independent HTTP clients. This allows host
name lookup data, peer status and idle connections to be shared between
clients.
http_client:
Acts much like a browser; it is not dedicated to a single host. Client can
accept requests to different hosts, which can be served at different IPs.
Redirects are handled in the background by making a new connection.
Connections to new hosts are created once needed for servicing a request.
http_client_request:
The request semantics are similar to imapc commands. Create a request,
optionally modify some aspects of it, and finally submit it. Once finished,
a callback is called with the returned response.
http_client_host_shared:
We maintain a 'cache' of hosts for which we have looked up IPs. This cache
is maintained in client context, so multiple clients can share it. One host
can have multiple IPs.
http_client_host:
A host object maintains client-specific information for a host. The queues
that the client has for this host are listed here. For one host, there is a
separate queue for each used server port.
http_client_queue:
Requests are queued in a queue object. These queues are maintained for each
host:port target and listed in the host object. The queue object is
responsible for starting connection attempts to TCP port at the various IPs
known for the host.
http_client_peer_pool:
A peer pool lists all unused and pending connections to a peer, grouped by
a compatible configuration, e.g. in terms of SSL and rawlog. Once needed,
http_client_peer_shared:
The shared peer object records state information about a peer, which is a
service access point (ip:port or unix socket path). The peer object also
maintains lists of idle and pending connections to this service, which are
grouped in pools with compatible client configuration. Each client has a
separate (non-shared) peer object for client-specific state information.
http_client_peer:
A peer object maintains client-specific information for a peer. Claimed
connections are dedicated to one peer (and therefore one client).
http-client-connection:
This is an actual connection to a server. Once a connection is ready to
handle requests, it claims a request from a queue object. One connection can
service multiple hosts and one host can have multiple associated connections,
possibly to different ips and ports.
*/
static void
struct http_client *client);
static void
struct http_client *client);
/*
* Client
*/
struct http_client *
const struct http_client_settings *set)
{
static unsigned int id = 0;
const char *log_prefix;
/* create private context if none is provided */
id++;
} else {
log_prefix = "http-client: ";
}
parent_event = NULL;
else {
/* FIXME: we could use cctx->event, but it already has a log
prefix that we don't want.. should we update event API to
support replacing parent's log prefix? */
}
/* merge provided settings with context defaults */
}
}
if (set->max_idle_time_msecs > 0)
if (set->max_parallel_connections > 0)
if (set->max_pipelined_requests > 0)
if (set->max_attempts > 0)
if (set->max_connect_attempts > 0)
if (set->connect_backoff_time_msecs > 0) {
}
if (set->connect_backoff_max_time_msecs > 0) {
}
if (set->max_redirects > 0)
if (set->request_absolute_timeout_msecs > 0) {
}
if (set->request_timeout_msecs > 0)
if (set->connect_timeout_msecs > 0)
if (set->soft_connect_timeout_msecs > 0)
if (set->socket_send_buffer_size > 0)
if (set->socket_recv_buffer_size > 0)
if (set->max_auto_retry_delay > 0)
}
return client;
}
struct http_client *
{
}
struct http_client *
{
}
{
/* destroy requests without calling callbacks */
}
/* free peers */
}
/* free hosts */
}
}
{
/* move peers */
/* move timeouts */
}
}
{
return prev_ioloop;
}
{
if (client->requests_count == 0)
return;
/* either we're waiting for network I/O or we're getting out of a
callback using timeout_add_short(0) */
do {
} while (client->requests_count > 0);
if (prev_client_ioloop != NULL)
else
(void)http_client_switch_ioloop(client);
}
{
return client->requests_count;
}
{
const char *error;
return 0;
*error_r = "Requested https connection, but no SSL settings given";
return -1;
}
error);
return -1;
}
return 0;
}
/*
* Delayed request errors
*/
static void
{
}
}
struct http_client_request *req)
{
}
}
struct http_client_request *req)
{
unsigned int i, count;
for (i = 0; i < count; i++) {
return;
}
}
}
/*
* Client shared context
*/
struct http_client_context *
{
}
}
set->connect_backoff_time_msecs == 0 ?
set->connect_backoff_max_time_msecs == 0 ?
set->request_timeout_msecs == 0 ?
return cctx;
}
{
}
{
return;
/* free hosts */
}
/* close all idle connections */
}
}
static unsigned int
{
if (set->connect_timeout_msecs > 0)
return set->connect_timeout_msecs;
if (set->request_timeout_msecs > 0)
return set->request_timeout_msecs;
}
static void
{
bool debug;
/* revert back to context settings */
/* override with available client settings */
unsigned dns_lookup_timeout_msecs =
}
if (dns_lookup_timeout_msecs != 0 &&
}
}
}
static void
struct http_client *client)
{
}
static void
struct http_client *client)
{
}
}
{
/* Switching to NULL ioloop;
close all hosts, peers, and connections */
(struct http_client_connection *)_conn;
_conn = _conn_next;
}
}
}
}
static void
{
/* move connections */
/* FIXME: we wouldn't necessarily need to switch all of them
immediately, only those that have requests now. but also connections
that get new requests before ioloop is switched again.. */
(struct http_client_connection *)_conn;
}
/* move backoff timeouts */
/* move dns lookups and delayed requests */
}
{
}
static void http_client_global_context_free(void)
{
}
static void
{
if (current_ioloop == NULL) {
return;
}
/* follow the current ioloop if there is no client */
}
}
{
if (http_client_global_context != NULL)
return http_client_global_context;
/* keep this a bit higher than lib-ssl-iostream */
return http_client_global_context;
}