diff options
Diffstat (limited to 'src/lib-http/http-client-private.h')
-rw-r--r-- | src/lib-http/http-client-private.h | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/src/lib-http/http-client-private.h b/src/lib-http/http-client-private.h new file mode 100644 index 0000000..86b1d96 --- /dev/null +++ b/src/lib-http/http-client-private.h @@ -0,0 +1,718 @@ +#ifndef HTTP_CLIENT_PRIVATE_H +#define HTTP_CLIENT_PRIVATE_H + +#include "connection.h" + +#include "http-url.h" +#include "http-client.h" + +/* + * Defaults + */ + +#define HTTP_CLIENT_CONTINUE_TIMEOUT_MSECS (1000*2) +#define HTTP_CLIENT_DEFAULT_REQUEST_TIMEOUT_MSECS (1000*60*1) +#define HTTP_CLIENT_DEFAULT_DNS_LOOKUP_TIMEOUT_MSECS (1000*10) +#define HTTP_CLIENT_DEFAULT_BACKOFF_TIME_MSECS (100) +#define HTTP_CLIENT_DEFAULT_BACKOFF_MAX_TIME_MSECS (1000*60) +#define HTTP_CLIENT_DEFAULT_DNS_TTL_MSECS (1000*60*30) +#define HTTP_CLIENT_MIN_IDLE_TIMEOUT_MSECS 50 + +/* + * Types + */ + +struct http_client_connection; +struct http_client_peer_pool; +struct http_client_peer_shared; +struct http_client_peer; +struct http_client_queue; +struct http_client_host_shared; +struct http_client_host; + +ARRAY_DEFINE_TYPE(http_client_request, struct http_client_request *); +ARRAY_DEFINE_TYPE(http_client_connection, struct http_client_connection *); +ARRAY_DEFINE_TYPE(http_client_peer, struct http_client_peer *); +ARRAY_DEFINE_TYPE(http_client_peer_shared, struct http_client_peer_shared *); +ARRAY_DEFINE_TYPE(http_client_peer_pool, struct http_client_peer_pool *); +ARRAY_DEFINE_TYPE(http_client_queue, struct http_client_queue *); +ARRAY_DEFINE_TYPE(http_client_host, struct http_client_host_shared *); + +HASH_TABLE_DEFINE_TYPE(http_client_peer_shared, + const struct http_client_peer_addr *, + struct http_client_peer_shared *); +HASH_TABLE_DEFINE_TYPE(http_client_host_shared, const char *, + struct http_client_host_shared *); + +enum http_client_peer_addr_type { + HTTP_CLIENT_PEER_ADDR_HTTP = 0, + HTTP_CLIENT_PEER_ADDR_HTTPS, + HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL, + HTTP_CLIENT_PEER_ADDR_RAW, + HTTP_CLIENT_PEER_ADDR_UNIX, +}; + +struct http_client_peer_addr { + enum http_client_peer_addr_type type; + union { + struct { + const char *https_name; /* TLS SNI */ + struct ip_addr ip; + in_port_t port; + } tcp; + struct { + const char *path; + } un; + } a; +}; + +/* + * Objects + */ + +struct http_client_request { + pool_t pool; + unsigned int refcount; + const char *label; + unsigned int id; + + struct http_client_request *prev, *next; + + const char *method, *target; + struct http_url origin_url; + const char *username, *password; + + const char *host_socket; + const struct http_url *host_url; + const char *authority; + + struct http_client *client; + struct http_client_host *host; + struct http_client_queue *queue; + struct http_client_peer *peer; + struct http_client_connection *conn; + + struct event *event; + const char *const *event_headers; + unsigned int last_status; + + string_t *headers; + time_t date; + + struct istream *payload_input; + uoff_t payload_size, payload_offset; + struct ostream *payload_output; + + /* Time when request can be sent the next time. This is set by + http_client_request_delay*(). Default is 0 = immediately. Retries + can update this. */ + struct timeval release_time; + /* Time when http_client_request_submit() was called. */ + struct timeval submit_time; + /* Time when the request was first sent to the server. The HTTP + connection already exists at this time. */ + struct timeval first_sent_time; + /* Time when the request was last sent to the server (if it was + retried). */ + struct timeval sent_time; + /* Time when the HTTP response header was last received. */ + struct timeval response_time; + /* Time when the request will be aborted. Set by + http_client_request_set_timeout(). */ + struct timeval timeout_time; + unsigned int timeout_msecs; + unsigned int attempt_timeout_msecs; + unsigned int max_attempts; + + uoff_t response_offset, request_offset; + uoff_t bytes_in, bytes_out; + + unsigned int attempts, send_attempts; + unsigned int redirects; + uint64_t sent_global_ioloop_usecs; + uint64_t sent_http_ioloop_usecs; + uint64_t sent_lock_usecs; + + unsigned int delayed_error_status; + const char *delayed_error; + + http_client_request_callback_t *callback; + void *context; + + void (*destroy_callback)(void *); + void *destroy_context; + + enum http_request_state state; + + bool have_hdr_authorization:1; + bool have_hdr_body_spec:1; + bool have_hdr_connection:1; + bool have_hdr_date:1; + bool have_hdr_expect:1; + bool have_hdr_host:1; + bool have_hdr_user_agent:1; + + bool payload_sync:1; + bool payload_sync_continue:1; + bool payload_chunked:1; + bool payload_wait:1; + bool payload_finished:1; + bool payload_empty:1; + bool urgent:1; + bool submitted:1; + bool listed:1; + bool connect_tunnel:1; + bool connect_direct:1; + bool ssl_tunnel:1; + bool preserve_exact_reason:1; +}; + +struct http_client_connection { + struct connection conn; + struct event *event; + unsigned int refcount; + + struct http_client_peer_pool *ppool; + struct http_client_peer *peer; + + int connect_errno; + struct timeval connect_start_timestamp; + struct timeval connected_timestamp; + struct http_client_request *connect_request; + + struct ssl_iostream *ssl_iostream; + struct http_response_parser *http_parser; + struct timeout *to_connect, *to_input, *to_idle, *to_response; + struct timeout *to_requests; + + struct http_client_request *pending_request; + struct istream *incoming_payload; + struct io *io_req_payload; + struct ioloop *last_ioloop; + struct io_wait_timer *io_wait_timer; + + /* Requests that have been sent, waiting for response */ + ARRAY_TYPE(http_client_request) request_wait_list; + + bool connected:1; /* Connection is connected */ + bool idle:1; /* Connection is idle */ + bool tunneling:1; /* Last sent request turns this + connection into tunnel */ + bool connect_succeeded:1; /* Connection succeeded including SSL */ + bool connect_failed:1; /* Connection failed */ + bool lost_prematurely:1; /* Lost connection before receiving any data */ + bool closing:1; + bool disconnected:1; + bool close_indicated:1; + bool output_locked:1; /* Output is locked; no pipelining */ + bool output_broken:1; /* Output is broken; no more requests */ + bool in_req_callback:1; /* Performing request callback (busy) */ + bool debug:1; +}; + +struct http_client_peer_shared { + unsigned int refcount; + struct http_client_peer_addr addr; + char *addr_name; + struct event *event; + + char *label; + + struct http_client_context *cctx; + struct http_client_peer_shared *prev, *next; + + struct http_client_peer_pool *pools_list; + + struct http_client_peer *peers_list; + unsigned int peers_count; + + /* Connection retry */ + struct timeval last_failure; + struct timeout *to_backoff; + unsigned int backoff_initial_time_msecs; + unsigned int backoff_current_time_msecs; + unsigned int backoff_max_time_msecs; + + bool no_payload_sync:1; /* Expect: 100-continue failed before */ + bool seen_100_response:1; /* Expect: 100-continue succeeded before */ + bool allows_pipelining:1; /* Peer is known to allow persistent + connections */ +}; + +struct http_client_peer_pool { + unsigned int refcount; + struct http_client_peer_shared *peer; + struct http_client_peer_pool *prev, *next; + struct event *event; + + /* All connections to this peer */ + ARRAY_TYPE(http_client_connection) conns; + + /* Pending connections (not ready connecting) */ + ARRAY_TYPE(http_client_connection) pending_conns; + + /* Available connections to this peer */ + ARRAY_TYPE(http_client_connection) idle_conns; + + /* Distinguishing settings for these connections */ + struct ssl_iostream_context *ssl_ctx; + char *rawlog_dir; + struct pcap_output *pcap_output; + + bool destroyed:1; /* Peer pool is being destroyed */ +}; + +struct http_client_peer { + unsigned int refcount; + struct http_client_peer_shared *shared; + struct http_client_peer *shared_prev, *shared_next; + + struct http_client *client; + struct http_client_peer *client_prev, *client_next; + + struct http_client_peer_pool *ppool; + struct event *event; + + /* Queues using this peer */ + ARRAY_TYPE(http_client_queue) queues; + + /* Active connections to this peer */ + ARRAY_TYPE(http_client_connection) conns; + /* Pending connections (not ready connecting) */ + ARRAY_TYPE(http_client_connection) pending_conns; + + /* Zero time-out for consolidating request handling */ + struct timeout *to_req_handling; + + bool connect_failed:1; /* Last connection attempt failed */ + bool connect_backoff:1; /* Peer is waiting for backoff timout*/ + bool disconnected:1; /* Peer is already disconnected */ + bool handling_requests:1; /* Currently running request handler */ +}; + +struct http_client_queue { + struct http_client *client; + struct http_client_queue *prev, *next; + + struct http_client_host *host; + char *name; + struct event *event; + + struct http_client_peer_addr addr; + char *addr_name; + + /* Current index in host->ips */ + unsigned int ips_connect_idx; + /* The first IP that started the current round of connection attempts. + initially 0, and later set to the ip index of the last successful + connected IP */ + unsigned int ips_connect_start_idx; + + struct timeval first_connect_time; + unsigned int connect_attempts; + + /* Peers we are trying to connect to; + this can be more than one when soft connect timeouts are enabled */ + ARRAY_TYPE(http_client_peer) pending_peers; + + /* Currently active peer */ + struct http_client_peer *cur_peer; + + /* All requests associated to this queue + (ordered by earliest timeout first) */ + ARRAY_TYPE(http_client_request) requests; + + /* Delayed requests waiting to be released after delay */ + ARRAY_TYPE(http_client_request) delayed_requests; + + /* Requests pending in queue to be picked up by connections */ + ARRAY_TYPE(http_client_request) queued_requests, queued_urgent_requests; + + struct timeout *to_connect, *to_request, *to_delayed; +}; + +struct http_client_host_shared { + struct http_client_host_shared *prev, *next; + + struct http_client_context *cctx; + char *name; + struct event *event; + + /* The ip addresses DNS returned for this host */ + unsigned int ips_count; + struct ip_addr *ips; + struct timeval ips_timeout; + + /* Private instance for each client that uses this host */ + struct http_client_host *hosts_list; + + /* Active DNS lookup */ + struct dns_lookup *dns_lookup; + + /* Timeouts */ + struct timeout *to_idle; + + bool destroyed:1; /* Shared host object is being destroyed */ + bool unix_local:1; + bool explicit_ip:1; +}; + +struct http_client_host { + struct http_client_host_shared *shared; + struct http_client_host *shared_prev, *shared_next; + + struct http_client *client; + struct http_client_host *client_prev, *client_next; + + /* Separate queue for each host port */ + ARRAY_TYPE(http_client_queue) queues; +}; + +struct http_client { + pool_t pool; + struct http_client_context *cctx; + struct http_client_settings set; + + struct http_client *prev, *next; + + struct event *event; + struct ioloop *ioloop; + struct ssl_iostream_context *ssl_ctx; + + /* List of failed requests that are waiting for ioloop */ + ARRAY(struct http_client_request *) delayed_failing_requests; + struct timeout *to_failing_requests; + + struct http_client_host *hosts_list; + struct http_client_peer *peers_list; + + struct http_client_request *requests_list; + unsigned int requests_count; + + bool waiting:1; +}; + +struct http_client_context { + pool_t pool; + unsigned int refcount; + struct event *event; + struct ioloop *ioloop; + + struct http_client_settings set; + + struct dns_client *dns_client; + const char *dns_client_socket_path; + unsigned int dns_ttl_msecs; + unsigned int dns_lookup_timeout_msecs; + + struct http_client *clients_list; + struct connection_list *conn_list; + + HASH_TABLE_TYPE(http_client_peer_shared) peers; + struct http_client_peer_shared *peers_list; + HASH_TABLE_TYPE(http_client_host_shared) hosts; + struct http_client_host_shared *unix_host; + struct http_client_host_shared *hosts_list; +}; + +/* + * 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 bool +http_client_request_to_proxy(const struct http_client_request *req) +{ + return (req->host_url != &req->origin_url); +} + +const char *http_client_request_label(struct http_client_request *req); + +void http_client_request_ref(struct http_client_request *req); +/* Returns FALSE if unrefing destroyed the request entirely */ +bool http_client_request_unref(struct http_client_request **_req); +void http_client_request_destroy(struct http_client_request **_req); + +void http_client_request_get_peer_addr(const struct http_client_request *req, + struct http_client_peer_addr *addr); +enum http_response_payload_type +http_client_request_get_payload_type(struct http_client_request *req); +int http_client_request_send(struct http_client_request *req, bool pipelined); +int http_client_request_send_more(struct http_client_request *req, + bool pipelined); + +bool http_client_request_callback(struct http_client_request *req, + struct http_response *response); +void http_client_request_connect_callback(struct http_client_request *req, + const struct http_client_tunnel *tunnel, + struct http_response *response); + +void http_client_request_resubmit(struct http_client_request *req); +void http_client_request_retry(struct http_client_request *req, + unsigned int status, const char *error); +void http_client_request_error_delayed(struct http_client_request **_req); +void http_client_request_error(struct http_client_request **req, + unsigned int status, const char *error); +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 + */ + +struct connection_list *http_client_connection_list_init(void); + +struct http_client_connection * +http_client_connection_create(struct http_client_peer *peer); +void http_client_connection_ref(struct http_client_connection *conn); +/* Returns FALSE if unrefing destroyed the connection entirely */ +bool http_client_connection_unref(struct http_client_connection **_conn); +void http_client_connection_close(struct http_client_connection **_conn); + +void http_client_connection_lost(struct http_client_connection **_conn, + const char *error) ATTR_NULL(2); + +void http_client_connection_peer_closed(struct http_client_connection **_conn); +void http_client_connection_request_destroyed( + struct http_client_connection *conn, struct http_client_request *req); + +void http_client_connection_handle_output_error( + struct http_client_connection *conn); +int http_client_connection_output(struct http_client_connection *conn); +void http_client_connection_start_request_timeout( + struct http_client_connection *conn); +void http_client_connection_reset_request_timeout( + struct http_client_connection *conn); +void http_client_connection_stop_request_timeout( + struct http_client_connection *conn); +unsigned int +http_client_connection_count_pending(struct http_client_connection *conn); +int http_client_connection_check_ready(struct http_client_connection *conn); +bool http_client_connection_is_idle(struct http_client_connection *conn); +bool http_client_connection_is_active(struct http_client_connection *conn); +int http_client_connection_next_request(struct http_client_connection *conn); +void http_client_connection_check_idle(struct http_client_connection *conn); +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); +void http_client_connection_lost_peer(struct http_client_connection *conn); +void http_client_connection_claim_idle(struct http_client_connection *conn, + struct http_client_peer *peer); + +/* + * Peer + */ + +/* address */ + +unsigned int +http_client_peer_addr_hash(const struct http_client_peer_addr *peer) ATTR_PURE; +int http_client_peer_addr_cmp(const struct http_client_peer_addr *peer1, + const struct http_client_peer_addr *peer2) + ATTR_PURE; + +/* connection pool */ + +void http_client_peer_pool_ref(struct http_client_peer_pool *ppool); +void http_client_peer_pool_unref(struct http_client_peer_pool **_ppool); + +void http_client_peer_pool_close(struct http_client_peer_pool **_ppool); + +/* peer (shared) */ + +const char * +http_client_peer_shared_label(struct http_client_peer_shared *pshared); + +void http_client_peer_shared_ref(struct http_client_peer_shared *pshared); +void http_client_peer_shared_unref(struct http_client_peer_shared **_pshared); +void http_client_peer_shared_close(struct http_client_peer_shared **_pshared); + +void http_client_peer_shared_switch_ioloop( + struct http_client_peer_shared *pshared); + +unsigned int +http_client_peer_shared_max_connections( + struct http_client_peer_shared *pshared); + +/* peer */ + +struct http_client_peer * +http_client_peer_get(struct http_client *client, + const struct http_client_peer_addr *addr); +void http_client_peer_ref(struct http_client_peer *peer); +bool http_client_peer_unref(struct http_client_peer **_peer); +void http_client_peer_close(struct http_client_peer **_peer); + +bool http_client_peer_have_queue(struct http_client_peer *peer, + struct http_client_queue *queue); +void http_client_peer_link_queue(struct http_client_peer *peer, + struct http_client_queue *queue); +void http_client_peer_unlink_queue(struct http_client_peer *peer, + struct http_client_queue *queue); +struct http_client_request * +http_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent); +void http_client_peer_trigger_request_handler(struct http_client_peer *peer); +void http_client_peer_connection_success(struct http_client_peer *peer); +void http_client_peer_connection_failure(struct http_client_peer *peer, + const char *reason); +void http_client_peer_connection_lost(struct http_client_peer *peer, + bool premature); +bool http_client_peer_is_connected(struct http_client_peer *peer); +unsigned int +http_client_peer_idle_connections(struct http_client_peer *peer); +unsigned int +http_client_peer_active_connections(struct http_client_peer *peer); +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_get(struct http_client_host *host, + const struct http_client_peer_addr *addr); +void http_client_queue_free(struct http_client_queue *queue); +void http_client_queue_connection_setup(struct http_client_queue *queue); +unsigned int +http_client_queue_host_lookup_done(struct http_client_queue *queue); +void http_client_queue_host_lookup_failure(struct http_client_queue *queue, + const char *error); +void http_client_queue_submit_request(struct http_client_queue *queue, + struct http_client_request *req); +void http_client_queue_drop_request(struct http_client_queue *queue, + struct http_client_request *req); +struct http_client_request * +http_client_queue_claim_request(struct http_client_queue *queue, + const struct http_client_peer_addr *addr, + bool no_urgent); +unsigned int +http_client_queue_requests_pending(struct http_client_queue *queue, + unsigned int *num_urgent_r) ATTR_NULL(2); +unsigned int http_client_queue_requests_active(struct http_client_queue *queue); +void http_client_queue_connection_success(struct http_client_queue *queue, + struct http_client_peer *peer); +void http_client_queue_connection_failure(struct http_client_queue *queue, + struct http_client_peer *peer, + const char *reason); +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); + +/* + * Host + */ + +/* host (shared) */ + +void http_client_host_shared_free(struct http_client_host_shared **_hshared); +void http_client_host_shared_switch_ioloop( + struct http_client_host_shared *hshared); + +/* host */ + +static inline unsigned int +http_client_host_get_ips_count(struct http_client_host *host) +{ + return host->shared->ips_count; +} + +static inline const struct ip_addr * +http_client_host_get_ip(struct http_client_host *host, unsigned int idx) +{ + i_assert(idx < host->shared->ips_count); + return &host->shared->ips[idx]; +} + +static inline bool +http_client_host_ready(struct http_client_host *host) +{ + return host->shared->dns_lookup == NULL; +} + +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_host_check_idle(struct http_client_host *host); +int http_client_host_refresh(struct http_client_host *host); +bool http_client_host_get_ip_idx(struct http_client_host *host, + const struct ip_addr *ip, unsigned int *idx_r); + +/* + * 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); + +/* + * Client shared context + */ + +void http_client_context_switch_ioloop(struct http_client_context *cctx); + +#endif |