http-client.c revision 56d1345c43bbd28c36b7faa85e4163bd9e874290
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#include "lib.h"
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#include "net.h"
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#include "str.h"
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#include "hash.h"
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#include "array.h"
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#include "ioloop.h"
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#include "istream.h"
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync#include "ostream.h"
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#include "connection.h"
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync#include "dns-lookup.h"
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#include "iostream-rawlog.h"
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#include "iostream-ssl.h"
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#include "http-url.h"
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#include "http-client-private.h"
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#define HTTP_DEFAULT_PORT 80
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync#define HTTPS_DEFAULT_PORT 443
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync/* FIXME: This implementation not yet finished. The essence works: it is
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync possible to submit requests through the client. Responses are dumped to
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync stdout
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync Structure so far:
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync Client - Acts much like a browser; it is not dedicated to a single host.
0c437bb10c61b229407a7517efde04dfe3b1e4a1vboxsync Client can accept requests to different hosts, which can be served
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync at different IPs. Redirects can be handled in the background by
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync making a new connection. Connections to new hosts are created once
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync needed for servicing a request.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync Requests - Semantics are similar to imapc commands. Create a request,
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync optionally modify some aspects of it and finally submit it.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync Hosts - We maintain a 'cache' of hosts for which we have looked up IPs.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync Requests are first queued in the host struct on a per-port basis.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync Peers - Group connections to the same ip/port (== peer_addr).
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync Connections - Actual connections to a server. Once a connection is ready to
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync handle requests, it claims a request from a host object. One
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync connection hand service multiple hosts and one host can have
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync multiple associated connections, possibly to different ips and
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync ports.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync TODO: lots of cleanup, authentication, ssl, timeouts, rawlog etc.
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync/*
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Logging
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncstatic inline void
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsynchttp_client_debug(struct http_client *client,
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync const char *format, ...) ATTR_FORMAT(2, 3);
0493c79a1867f7760af6bc330c8c42a09da852abvboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncstatic inline void
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsynchttp_client_debug(struct http_client *client,
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync const char *format, ...)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync{
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync va_list args;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync va_start(args, format);
22ec733a5e041fcdfe02fce2eafc9faf8b0077ddvboxsync if (client->set.debug)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync i_debug("http-client: %s", t_strdup_vprintf(format, args));
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync va_end(args);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync}
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync/*
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync * Client
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncstruct http_client *http_client_init(const struct http_client_settings *set)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync{
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct http_client *client;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync pool_t pool;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync pool = pool_alloconly_create("http client", 1024);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client = p_new(pool, struct http_client, 1);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->pool = pool;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync if (set->dns_client_socket_path != NULL && *set->dns_client_socket_path != '\0') {
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.dns_client_socket_path =
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync p_strdup(pool, set->dns_client_socket_path);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync }
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync if (set->rawlog_dir != NULL && *set->rawlog_dir != '\0')
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.rawlog_dir = p_strdup(pool, set->rawlog_dir);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir);
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync client->set.ssl_ca_file = p_strdup(pool, set->ssl_ca_file);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.ssl_ca = p_strdup(pool, set->ssl_ca);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.ssl_crypto_device = p_strdup(pool, set->ssl_crypto_device);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.ssl_allow_invalid_cert = set->ssl_allow_invalid_cert;
cfb87b49d2b36055eb5aea681936cdc130b54267vboxsync client->set.max_idle_time_msecs = set->max_idle_time_msecs;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.max_parallel_connections =
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync (set->max_parallel_connections > 0 ? set->max_parallel_connections : 1);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.max_pipelined_requests =
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync (set->max_pipelined_requests > 0 ? set->max_pipelined_requests : 1);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.max_attempts = set->max_attempts;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.max_redirects = set->max_redirects;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->set.debug = set->debug;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->conn_list = http_client_connection_list_init();
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync hash_table_create(&client->hosts, default_pool, 0, str_hash, strcmp);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync hash_table_create(&client->peers, default_pool, 0,
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync http_client_peer_addr_hash, http_client_peer_addr_cmp);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync return client;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync}
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncvoid http_client_deinit(struct http_client **_client)
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync{
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct http_client *client = *_client;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct http_client_host *host;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct http_client_peer *peer;
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /* free peers */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync while (client->peers_list != NULL) {
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync peer = client->peers_list;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync http_client_peer_free(&peer);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync }
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync hash_table_destroy(&client->peers);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
b4fc07ae3f394595370fc5ed6ab1c353a87259dbvboxsync /* free hosts */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync while (client->hosts_list != NULL) {
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync host = client->hosts_list;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync http_client_host_free(&host);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync }
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync hash_table_destroy(&client->hosts);
b4fc07ae3f394595370fc5ed6ab1c353a87259dbvboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync connection_list_deinit(&client->conn_list);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync if (client->ssl_ctx != NULL)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync ssl_iostream_context_deinit(&client->ssl_ctx);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync pool_unref(&client->pool);
8bed792bc65abd39393889351f22263ce6c289bfvboxsync *_client = NULL;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync}
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncvoid http_client_switch_ioloop(struct http_client *client)
b4fc07ae3f394595370fc5ed6ab1c353a87259dbvboxsync{
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct connection *_conn = client->conn_list->connections;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct http_client_host *host;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /* move connections */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /* FIXME: we wouldn't necessarily need to switch all of them
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync immediately, only those that have requests now. but also connections
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync that get new requests before ioloop is switched again.. */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync for (; _conn != NULL; _conn = _conn->next) {
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct http_client_connection *conn =
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync (struct http_client_connection *)_conn;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync http_client_connection_switch_ioloop(conn);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync }
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /* move dns lookups and delayed requests */
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync for (host = client->hosts_list; host != NULL; host = host->next)
9ee436b6765f11cddb90819b9c4fc67899ba479bvboxsync http_client_host_switch_ioloop(host);
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync}
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsyncvoid http_client_wait(struct http_client *client)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync{
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct ioloop *prev_ioloop = current_ioloop;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync i_assert(client->ioloop == NULL);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync if (client->pending_requests == 0)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync return;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync client->ioloop = io_loop_create();
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync http_client_switch_ioloop(client);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync /* either we're waiting for network I/O or we're getting out of a
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync callback using timeout_add_short(0) */
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync i_assert(io_loop_have_ios(client->ioloop) ||
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync io_loop_have_immediate_timeouts(client->ioloop));
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync do {
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync http_client_debug(client,
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync "Waiting for %d requests to finish", client->pending_requests);
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync io_loop_run(client->ioloop);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync } while (client->pending_requests > 0);
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync http_client_debug(client, "All requests finished");
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync current_ioloop = prev_ioloop;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync http_client_switch_ioloop(client);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync current_ioloop = client->ioloop;
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync io_loop_destroy(&client->ioloop);
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync}
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsyncint http_client_init_ssl_ctx(struct http_client *client, const char **error_r)
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync{
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync struct ssl_iostream_settings ssl_set;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync const char *error;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync if (client->ssl_ctx != NULL)
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync return 0;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync memset(&ssl_set, 0, sizeof(ssl_set));
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync ssl_set.ca_dir = client->set.ssl_ca_dir;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync ssl_set.ca_file = client->set.ssl_ca_file;
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync ssl_set.ca = client->set.ssl_ca;
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync ssl_set.verify_remote_cert = TRUE;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync ssl_set.crypto_device = client->set.ssl_crypto_device;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync ssl_set.verbose = client->set.debug;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync ssl_set.verbose_invalid_cert = client->set.debug;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync if (ssl_iostream_context_init_client(&ssl_set, &client->ssl_ctx, &error) < 0) {
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync *error_r = t_strdup_printf("Couldn't initialize SSL context: %s",
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync error);
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync return -1;
a0f8619bc2bbe3614578c21b5b50a88d2841e7aavboxsync }
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync return 0;
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync}
632b4638bd18092c6b8edb4e1028c9be112f5076vboxsync