#include "http-url.h"
#include "http-client.h"
+/*
+ * Defaults
+ */
+
#define HTTP_DEFAULT_PORT 80
#define HTTPS_DEFAULT_PORT 443
#define HTTP_CLIENT_DEFAULT_BACKOFF_TIME_MSECS (100)
#define HTTP_CLIENT_DEFAULT_BACKOFF_MAX_TIME_MSECS (1000*60)
+/*
+ * Types
+ */
+
enum http_response_payload_type;
struct http_client_host;
} a;
};
+/*
+ * Objects
+ */
+
struct http_client_request {
pool_t pool;
unsigned int refcount;
unsigned int requests_count;
};
-int http_client_init_ssl_ctx(struct http_client *client, const char **error_r);
+/*
+ * Peer address
+ */
+
+static inline bool
+http_client_peer_addr_is_https(const struct http_client_peer_addr *addr)
+{
+ switch (addr->type) {
+ case HTTP_CLIENT_PEER_ADDR_HTTPS:
+ case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static inline const char *
+http_client_peer_addr_get_https_name(const struct http_client_peer_addr *addr)
+{
+ switch (addr->type) {
+ case HTTP_CLIENT_PEER_ADDR_HTTPS:
+ case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+ return addr->a.tcp.https_name;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static inline const char *
+http_client_peer_addr2str(const struct http_client_peer_addr *addr)
+{
+ switch (addr->type) {
+ case HTTP_CLIENT_PEER_ADDR_HTTP:
+ case HTTP_CLIENT_PEER_ADDR_HTTPS:
+ case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+ case HTTP_CLIENT_PEER_ADDR_RAW:
+ if (addr->a.tcp.ip.family == AF_INET6) {
+ return t_strdup_printf("[%s]:%u",
+ net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port);
+ }
+ return t_strdup_printf("%s:%u",
+ net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port);
+ case HTTP_CLIENT_PEER_ADDR_UNIX:
+ return t_strdup_printf("unix:%s", addr->a.un.path);
+ default:
+ break;
+ }
+ i_unreached();
+ return "";
+}
+
+/*
+ * Request
+ */
+
+static inline const char *
+http_client_request_label(struct http_client_request *req)
+{
+ if (req->label == NULL) {
+ return t_strdup_printf("[Req%u: %s %s%s]", req->id,
+ req->method, http_url_create(&req->origin_url), req->target);
+ }
+ return req->label;
+}
+
+static inline bool
+http_client_request_to_proxy(const struct http_client_request *req)
+{
+ return (req->host_url != &req->origin_url);
+}
void http_client_request_ref(struct http_client_request *req);
/* Returns FALSE if unrefing destroyed the request entirely */
unsigned int status, const char *location);
void http_client_request_finish(struct http_client_request *req);
+/*
+ * Connection
+ */
+
+static inline const char *
+http_client_connection_label(struct http_client_connection *conn)
+{
+ return t_strdup_printf("%s%s [%d]",
+ http_client_peer_addr2str(&conn->peer->addr),
+ (conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL ?
+ " (tunnel)" : ""), conn->id);
+}
+
struct connection_list *http_client_connection_list_init(void);
struct http_client_connection *
void http_client_connection_start_tunnel(struct http_client_connection **_conn,
struct http_client_tunnel *tunnel);
+/*
+ * Peer
+ */
+
+static inline const char *
+http_client_peer_label(struct http_client_peer *peer)
+{
+ if (peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL) {
+ return t_strconcat
+ (http_client_peer_addr2str(&peer->addr), " (tunnel)", NULL);
+ }
+ return http_client_peer_addr2str(&peer->addr);
+}
unsigned int http_client_peer_addr_hash
(const struct http_client_peer_addr *peer) ATTR_PURE;
http_client_peer_pending_connections(struct http_client_peer *peer);
void http_client_peer_switch_ioloop(struct http_client_peer *peer);
+/*
+ * Queue
+ */
+
struct http_client_queue *
http_client_queue_create(struct http_client_host *host,
const struct http_client_peer_addr *addr);
struct http_client_peer *peer);
void http_client_queue_switch_ioloop(struct http_client_queue *queue);
-struct http_client_host *
-http_client_host_get(struct http_client *client,
- const struct http_url *host_url);
-void http_client_host_free(struct http_client_host **_host);
-void http_client_host_submit_request(struct http_client_host *host,
- struct http_client_request *req);
-void http_client_host_switch_ioloop(struct http_client_host *host);
-
-void http_client_delay_request_error(struct http_client *client,
- struct http_client_request *req);
-void http_client_remove_request_error(struct http_client *client,
- struct http_client_request *req);
-
-
-static inline bool
-http_client_peer_addr_is_https(const struct http_client_peer_addr *addr)
-{
- switch (addr->type) {
- case HTTP_CLIENT_PEER_ADDR_HTTPS:
- case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
- return TRUE;
- default:
- break;
- }
- return FALSE;
-}
-
-static inline const char *
-http_client_peer_addr_get_https_name(const struct http_client_peer_addr *addr)
-{
- switch (addr->type) {
- case HTTP_CLIENT_PEER_ADDR_HTTPS:
- case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
- return addr->a.tcp.https_name;
- default:
- break;
- }
- return NULL;
-}
-
-static inline const char *
-http_client_peer_addr2str(const struct http_client_peer_addr *addr)
-{
- switch (addr->type) {
- case HTTP_CLIENT_PEER_ADDR_HTTP:
- case HTTP_CLIENT_PEER_ADDR_HTTPS:
- case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
- case HTTP_CLIENT_PEER_ADDR_RAW:
- if (addr->a.tcp.ip.family == AF_INET6) {
- return t_strdup_printf("[%s]:%u",
- net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port);
- }
- return t_strdup_printf("%s:%u",
- net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port);
- case HTTP_CLIENT_PEER_ADDR_UNIX:
- return t_strdup_printf("unix:%s", addr->a.un.path);
- default:
- break;
- }
- i_unreached();
- return "";
-}
-
-static inline const char *
-http_client_request_label(struct http_client_request *req)
-{
- if (req->label == NULL) {
- return t_strdup_printf("[Req%u: %s %s%s]", req->id,
- req->method, http_url_create(&req->origin_url), req->target);
- }
- return req->label;
-}
-
-static inline bool
-http_client_request_to_proxy(const struct http_client_request *req)
-{
- return (req->host_url != &req->origin_url);
-}
-
-static inline const char *
-http_client_connection_label(struct http_client_connection *conn)
-{
- return t_strdup_printf("%s%s [%d]",
- http_client_peer_addr2str(&conn->peer->addr),
- (conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL ?
- " (tunnel)" : ""), conn->id);
-}
-
-static inline const char *
-http_client_peer_label(struct http_client_peer *peer)
-{
- if (peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL) {
- return t_strconcat
- (http_client_peer_addr2str(&peer->addr), " (tunnel)", NULL);
- }
- return http_client_peer_addr2str(&peer->addr);
-}
+/*
+ * Host
+ */
static inline unsigned int
http_client_host_get_ip_idx(struct http_client_host *host,
i_unreached();
}
+struct http_client_host *
+http_client_host_get(struct http_client *client,
+ const struct http_url *host_url);
+void http_client_host_free(struct http_client_host **_host);
+void http_client_host_submit_request(struct http_client_host *host,
+ struct http_client_request *req);
+void http_client_host_switch_ioloop(struct http_client_host *host);
+
+/*
+ * Client
+ */
+
+int http_client_init_ssl_ctx(struct http_client *client,
+ const char **error_r);
+
+void http_client_delay_request_error(struct http_client *client,
+ struct http_client_request *req);
+void http_client_remove_request_error(struct http_client *client,
+ struct http_client_request *req);
#endif
struct http_client;
struct http_client_request;
-enum http_client_request_error {
- HTTP_CLIENT_REQUEST_ERROR_ABORTED = 9000,
- HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED,
- HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED,
- HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
- HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST,
- HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD,
- HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE,
- HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT,
-};
-
-enum http_request_state {
- HTTP_REQUEST_STATE_NEW = 0,
- HTTP_REQUEST_STATE_QUEUED,
- HTTP_REQUEST_STATE_PAYLOAD_OUT,
- HTTP_REQUEST_STATE_WAITING,
- HTTP_REQUEST_STATE_GOT_RESPONSE,
- HTTP_REQUEST_STATE_PAYLOAD_IN,
- HTTP_REQUEST_STATE_FINISHED,
- HTTP_REQUEST_STATE_ABORTED
-};
-extern const char *http_request_state_names[];
+/*
+ * Client settings
+ */
struct http_client_settings {
/* a) If dns_client is set, all lookups are done via it.
struct dns_client *dns_client;
const char *dns_client_socket_path;
+ /* ssl configuration */
const char *ssl_ca_dir, *ssl_ca_file, *ssl_ca;
const char *ssl_crypto_device;
bool ssl_allow_invalid_cert;
const char *proxy_username;
const char *proxy_password;
+ /* directory for writing raw log data for debugging purposes */
const char *rawlog_dir;
+ /* maximum time a connection will idle. if parallel connections are idle,
+ the duplicates will end earlier based on how many idle connections exist
+ to that same service */
unsigned int max_idle_time_msecs;
/* maximum number of parallel connections per peer (default = 1) */
bool debug;
};
+/*
+ * Request
+ */
+
+enum http_client_request_error {
+ /* The request was aborted */
+ HTTP_CLIENT_REQUEST_ERROR_ABORTED = 9000,
+ /* Failed to perform DNS lookup for the host */
+ HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED,
+ /* Failed to setup any connection for the host and client settings allowed
+ no more attempts */
+ HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED,
+ /* Service returned an invalid redirect response for this request */
+ HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
+ /* The connection was lost unexpectedly while handling the request and
+ client settings allowed no more attempts */
+ HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST,
+ /* The input stream passed to the request using
+ http_client_request_set_payload() returned an error while sending the
+ request. */
+ HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD,
+ /* The service returned a bad response */
+ HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE,
+ /* The request timed out (either this was the last attempt or the
+ absolute timeout was hit) */
+ HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT,
+};
+
+enum http_request_state {
+ /* New request; not yet submitted */
+ HTTP_REQUEST_STATE_NEW = 0,
+ /* Request is queued; waiting for a connection */
+ HTTP_REQUEST_STATE_QUEUED,
+ /* Request header is sent; still sending request payload to server */
+ HTTP_REQUEST_STATE_PAYLOAD_OUT,
+ /* Request is fully sent; waiting for response */
+ HTTP_REQUEST_STATE_WAITING,
+ /* Response header is received for the request */
+ HTTP_REQUEST_STATE_GOT_RESPONSE,
+ /* Reading response payload; response handler still needs to read more
+ payload. */
+ HTTP_REQUEST_STATE_PAYLOAD_IN,
+ /* Request is finished; still lingering due to references */
+ HTTP_REQUEST_STATE_FINISHED,
+ /* Request is aborted; still lingering due to references */
+ HTTP_REQUEST_STATE_ABORTED
+};
+extern const char *http_request_state_names[];
+
struct http_client_tunnel {
int fd_in, fd_out;
struct istream *input;
http_client_request_callback_t(const struct http_response *response,
void *context);
-struct http_client *http_client_init(const struct http_client_settings *set);
-void http_client_deinit(struct http_client **_client);
-
/* create new HTTP request */
struct http_client_request *
http_client_request(struct http_client *client,
const struct http_response *response, typeof(context))), \
(http_client_request_callback_t *)callback, context)
+/* create net HTTP request using provided URL. This implicitly sets
+ port, ssl, and username:password if provided. */
struct http_client_request *
http_client_request_url(struct http_client *client,
const char *method, const struct http_url *target_url,
CALLBACK_TYPECHECK(callback, void (*)( \
const struct http_response *response, typeof(context))), \
(http_client_request_callback_t *)callback, context)
+
+/* same as http_client_request_connect, but uses an IP rather than a host
+ name. */
struct http_client_request *
http_client_request_connect_ip(struct http_client *client,
const struct ip_addr *ip, in_port_t port,
const struct http_response *response, typeof(context))), \
(http_client_request_callback_t *)callback, context)
+/* set the port for the service the request is directed at */
void http_client_request_set_port(struct http_client_request *req,
in_port_t port);
+/* indicate whether service the request is directed at uses ssl */
void http_client_request_set_ssl(struct http_client_request *req,
bool ssl);
+/* set the urgent flag: this means that this request will get priority over
+ non-urgent request. Also, if no idle connection is available, a new
+ connection is created. Urgent requests are never pipelined. */
void http_client_request_set_urgent(struct http_client_request *req);
void http_client_request_set_preserve_exact_reason(struct http_client_request *req);
+/* add a custom header to the request. This can override headers that are
+ otherwise created implicitly. */
void http_client_request_add_header(struct http_client_request *req,
const char *key, const char *value);
+/* remove a header added earlier. This has no influence on implicitly created
+ headers. */
void http_client_request_remove_header(struct http_client_request *req,
const char *key);
+
+/* set the value of the "Date" header for the request using a time_t value.
+ Use this instead of setting it directly using
+ http_client_request_add_header() */
void http_client_request_set_date(struct http_client_request *req,
time_t date);
+/* assign an input stream for the outgoing payload of this request. The input
+ stream is read asynchronously while the request is sent to the server.
+
+ when sync=TRUE a "100 Continue" response is requested from the service. The
+ client will then postpone sending the payload until a provisional response
+ with code 100 is received. This way, an error response can be sent by the
+ service before any potentially big payload is transmitted. Use this only for
+ payload that can be large. */
void http_client_request_set_payload(struct http_client_request *req,
struct istream *input, bool sync);
+/* assign payload data to the request. The data is copied to the request pool.
+ If your data is already durably allocated during the existence of the
+ request, you should consider using http_client_request_set_payload() with
+ a data input stream instead. This will avoid copying the data unnecessarily.
+ */
void http_client_request_set_payload_data(struct http_client_request *req,
const unsigned char *data, size_t size);
+/* set an absolute timeout for this request specifically, overriding the
+ default client-wide absolute request timeout */
void http_client_request_set_timeout_msecs(struct http_client_request *req,
unsigned int msecs);
void http_client_request_set_timeout(struct http_client_request *req,
void http_client_request_set_max_attempts(struct http_client_request *req,
unsigned int max_attempts);
+/* set the username:password credentials for this request for simple
+ authentication. This function is meant for simple schemes that use a
+ password. More complex schemes will need to be handled manually.
+
+ This currently only supports the "basic" authentication scheme. */
void http_client_request_set_auth_simple(struct http_client_request *req,
const char *username, const char *password);
+/* delay handling of this request to a later time. This way, a request can be
+ submitted that is held for some time until a certain time period has passed.
+ */
void http_client_request_delay_until(struct http_client_request *req,
time_t time);
void http_client_request_delay(struct http_client_request *req,
void http_client_request_delay_msecs(struct http_client_request *req,
unsigned int msecs);
-const char *http_client_request_get_method(struct http_client_request *req);
-const char *http_client_request_get_target(struct http_client_request *req);
+/* return the HTTP method for the request */
+const char *
+http_client_request_get_method(struct http_client_request *req);
+/* return the HTTP target for the request */
+const char *
+http_client_request_get_target(struct http_client_request *req);
+/* return the request state */
enum http_request_state
http_client_request_get_state(struct http_client_request *req);
+
+/* submit the request. It is queued for transmission to the service */
void http_client_request_submit(struct http_client_request *req);
+
+/* attempt to retry the request. This function is called within the request
+ callback. It returns false if the request cannot be retried */
bool http_client_request_try_retry(struct http_client_request *req);
+/* abort the request immediately. It may still linger for a while when it is
+ already sent to the service, but the callback will not be called anymore. */
void http_client_request_abort(struct http_client_request **req);
-/* Call the specified callback when HTTP request is destroyed. */
+/* call the specified callback when HTTP request is destroyed. */
void http_client_request_set_destroy_callback(struct http_client_request *req,
void (*callback)(void *),
void *context);
-/* submits request and blocks until provided payload is sent. Multiple calls
- are allowed; payload transmission is ended with
+/* submits request and blocks until the provided payload is sent. Multiple
+ calls are allowed; payload transmission is ended with
http_client_request_finish_payload(). If the sending fails, returns -1
and sets req=NULL to indicate that the request was freed, otherwise
returns 0 and req is unchanged. */
int http_client_request_send_payload(struct http_client_request **req,
const unsigned char *data, size_t size);
-/* Finish sending the payload. Always frees req and sets it to NULL.
+/* finish sending the payload. Always frees req and sets it to NULL.
Returns 0 on success, -1 on error. */
int http_client_request_finish_payload(struct http_client_request **req);
+/* take over the connection this request was sent over for use as a HTTP
+ CONNECT tunnel. This only applies to requests that were created using
+ http_client_request_connect() or http_client_request_connect_ip(). */
void http_client_request_start_tunnel(struct http_client_request *req,
struct http_client_tunnel *tunnel);
+/*
+ * Client
+ */
+
+struct http_client *http_client_init(const struct http_client_settings *set);
+void http_client_deinit(struct http_client **_client);
+
+/* switch this client to the current ioloop */
void http_client_switch_ioloop(struct http_client *client);
/* blocks until all currently submitted requests are handled */
void http_client_wait(struct http_client *client);
-/* Returns number of pending HTTP requests. */
-unsigned int http_client_get_pending_request_count(struct http_client *client);
+
+/* Returns the total number of pending HTTP requests. */
+unsigned int
+http_client_get_pending_request_count(struct http_client *client);
#endif