http-client.c revision e8a1b62fe4a81b211dcccd1a58b44f254074eab6
/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "net.h"
#include "str.h"
#include "hash.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"
#define HTTP_DEFAULT_PORT 80
#define HTTPS_DEFAULT_PORT 443
/* 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.
*/
/*
* Client
*/
struct http_client *
const struct http_client_settings *set)
{
static unsigned int id = 0;
struct http_client *client;
const char *log_prefix;
/* create private context if none is provided */
id++;
} else {
log_prefix = "http-client: ";
}
struct event *parent_event;
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_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->max_auto_retry_delay > 0)
}
return client;
}
struct http_client *
{
}
{
struct http_client_request *req;
struct http_client_host *host;
struct http_client_peer *peer;
/* destroy requests without calling callbacks */
}
/* free peers */
}
/* free hosts */
}
}
{
struct http_client_peer *peer;
struct http_client_host *host;
/* 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 *const *req_idx;
}
}
struct http_client_request *req)
{
}
}
struct http_client_request *req)
{
struct http_client_request *const *reqs;
unsigned int i, count;
for (i = 0; i < count; i++) {
return;
}
}
}
/*
* Client shared context
*/
struct http_client_context *
{
struct http_client_context *cctx;
}
}
set->connect_backoff_time_msecs == 0 ?
set->connect_backoff_max_time_msecs == 0 ?
set->request_timeout_msecs == 0 ?
return cctx;
}
{
}
{
struct http_client_peer_shared *peer;
struct http_client_host_shared *hshared;
return;
/* free hosts */
}
/* close all idle connections */
}
}
{
struct http_client_host_shared *hshared;
/* 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 =
(struct http_client_connection *)_conn;
}
/* move dns lookups and delayed requests */
}