http-client-request.c revision 0753b82010b1ae517d3cec8988a2cffcfbc0dc5b
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "payload_out",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "got_response",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen "payload_in",
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Boschhttp_client_request_send_error(struct http_client_request *req,
7abab3b191860a3d77af5192b0649833c8a0c803Stephan Boschhttp_client_request_label(struct http_client_request *req)
7a358ecd8918d1280a0b68cdf348c23f6721c799Stephan Bosch http_url_create_host(&req->origin_url), req->target);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainenhttp_client_request_update_event(struct http_client_request *req)
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen event_add_str(req->event, "method", req->method);
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)));
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschhttp_client_request_new(struct http_client *client, const char *method,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_callback_t *callback, void *context)
47a53a80656dc400ff8effdc1432a69fbf5ae8baTimo Sirainen static unsigned int id_counter = 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch pool = pool_alloconly_create("http client request", 2048);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req = p_new(pool, struct http_client_request, 1);
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen /* default to client-wide settings: */
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->attempt_timeout_msecs = client->set.request_timeout_msecs;
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 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));
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 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 req->username = p_strdup(req->pool, target_url->user);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->password = p_strdup(req->pool, target_url->password);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Boschhttp_client_request_url_str(struct http_client *client,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch http_client_request_callback_t *callback, void *context)
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch if (http_url_parse(url_str, NULL, HTTP_URL_ALLOW_USERINFO_PART,
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch "[Req%u: %s %s]", req->id, req->method, url_str);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch t_strdup_printf("Invalid HTTP URL: %s", error));
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 req->username = p_strdup(req->pool, target_url->user);
e46130f48a6962734bfde7e71906d3894bedf45bStephan Bosch req->password = p_strdup(req->pool, target_url->password);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschhttp_client_request_connect(struct http_client *client,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req = http_client_request_new(client, "CONNECT", callback, context);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch req->origin_url.host.name = p_strdup(req->pool, host);
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Boschhttp_client_request_connect_ip(struct http_client *client,
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainenvoid http_client_request_set_event(struct http_client_request *req,
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Boschhttp_client_request_add(struct http_client_request *req)
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Boschhttp_client_request_remove(struct http_client_request *req)
6ab81c81be13f33486746deeffe02a1ef2bcc821Stephan Bosch /* only decrease pending request counter if this request was submitted */
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch if (client->requests_count == 0 && client->waiting)
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_ref(struct http_client_request *req)
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainenbool http_client_request_unref(struct http_client_request **_req)
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Free (client already destroyed)");
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Free (requests left=%d)",
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* cannot be destroyed while it is still pending */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client->requests_count == 0 && client->waiting)
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch http_client_remove_request_error(req->client, req);
79f8a20424633e806447bc9375a5ab403aabc758Stephan Boschvoid http_client_request_destroy(struct http_client_request **_req)
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch struct http_client_request *req = *_req, *tmp_req;
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Destroy (client already destroyed)");
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch e_debug(req->event, "Destroy (requests left=%d)",
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch http_client_queue_drop_request(req->queue, req);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (client != NULL && req->delayed_error != NULL)
561a21c8ecefaade916d686c763f2123e1f9a151Stephan Bosch http_client_remove_request_error(req->client, req);
79f8a20424633e806447bc9375a5ab403aabc758Stephan Bosch void (*callback)(void *) = req->destroy_callback;
567d1c82e3e59a8e8acc2e1e01228585bab83bc6Stephan Bosch http_client_connection_request_destroyed(req->conn, req);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_port(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_ssl(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_urgent(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainenvoid http_client_request_set_preserve_exact_reason(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_add_header(struct http_client_request *req,
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 ||
887c92aa9f171ab90e417de48ffa08d4909c809aTimo Sirainen /* make sure key or value can't break HTTP headers entirely */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* mark presence of special headers */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch switch (key[0]) {
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch else if (strcasecmp(key, "Content-Length") == 0)
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch i_assert(strcasecmp(key, "Proxy-Authorization") != 0);
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (strcasecmp(key, "Transfer-Encoding") == 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_printfa(req->headers, "%s: %s\r\n", key, value);
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainenvoid http_client_request_remove_header(struct http_client_request *req,
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen const char *key)
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen const unsigned char *data, *p;
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 while ((p = memchr(data, '\n', size)) != NULL) {
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);
6d573191bea1a64d6046be070487a5705a2d0204Stephan Boschvoid http_client_request_set_date(struct http_client_request *req,
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_set_payload(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
4521d35c263add6af3f1ae55b3760291767ce50cTimo Sirainen if ((ret = i_stream_get_size(input, TRUE, &req->payload_size)) <= 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* prepare request payload sync using 100 Continue response from server */
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen if ((req->payload_chunked || req->payload_size > 0) && sync)
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Boschvoid http_client_request_set_payload_data(struct http_client_request *req,
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch unsigned char *payload_data;
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch input = i_stream_create_from_data(payload_data, size);
7a13cd2268a5a99d2975a1648d6d14ffe1b6ccb0Stephan Bosch http_client_request_set_payload(req, input, FALSE);
4d1cc687f40dc2697005a4977e7ac40dda3d7dcfStephan Boschvoid http_client_request_set_payload_empty(struct http_client_request *req)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschvoid http_client_request_set_timeout_msecs(struct http_client_request *req,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch unsigned int msecs)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschvoid http_client_request_set_timeout(struct http_client_request *req,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainenvoid http_client_request_set_attempt_timeout_msecs(struct http_client_request *req,
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen unsigned int msecs)
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainenvoid http_client_request_set_max_attempts(struct http_client_request *req,
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
5b6bfa39481f719c1fd6ed3febeaf7f0be9e8886Timo Sirainen req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Boschvoid http_client_request_set_auth_simple(struct http_client_request *req,
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->username = p_strdup(req->pool, username);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->password = p_strdup(req->pool, password);
befccf297cba74009dfd0447a0dcea018af756feStephan Boschvoid http_client_request_set_proxy_url(struct http_client_request *req,
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_url = http_url_clone_authority(req->pool, proxy_url);
befccf297cba74009dfd0447a0dcea018af756feStephan Boschvoid http_client_request_set_proxy_socket(struct http_client_request *req,
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch req->host_socket = p_strdup(req->pool, proxy_socket);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_request_delay_until(struct http_client_request *req,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_request_delay(struct http_client_request *req,
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Boschvoid http_client_request_delay_msecs(struct http_client_request *req,
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch unsigned int msecs)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschint http_client_request_delay_from_response(struct http_client_request *req,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch unsigned int max;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return 0; /* no delay */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return 0; /* delay already expired */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch max = (req->client->set.max_auto_retry_delay == 0 ?
19db4c57fd7acc9e54e5724ccfa0633a5665dfefTimo Sirainen if ((unsigned int)(retry_after - ioloop_time) > max)
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschhttp_client_request_get_method(const struct http_client_request *req)
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschhttp_client_request_get_target(const struct http_client_request *req)
2a9cadfccc8fb2c609eedbb929952b49181b6d25Stephan Boschhttp_client_request_get_state(const struct http_client_request *req)
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Boschvoid http_client_request_get_stats(struct http_client_request *req,
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);
bf7740f6bcea93a45e68113f1334c2be7f3cea4dTimo Sirainen /* elapsed time since message was first sent */
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);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* elapsed time since message was last sent */
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 /* time spent in other ioloops */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch i_assert(ioloop_global_wait_usecs >= req->sent_global_ioloop_usecs);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch (ioloop_global_wait_usecs - req->sent_global_ioloop_usecs + 999) / 1000;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* time spent in the http-client's own ioloop */
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 (wait_usecs - req->sent_http_ioloop_usecs + 999) / 1000;
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 /* total time spent on waiting for file locks */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch wait_usecs = file_lock_wait_get_total_usecs();
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch (wait_usecs - req->sent_lock_usecs + 999) / 1000;
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch /* number of attempts for this request */
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Boschvoid http_client_request_append_stats_text(struct http_client_request *req,
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen stats.total_msecs/1000, stats.total_msecs%1000);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch str_printfa(str, ", %u attempts in %u.%03u secs",
f27497406f8819ac97bc16cd9b16d20630899187Timo Sirainen stats.first_sent_msecs/1000, stats.first_sent_msecs%1000);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch str_printfa(str, ", %u.%03u in other ioloops",
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats.other_ioloop_msecs/1000, stats.other_ioloop_msecs%1000);
28be42b4c27b0415da92ae7dd2c6e9cee2836c51Stephan Bosch stats.lock_msecs/1000, stats.lock_msecs%1000);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschhttp_client_request_get_payload_type(struct http_client_request *req)
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 3.3:
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 return HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch return HTTP_RESPONSE_PAYLOAD_TYPE_ONLY_UNSUCCESSFUL;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Boschstatic void http_client_request_do_submit(struct http_client_request *req)
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);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch authority = http_url_create_authority(&req->origin_url);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* connect requests require authority form for request target */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* absolute target url */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch (http_url_create_host(&req->origin_url), req->target, NULL);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* determine what host to contact to submit this request */
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch if (req->host_socket != NULL) { /* specific socket proxy */
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch } else if (req->host_url != NULL) { /* specific normal proxy */
befccf297cba74009dfd0447a0dcea018af756feStephan Bosch !client->set.no_ssl_tunnel && !req->connect_tunnel) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_url = &req->origin_url; /* tunnel to origin server */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_socket = proxy_socket_path; /* proxy on unix socket */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_url = proxy_url; /* normal proxy server */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch req->host_url = &req->origin_url; /* origin server */
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch /* use submission date if no date is set explicitly */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* prepare value for Host header */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->authority = p_strdup(req->pool, authority);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* debug label */
47a53a80656dc400ff8effdc1432a69fbf5ae8baTimo Sirainen req->label = p_strdup_printf(req->pool, "[Req%u: %s %s]", req->id, req->method, target);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* update request target */
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch /* if we don't have a proxy, CONNECT requests are handled by creating
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch the requested connection directly */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch timeval_add_msecs(&req->timeout_time, req->timeout_msecs);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch } else if ( client->set.request_absolute_timeout_msecs > 0) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch timeval_add_msecs(&req->timeout_time, client->set.request_absolute_timeout_msecs);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch host = http_client_host_get(client, req->host_url);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Boschvoid http_client_request_submit(struct http_client_request *req)
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Submitted (requests left=%d)",
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Boschhttp_client_request_get_peer_addr(const struct http_client_request *req,
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch const struct http_url *host_url = req->host_url;
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. */
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch (host_url->port != 0 ? host_url->port : HTTPS_DEFAULT_PORT);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL;
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch (host_url->port != 0 ? host_url->port : HTTPS_DEFAULT_PORT);
0af543c1f5407f2fafd8685642e55385abade14aTimo Sirainen (host_url->port != 0 ? host_url->port : HTTP_DEFAULT_PORT);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenhttp_client_request_finish_payload_out(struct http_client_request *req)
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch /* drop payload output stream */
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 /* we're now waiting for a response from the server */
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch http_client_connection_start_request_timeout(req->conn);
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch /* release connection */
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Finished sending%s payload",
aab7256cdcfb7abd01c822e3df8dd77a30c572e0Stephan Bosch (req->state == HTTP_REQUEST_STATE_ABORTED ? " aborted" : ""));
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenhttp_client_request_continue_payload(struct http_client_request **_req,
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch struct ioloop *prev_ioloop, *client_ioloop, *prev_client_ioloop;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch struct http_client_connection *conn = req->conn;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW ||
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT)
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch req->payload_input = i_stream_create_from_data(data, size);
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen i_stream_set_name(req->payload_input, "<HTTP request payload>");
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (req->state == HTTP_REQUEST_STATE_ABORTED) {
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch /* Request already failed */
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.
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch http_client_remove_request_error(client, req);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch /* Wait for payload data to be written */
e8a1b62fe4a81b211dcccd1a58b44f254074eab6Stephan Bosch prev_client_ioloop = http_client_switch_ioloop(client);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch dns_client_switch_ioloop(client->set.dns_client);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch while (req->state < HTTP_REQUEST_STATE_PAYLOAD_IN) {
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Waiting for request to finish");
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT)
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch o_stream_set_flush_pending(req->payload_output, TRUE);
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT &&
bf45537f0263bdc02dea119ef2ab79599bf1a5c8Stephan Bosch dns_client_switch_ioloop(client->set.dns_client);
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch /* callback may have messed with our pointer,
b36e026fb1e31bb76524cb345eb40c73e528507bStephan Bosch so unref using local variable */
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch /* Return status */
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenint http_client_request_send_payload(struct http_client_request **_req,
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen ret = http_client_request_continue_payload(&req, data, size);
b284962364068d5e9d60b8b1e98ba61b4b6593ecStephan Bosch /* failed to send payload */
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 /* not finished sending payload */
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainenint http_client_request_finish_payload(struct http_client_request **_req)
c3a4c931e95737a52e1cebeeb109a2e1cc4d47d6Timo Sirainen ret = http_client_request_continue_payload(&req, NULL, 0);
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Boschstatic void http_client_request_payload_input(struct http_client_request *req)
a4e186e3ef267fc7a6b592788067c8c9c87d0785Stephan Bosch struct http_client_connection *conn = req->conn;
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainenint http_client_request_send_more(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_connection *conn = req->conn;
a68aaff537e2e30d782bb8b9d8782e1a10a17d1aStephan Bosch struct http_client_context *cctx = conn->ppool->peer->cctx;
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen /* chunked ostream needs to write to the parent stream's buffer */
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen res = o_stream_send_istream(output, req->payload_input);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_set_max_buffer_size(output, (size_t)-1);
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),
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* finished sending */
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 req->payload_input->v_offset, req->payload_offset, req->payload_size);
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* this chunk of input is finished
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch (client needs to act; disable timeout) */
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch http_client_connection_stop_request_timeout(conn);
fda72a49ea41acd0bd085ec2e63813e01f156fdfStephan Bosch if (req->client != NULL && req->client->waiting)
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* finished sending payload */
3d1edb8e3a07d91860cc6b4b3cec8282caa70891Stephan Bosch /* input is blocking (client needs to act; disable timeout) */
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch http_client_connection_stop_request_timeout(conn);
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen /* output is blocking (server needs to act; enable timeout) */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen http_client_connection_start_request_timeout(conn);
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Partially sent payload");
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 /* the payload stream assigned to this request is broken,
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen fail this the request immediately */
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen "Broken payload stream");
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",
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainenstatic int http_client_request_send_real(struct http_client_request *req,
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch const struct http_client_settings *set = &req->client->set;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_client_connection *conn = req->conn;
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* create request line */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* create special headers implicitly if not set explicitly using
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch http_client_request_add_header() */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, http_date_create(req->date));
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch req->username != NULL && req->password != NULL) {
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch http_auth_create_credentials(rtext, &auth_creds);
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch set->proxy_username != NULL && set->proxy_password != NULL) {
4c4c4a740bbb1b674d4b0dae009d1919f8ad96b7Stephan Bosch http_auth_create_credentials(rtext, &auth_creds);
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen if (!req->have_hdr_user_agent && req->client->set.user_agent != NULL) {
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch if (!req->have_hdr_expect && req->payload_sync) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch str_append(rtext, "Expect: 100-continue\r\n");
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 str_append(rtext, "Transfer-Encoding: chunked\r\n");
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 str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch Section 19.7.1:
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.
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch str_append(rtext, "Connection: Keep-Alive\r\n");
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* request line + implicit headers */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* explicit headers */
87c121a4c05b9cee46f1f757ec6999d441519abfStephan Bosch /* end of header */
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 io_wait_timer_get_usecs(req->conn->io_wait_timer);
99feb6521535a7dc59d8dda89981ceac084b3e88Timo Sirainen if (o_stream_sendv(output, iov, N_ELEMENTS(iov)) < 0) {
dde71564d306d07cba63bdf0f40996ffb90ca47aTimo Sirainen *error_r = t_strdup_printf("write(%s) failed: %s",
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Waiting for 100-continue");
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch http_client_connection_start_request_timeout(req->conn);
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch if (ret >= 0 && o_stream_uncork_flush(output) < 0) {
0753b82010b1ae517d3cec8988a2cffcfbc0dc5bStephan Bosch *error_r = t_strdup_printf("flush(%s) failed: %s",
ccd968b44a40b9c2cf6278fabfa2a80cc5d9e46bTimo Sirainenint http_client_request_send(struct http_client_request *req,
173d53863744e8ed87e8fa4c32dfcf3759e2ceb0Stephan Bosch ret = http_client_request_send_real(req, pipelined, error_r);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainenbool http_client_request_callback(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_callback_t *callback = req->callback;
93c9909f68f7d057e38cca3b4612ec8d0bf42999Timo Sirainen struct http_response response_copy = *response;
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);
f733f54c88d6acccad5e057f03a10d5221216c61Timo Sirainen "%s (%u retries in %u.%03u secs)",
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen /* retrying */
6c6915f4d75c352c63156df202fa51cd97524babStephan Bosch /* release payload early (prevents server/client deadlock in proxy) */
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request_send_error(struct http_client_request *req,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch bool sending = (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT);
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch /* retrying */
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch /* release payload early (prevents server/client deadlock in proxy) */
129596c93692b21d6c6b1313b389774af24c2983Stephan Boschvoid http_client_request_error_delayed(struct http_client_request **_req)
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch unsigned int status = req->delayed_error_status;
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_ABORTED);
8d845733408c0cb06a8884d12101beb0d40e6869Stephan Bosch destroy = http_client_request_send_error(req, status, error);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainenvoid http_client_request_error(struct http_client_request **_req,
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen i_assert(req->state < HTTP_REQUEST_STATE_FINISHED);
840abb812d2e8edc42b5e2a4e3838b8d5e759e6aStephan Bosch http_client_queue_drop_request(req->queue, req);
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);
fca68889b287d8eed4babe72a231bd6079da012dStephan Bosch http_client_delay_request_error(req->client, req);
d6a88217c273cc67f5de93949bdd065a15eeabe0Stephan Bosch if (http_client_request_send_error(req, status, error))
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_abort(struct http_client_request **_req)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch bool sending = (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT);
561a21c8ecefaade916d686c763f2123e1f9a151Stephan Bosch if (req->state >= HTTP_REQUEST_STATE_FINISHED &&
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* release payload early (prevents server/client deadlock in proxy) */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch http_client_queue_drop_request(req->queue, req);
d1f964d3f1dd9c5868b134c4f44dd63f3722eef7Timo Sirainenvoid http_client_request_finish(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (req->state >= HTTP_REQUEST_STATE_FINISHED)
324f41beb33c20a6cd9248771c7fa4f58533bb3fTimo Sirainen e_debug(event_create_passthrough(req->event)->
324f41beb33c20a6cd9248771c7fa4f58533bb3fTimo Sirainen set_name("http_request_finished")->event(), "Finished");
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_redirect(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* parse URL */
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 if (++req->redirects > req->client->set.max_redirects) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch t_strdup_printf("Redirected more than %d times",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Redirect refused");
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 "Redirect failed: Cannot resend payload; stream is not seekable");
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_stream_seek(req->payload_input, req->payload_offset);
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch /* drop payload output stream from previous attempt */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_url_copy(req->pool, &req->origin_url, url);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch origin_url = http_url_create(&req->origin_url);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->label = p_strdup_printf(req->pool, "[%s %s%s]",
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7231, Section 6.4.4:
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 if (status == 303 && strcasecmp(req->method, "HEAD") != 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 /* drop payload */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* resubmit */
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_resubmit(struct http_client_request *req)
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 "Resubmission failed: Cannot resend payload; stream is not seekable");
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen i_stream_seek(req->payload_input, req->payload_offset);
27421074812b84d144b68388e597f4700f4f1c1bStephan Bosch /* drop payload output stream from previous attempt */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch http_client_host_submit_request(req->host, req);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_retry(struct http_client_request *req,
d5c665cf2989d49922b63439ac45714e9755838aTimo Sirainen http_client_request_error(&req, status, error);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainenbool http_client_request_try_retry(struct http_client_request *req)
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)
e1d8d185fa74752ba6807e415a9c82ebfdb2082cStephan Bosch (!req->payload_sync || req->payload_sync_continue))
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen /* limit the number of attempts for each request */
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(req->event, "Retrying (attempts=%d)", req->attempts);
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainenvoid http_client_request_set_destroy_callback(struct http_client_request *req,
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen void (*callback)(void *),
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschvoid http_client_request_start_tunnel(struct http_client_request *req,
9465a050729f555318cbda2c5b9d531b04dbce7fStephan Bosch struct http_client_connection *conn = req->conn;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_GOT_RESPONSE);