http-client-request.c revision 2ff548b46061f984def8d36736745333b8405a31
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2013-2015 Dovecot authors, see the included COPYING file */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "lib.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "net.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "str.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "hash.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "array.h"
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch#include "llist.h"
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch#include "time-util.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "istream.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "ostream.h"
0d5c9a80e91a4073d5fd6820e9ddce2755221f64Stephan Bosch#include "dns-lookup.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-url.h"
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch#include "http-date.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-response-parser.h"
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen#include "http-transfer.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-client-private.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenconst char *http_request_state_names[] = {
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "new",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "queued",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "payload_out",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "waiting",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "got_response",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "payload_in",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "finished",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "aborted"
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen};
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch/*
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch * Logging
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic inline void
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request_debug(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *format, ...) ATTR_FORMAT(2, 3);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic inline void
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request_debug(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *format, ...)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch va_list args;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (req->client->set.debug) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch va_start(args, format);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_debug("http-client: request %s: %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_label(req), t_strdup_vprintf(format, args));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch va_end(args);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch/*
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch * Request
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Boschstatic void
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Boschhttp_client_request_send_error(struct http_client_request *req,
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch unsigned int status, const char *error);
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschstatic struct http_client_request *
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschhttp_client_request_new(struct http_client *client, const char *method,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_callback_t *callback, void *context)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch pool_t pool;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_request *req;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch pool = pool_alloconly_create("http client request", 2048);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req = p_new(pool, struct http_client_request, 1);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->pool = pool;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->refcount = 1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->client = client;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->method = p_strdup(pool, method);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->callback = callback;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->context = context;
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch req->date = (time_t)-1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_NEW;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return req;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch#undef http_client_request
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschstruct http_client_request *
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschhttp_client_request(struct http_client *client,
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const char *method, const char *host, const char *target,
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_client_request_callback_t *callback, void *context)
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch{
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch struct http_client_request *req;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req = http_client_request_new(client, method, callback, context);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->origin_url.host_name = p_strdup(req->pool, host);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->target = (target == NULL ? "/" : p_strdup(req->pool, target));
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch return req;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch}
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch#undef http_client_request_url
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{
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch struct http_client_request *req;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req = http_client_request_new(client, method, callback, context);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_url_copy_authority(req->pool, &req->origin_url, target_url);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->target = p_strdup(req->pool, http_url_create_target(target_url));
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch return req;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch#undef http_client_request_connect
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{
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct http_client_request *req;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req = http_client_request_new(client, "CONNECT", callback, context);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->origin_url.host_name = p_strdup(req->pool, host);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->origin_url.port = port;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->origin_url.have_port = TRUE;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->connect_tunnel = TRUE;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->target = req->origin_url.host_name;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch return req;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch}
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch#undef http_client_request_connect_ip
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{
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch struct http_client_request *req;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch const char *hostname = net_ip2addr(ip);
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req = http_client_request_connect
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch (client, hostname, port, callback, context);
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->origin_url.host_ip = *ip;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->origin_url.have_host_ip = TRUE;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch return req;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch}
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_ref(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->refcount++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_unref(struct http_client_request **_req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_request *req = *_req;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client *client = req->client;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->refcount > 0);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (--req->refcount > 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* cannot be destroyed while it is still pending */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(req->conn == NULL || req->conn->pending_request == NULL);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->queue != NULL)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
fe681e6db72f30bd754b622005bbe298e5ca775aTimo Sirainen if (req->destroy_callback != NULL) {
fe681e6db72f30bd754b622005bbe298e5ca775aTimo Sirainen req->destroy_callback(req->destroy_context);
fe681e6db72f30bd754b622005bbe298e5ca775aTimo Sirainen req->destroy_callback = NULL;
fe681e6db72f30bd754b622005bbe298e5ca775aTimo Sirainen }
fe681e6db72f30bd754b622005bbe298e5ca775aTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* only decrease pending request counter if this request was submitted */
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch if (req->submitted) {
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch DLLIST_REMOVE(&client->requests_list, req);
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch client->requests_count--;
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_debug(req, "Destroy (requests left=%d)",
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch client->requests_count);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
16eb9a737d42017fc875ef7b68afc25c3c9f8979Stephan Bosch if (req->queue != NULL)
16eb9a737d42017fc875ef7b68afc25c3c9f8979Stephan Bosch http_client_queue_drop_request(req->queue, req);
16eb9a737d42017fc875ef7b68afc25c3c9f8979Stephan Bosch
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch if (client->requests_count == 0 && client->ioloop != NULL)
fb1be3de0159d6a10e916ad992e2bc53be64c6d5Timo Sirainen io_loop_stop(client->ioloop);
fb1be3de0159d6a10e916ad992e2bc53be64c6d5Timo Sirainen
129596c93692b21d6c6b1313b389774af24c2983Stephan Bosch if (req->delayed_error != NULL)
fca68889b287d8eed4babe72a231bd6079da012dStephan Bosch http_client_remove_request_error(req->client, req);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input != NULL)
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_stream_unref(&req->payload_input);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_output != NULL)
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen o_stream_unref(&req->payload_output);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (req->headers != NULL)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch str_free(&req->headers);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch pool_unref(&req->pool);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch *_req = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_port(struct http_client_request *req,
fc94140acba51adafedafbc8491a3223a51db7a8Stephan Bosch in_port_t port)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->origin_url.port = port;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->origin_url.have_port = TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_ssl(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch bool ssl)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->origin_url.have_ssl = ssl;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_urgent(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->urgent = TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_add_header(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *key, const char *value)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
49287618521ff2c69385456de116e5d1581426c0Timo Sirainen i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
49287618521ff2c69385456de116e5d1581426c0Timo Sirainen /* allow calling for retries */
49287618521ff2c69385456de116e5d1581426c0Timo Sirainen req->state == HTTP_REQUEST_STATE_GOT_RESPONSE ||
49287618521ff2c69385456de116e5d1581426c0Timo Sirainen req->state == HTTP_REQUEST_STATE_ABORTED);
49287618521ff2c69385456de116e5d1581426c0Timo Sirainen
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* mark presence of special headers */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch switch (key[0]) {
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch case 'c': case 'C':
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (strcasecmp(key, "Connection") == 0)
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch req->have_hdr_connection = TRUE;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch else if (strcasecmp(key, "Content-Length") == 0)
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch req->have_hdr_body_spec = TRUE;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch break;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch case 'd': case 'D':
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (strcasecmp(key, "Date") == 0)
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch req->have_hdr_date = TRUE;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch break;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch case 'e': case 'E':
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (strcasecmp(key, "Expect") == 0)
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch req->have_hdr_expect = TRUE;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch break;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch case 'h': case 'H':
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (strcasecmp(key, "Host") == 0)
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch req->have_hdr_host = TRUE;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch break;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch case 't': case 'T':
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (strcasecmp(key, "Transfer-Encoding") == 0)
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch req->have_hdr_body_spec = TRUE;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch break;
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen case 'u': case 'U':
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen if (strcasecmp(key, "User-Agent") == 0)
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen req->have_hdr_user_agent = TRUE;
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen break;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if (req->headers == NULL)
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->headers = str_new(default_pool, 256);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_printfa(req->headers, "%s: %s\r\n", key, value);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainenvoid http_client_request_remove_header(struct http_client_request *req,
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen const char *key)
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen{
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen const unsigned char *data, *p;
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen size_t size, line_len, line_start_pos;
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen unsigned int key_len = strlen(key);
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen /* allow calling for retries */
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen req->state == HTTP_REQUEST_STATE_GOT_RESPONSE ||
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen req->state == HTTP_REQUEST_STATE_ABORTED);
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen data = str_data(req->headers);
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen size = str_len(req->headers);
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen while ((p = memchr(data, '\n', size)) != NULL) {
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen line_len = (p+1) - data;
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen if (size > key_len && i_memcasecmp(data, key, key_len) == 0 &&
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen data[key_len] == ':' && data[key_len+1] == ' ') {
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen /* key was found from header, replace its value */
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen line_start_pos = str_len(req->headers) - size;
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen str_delete(req->headers, line_start_pos, line_len);
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen break;
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen }
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen size -= line_len;
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen data += line_len;
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen }
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen}
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen
6d573191bea1a64d6046be070487a5705a2d0204Stephan Boschvoid http_client_request_set_date(struct http_client_request *req,
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch time_t date)
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch{
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch req->date = date;
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch}
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_payload(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct istream *input, bool sync)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
4521d35c263add6af3f1ae55b3760291767ce50cTimo Sirainen int ret;
4521d35c263add6af3f1ae55b3760291767ce50cTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_assert(req->payload_input == NULL);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_ref(input);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen req->payload_input = input;
4521d35c263add6af3f1ae55b3760291767ce50cTimo Sirainen if ((ret = i_stream_get_size(input, TRUE, &req->payload_size)) <= 0) {
4521d35c263add6af3f1ae55b3760291767ce50cTimo Sirainen if (ret < 0) {
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen i_error("i_stream_get_size(%s) failed: %s",
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen i_stream_get_name(input),
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen i_stream_get_error(input));
4521d35c263add6af3f1ae55b3760291767ce50cTimo Sirainen }
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen req->payload_size = 0;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen req->payload_chunked = TRUE;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen }
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen req->payload_offset = input->v_offset;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* prepare request payload sync using 100 Continue response from server */
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if ((req->payload_chunked || req->payload_size > 0) && sync)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->payload_sync = TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschvoid http_client_request_set_timeout_msecs(struct http_client_request *req,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch unsigned int msecs)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch{
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->timeout_msecs = msecs;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch}
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschvoid http_client_request_set_timeout(struct http_client_request *req,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch const struct timeval *time)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch{
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->timeout_time = *time;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->timeout_msecs = 0;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch}
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_request_delay_until(struct http_client_request *req,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch time_t time)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch{
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->release_time.tv_sec = time;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->release_time.tv_usec = 0;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch}
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_request_delay(struct http_client_request *req,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch time_t seconds)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch{
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->release_time = ioloop_timeval;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->release_time.tv_sec += seconds;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch}
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Boschvoid http_client_request_delay_msecs(struct http_client_request *req,
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch unsigned int msecs)
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch{
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch req->release_time = ioloop_timeval;
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch timeval_add_msecs(&req->release_time, msecs);
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch}
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschint http_client_request_delay_from_response(struct http_client_request *req,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch const struct http_response *response)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch{
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch time_t retry_after = response->retry_after;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch unsigned int max;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch if (retry_after == (time_t)-1)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return 0; /* no delay */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch if (retry_after < ioloop_time)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return 0; /* delay already expired */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch max = (req->client->set.max_auto_retry_delay == 0 ?
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->client->set.request_timeout_msecs / 1000 :
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->client->set.max_auto_retry_delay);
19db4c57fd7acc9e54e5724ccfa0633a5665dfefTimo Sirainen if ((unsigned int)(retry_after - ioloop_time) > max)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return -1; /* delay too long */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->release_time.tv_sec = retry_after;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->release_time.tv_usec = 0;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return 1; /* valid delay */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch}
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainenconst char *http_client_request_get_method(struct http_client_request *req)
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen{
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen return req->method;
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen}
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainenconst char *http_client_request_get_target(struct http_client_request *req)
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen{
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen return req->target;
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen}
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenenum http_request_state
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenhttp_client_request_get_state(struct http_client_request *req)
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen{
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen return req->state;
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen}
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschenum http_response_payload_type
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschhttp_client_request_get_payload_type(struct http_client_request *req)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch{
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.3:
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch The presence of a message body in a response depends on both the
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch request method to which it is responding and the response status code
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch (Section 3.1.2 of [RFC7230]). Responses to the HEAD request method
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch (Section 4.3.2 of [RFC7231]) never include a message body because the
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch associated response header fields (e.g., Transfer-Encoding,
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch Content-Length, etc.), if present, indicate only what their values
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch would have been if the request method had been GET (Section 4.3.1 of
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch [RFC7231]). 2xx (Successful) responses to a CONNECT request method
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch (Section 4.3.6 of [RFC7231]) switch to tunnel mode instead of having a
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch message body.
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (strcmp(req->method, "HEAD") == 0)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch return HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (strcmp(req->method, "CONNECT") == 0)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch return HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch return HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Boschstatic void http_client_request_do_submit(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch struct http_client *client = req->client;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_host *host;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const struct http_url *proxy_url = client->set.proxy_url;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch const char *authority, *target;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch authority = http_url_create_authority(&req->origin_url);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (req->connect_tunnel) {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* connect requests require authority form for request target */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch target = authority;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch } else {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* absolute target url */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch target = t_strconcat
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch (http_url_create_host(&req->origin_url), req->target, NULL);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* determine what host to contact to submit this request */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if (proxy_url != NULL) {
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch if (req->origin_url.have_ssl && !client->set.no_ssl_tunnel &&
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch !req->connect_tunnel) {
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->host_url = &req->origin_url; /* tunnel to origin server */
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->ssl_tunnel = TRUE;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch } else {
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->host_url = proxy_url; /* proxy server */
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch } else {
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->host_url = &req->origin_url; /* origin server */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch /* use submission date if no date is set explicitly */
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch if (req->date == (time_t)-1)
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch req->date = ioloop_time;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* prepare value for Host header */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->authority = p_strdup(req->pool, authority);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* debug label */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->label = p_strdup_printf(req->pool, "[%s %s]", req->method, target);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* update request target */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (req->connect_tunnel || proxy_url != NULL)
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->target = p_strdup(req->pool, target);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (proxy_url == NULL) {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* if we don't have a proxy, CONNECT requests are handled by creating
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch the requested connection directly */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->connect_direct = req->connect_tunnel;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (req->connect_direct)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->urgent = TRUE;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch }
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->timeout_time.tv_sec == 0) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->timeout_msecs > 0) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->timeout_time = ioloop_timeval;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch timeval_add_msecs(&req->timeout_time, req->timeout_msecs);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch } else if ( client->set.request_absolute_timeout_msecs > 0) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->timeout_time = ioloop_timeval;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch timeval_add_msecs(&req->timeout_time, client->set.request_absolute_timeout_msecs);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch }
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch }
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch host = http_client_host_get(req->client, req->host_url);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_QUEUED;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
57962a937b214be3a131f78005509afaa26fe4bfTimo Sirainen http_client_host_submit_request(host, req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Boschvoid http_client_request_submit(struct http_client_request *req)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch{
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch struct http_client *client = req->client;
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch
31fa529029f35f65451fb1d119ed1d5435b62e46Timo Sirainen req->submit_time = ioloop_timeval;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_request_do_submit(req);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_client_request_debug(req, "Submitted");
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen req->submitted = TRUE;
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch DLLIST_PREPEND(&client->requests_list, req);
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch client->requests_count++;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch}
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Boschstatic void
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenhttp_client_request_finish_payload_out(struct http_client_request *req)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch{
791fb70b3255a11a91ce0c2dc3ae1460d4cf8459Timo Sirainen i_assert(req->conn != NULL);
791fb70b3255a11a91ce0c2dc3ae1460d4cf8459Timo Sirainen
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch /* drop payload output stream */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->payload_output != NULL) {
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch o_stream_unref(&req->payload_output);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_output = NULL;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch }
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch /* advance state only when request didn't get aborted in the mean time */
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch if (req->state != HTTP_REQUEST_STATE_ABORTED) {
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT);
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch req->state = HTTP_REQUEST_STATE_WAITING;
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch }
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch /* release connection */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->conn->output_locked = FALSE;
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch http_client_request_debug(req, "Finished sending%s payload",
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch (req->state == HTTP_REQUEST_STATE_ABORTED ? " aborted" : ""));
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch}
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenstatic int
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenhttp_client_request_continue_payload(struct http_client_request **_req,
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch const unsigned char *data, size_t size)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch{
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch struct ioloop *prev_ioloop = current_ioloop;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch struct http_client_request *req = *_req;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch struct http_client_connection *conn = req->conn;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch struct http_client *client = req->client;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch int ret;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch i_assert(req->payload_input == NULL);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (conn != NULL)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_connection_ref(conn);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_request_ref(req);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_wait = TRUE;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (data == NULL) {
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_input = NULL;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT)
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen http_client_request_finish_payload_out(req);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch } else {
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_input = i_stream_create_from_data(data, size);
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen i_stream_set_name(req->payload_input, "<HTTP request payload>");
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch }
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_size = 0;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_chunked = TRUE;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->state == HTTP_REQUEST_STATE_NEW)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_request_submit(req);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch /* Wait for payload data to be written */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch i_assert(client->ioloop == NULL);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch client->ioloop = io_loop_create();
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_switch_ioloop(client);
0d5c9a80e91a4073d5fd6820e9ddce2755221f64Stephan Bosch if (client->set.dns_client != NULL)
0d5c9a80e91a4073d5fd6820e9ddce2755221f64Stephan Bosch dns_client_switch_ioloop(client->set.dns_client);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen while (req->state < HTTP_REQUEST_STATE_PAYLOAD_IN) {
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_request_debug(req, "Waiting for request to finish");
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT)
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen o_stream_set_flush_pending(req->payload_output, TRUE);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch io_loop_run(client->ioloop);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT &&
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_input->eof) {
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch i_stream_unref(&req->payload_input);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_input = NULL;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch break;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch }
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch }
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
35f3b7e05afecacd0332c210c6e253911c2813d8Timo Sirainen io_loop_set_current(prev_ioloop);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_switch_ioloop(client);
0d5c9a80e91a4073d5fd6820e9ddce2755221f64Stephan Bosch if (client->set.dns_client != NULL)
0d5c9a80e91a4073d5fd6820e9ddce2755221f64Stephan Bosch dns_client_switch_ioloop(client->set.dns_client);
35f3b7e05afecacd0332c210c6e253911c2813d8Timo Sirainen io_loop_set_current(client->ioloop);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch io_loop_destroy(&client->ioloop);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen switch (req->state) {
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen case HTTP_REQUEST_STATE_PAYLOAD_IN:
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen case HTTP_REQUEST_STATE_FINISHED:
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch ret = 1;
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen break;
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen case HTTP_REQUEST_STATE_ABORTED:
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen ret = -1;
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen break;
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen default:
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen ret = 0;
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen break;
a0613a630a412b0649b83c40c83f9fcfe50e1ad7Timo Sirainen }
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_wait = FALSE;
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch /* callback may have messed with our pointer,
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch so unref using local variable */
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch http_client_request_unref(&req);
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch if (req == NULL)
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch *_req = NULL;
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (conn != NULL)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_connection_unref(&conn);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch /* Return status */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch return ret;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch}
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenint http_client_request_send_payload(struct http_client_request **_req,
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen const unsigned char *data, size_t size)
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen{
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen i_assert(data != NULL);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen return http_client_request_continue_payload(_req, data, size);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen}
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenint http_client_request_finish_payload(struct http_client_request **_req)
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen{
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen return http_client_request_continue_payload(_req, NULL, 0);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen}
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Boschstatic void http_client_request_payload_input(struct http_client_request *req)
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch{
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch struct http_client_connection *conn = req->conn;
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch if (conn->io_req_payload != NULL)
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch io_remove(&conn->io_req_payload);
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch (void)http_client_connection_output(conn);
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch}
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainenint http_client_request_send_more(struct http_client_request *req,
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_connection *conn = req->conn;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen struct ostream *output = req->payload_output;
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen off_t ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_assert(req->payload_input != NULL);
667de5cf294d833b3d47dd455bacff4fd68dd146Timo Sirainen i_assert(req->payload_output != NULL);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch if (conn->io_req_payload != NULL)
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch io_remove(&conn->io_req_payload);
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen /* chunked ostream needs to write to the parent stream's buffer */
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE);
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen ret = o_stream_send_istream(output, req->payload_input);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_set_max_buffer_size(output, (size_t)-1);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen if (req->payload_input->stream_errno != 0) {
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch /* the payload stream assigned to this request is broken,
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch fail this the request immediately */
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch http_client_request_send_error(req,
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD,
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch "Broken payload stream");
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch /* we're in the middle of sending a request, so the connection
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch will also have to be aborted */
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen errno = req->payload_input->stream_errno;
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen *error_r = t_strdup_printf("read(%s) failed: %s",
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen i_stream_get_name(req->payload_input),
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen i_stream_get_error(req->payload_input));
ea2c9ff03053b7d255984d6bbcb3a48c052d1e4dTimo Sirainen return -1;
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen } else if (output->stream_errno != 0) {
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Bosch /* failed to send request */
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen errno = output->stream_errno;
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen *error_r = t_strdup_printf("write(%s) failed: %s",
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen o_stream_get_name(output),
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen o_stream_get_error(output));
ea2c9ff03053b7d255984d6bbcb3a48c052d1e4dTimo Sirainen return -1;
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen }
ea2c9ff03053b7d255984d6bbcb3a48c052d1e4dTimo Sirainen i_assert(ret >= 0);
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen
ea2c9ff03053b7d255984d6bbcb3a48c052d1e4dTimo Sirainen if (i_stream_is_eof(req->payload_input)) {
ea2c9ff03053b7d255984d6bbcb3a48c052d1e4dTimo Sirainen if (!req->payload_chunked &&
5777eef991bdb9dc487e9b8e8da8a4579fc67f6cTimo Sirainen req->payload_input->v_offset - req->payload_offset != req->payload_size) {
b7540564b9d7b69ce8f6e5a80011ccd5f8b86005Timo Sirainen *error_r = t_strdup_printf("BUG: stream '%s' input size changed: "
b7540564b9d7b69ce8f6e5a80011ccd5f8b86005Timo Sirainen "%"PRIuUOFF_T"-%"PRIuUOFF_T" != %"PRIuUOFF_T,
b7540564b9d7b69ce8f6e5a80011ccd5f8b86005Timo Sirainen i_stream_get_name(req->payload_input),
b7540564b9d7b69ce8f6e5a80011ccd5f8b86005Timo Sirainen req->payload_input->v_offset, req->payload_offset, req->payload_size);
b7540564b9d7b69ce8f6e5a80011ccd5f8b86005Timo Sirainen i_error("%s", *error_r); //FIXME: remove?
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->payload_wait) {
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen conn->output_locked = TRUE;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->client->ioloop != NULL)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch io_loop_stop(req->client->ioloop);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch } else {
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen http_client_request_finish_payload_out(req);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch }
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch } else if (i_stream_get_data_size(req->payload_input) > 0) {
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch /* output is blocking */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch conn->output_locked = TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_set_flush_pending(output, TRUE);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_debug(req, "Partially sent payload");
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch } else {
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch /* input is blocking */
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch conn->output_locked = TRUE;
37703e8d00a3a486aafba6a276fef35b38eab948Timo Sirainen conn->io_req_payload = io_add_istream(req->payload_input,
37703e8d00a3a486aafba6a276fef35b38eab948Timo Sirainen http_client_request_payload_input, req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
ea2c9ff03053b7d255984d6bbcb3a48c052d1e4dTimo Sirainen return 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainenstatic int http_client_request_send_real(struct http_client_request *req,
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_connection *conn = req->conn;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct ostream *output = conn->conn.output;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch string_t *rtext = t_str_new(256);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct const_iovec iov[3];
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int ret = 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(!req->conn->output_locked);
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch i_assert(req->payload_output == NULL);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* create request line */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_append(rtext, req->method);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_append(rtext, " ");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_append(rtext, req->target);
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, " HTTP/1.1\r\n");
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* create special headers implicitly if not set explicitly using
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch http_client_request_add_header() */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (!req->have_hdr_host) {
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, "Host: ");
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch str_append(rtext, req->authority);
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, "\r\n");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (!req->have_hdr_date) {
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, "Date: ");
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, http_date_create(req->date));
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, "\r\n");
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch }
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen if (!req->have_hdr_user_agent && req->client->set.user_agent != NULL) {
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen str_printfa(rtext, "User-Agent: %s\r\n",
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen req->client->set.user_agent);
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen }
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (!req->have_hdr_expect && req->payload_sync) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_append(rtext, "Expect: 100-continue\r\n");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen if (req->payload_input != NULL) {
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen if (req->payload_chunked) {
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen // FIXME: can't do this for a HTTP/1.0 server
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen if (!req->have_hdr_body_spec)
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen str_append(rtext, "Transfer-Encoding: chunked\r\n");
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen req->payload_output =
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen http_transfer_chunked_ostream_create(output);
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen } else {
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen /* send Content-Length if we have specified a payload,
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen even if it's 0 bytes. */
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen if (!req->have_hdr_body_spec) {
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen req->payload_size);
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen }
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen req->payload_output = output;
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen o_stream_ref(output);
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if (!req->have_hdr_connection && req->host_url == &req->origin_url) {
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* https://tools.ietf.org/html/rfc2068
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch Section 19.7.1:
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch A client MUST NOT send the Keep-Alive connection token to a proxy
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch server as HTTP/1.0 proxy servers do not obey the rules of HTTP/1.1
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch for parsing the Connection header field.
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, "Connection: Keep-Alive\r\n");
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* request line + implicit headers */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch iov[0].iov_base = str_data(rtext);
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch iov[0].iov_len = str_len(rtext);
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* explicit headers */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (req->headers != NULL) {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch iov[1].iov_base = str_data(req->headers);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch iov[1].iov_len = str_len(req->headers);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch } else {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch iov[1].iov_base = "";
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch iov[1].iov_len = 0;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch }
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* end of header */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch iov[2].iov_base = "\r\n";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch iov[2].iov_len = 2;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_PAYLOAD_OUT;
31fa529029f35f65451fb1d119ed1d5435b62e46Timo Sirainen req->sent_time = ioloop_timeval;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_cork(output);
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen if (o_stream_sendv(output, iov, N_ELEMENTS(iov)) < 0) {
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen *error_r = t_strdup_printf("write(%s) failed: %s",
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen o_stream_get_name(output),
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen o_stream_get_error(output));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ret = -1;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch } else {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch http_client_request_debug(req, "Sent header");
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch if (req->payload_output != NULL) {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch if (!req->payload_sync) {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch if (http_client_request_send_more(req, error_r) < 0)
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch ret = -1;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch } else {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch http_client_request_debug(req, "Waiting for 100-continue");
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch conn->output_locked = TRUE;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch } else {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch req->state = HTTP_REQUEST_STATE_WAITING;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch conn->output_locked = FALSE;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch }
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch if (ret >= 0 && o_stream_flush(output) < 0) {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch *error_r = t_strdup_printf("flush(%s) failed: %s",
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch o_stream_get_name(output),
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch o_stream_get_error(output));
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch ret = -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_uncork(output);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainenint http_client_request_send(struct http_client_request *req,
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen const char **error_r)
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen{
2ff548b46061f984def8d36736745333b8405a31Timo Sirainen char *errstr = NULL;
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen int ret;
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen T_BEGIN {
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen ret = http_client_request_send_real(req, error_r);
2ff548b46061f984def8d36736745333b8405a31Timo Sirainen if (ret < 0)
2ff548b46061f984def8d36736745333b8405a31Timo Sirainen errstr = i_strdup(*error_r);
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen } T_END;
2ff548b46061f984def8d36736745333b8405a31Timo Sirainen *error_r = t_strdup(errstr);
2ff548b46061f984def8d36736745333b8405a31Timo Sirainen i_free(errstr);
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen return ret;
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen}
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainenbool http_client_request_callback(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_response *response)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_callback_t *callback = req->callback;
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen unsigned int orig_attempts = req->attempts;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_GOT_RESPONSE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->callback = NULL;
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen if (callback != NULL) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch callback(response, req->context);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen if (req->attempts != orig_attempts) {
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen /* retrying */
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen req->callback = callback;
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen http_client_request_resubmit(req);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen return FALSE;
6c6915f4d75c352c63156df202fa51cd97524babStephan Bosch } else {
6c6915f4d75c352c63156df202fa51cd97524babStephan Bosch /* release payload early (prevents server/client deadlock in proxy) */
6c6915f4d75c352c63156df202fa51cd97524babStephan Bosch if (req->payload_input != NULL)
6c6915f4d75c352c63156df202fa51cd97524babStephan Bosch i_stream_unref(&req->payload_input);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen }
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen }
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen return TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Boschstatic void
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request_send_error(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int status, const char *error)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_callback_t *callback;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch bool sending = (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_ABORTED;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch callback = req->callback;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->callback = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (callback != NULL) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_response response;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
e8f1e510df3ab051a816715c2056f0d10aee929eStephan Bosch http_response_init(&response, status, error);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (void)callback(&response, req->context);
6c6915f4d75c352c63156df202fa51cd97524babStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* release payload early (prevents server/client deadlock in proxy) */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (!sending && req->payload_input != NULL)
6c6915f4d75c352c63156df202fa51cd97524babStephan Bosch i_stream_unref(&req->payload_input);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
129596c93692b21d6c6b1313b389774af24c2983Stephan Boschvoid http_client_request_error_delayed(struct http_client_request **_req)
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen{
129596c93692b21d6c6b1313b389774af24c2983Stephan Bosch struct http_client_request *req = *_req;
c6494255de7b934281dd052960fd8ab5aa48e79eTimo Sirainen
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_ABORTED);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
129596c93692b21d6c6b1313b389774af24c2983Stephan Bosch i_assert(req->delayed_error != NULL && req->delayed_error_status != 0);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen http_client_request_send_error(req, req->delayed_error_status,
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen req->delayed_error);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->queue != NULL)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
129596c93692b21d6c6b1313b389774af24c2983Stephan Bosch http_client_request_unref(_req);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen}
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_error(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int status, const char *error)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->state >= HTTP_REQUEST_STATE_FINISHED)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch return;
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch req->state = HTTP_REQUEST_STATE_ABORTED;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
840abb812d2e8edc42b5e2a4e3838b8d5e759e6aStephan Bosch if (req->queue != NULL)
840abb812d2e8edc42b5e2a4e3838b8d5e759e6aStephan Bosch http_client_queue_drop_request(req->queue, req);
840abb812d2e8edc42b5e2a4e3838b8d5e759e6aStephan Bosch
5e4cdaaf560cfa94bfc014ce8e1a52e4d0a85b48Stephan Bosch if (!req->submitted ||
5e4cdaaf560cfa94bfc014ce8e1a52e4d0a85b48Stephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE) {
5e4cdaaf560cfa94bfc014ce8e1a52e4d0a85b48Stephan Bosch /* we're still in http_client_request_submit() or in the callback
5e4cdaaf560cfa94bfc014ce8e1a52e4d0a85b48Stephan Bosch during a retry attempt. delay reporting the error, so the caller
5e4cdaaf560cfa94bfc014ce8e1a52e4d0a85b48Stephan Bosch doesn't have to handle immediate or nested callbacks. */
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen i_assert(req->delayed_error == NULL);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen req->delayed_error = p_strdup(req->pool, error);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen req->delayed_error_status = status;
fca68889b287d8eed4babe72a231bd6079da012dStephan Bosch http_client_delay_request_error(req->client, req);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen } else {
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen http_client_request_send_error(req, status, error);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen http_client_request_unref(&req);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_abort(struct http_client_request **_req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_request *req = *_req;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch bool sending = (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (req->state >= HTTP_REQUEST_STATE_FINISHED)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->callback = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_ABORTED;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* release payload early (prevents server/client deadlock in proxy) */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (!sending && req->payload_input != NULL)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_stream_unref(&req->payload_input);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch if (req->queue != NULL)
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch http_client_queue_drop_request(req->queue, req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_unref(_req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_finish(struct http_client_request **_req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_request *req = *_req;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (req->state >= HTTP_REQUEST_STATE_FINISHED)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_debug(req, "Finished");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->callback = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_FINISHED;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->queue != NULL)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->payload_wait && req->client->ioloop != NULL)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch io_loop_stop(req->client->ioloop);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_unref(_req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_redirect(struct http_client_request *req,
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch unsigned int status, const char *location)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_url *url;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const char *error, *target, *origin_url;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen i_assert(!req->payload_wait);
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* parse URL */
d45ab3fff7c47f1719b9cd310228c0dac2bdd1b2Timo Sirainen if (http_url_parse(location, NULL, 0,
d45ab3fff7c47f1719b9cd310228c0dac2bdd1b2Timo Sirainen pool_datastack_create(), &url, &error) < 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_error(req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch t_strdup_printf("Invalid redirect location: %s", error));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (++req->redirects > req->client->set.max_redirects) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (req->client->set.max_redirects > 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_error(req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch t_strdup_printf("Redirected more than %d times",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->client->set.max_redirects));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch } else {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_error(req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Redirect refused");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen /* rewind payload stream */
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input != NULL && req->payload_size > 0 && status != 303) {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input->v_offset != req->payload_offset &&
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen !req->payload_input->seekable) {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen http_client_request_error(req,
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen HTTP_CLIENT_REQUEST_ERROR_ABORTED,
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen "Redirect failed: Cannot resend payload; stream is not seekable");
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen return;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen } else {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_stream_seek(req->payload_input, req->payload_offset);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch }
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch }
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch /* drop payload output stream from previous attempt */
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch if (req->payload_output != NULL)
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch o_stream_unref(&req->payload_output);
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch
069def4dc35022852d569b7ab75a3b19d2cb0f1cTimo Sirainen target = http_url_create_target(url);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_url_copy(req->pool, &req->origin_url, url);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->target = p_strdup(req->pool, target);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if (req->host_url == &req->origin_url) {
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->authority =
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch p_strdup(req->pool, http_url_create_authority(req->host_url));
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->host = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->conn = NULL;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch origin_url = http_url_create(&req->origin_url);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_client_request_debug(req, "Redirecting to %s%s",
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch origin_url, target);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->label = p_strdup_printf(req->pool, "[%s %s%s]",
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->method, origin_url, req->target);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7231, Section 6.4.4:
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen -> A 303 `See Other' redirect status response is handled a bit differently.
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen Basically, the response content is located elsewhere, but the original
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen (POST) request is handled already.
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen */
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (status == 303 && strcasecmp(req->method, "HEAD") != 0 &&
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen strcasecmp(req->method, "GET") != 0) {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen // FIXME: should we provide the means to skip this step? The original
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen // request was already handled at this point.
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen req->method = p_strdup(req->pool, "GET");
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen /* drop payload */
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input != NULL)
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_stream_unref(&req->payload_input);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen req->payload_size = 0;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen req->payload_offset = 0;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch }
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* resubmit */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_NEW;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_request_do_submit(req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_resubmit(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen i_assert(!req->payload_wait);
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_debug(req, "Resubmitting request");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* rewind payload stream */
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input != NULL && req->payload_size > 0) {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input->v_offset != req->payload_offset &&
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen !req->payload_input->seekable) {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen http_client_request_error(req,
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen HTTP_CLIENT_REQUEST_ERROR_ABORTED,
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen "Resubmission failed: Cannot resend payload; stream is not seekable");
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen return;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen } else {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_stream_seek(req->payload_input, req->payload_offset);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen }
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen }
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen /* rewind payload stream */
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input != NULL && req->payload_size > 0) {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if (req->payload_input->v_offset != req->payload_offset &&
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen !req->payload_input->seekable) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch http_client_request_error(req,
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch HTTP_CLIENT_REQUEST_ERROR_ABORTED,
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch "Resubmission failed: Cannot resend payload; stream is not seekable");
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch return;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch } else {
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_stream_seek(req->payload_input, req->payload_offset);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch }
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch }
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch /* drop payload output stream from previous attempt */
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch if (req->payload_output != NULL)
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch o_stream_unref(&req->payload_output);
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->conn = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->peer = NULL;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_QUEUED;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch http_client_host_submit_request(req->host, req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_retry(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int status, const char *error)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen if (!http_client_request_try_retry(req))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_error(req, status, error);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainenbool http_client_request_try_retry(struct http_client_request *req)
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen{
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch /* don't ever retry if we're sending data in small blocks via
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch http_client_request_send_payload() and we're not waiting for a
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch 100 continue (there's no way to rewind the payload for a retry)
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch */
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch if (req->payload_wait &&
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch (!req->payload_sync || req->conn->payload_continue))
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch return FALSE;
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen /* limit the number of attempts for each request */
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen if (req->attempts+1 >= req->client->set.max_attempts)
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen return FALSE;
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen req->attempts++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen http_client_request_debug(req, "Retrying (attempts=%d)", req->attempts);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen if (req->callback != NULL)
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen http_client_request_resubmit(req);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen return TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainenvoid http_client_request_set_destroy_callback(struct http_client_request *req,
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen void (*callback)(void *),
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen void *context)
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen{
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen req->destroy_callback = callback;
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen req->destroy_context = context;
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschvoid http_client_request_start_tunnel(struct http_client_request *req,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct http_client_tunnel *tunnel)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch{
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch http_client_connection_start_tunnel(&req->conn, tunnel);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}