summaryrefslogtreecommitdiffstats
path: root/src/lib-http/http-client.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-http/http-client.h')
-rw-r--r--src/lib-http/http-client.h496
1 files changed, 496 insertions, 0 deletions
diff --git a/src/lib-http/http-client.h b/src/lib-http/http-client.h
new file mode 100644
index 0000000..4f04222
--- /dev/null
+++ b/src/lib-http/http-client.h
@@ -0,0 +1,496 @@
+#ifndef HTTP_CLIENT_H
+#define HTTP_CLIENT_H
+
+#include "net.h"
+
+#include "http-common.h"
+#include "http-response.h"
+
+struct timeval;
+struct http_response;
+
+struct http_client_request;
+struct http_client;
+struct http_client_context;
+
+struct ssl_iostream_settings;
+
+/*
+ * Client settings
+ */
+
+struct http_client_settings {
+ /* a) If dns_client is set, all lookups are done via it.
+ b) If dns_client_socket_path is set, each DNS lookup does its own
+ dns-lookup UNIX socket connection.
+ c) Otherwise, blocking gethostbyname() lookups are used. */
+ struct dns_client *dns_client;
+ const char *dns_client_socket_path;
+ /* How long to cache DNS records internally
+ (default = HTTP_CLIENT_DEFAULT_DNS_TTL_MSECS) */
+ unsigned int dns_ttl_msecs;
+
+ const struct ssl_iostream_settings *ssl;
+
+ /* User-Agent: header (default: none) */
+ const char *user_agent;
+
+ /* proxy on unix socket */
+ const char *proxy_socket_path;
+ /* URL for normal proxy (ignored if proxy_socket_path is set) */
+ const struct http_url *proxy_url;
+ /* credentials for proxy */
+ 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) */
+ unsigned int max_parallel_connections;
+
+ /* maximum number of pipelined requests per connection (default = 1) */
+ unsigned int max_pipelined_requests;
+
+ /* don't automatically act upon redirect responses */
+ bool no_auto_redirect;
+
+ /* never automatically retry requests */
+ bool no_auto_retry;
+
+ /* if we use a proxy, delegate SSL negotiation to proxy, rather than
+ creating a CONNECT tunnel through the proxy for the SSL link */
+ bool no_ssl_tunnel;
+
+ /* maximum number of redirects for a request
+ (default = 0; redirects refused)
+ */
+ unsigned int max_redirects;
+
+ /* maximum number of attempts for a request */
+ unsigned int max_attempts;
+
+ /* maximum number of connection attempts to a host before all associated
+ requests fail.
+
+ if > 1, the maximum will be enforced across all IPs for that host,
+ meaning that IPs may be tried more than once eventually if the number
+ of IPs is smaller than the specified maximum attempts. If the number of IPs
+ is higher than the maximum attempts, not all IPs are tried. If <= 1, all
+ IPs are tried at most once.
+ */
+ unsigned int max_connect_attempts;
+
+ /* Initial backoff time; doubled at each connection failure
+ (default = HTTP_CLIENT_DEFAULT_BACKOFF_TIME_MSECS) */
+ unsigned int connect_backoff_time_msecs;
+ /* Maximum backoff time
+ (default = HTTP_CLIENT_DEFAULT_BACKOFF_MAX_TIME_MSECS) */
+ unsigned int connect_backoff_max_time_msecs;
+
+ /* response header limits */
+ struct http_header_limits response_hdr_limits;
+
+ /* max total time to wait for HTTP request to finish
+ this can be overridden/reset for individual requests using
+ http_client_request_set_timeout() and friends.
+ (default is no timeout)
+ */
+ unsigned int request_absolute_timeout_msecs;
+ /* max time to wait for HTTP request to finish before retrying
+ (default = HTTP_CLIENT_DEFAULT_REQUEST_TIMEOUT_MSECS) */
+ unsigned int request_timeout_msecs;
+ /* max time to wait for connect() (and SSL handshake) to finish before
+ retrying (default = request_timeout_msecs) */
+ unsigned int connect_timeout_msecs;
+ /* time to wait for connect() (and SSL handshake) to finish for the first
+ connection before trying the next IP in parallel
+ (default = 0; wait until current connection attempt finishes) */
+ unsigned int soft_connect_timeout_msecs;
+
+ /* maximum acceptable delay in seconds for automatically
+ retrying/redirecting requests. if a server sends a response with a
+ Retry-After header that causes a delay longer than this, the request
+ is not automatically retried and the response is returned */
+ unsigned int max_auto_retry_delay_secs;
+
+ /* the kernel send/receive buffer sizes used for the connection sockets.
+ Configuring this is mainly useful for the test suite. The kernel
+ defaults are used when these settings are 0. */
+ size_t socket_send_buffer_size;
+ size_t socket_recv_buffer_size;
+
+ /* Event to use as parent for the http client event. For specific
+ requests this can be overridden with http_client_request_set_event().
+ */
+ struct event *event_parent;
+
+ /* enable logging debug messages */
+ bool debug;
+};
+
+/*
+ * Request
+ */
+
+enum http_client_request_error {
+ /* The request was aborted */
+ HTTP_CLIENT_REQUEST_ERROR_ABORTED = HTTP_RESPONSE_STATUS_INTERNAL,
+ /* Failed to parse HTTP target url */
+ HTTP_CLIENT_REQUEST_ERROR_INVALID_URL,
+ /* 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;
+ struct ostream *output;
+};
+
+struct http_client_request_stats {
+ /* Total elapsed time since message was submitted */
+ unsigned int total_msecs;
+ /* Elapsed time since message was first sent */
+ unsigned int first_sent_msecs;
+ /* Elapsed time since message was last sent */
+ unsigned int last_sent_msecs;
+
+ /* Time spent in other ioloops */
+ unsigned int other_ioloop_msecs;
+ /* Time spent in the http-client's own ioloop */
+ unsigned int http_ioloop_msecs;
+ /* Total time spent on waiting for file locks */
+ unsigned int lock_msecs;
+
+ /* Number of times this request was retried */
+ unsigned int attempts;
+ /* Number of times the client attempted to actually send the request
+ to a server */
+ unsigned int send_attempts;
+};
+
+typedef void
+http_client_request_callback_t(const struct http_response *response,
+ void *context);
+
+/* create new HTTP request */
+struct http_client_request *
+http_client_request(struct http_client *client,
+ const char *method, const char *host, const char *target,
+ http_client_request_callback_t *callback, void *context);
+#define http_client_request(client, method, host, target, callback, context) \
+ http_client_request(client, method, host, target - \
+ CALLBACK_TYPECHECK(callback, void (*)( \
+ const struct http_response *response, typeof(context))), \
+ (http_client_request_callback_t *)callback, context)
+
+/* create new 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,
+ http_client_request_callback_t *callback, void *context);
+#define http_client_request_url(client, method, target_url, callback, context) \
+ http_client_request_url(client, method, target_url - \
+ CALLBACK_TYPECHECK(callback, void (*)( \
+ const struct http_response *response, typeof(context))), \
+ (http_client_request_callback_t *)callback, context)
+struct http_client_request *
+http_client_request_url_str(struct http_client *client,
+ const char *method, const char *url_str,
+ http_client_request_callback_t *callback, void *context);
+#define http_client_request_url_str(client, method, url_str, callback, context) \
+ http_client_request_url_str(client, method, url_str - \
+ CALLBACK_TYPECHECK(callback, void (*)( \
+ const struct http_response *response, typeof(context))), \
+ (http_client_request_callback_t *)callback, context)
+
+/* create new HTTP CONNECT request. If this HTTP is configured to use a proxy,
+ a CONNECT request will be submitted at that proxy, otherwise the connection
+ is created directly. Call http_client_request_start_tunnel() to
+ to take over the connection.
+ */
+struct http_client_request *
+http_client_request_connect(struct http_client *client,
+ const char *host, in_port_t port,
+ http_client_request_callback_t *callback,
+ void *context);
+#define http_client_request_connect(client, host, port, callback, context) \
+ http_client_request_connect(client, host, port - \
+ 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,
+ http_client_request_callback_t *callback,
+ void *context);
+#define http_client_request_connect_ip(client, ip, port, callback, context) \
+ http_client_request_connect_ip(client, ip, port - \
+ CALLBACK_TYPECHECK(callback, void (*)( \
+ const struct http_response *response, typeof(context))), \
+ (http_client_request_callback_t *)callback, context)
+
+void http_client_request_set_event(struct http_client_request *req,
+ struct event *event);
+/* 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. If the same header key was already added,
+ the value is replaced. */
+void http_client_request_add_header(struct http_client_request *req,
+ const char *key, const char *value);
+/* add a custom header to the request. Do nothing if it was already added. */
+void http_client_request_add_missing_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);
+/* lookup the value for a header added earlier. Returns NULL if not found. */
+const char *http_client_request_lookup_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);
+/* send an empty payload for this request. This means that a Content-Length
+ header is generated with zero size. Calling this function is not necessary
+ for the standard POST and PUT methods, for which this is done implicitly if
+ there is no payload set. */
+void http_client_request_set_payload_empty(struct http_client_request *req);
+
+/* 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,
+ const struct timeval *time);
+
+/* Override http_client_settings.request_timeout_msecs */
+void http_client_request_set_attempt_timeout_msecs(struct http_client_request *req,
+ unsigned int msecs);
+/* Override http_client_settings.max_attempts */
+void http_client_request_set_max_attempts(struct http_client_request *req,
+ unsigned int max_attempts);
+
+/* Include the specified HTTP response headers in the http_request_finished
+ event parameters with "http_hdr_" prefix. */
+void http_client_request_set_event_headers(struct http_client_request *req,
+ const char *const *headers);
+
+/* 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);
+
+/* Assign a proxy to use for this particular request. This overrides any
+ proxy defined in the client settings. */
+void http_client_request_set_proxy_url(struct http_client_request *req,
+ const struct http_url *proxy_url);
+/* Like http_client_request_set_proxy_url(), but the proxy is behind a unix
+ socket. */
+void http_client_request_set_proxy_socket(struct http_client_request *req,
+ const char *proxy_socket);
+
+/* 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,
+ time_t seconds);
+void http_client_request_delay_msecs(struct http_client_request *req,
+ unsigned int msecs);
+
+/* Try to set request delay based on the Retry-After header. Returns 1 if
+ successful, 0 if it doesn't exist or is already expired, -1 if the delay
+ would be too long. */
+int http_client_request_delay_from_response(struct http_client_request *req,
+ const struct http_response *response);
+
+/* return the HTTP method for the request */
+const char *
+http_client_request_get_method(const struct http_client_request *req)
+ ATTR_PURE;
+/* return the HTTP target for the request */
+const char *
+http_client_request_get_target(const struct http_client_request *req)
+ ATTR_PURE;
+/* return the request state */
+enum http_request_state
+http_client_request_get_state(const struct http_client_request *req)
+ ATTR_PURE;
+/* return number of retry attempts */
+unsigned int
+http_client_request_get_attempts(const struct http_client_request *req)
+ ATTR_PURE;
+/* return origin_url */
+const struct http_url *
+http_client_request_get_origin_url(const struct http_client_request *req)
+ ATTR_PURE;
+
+/* get statistics for the request */
+void http_client_request_get_stats(struct http_client_request *req,
+ struct http_client_request_stats *stats);
+/* append text with request statistics to provided string buffer */
+void http_client_request_append_stats_text(struct http_client_request *req,
+ string_t *str);
+
+/* 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. */
+void http_client_request_set_destroy_callback(struct http_client_request *req,
+ void (*callback)(void *),
+ void *context);
+#define http_client_request_set_destroy_callback(req, callback, context) \
+ http_client_request_set_destroy_callback(req, (void(*)(void*))callback, \
+ TRUE ? context : \
+ CALLBACK_TYPECHECK(callback, void (*)(typeof(context))))
+
+/* 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.
+ 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
+ */
+
+/* Create a client using the global shared client context. */
+struct http_client *
+http_client_init(const struct http_client_settings *set);
+/* Create a client without a shared context. */
+struct http_client *
+http_client_init_private(const struct http_client_settings *set);
+struct http_client *
+http_client_init_shared(struct http_client_context *cctx,
+ const struct http_client_settings *set) ATTR_NULL(1);
+void http_client_deinit(struct http_client **_client);
+
+/* switch this client to the current ioloop */
+struct ioloop *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 the total number of pending HTTP requests. */
+unsigned int
+http_client_get_pending_request_count(struct http_client *client);
+
+/*
+ * Client shared context
+ */
+
+struct http_client_context *
+http_client_context_create(const struct http_client_settings *set);
+void http_client_context_ref(struct http_client_context *cctx);
+void http_client_context_unref(struct http_client_context **_cctx);
+
+/* Return the default global shared client context, creating it if necessary.
+ The context is freed automatically at exit. Don't unreference the
+ returned context. */
+struct http_client_context *http_client_get_global_context(void);
+
+#endif