From: Stephan Bosch Date: Sat, 16 Apr 2016 00:03:10 +0000 (+0200) Subject: lib-http: client: Cleaned up headers and added some more documentation there. X-Git-Tag: 2.2.27~232 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=390a66a1de6431eb9a32577b6c8edbe44a167238;p=thirdparty%2Fdovecot%2Fcore.git lib-http: client: Cleaned up headers and added some more documentation there. --- diff --git a/src/lib-http/http-client-private.h b/src/lib-http/http-client-private.h index 876be910a4..0926f630e2 100644 --- a/src/lib-http/http-client-private.h +++ b/src/lib-http/http-client-private.h @@ -6,6 +6,10 @@ #include "http-url.h" #include "http-client.h" +/* + * Defaults + */ + #define HTTP_DEFAULT_PORT 80 #define HTTPS_DEFAULT_PORT 443 @@ -15,6 +19,10 @@ #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; @@ -55,6 +63,10 @@ struct http_client_peer_addr { } a; }; +/* + * Objects + */ + struct http_client_request { pool_t pool; unsigned int refcount; @@ -278,7 +290,78 @@ struct http_client { 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 */ @@ -310,6 +393,19 @@ void http_client_request_redirect(struct http_client_request *req, 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 * @@ -339,6 +435,19 @@ void http_client_connection_switch_ioloop(struct http_client_connection *conn); 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; @@ -376,6 +485,10 @@ unsigned int 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); @@ -403,103 +516,9 @@ void http_client_queue_peer_disconnected(struct http_client_queue *queue, 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, @@ -514,5 +533,24 @@ 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 diff --git a/src/lib-http/http-client.h b/src/lib-http/http-client.h index b8c7919c7c..6d595737c1 100644 --- a/src/lib-http/http-client.h +++ b/src/lib-http/http-client.h @@ -11,28 +11,9 @@ struct http_response; 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. @@ -42,6 +23,7 @@ struct http_client_settings { 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; @@ -59,8 +41,12 @@ struct http_client_settings { 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) */ @@ -139,6 +125,55 @@ struct http_client_settings { 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; @@ -149,9 +184,6 @@ typedef void 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, @@ -163,6 +195,8 @@ 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, @@ -188,6 +222,9 @@ http_client_request_connect(struct http_client *client, 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, @@ -199,25 +236,53 @@ http_client_request_connect_ip(struct http_client *client, 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, @@ -230,9 +295,17 @@ void http_client_request_set_attempt_timeout_msecs(struct http_client_request *r 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, @@ -240,39 +313,64 @@ 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