http-client.h revision 42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2c
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#ifndef HTTP_CLIENT_H
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#define HTTP_CLIENT_H
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
fc94140acba51adafedafbc8491a3223a51db7a8Stephan Bosch#include "net.h"
fc94140acba51adafedafbc8491a3223a51db7a8Stephan Bosch
1920ef85b63738a06914e56508049dd0afe38732Timo Sirainen#include "http-response.h"
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_response;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_client;
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_client_request;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschenum http_client_request_error {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_ABORTED = 9000,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST,
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch};
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenenum http_request_state {
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_NEW = 0,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_QUEUED,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_PAYLOAD_OUT,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_WAITING,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_GOT_RESPONSE,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_PAYLOAD_IN,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_FINISHED,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen HTTP_REQUEST_STATE_ABORTED
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen};
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenextern const char *http_request_state_names[];
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_client_settings {
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen /* a) If dns_client is set, all lookups are done via it.
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen b) If dns_client_socket_path is set, each DNS lookup does its own
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen dns-lookup UNIX socket connection.
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen c) Otherwise, blocking gethostbyname() lookups are used. */
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen struct dns_client *dns_client;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *dns_client_socket_path;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
56d1345c43bbd28c36b7faa85e4163bd9e874290Timo Sirainen const char *ssl_ca_dir, *ssl_ca_file, *ssl_ca;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *ssl_crypto_device;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch bool ssl_allow_invalid_cert;
35e962a9186b4e9b2001628c1d7b55c24b33ce84Timo Sirainen /* user cert */
35e962a9186b4e9b2001628c1d7b55c24b33ce84Timo Sirainen const char *ssl_cert, *ssl_key, *ssl_key_password;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen /* User-Agent: header (default: none) */
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen const char *user_agent;
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* configuration for using a proxy */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const char *proxy_socket_path; /* FIXME: implement */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const struct http_url *proxy_url;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const char *proxy_username; /* FIXME: implement */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const char *proxy_password;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *rawlog_dir;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int max_idle_time_msecs;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* maximum number of parallel connections per peer (default = 1) */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int max_parallel_connections;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* maximum number of pipelined requests per connection (default = 1) */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int max_pipelined_requests;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
ad03049781fc14807248007d524be4daf06c3ee2Stephan Bosch /* don't automatically act upon redirect responses */
ad03049781fc14807248007d524be4daf06c3ee2Stephan Bosch bool no_auto_redirect;
ad03049781fc14807248007d524be4daf06c3ee2Stephan Bosch
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch /* if we use a proxy, delegate SSL negotiation to proxy, rather than
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch creating a CONNECT tunnel through the proxy for the SSL link */
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch bool no_ssl_tunnel;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* maximum number of redirects for a request
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (default = 0; redirects refused)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int max_redirects;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* maximum number of attempts for a request */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int max_attempts;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* response header limits */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch struct http_header_limits response_hdr_limits;
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen /* max time to wait for HTTP request to finish before retrying
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen (default = unlimited) */
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen unsigned int request_timeout_msecs;
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen /* max time to wait for connect() (and SSL handshake) to finish before
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen retrying (default = request_timeout_msecs) */
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen unsigned int connect_timeout_msecs;
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen /* time to wait for connect() (and SSL handshake) to finish for the first
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen connection before trying the next IP in parallel
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen (default = 0; wait until current connection attempt finishes) */
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen unsigned int soft_connect_timeout_msecs;
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch /* maximum acceptable delay in seconds for automatically
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch retrying/redirecting requests. if a server sends a response with a
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch Retry-After header that causes a delay longer than this, the request
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch is not automatically retried and the response is returned */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch unsigned int max_auto_retry_delay;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch bool debug;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch};
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschstruct http_client_tunnel {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch int fd_in, fd_out;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct istream *input;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct ostream *output;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch};
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschtypedef void
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request_callback_t(const struct http_response *response,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch void *context);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_client *http_client_init(const struct http_client_settings *set);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_deinit(struct http_client **_client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch/* create new HTTP request */
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_client_request *
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request(struct http_client *client,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *method, const char *host, const char *target,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_callback_t *callback, void *context);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#define http_client_request(client, method, host, target, callback, context) \
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request(client, method, host, target + \
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch CALLBACK_TYPECHECK(callback, void (*)( \
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const struct http_response *response, typeof(context))), \
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (http_client_request_callback_t *)callback, context)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschstruct http_client_request *
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschhttp_client_request_url(struct http_client *client,
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const char *method, const struct http_url *target_url,
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_client_request_callback_t *callback, void *context);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch#define http_client_request_url(client, method, target_url, callback, context) \
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_client_request_url(client, method, target_url + \
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch CALLBACK_TYPECHECK(callback, void (*)( \
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const struct http_response *response, typeof(context))), \
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch (http_client_request_callback_t *)callback, context)
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch/* create new HTTP CONNECT request. If this HTTP is configured to use a proxy,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch a CONNECT request will be submitted at that proxy, otherwise the connection
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch is created directly. Call http_client_request_start_tunnel() to
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch to take over the connection.
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschstruct http_client_request *
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschhttp_client_request_connect(struct http_client *client,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch const char *host, in_port_t port,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch http_client_request_callback_t *callback,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch void *context);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch#define http_client_request_connect(client, host, port, callback, context) \
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch http_client_request_connect(client, host, port + \
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch CALLBACK_TYPECHECK(callback, void (*)( \
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch const struct http_response *response, typeof(context))), \
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch (http_client_request_callback_t *)callback, context)
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Boschstruct http_client_request *
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Boschhttp_client_request_connect_ip(struct http_client *client,
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch const struct ip_addr *ip, in_port_t port,
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch http_client_request_callback_t *callback,
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch void *context);
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch#define http_client_request_connect_ip(client, ip, port, callback, context) \
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch http_client_request_connect_ip(client, ip, port + \
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch CALLBACK_TYPECHECK(callback, void (*)( \
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch const struct http_response *response, typeof(context))), \
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch (http_client_request_callback_t *)callback, context)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_port(struct http_client_request *req,
fc94140acba51adafedafbc8491a3223a51db7a8Stephan Bosch in_port_t port);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_ssl(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch bool ssl);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_urgent(struct http_client_request *req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_add_header(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *key, const char *value);
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainenvoid http_client_request_remove_header(struct http_client_request *req,
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen const char *key);
6d573191bea1a64d6046be070487a5705a2d0204Stephan Boschvoid http_client_request_set_date(struct http_client_request *req,
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch time_t date);
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_payload(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct istream *input, bool sync);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_request_delay_until(struct http_client_request *req,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch time_t time);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_request_delay(struct http_client_request *req,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch time_t seconds);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainenconst char *http_client_request_get_method(struct http_client_request *req);
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainenconst char *http_client_request_get_target(struct http_client_request *req);
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenenum http_request_state
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenhttp_client_request_get_state(struct http_client_request *req);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_submit(struct http_client_request *req);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainenbool http_client_request_try_retry(struct http_client_request *req);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_abort(struct http_client_request **req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen/* Call the specified callback when HTTP request is destroyed. */
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainenvoid http_client_request_set_destroy_callback(struct http_client_request *req,
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen void (*callback)(void *),
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen void *context);
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch/* submits request and blocks until provided payload is sent. Multiple calls
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen are allowed; payload transmission is ended with
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen http_client_request_finish_payload(). */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Boschint http_client_request_send_payload(struct http_client_request **req,
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch const unsigned char *data, size_t size);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenint http_client_request_finish_payload(struct http_client_request **req);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschvoid http_client_request_start_tunnel(struct http_client_request *req,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct http_client_tunnel *tunnel);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_switch_ioloop(struct http_client *client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch/* blocks until all currently submitted requests are handled */
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_wait(struct http_client *client);
17cd0e0963f2fb0e66d49703e8cd0bda1b842468Timo Sirainen/* Returns number of pending HTTP requests. */
17cd0e0963f2fb0e66d49703e8cd0bda1b842468Timo Sirainenunsigned int http_client_get_pending_request_count(struct http_client *client);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#endif