http-client-request.c revision 0753b82010b1ae517d3cec8988a2cffcfbc0dc5b
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 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"
86ab71bf134573e45dd9ab6bde9d55da4c44fc48Timo Sirainen#include "file-lock.h"
0d5c9a80e91a4073d5fd6820e9ddce2755221f64Stephan Bosch#include "dns-lookup.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-url.h"
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch#include "http-date.h"
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch#include "http-auth.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 * Request
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Boschstatic bool
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Boschhttp_client_request_send_error(struct http_client_request *req,
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch unsigned int status, const char *error);
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Boschconst char *
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Boschhttp_client_request_label(struct http_client_request *req)
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Bosch{
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Bosch if (req->label == NULL) {
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Bosch req->label = p_strdup_printf(req->pool,
7a358ecd8918d1280a0b68cdf348c23f6721c799Stephan Bosch "[Req%u: %s %s%s]", req->id, req->method,
7a358ecd8918d1280a0b68cdf348c23f6721c799Stephan Bosch http_url_create_host(&req->origin_url), req->target);
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Bosch }
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Bosch return req->label;
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Bosch}
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Bosch
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainenstatic void
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainenhttp_client_request_update_event(struct http_client_request *req)
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen{
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen event_add_str(req->event, "method", req->method);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen if (req->target != NULL)
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen event_add_str(req->event, "target", req->target);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen event_set_append_log_prefix(req->event, t_strdup_printf(
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen "request %s: ", http_client_request_label(req)));
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen}
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen
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{
47a53a80656dc400ff8effdc1432a69fbf5ae8baTimo Sirainen static unsigned int id_counter = 0;
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;
47a53a80656dc400ff8effdc1432a69fbf5ae8baTimo Sirainen req->id = ++id_counter;
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;
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen req->event = event_create(client->event);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen /* default to client-wide settings: */
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->max_attempts = client->set.max_attempts;
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->attempt_timeout_msecs = client->set.request_timeout_msecs;
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen
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);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch req->origin_url.host.name = p_strdup(req->pool, host);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->target = (target == NULL ? "/" : p_strdup(req->pool, target));
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen http_client_request_update_event(req);
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));
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch if (target_url->user != NULL && *target_url->user != '\0' &&
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch target_url->password != NULL) {
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->username = p_strdup(req->pool, target_url->user);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->password = p_strdup(req->pool, target_url->password);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch }
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen http_client_request_update_event(req);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch return req;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch#undef http_client_request_url_str
e46130f48a6962734bfde7e71906d3894bedf45bStephan Boschstruct http_client_request *
e46130f48a6962734bfde7e71906d3894bedf45bStephan Boschhttp_client_request_url_str(struct http_client *client,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch const char *method, const char *url_str,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch http_client_request_callback_t *callback, void *context)
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch{
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch struct http_client_request *req, *tmpreq;
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch struct http_url *target_url;
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch const char *error;
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req = tmpreq = http_client_request_new
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch (client, method, callback, context);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch if (http_url_parse(url_str, NULL, HTTP_URL_ALLOW_USERINFO_PART,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req->pool, &target_url, &error) < 0) {
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req->label = p_strdup_printf(req->pool,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch "[Req%u: %s %s]", req->id, req->method, url_str);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch http_client_request_error(&tmpreq,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch HTTP_CLIENT_REQUEST_ERROR_INVALID_URL,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch t_strdup_printf("Invalid HTTP URL: %s", error));
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen http_client_request_update_event(req);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch return req;
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch }
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req->origin_url = *target_url;
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req->target = p_strdup(req->pool, http_url_create_target(target_url));
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch if (target_url->user != NULL && *target_url->user != '\0' &&
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch target_url->password != NULL) {
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req->username = p_strdup(req->pool, target_url->user);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req->password = p_strdup(req->pool, target_url->password);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch }
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen http_client_request_update_event(req);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch return req;
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch}
e46130f48a6962734bfde7e71906d3894bedf45bStephan 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);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch req->origin_url.host.name = p_strdup(req->pool, host);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->origin_url.port = port;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->connect_tunnel = TRUE;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch req->target = req->origin_url.host.name;
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen http_client_request_update_event(req);
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;
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch const char *hostname;
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch i_assert(ip->family != 0);
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch hostname = net_ip2addr(ip);
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req = http_client_request_connect
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch (client, hostname, port, callback, context);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch req->origin_url.host.ip = *ip;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch return req;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch}
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainenvoid http_client_request_set_event(struct http_client_request *req,
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen struct event *event)
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen{
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen event_unref(&req->event);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen req->event = event_create(event);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen if (req->client->set.debug)
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen event_set_forced_debug(req->event, TRUE);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen http_client_request_update_event(req);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen}
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Boschstatic void
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Boschhttp_client_request_add(struct http_client_request *req)
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch{
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch struct http_client *client = req->client;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch DLLIST_PREPEND(&client->requests_list, req);
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch client->requests_count++;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch req->listed = TRUE;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch}
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Boschstatic void
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Boschhttp_client_request_remove(struct http_client_request *req)
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch{
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch struct http_client *client = req->client;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client == NULL) {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(!req->listed);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch return;
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch }
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch if (req->listed) {
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch /* only decrease pending request counter if this request was submitted */
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch DLLIST_REMOVE(&client->requests_list, req);
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch client->requests_count--;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch }
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch req->listed = FALSE;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch if (client->requests_count == 0 && client->waiting)
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch io_loop_stop(client->ioloop);
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch}
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_ref(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
e9228a3918aa0243eff4aae1ff5462bd3198417fTimo Sirainen i_assert(req->refcount > 0);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->refcount++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainenbool 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
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen *_req = NULL;
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (--req->refcount > 0)
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen return TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client == NULL) {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Free (client already destroyed)");
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch } else {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Free (requests left=%d)",
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch client->requests_count);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch }
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* cannot be destroyed while it is still pending */
3e9055cee8e331a84522dd1e65d0d0e09a4e8803Stephan Bosch i_assert(req->conn == 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
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch http_client_request_remove(req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client != NULL) {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client->requests_count == 0 && client->waiting)
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch io_loop_stop(client->ioloop);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (req->delayed_error != NULL)
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch http_client_remove_request_error(req->client, req);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch }
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek i_stream_unref(&req->payload_input);
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek o_stream_unref(&req->payload_output);
e9fbe5e18b798728041b7e2ffc6c4fa964fc35a3Josef 'Jeff' Sipek str_free(&req->headers);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen event_unref(&req->event);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch pool_unref(&req->pool);
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen return FALSE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
79f8a20424633e806447bc9375a5ab403aabc758Stephan Boschvoid http_client_request_destroy(struct http_client_request **_req)
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch{
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch struct http_client_request *req = *_req, *tmp_req;
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch struct http_client *client = req->client;
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen *_req = NULL;
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client == NULL) {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Destroy (client already destroyed)");
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch } else {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Destroy (requests left=%d)",
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch client->requests_count);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch }
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch if (req->state < HTTP_REQUEST_STATE_FINISHED)
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch req->state = HTTP_REQUEST_STATE_ABORTED;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch req->callback = NULL;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch if (req->queue != NULL)
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch http_client_queue_drop_request(req->queue, req);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client != NULL && req->delayed_error != NULL)
561a21c8ecefaade916d686c763f2123e1f9a151Stephan Bosch http_client_remove_request_error(req->client, req);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch req->delayed_error = NULL;
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch if (req->destroy_callback != NULL) {
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch void (*callback)(void *) = req->destroy_callback;
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch req->destroy_callback = NULL;
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch callback(req->destroy_context);
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch }
567d1c82e3e59a8e8acc2e1e01228585bab83bc6Stephan Bosch
567d1c82e3e59a8e8acc2e1e01228585bab83bc6Stephan Bosch if (req->conn != NULL)
567d1c82e3e59a8e8acc2e1e01228585bab83bc6Stephan Bosch http_client_connection_request_destroyed(req->conn, req);
567d1c82e3e59a8e8acc2e1e01228585bab83bc6Stephan Bosch
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch tmp_req = req;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch http_client_request_remove(req);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (http_client_request_unref(&tmp_req))
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch req->client = NULL;
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch}
79f8a20424633e806447bc9375a5ab403aabc758Stephan 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;
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
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainenvoid http_client_request_set_preserve_exact_reason(struct http_client_request *req)
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen{
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen req->preserve_exact_reason = TRUE;
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen}
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen
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);
887c92aa9f171ab90e417de48ffa08d4909c809aTimo Sirainen /* make sure key or value can't break HTTP headers entirely */
887c92aa9f171ab90e417de48ffa08d4909c809aTimo Sirainen i_assert(strpbrk(key, ":\r\n") == NULL);
887c92aa9f171ab90e417de48ffa08d4909c809aTimo Sirainen i_assert(strpbrk(value, "\r\n") == NULL);
49287618521ff2c69385456de116e5d1581426c0Timo Sirainen
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* mark presence of special headers */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch switch (key[0]) {
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch case 'a': case 'A':
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch if (strcasecmp(key, "Authorization") == 0)
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->have_hdr_authorization = TRUE;
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch break;
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;
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch case 'p': case 'P':
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch i_assert(strcasecmp(key, "Proxy-Authorization") != 0);
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan 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;
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t 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
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Boschvoid http_client_request_set_payload_data(struct http_client_request *req,
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch const unsigned char *data, size_t size)
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch{
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch struct istream *input;
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch unsigned char *payload_data;
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch if (size == 0)
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch return;
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch payload_data = p_malloc(req->pool, size);
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch memcpy(payload_data, data, size);
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch input = i_stream_create_from_data(payload_data, size);
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch http_client_request_set_payload(req, input, FALSE);
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch i_stream_unref(&input);
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch}
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Boschvoid http_client_request_set_payload_empty(struct http_client_request *req)
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch{
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch req->payload_empty = TRUE;
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch}
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan 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
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainenvoid http_client_request_set_attempt_timeout_msecs(struct http_client_request *req,
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen unsigned int msecs)
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen{
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->attempt_timeout_msecs = msecs;
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen}
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainenvoid http_client_request_set_max_attempts(struct http_client_request *req,
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen unsigned int max_attempts)
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen{
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->max_attempts = max_attempts;
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen}
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Boschvoid http_client_request_set_auth_simple(struct http_client_request *req,
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch const char *username, const char *password)
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch{
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->username = p_strdup(req->pool, username);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->password = p_strdup(req->pool, password);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch}
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch
befccf297cba74009dfd0447a0dcea018af756feStephan Boschvoid http_client_request_set_proxy_url(struct http_client_request *req,
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch const struct http_url *proxy_url)
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch{
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_url = http_url_clone_authority(req->pool, proxy_url);
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_socket = NULL;
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch}
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch
befccf297cba74009dfd0447a0dcea018af756feStephan Boschvoid http_client_request_set_proxy_socket(struct http_client_request *req,
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch const char *proxy_socket)
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch{
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_socket = p_strdup(req->pool, proxy_socket);
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_url = NULL;
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch}
befccf297cba74009dfd0447a0dcea018af756feStephan 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
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(req->client != NULL);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan 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 ?
eed02e3be957727e44afac3c20ab64ed80cc8644Timo Sirainen req->attempt_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
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschconst char *
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschhttp_client_request_get_method(const struct http_client_request *req)
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen{
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen return req->method;
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen}
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschconst char *
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschhttp_client_request_get_target(const struct http_client_request *req)
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen{
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen return req->target;
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen}
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenenum http_request_state
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschhttp_client_request_get_state(const struct http_client_request *req)
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen{
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen return req->state;
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen}
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Boschvoid http_client_request_get_stats(struct http_client_request *req,
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch struct http_client_request_stats *stats_r)
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch{
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch struct http_client *client = req->client;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch int diff_msecs;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch uint64_t wait_usecs;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch i_zero(stats_r);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch if (!req->submitted)
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch return;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* total elapsed time since message was submitted */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch diff_msecs = timeval_diff_msecs(&ioloop_timeval, &req->submit_time);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats_r->total_msecs = (unsigned int)I_MAX(diff_msecs, 0);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen /* elapsed time since message was first sent */
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen if (req->first_sent_time.tv_sec > 0) {
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen diff_msecs = timeval_diff_msecs(&ioloop_timeval, &req->first_sent_time);
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen stats_r->first_sent_msecs = (unsigned int)I_MAX(diff_msecs, 0);
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen }
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* elapsed time since message was last sent */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch if (req->sent_time.tv_sec > 0) {
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch diff_msecs = timeval_diff_msecs(&ioloop_timeval, &req->sent_time);
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen stats_r->last_sent_msecs = (unsigned int)I_MAX(diff_msecs, 0);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch }
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch if (req->conn != NULL) {
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* time spent in other ioloops */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch i_assert(ioloop_global_wait_usecs >= req->sent_global_ioloop_usecs);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats_r->other_ioloop_msecs = (unsigned int)
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch (ioloop_global_wait_usecs - req->sent_global_ioloop_usecs + 999) / 1000;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* time spent in the http-client's own ioloop */
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client != NULL && client->waiting) {
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch wait_usecs = io_wait_timer_get_usecs(req->conn->io_wait_timer);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch i_assert(wait_usecs >= req->sent_http_ioloop_usecs);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats_r->http_ioloop_msecs = (unsigned int)
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch (wait_usecs - req->sent_http_ioloop_usecs + 999) / 1000;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch i_assert(stats_r->other_ioloop_msecs >= stats_r->http_ioloop_msecs);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats_r->other_ioloop_msecs -= stats_r->http_ioloop_msecs;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch }
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch }
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* total time spent on waiting for file locks */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch wait_usecs = file_lock_wait_get_total_usecs();
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch i_assert(wait_usecs >= req->sent_lock_usecs);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats_r->lock_msecs = (unsigned int)
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch (wait_usecs - req->sent_lock_usecs + 999) / 1000;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* number of attempts for this request */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats_r->attempts = req->attempts;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch}
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Boschvoid http_client_request_append_stats_text(struct http_client_request *req,
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch string_t *str)
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch{
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch struct http_client_request_stats stats;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch if (!req->submitted) {
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch str_append(str, "not yet submitted");
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch return;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch }
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch http_client_request_get_stats(req, &stats);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen str_printfa(str, "queued %u.%03u secs ago",
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen stats.total_msecs/1000, stats.total_msecs%1000);
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen if (stats.first_sent_msecs == 0)
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen str_append(str, ", not yet sent");
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen else {
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch str_printfa(str, ", %u attempts in %u.%03u secs",
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats.attempts + 1,
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen stats.first_sent_msecs/1000, stats.first_sent_msecs%1000);
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen if (stats.attempts > 0) {
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen str_printfa(str, ", %u.%03u in last attempt",
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen stats.last_sent_msecs/1000,
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen stats.last_sent_msecs%1000);
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen }
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch }
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch if (stats.http_ioloop_msecs > 0) {
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch str_printfa(str, ", %u.%03u in http ioloop",
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats.http_ioloop_msecs/1000,
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats.http_ioloop_msecs%1000);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch }
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch str_printfa(str, ", %u.%03u in other ioloops",
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats.other_ioloop_msecs/1000, stats.other_ioloop_msecs%1000);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch if (stats.lock_msecs > 0) {
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch str_printfa(str, ", %u.%03u in locks",
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats.lock_msecs/1000, stats.lock_msecs%1000);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch }
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch}
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch
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;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch const char *proxy_socket_path = client->set.proxy_socket_path;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const struct http_url *proxy_url = client->set.proxy_url;
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch bool have_proxy = (proxy_socket_path != NULL) || (proxy_url != NULL) ||
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch (req->host_socket != NULL) || (req->host_url != NULL);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch const char *authority, *target;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch if (req->state == HTTP_REQUEST_STATE_ABORTED)
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch return;
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(client != NULL);
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 */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (have_proxy) {
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch if (req->host_socket != NULL) { /* specific socket proxy */
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_url = NULL;
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch } else if (req->host_url != NULL) { /* specific normal proxy */
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_socket = NULL;
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch } else if (req->origin_url.have_ssl &&
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch !client->set.no_ssl_tunnel && !req->connect_tunnel) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_url = &req->origin_url; /* tunnel to origin server */
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->ssl_tunnel = TRUE;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch } else if (proxy_socket_path != NULL) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_socket = proxy_socket_path; /* proxy on unix socket */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_url = NULL;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch } else {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_url = proxy_url; /* normal proxy server */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_socket = NULL;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch } else {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan 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 */
47a53a80656dc400ff8effdc1432a69fbf5ae8baTimo Sirainen req->label = p_strdup_printf(req->pool, "[Req%u: %s %s]", req->id, req->method, target);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* update request target */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (req->connect_tunnel || have_proxy)
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->target = p_strdup(req->pool, target);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (!have_proxy) {
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
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch host = http_client_host_get(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{
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(req->client != NULL);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch
31fa529029f35f65451fb1d119ed1d5435b62e46Timo Sirainen req->submit_time = ioloop_timeval;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch http_client_request_do_submit(req);
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen req->submitted = TRUE;
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch http_client_request_add(req);
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Submitted (requests left=%d)",
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch req->client->requests_count);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch}
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Boschvoid
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Boschhttp_client_request_get_peer_addr(const struct http_client_request *req,
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch struct http_client_peer_addr *addr)
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch{
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch const char *host_socket = req->host_socket;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch const struct http_url *host_url = req->host_url;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch
8192e6fcab193e174a3258457e967a6fcc60b05eStephan Bosch /* the IP address may be unassigned in the returned peer address, since
8192e6fcab193e174a3258457e967a6fcc60b05eStephan Bosch that is only available at this stage when the target URL has an
8192e6fcab193e174a3258457e967a6fcc60b05eStephan Bosch explicit IP address. */
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(addr);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (host_socket != NULL) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->type = HTTP_CLIENT_PEER_ADDR_UNIX;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->a.un.path = host_socket;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch } else if (req->connect_direct) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->type = HTTP_CLIENT_PEER_ADDR_RAW;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch addr->a.tcp.ip = host_url->host.ip;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->a.tcp.port =
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch (host_url->port != 0 ? host_url->port : HTTPS_DEFAULT_PORT);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch } else if (host_url->have_ssl) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (req->ssl_tunnel)
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch else
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch addr->a.tcp.ip = host_url->host.ip;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch addr->a.tcp.https_name = host_url->host.name;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->a.tcp.port =
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch (host_url->port != 0 ? host_url->port : HTTPS_DEFAULT_PORT);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch } else {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->type = HTTP_CLIENT_PEER_ADDR_HTTP;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch addr->a.tcp.ip = host_url->host.ip;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->a.tcp.port =
0af543c1f5407f2fafd8685642e55385abade14aTimo Sirainen (host_url->port != 0 ? host_url->port : HTTP_DEFAULT_PORT);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch }
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch}
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan 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);
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* we're now waiting for a response from the server */
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch req->state = HTTP_REQUEST_STATE_WAITING;
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch http_client_connection_start_request_timeout(req->conn);
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch }
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch /* release connection */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->conn->output_locked = FALSE;
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "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{
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch struct ioloop *prev_ioloop, *client_ioloop, *prev_client_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
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(client != NULL);
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);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (req->state == HTTP_REQUEST_STATE_ABORTED) {
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch /* Request already failed */
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (req->delayed_error != NULL) {
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch struct http_client_request *tmpreq = req;
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch /* Handle delayed error outside ioloop; the caller expects callbacks
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch occurring, so there is no need for delay. Also, it is very
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch important that any error triggers a callback before
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch http_client_request_send_payload() finishes, since its return
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch value is not always checked.
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch */
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch http_client_remove_request_error(client, req);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch http_client_request_error_delayed(&tmpreq);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch }
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch } else {
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch /* Wait for payload data to be written */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch prev_ioloop = current_ioloop;
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch client_ioloop = io_loop_create();
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch prev_client_ioloop = http_client_switch_ioloop(client);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (client->set.dns_client != NULL)
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch dns_client_switch_ioloop(client->set.dns_client);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch client->waiting = TRUE;
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch while (req->state < HTTP_REQUEST_STATE_PAYLOAD_IN) {
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Waiting for request to finish");
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT)
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch o_stream_set_flush_pending(req->payload_output, TRUE);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch io_loop_run(client_ioloop);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT &&
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch req->payload_input->eof) {
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch i_stream_unref(&req->payload_input);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch req->payload_input = NULL;
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch break;
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch }
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch }
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch client->waiting = FALSE;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch if (prev_client_ioloop != NULL)
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch io_loop_set_current(prev_client_ioloop);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch else
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch io_loop_set_current(prev_ioloop);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch (void)http_client_switch_ioloop(client);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (client->set.dns_client != NULL)
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch dns_client_switch_ioloop(client->set.dns_client);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch io_loop_set_current(client_ioloop);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch io_loop_destroy(&client_ioloop);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch }
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 */
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen if (!http_client_request_unref(&req))
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{
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen struct http_client_request *req = *_req;
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen int ret;
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen i_assert(data != NULL);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen ret = http_client_request_continue_payload(&req, data, size);
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch if (ret < 0) {
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch /* failed to send payload */
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen *_req = NULL;
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch } else if (ret > 0) {
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch /* premature end of request;
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch server sent error before all payload could be sent */
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch ret = -1;
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch *_req = NULL;
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch } else {
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch /* not finished sending payload */
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen i_assert(req != NULL);
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen }
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen return ret;
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen}
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenint http_client_request_finish_payload(struct http_client_request **_req)
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen{
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen struct http_client_request *req = *_req;
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen int ret;
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen *_req = NULL;
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen ret = http_client_request_continue_payload(&req, NULL, 0);
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen i_assert(ret != 0);
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen return ret < 0 ? -1 : 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
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek 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,
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch bool pipelined, const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_connection *conn = req->conn;
a68aaff537e2e30d782bb8b9d8782e1a10a17d1aStephan Bosch struct http_client_context *cctx = conn->ppool->peer->cctx;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen struct ostream *output = req->payload_output;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen enum ostream_send_istream_result res;
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch uoff_t offset;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_assert(req->payload_input != NULL);
667de5cf294d833b3d47dd455bacff4fd68dd146Timo Sirainen i_assert(req->payload_output != NULL);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
5f1d689131a75c39f064cbd4202373e7edf78f18Josef 'Jeff' Sipek io_remove(&conn->io_req_payload);
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen /* chunked ostream needs to write to the parent stream's buffer */
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch offset = req->payload_input->v_offset;
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen res = o_stream_send_istream(output, req->payload_input);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_set_max_buffer_size(output, (size_t)-1);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(req->payload_input->v_offset >= offset);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Send more (sent %"PRIuUOFF_T", buffered=%"PRIuSIZE_T")",
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch (uoff_t)(req->payload_input->v_offset - offset),
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch o_stream_get_buffer_used_size(output));
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen switch (res) {
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* finished sending */
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) {
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* this chunk of input is finished
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch (client needs to act; disable timeout) */
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch i_assert(!pipelined);
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen conn->output_locked = TRUE;
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch http_client_connection_stop_request_timeout(conn);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (req->client != NULL && req->client->waiting)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch io_loop_stop(req->client->ioloop);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch } else {
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* finished sending payload */
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen http_client_request_finish_payload_out(req);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch }
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return 0;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* input is blocking (client needs to act; disable timeout) */
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch conn->output_locked = TRUE;
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch if (!pipelined)
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch http_client_connection_stop_request_timeout(conn);
a68aaff537e2e30d782bb8b9d8782e1a10a17d1aStephan Bosch conn->io_req_payload = io_add_istream_to(
a68aaff537e2e30d782bb8b9d8782e1a10a17d1aStephan Bosch cctx->ioloop, req->payload_input,
37703e8d00a3a486aafba6a276fef35b38eab948Timo Sirainen http_client_request_payload_input, req);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return 0;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen /* output is blocking (server needs to act; enable timeout) */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen conn->output_locked = TRUE;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen if (!pipelined)
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen http_client_connection_start_request_timeout(conn);
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Partially sent payload");
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return 0;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen /* we're in the middle of sending a request, so the connection
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen will also have to be aborted */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %s",
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen i_stream_get_name(req->payload_input),
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen i_stream_get_error(req->payload_input));
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen /* the payload stream assigned to this request is broken,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen fail this the request immediately */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen http_client_request_error(&req,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen "Broken payload stream");
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return -1;
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen /* failed to send request */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen *error_r = t_strdup_printf("write(%s) failed: %s",
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen o_stream_get_name(output),
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen o_stream_get_error(output));
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen i_unreached();
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainenstatic int http_client_request_send_real(struct http_client_request *req,
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch bool pipelined, const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch const struct http_client_settings *set = &req->client->set;
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 }
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch if (!req->have_hdr_authorization &&
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->username != NULL && req->password != NULL) {
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch struct http_auth_credentials auth_creds;
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch http_auth_basic_credentials_init(&auth_creds,
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->username, req->password);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch str_append(rtext, "Authorization: ");
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch http_auth_create_credentials(rtext, &auth_creds);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch str_append(rtext, "\r\n");
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch }
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch if (http_client_request_to_proxy(req) &&
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch set->proxy_username != NULL && set->proxy_password != NULL) {
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch struct http_auth_credentials auth_creds;
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch http_auth_basic_credentials_init(&auth_creds,
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch set->proxy_username, set->proxy_password);
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch str_append(rtext, "Proxy-Authorization: ");
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch http_auth_create_credentials(rtext, &auth_creds);
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch str_append(rtext, "\r\n");
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan 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 }
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch if (req->payload_input != NULL && req->payload_chunked) {
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch // FIXME: can't do this for a HTTP/1.0 server
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch if (!req->have_hdr_body_spec)
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch str_append(rtext, "Transfer-Encoding: chunked\r\n");
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch req->payload_output =
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch http_transfer_chunked_ostream_create(output);
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch } else if (req->payload_input != NULL ||
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch req->payload_empty ||
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch strcasecmp(req->method, "POST") == 0 ||
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch strcasecmp(req->method, "PUT") == 0) {
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch /* send Content-Length if we have specified a payload
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch or when one is normally expected, even if it's 0 bytes. */
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch i_assert(req->payload_input != NULL || req->payload_size == 0);
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch if (!req->have_hdr_body_spec) {
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch req->payload_size);
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch }
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Bosch if (req->payload_input != NULL) {
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen req->payload_output = output;
35df1d3e03ffb05ee21077018f5154a4b1e47e37Timo Sirainen o_stream_ref(output);
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
91a4eaad6cae9b59820a1fa3ad42ef7aea7d1e67Stephan Bosch if (!req->have_hdr_connection &&
91a4eaad6cae9b59820a1fa3ad42ef7aea7d1e67Stephan Bosch !http_client_request_to_proxy(req)) {
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;
f0452e0b310ddc74d84c2492f72fea41ffcb96a8Timo Sirainen if (req->first_sent_time.tv_sec == 0)
f0452e0b310ddc74d84c2492f72fea41ffcb96a8Timo Sirainen req->first_sent_time = ioloop_timeval;
31fa529029f35f65451fb1d119ed1d5435b62e46Timo Sirainen req->sent_time = ioloop_timeval;
86ab71bf134573e45dd9ab6bde9d55da4c44fc48Timo Sirainen req->sent_lock_usecs = file_lock_wait_get_total_usecs();
86ab71bf134573e45dd9ab6bde9d55da4c44fc48Timo Sirainen req->sent_global_ioloop_usecs = ioloop_global_wait_usecs;
92f66f98f64ee3febbfba6618c4e144080d81a66Timo Sirainen req->sent_http_ioloop_usecs =
92f66f98f64ee3febbfba6618c4e144080d81a66Timo Sirainen io_wait_timer_get_usecs(req->conn->io_wait_timer);
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 {
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Sent header");
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch if (req->payload_output != NULL) {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch if (!req->payload_sync) {
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch if (http_client_request_send_more
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch (req, pipelined, error_r) < 0)
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch ret = -1;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch } else {
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Waiting for 100-continue");
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch conn->output_locked = TRUE;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch } else {
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch req->state = HTTP_REQUEST_STATE_WAITING;
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch if (!pipelined)
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch http_client_connection_start_request_timeout(req->conn);
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch conn->output_locked = FALSE;
22215c2d7538f4367c93e2d8b6ec4722463ac757Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch if (ret >= 0 && o_stream_uncork_flush(output) < 0) {
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch *error_r = t_strdup_printf("flush(%s) failed: %s",
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch o_stream_get_name(output),
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch o_stream_get_error(output));
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch ret = -1;
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch }
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainenint http_client_request_send(struct http_client_request *req,
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch bool pipelined, const char **error_r)
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen{
2ff548b46061f984def8d36736745333b8405a31Timo Sirainen char *errstr = NULL;
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen int ret;
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainen T_BEGIN {
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch ret = http_client_request_send_real(req, pipelined, 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) {
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen struct http_response response_copy = *response;
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen if (req->attempts > 0 && !req->preserve_exact_reason) {
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen unsigned int total_msecs =
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen timeval_diff_msecs(&ioloop_timeval, &req->submit_time);
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen response_copy.reason = t_strdup_printf(
f733f54c88d6acccad5e057f03a10d5221216c61Timo Sirainen "%s (%u retries in %u.%03u secs)",
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen response_copy.reason, req->attempts,
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen total_msecs/1000, total_msecs%1000);
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen }
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen callback(&response_copy, 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) */
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek i_stream_unref(&req->payload_input);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen }
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen }
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen return TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Boschstatic bool
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);
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch unsigned int orig_attempts = req->attempts;
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
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch if (req->attempts != orig_attempts) {
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch /* retrying */
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch req->callback = callback;
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch http_client_request_resubmit(req);
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch return FALSE;
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch } else {
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch /* release payload early (prevents server/client deadlock in proxy) */
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch if (!sending && req->payload_input != NULL)
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch i_stream_unref(&req->payload_input);
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
6e1bc1f4a080539860a3f121c5b51c9bfda1f1afStephan Bosch if (req->payload_wait) {
6e1bc1f4a080539860a3f121c5b51c9bfda1f1afStephan Bosch i_assert(req->client != NULL);
47ff1eaf3bc13a702c8491d248d8d34d08796937Timo Sirainen io_loop_stop(req->client->ioloop);
6e1bc1f4a080539860a3f121c5b51c9bfda1f1afStephan Bosch }
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch return TRUE;
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;
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch const char *error = req->delayed_error;
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch unsigned int status = req->delayed_error_status;
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch bool destroy;
c6494255de7b934281dd052960fd8ab5aa48e79eTimo Sirainen
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_ABORTED);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen *_req = NULL;
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch req->delayed_error = NULL;
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch req->delayed_error_status = 0;
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch i_assert(error != NULL && status != 0);
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch destroy = http_client_request_send_error(req, status, error);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->queue != NULL)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch if (destroy)
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch http_client_request_destroy(&req);
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen}
6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cbTimo Sirainen
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainenvoid http_client_request_error(struct http_client_request **_req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch unsigned int status, const char *error)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen struct http_client_request *req = *_req;
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen
15fe3500f08cfb1d4bde1f97afd7d287486dbd21Stephan Bosch *_req = NULL;
15fe3500f08cfb1d4bde1f97afd7d287486dbd21Stephan Bosch
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch i_assert(req->delayed_error_status == 0);
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen i_assert(req->state < HTTP_REQUEST_STATE_FINISHED);
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
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (req->client != NULL && (!req->submitted ||
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan 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 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 {
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch if (http_client_request_send_error(req, status, error))
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch http_client_request_destroy(&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
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen *_req = NULL;
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen
561a21c8ecefaade916d686c763f2123e1f9a151Stephan Bosch if (req->state >= HTTP_REQUEST_STATE_FINISHED &&
561a21c8ecefaade916d686c763f2123e1f9a151Stephan Bosch req->delayed_error_status == 0)
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);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch if (req->payload_wait) {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(req->client != NULL);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch i_assert(req->client->ioloop != NULL);
47ff1eaf3bc13a702c8491d248d8d34d08796937Timo Sirainen io_loop_stop(req->client->ioloop);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch }
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen http_client_request_destroy(&req);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainenvoid http_client_request_finish(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (req->state >= HTTP_REQUEST_STATE_FINISHED)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
3e9055cee8e331a84522dd1e65d0d0e09a4e8803Stephan Bosch i_assert(req->refcount > 0);
3e9055cee8e331a84522dd1e65d0d0e09a4e8803Stephan Bosch
324f41beb33c20a6cd9248771c7fa4f58533bb3fTimo Sirainen e_debug(event_create_passthrough(req->event)->
324f41beb33c20a6cd9248771c7fa4f58533bb3fTimo Sirainen add_int("attempts", req->attempts)->
324f41beb33c20a6cd9248771c7fa4f58533bb3fTimo Sirainen set_name("http_request_finished")->event(), "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);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch if (req->payload_wait) {
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(req->client != NULL);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch i_assert(req->client->ioloop != NULL);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch io_loop_stop(req->client->ioloop);
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch }
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainen 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
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch i_assert(req->client != NULL);
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) {
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen 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) {
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen 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 {
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen 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) {
d5c665cf2989d49922b63439ac45714e9755838aTimo 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 */
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek 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
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->host = NULL;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch origin_url = http_url_create(&req->origin_url);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "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 */
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek 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
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "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) {
d5c665cf2989d49922b63439ac45714e9755838aTimo 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
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch /* drop payload output stream from previous attempt */
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek o_stream_unref(&req->payload_output);
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch
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))
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen 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 &&
e1d8d185fa74752ba6807e415a9c82ebfdb2082cStephan Bosch (!req->payload_sync || req->payload_sync_continue))
d3d941cc89a8ef5fe0de16bd89e50030e5d22f5bStephan Bosch return FALSE;
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen /* limit the number of attempts for each request */
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen if (req->attempts+1 >= req->max_attempts)
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen return FALSE;
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen req->attempts++;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "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
e94584bf65f0985f1512a9f0c0651dfcc56ed0f2Aki Tuomi#undef http_client_request_set_destroy_callback
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{
9465a050729f555318cbda2c5b9d531b04dbce7fStephan Bosch struct http_client_connection *conn = req->conn;
9465a050729f555318cbda2c5b9d531b04dbce7fStephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
9465a050729f555318cbda2c5b9d531b04dbce7fStephan Bosch http_client_connection_start_tunnel(&conn, tunnel);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}