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