http-client-request.c revision 65c0e43da8cfc730eeb4634f8aa384081bbfa4e7
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "lib.h"
fc94140acba51adafedafbc8491a3223a51db7a8Stephan Bosch#include "net.h"
fc94140acba51adafedafbc8491a3223a51db7a8Stephan Bosch#include "str.h"
1920ef85b63738a06914e56508049dd0afe38732Timo Sirainen#include "hash.h"
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch#include "array.h"
74c09aceb0118b564f8443e1276c465738d19c17Timo Sirainen#include "istream.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "ostream.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-url.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-response-parser.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-transfer.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "http-client-private.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch/*
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch * Logging
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
50d1446e71cfbdc5b6d7bafcf91b7bff453989d3Stephan Boschstatic inline void
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request_debug(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *format, ...) ATTR_FORMAT(2, 3);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic inline void
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenhttp_client_request_debug(struct http_client_request *req,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen const char *format, ...)
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen{
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen va_list args;
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen if (req->client->set.debug) {
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen va_start(args, format);
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen i_debug("http-client: request %s: %s",
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen http_client_request_label(req), t_strdup_vprintf(format, args));
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen va_end(args);
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen }
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen/*
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen * Request
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen */
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen
7944646fad6aa1e7c649c3d33e454c516b0220b6Timo Sirainen#undef http_client_request
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstruct http_client_request *
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_client_request(struct http_client *client,
56d1345c43bbd28c36b7faa85e4163bd9e874290Timo Sirainen const char *method, const char *host, const char *target,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_callback_t *callback, void *context)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
35e962a9186b4e9b2001628c1d7b55c24b33ce84Timo Sirainen pool_t pool;
35e962a9186b4e9b2001628c1d7b55c24b33ce84Timo Sirainen struct http_client_request *req;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen pool = pool_alloconly_create("http client request", 2048);
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen req = p_new(pool, struct http_client_request, 1);
b99130e4cf4af4e6b103b949456222f3a2dff424Timo Sirainen req->pool = pool;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->refcount = 1;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->client = client;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->method = p_strdup(pool, method);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->hostname = p_strdup(pool, host);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->port = HTTP_DEFAULT_PORT;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->target = p_strdup(pool, target);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->callback = callback;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->context = context;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->headers = str_new(default_pool, 256);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->state = HTTP_REQUEST_STATE_NEW;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return req;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_ref(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
ad03049781fc14807248007d524be4daf06c3ee2Stephan Bosch req->refcount++;
ad03049781fc14807248007d524be4daf06c3ee2Stephan Bosch}
ad03049781fc14807248007d524be4daf06c3ee2Stephan Bosch
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Boschvoid http_client_request_unref(struct http_client_request **_req)
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch{
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch struct http_client_request *req = *_req;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch struct http_client *client = req->client;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->refcount > 0);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (--req->refcount > 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* only decrease pending request counter if this request was submitted */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (req->state > HTTP_REQUEST_STATE_NEW)
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch req->client->pending_requests--;
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch http_client_request_debug(req, "Destroy (requests left=%d)",
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch client->pending_requests);
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch if (req->payload_input != NULL)
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch i_stream_unref(&req->payload_input);
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch if (req->payload_output != NULL)
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch o_stream_unref(&req->payload_output);
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch str_free(&req->headers);
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch pool_unref(&req->pool);
fe2b0e3de834dd40b698bb579adc5357d5789ec9Stephan Bosch *_req = NULL;
fe2b0e3de834dd40b698bb579adc5357d5789ec9Stephan Bosch}
94d1b08c9e20d637db568a3eab3dfc2b9e96e62aStephan Bosch
94d1b08c9e20d637db568a3eab3dfc2b9e96e62aStephan Boschvoid http_client_request_set_port(struct http_client_request *req,
fe2b0e3de834dd40b698bb579adc5357d5789ec9Stephan Bosch unsigned int port)
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch{
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch req->port = port;
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch}
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschvoid http_client_request_set_ssl(struct http_client_request *req,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch bool ssl)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch{
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen if (ssl) {
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen if (!req->ssl && req->port == HTTP_DEFAULT_PORT)
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen req->port = HTTPS_DEFAULT_PORT;
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen } else {
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen if (req->ssl && req->port == HTTPS_DEFAULT_PORT)
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainen req->port = HTTP_DEFAULT_PORT;
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen }
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen req->ssl = ssl;
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen}
70505f4839520ac67895992621c97d2480c22e7fTimo Sirainen
a8c4e79ff50fac21b05a7368b052583d410ca15cTimo Sirainenvoid http_client_request_set_urgent(struct http_client_request *req)
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch{
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch req->urgent = TRUE;
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch}
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_request_add_header(struct http_client_request *req,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char *key, const char *value)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch str_printfa(req->headers, "%s: %s\r\n", key, value);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschvoid http_client_request_set_payload(struct http_client_request *req,
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct istream *input, bool sync)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_assert(req->payload_input == NULL);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_ref(input);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->payload_input = input;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (i_stream_get_size(input, TRUE, &req->payload_size) <= 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->payload_size = 0;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch req->payload_chunked = TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->payload_offset = input->v_offset;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* prepare request payload sync using 100 Continue response from server */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((req->payload_chunked || req->payload_size > 0) && sync)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->payload_sync = TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschvoid http_client_request_submit(struct http_client_request *req)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch struct http_client_host *host;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch i_assert(req->state == HTTP_REQUEST_STATE_NEW);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_client_request_debug(req, "Submitted");
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch host = http_client_host_get(req->client, req->hostname);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->state = HTTP_REQUEST_STATE_QUEUED;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch req->client->pending_requests++;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch http_client_host_submit_request(host, req);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Boschint http_client_request_send_more(struct http_client_request *req)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch{
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct http_client_connection *conn = req->conn;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch struct ostream *output = req->payload_output;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch int ret = 0;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch i_assert(req->payload_input != NULL);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch o_stream_set_max_buffer_size(output, 0);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (o_stream_send_istream(output, req->payload_input) < 0)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch ret = -1;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch o_stream_set_max_buffer_size(output, (size_t)-1);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (!i_stream_have_bytes_left(req->payload_input)) {
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch if (!req->payload_chunked &&
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->payload_input->v_offset - req->payload_offset != req->payload_size) {
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch i_error("stream input size changed"); //FIXME
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch return -1;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch }
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch o_stream_unref(&output);
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->payload_output = NULL;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch req->state = HTTP_REQUEST_STATE_WAITING;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch conn->output_locked = FALSE;
e47c2f17d8136c4d972d1074a3f84ba2ecef4fdcStephan Bosch http_client_request_debug(req, "Sent all payload");
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch } else {
fc94140acba51adafedafbc8491a3223a51db7a8Stephan Bosch conn->output_locked = TRUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch o_stream_set_flush_pending(output, TRUE);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_debug(req, "Partially sent payload");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return ret;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainenint http_client_request_send(struct http_client_request *req)
84740b03d3ee9e96a2e446a54729188764c99292Timo Sirainen{
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch struct http_client_connection *conn = req->conn;
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch struct ostream *output = conn->conn.output;
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch string_t *rtext = t_str_new(256);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct const_iovec iov[3];
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch int ret = 0;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(!req->conn->output_locked);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch str_append(rtext, req->method);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch str_append(rtext, " ");
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch str_append(rtext, req->target);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch str_append(rtext, " HTTP/1.1\r\n");
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch str_append(rtext, "Host: ");
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch str_append(rtext, req->hostname);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch if ((!req->ssl &&req->port != HTTP_DEFAULT_PORT) ||
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch (req->ssl && req->port != HTTPS_DEFAULT_PORT)) {
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch str_printfa(rtext, ":%u", req->port);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch }
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch str_append(rtext, "\r\n");
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch if (req->payload_sync) {
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch str_append(rtext, "Expect: 100-continue\r\n");
42630b23d5a1b03cf6db4eaa2eb21e3ec4033b2cTimo Sirainen }
b2a3fbfe1b436123bbe1849eeeef9bb0c28b1f90Timo Sirainen if (req->payload_chunked) {
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen str_append(rtext, "Transfer-Encoding: chunked\r\n");
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen req->payload_output =
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_transfer_chunked_ostream_create(output);
6c768e0e1ca2da178e79f7435c32ced01f6bcb24Timo Sirainen } else if (req->payload_size != 0) {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->payload_size);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch req->payload_output = output;
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen o_stream_ref(output);
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen }
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen iov[0].iov_base = str_data(rtext);
4124bebe6daab2cd05acb0416096fc47cb9abd92Timo Sirainen iov[0].iov_len = str_len(rtext);
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch iov[1].iov_base = str_data(req->headers);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen iov[1].iov_len = str_len(req->headers);
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen iov[2].iov_base = "\r\n";
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch iov[2].iov_len = 2;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch
069b28a2ef54072a221fe4ac67aaeb4e83fee6c1Timo Sirainen req->state = HTTP_REQUEST_STATE_PAYLOAD_OUT;
eb325a5a90c1d2655e74972bde0de6a699d2c864Stephan Bosch o_stream_cork(output);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch if (o_stream_sendv(output, iov, N_ELEMENTS(iov)) < 0)
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch ret = -1;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_debug(req, "Sent");
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (ret >= 0 && req->payload_output != NULL) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (!req->payload_sync) {
17cd0e0963f2fb0e66d49703e8cd0bda1b842468Timo Sirainen if (http_client_request_send_more(req) < 0)
17cd0e0963f2fb0e66d49703e8cd0bda1b842468Timo Sirainen ret = -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch } else {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch http_client_request_debug(req, "Waiting for 100-continue");
}
} else {
req->state = HTTP_REQUEST_STATE_WAITING;
conn->output_locked = FALSE;
}
o_stream_uncork(output);
return ret;
}
void http_client_request_callback(struct http_client_request *req,
struct http_response *response)
{
http_client_request_callback_t *callback = req->callback;
req->state = HTTP_REQUEST_STATE_GOT_RESPONSE;
req->callback = NULL;
if (callback != NULL)
callback(response, req->context);
}
static void
http_client_request_send_error(struct http_client_request *req,
unsigned int status, const char *error)
{
http_client_request_callback_t *callback;
req->state = HTTP_REQUEST_STATE_ABORTED;
callback = req->callback;
req->callback = NULL;
if (callback != NULL) {
struct http_response response;
memset(&response, 0, sizeof(response));
response.status = status;
response.reason = error;
(void)callback(&response, req->context);
}
}
void http_client_request_error(struct http_client_request *req,
unsigned int status, const char *error)
{
http_client_request_send_error(req, status, error);
http_client_request_unref(&req);
}
void http_client_request_abort(struct http_client_request **_req)
{
struct http_client_request *req = *_req;
if (req->state >= HTTP_REQUEST_STATE_FINISHED)
return;
req->callback = NULL;
req->state = HTTP_REQUEST_STATE_ABORTED;
if (req->host != NULL)
http_client_host_drop_request(req->host, req);
http_client_request_unref(_req);
}
void http_client_request_finish(struct http_client_request **_req)
{
struct http_client_request *req = *_req;
if (req->state >= HTTP_REQUEST_STATE_FINISHED)
return;
http_client_request_debug(req, "Finished");
req->callback = NULL;
req->state = HTTP_REQUEST_STATE_FINISHED;
http_client_request_unref(_req);
}
void http_client_request_redirect(struct http_client_request *req,
unsigned int status, const char *location)
{
struct http_url *url;
const char *error;
unsigned int newport;
/* parse URL */
if (http_url_parse(location, NULL, 0,
pool_datastack_create(), &url, &error) < 0) {
http_client_request_error(req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
t_strdup_printf("Invalid redirect location: %s", error));
return;
}
if (++req->redirects > req->client->set.max_redirects) {
if (req->client->set.max_redirects > 0) {
http_client_request_error(req,
HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
t_strdup_printf("Redirected more than %d times",
req->client->set.max_redirects));
} else {
http_client_request_error(req,
HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT,
"Redirect refused");
}
return;
}
/* rewind payload stream */
if (req->payload_input != NULL && req->payload_size > 0 && status != 303) {
if (req->payload_input->v_offset != req->payload_offset &&
!req->payload_input->seekable) {
http_client_request_error(req,
HTTP_CLIENT_REQUEST_ERROR_ABORTED,
"Redirect failed: Cannot resend payload; stream is not seekable");
return;
} else {
i_stream_seek(req->payload_input, req->payload_offset);
}
}
/* rewind payload stream */
if (req->payload_input != NULL && req->payload_size > 0 && status != 303) {
if (req->payload_input->v_offset != req->payload_offset &&
!req->payload_input->seekable) {
http_client_request_error(req,
HTTP_CLIENT_REQUEST_ERROR_ABORTED,
"Redirect failed: Cannot resend payload; stream is not seekable");
return;
} else {
i_stream_seek(req->payload_input, req->payload_offset);
}
}
newport = (url->have_port ? url->port : (url->have_ssl ? 443 : 80));
http_client_request_debug(req, "Redirecting to http://%s:%u%s",
url->host_name, newport, url->path);
// FIXME: handle literal IP specially (avoid duplicate parsing)
req->host = NULL;
req->conn = NULL;
req->hostname = p_strdup(req->pool, url->host_name);
req->port = newport;
req->target = p_strdup(req->pool, url->path);
req->ssl = url->have_ssl;
/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
Section-7.4.4
-> A 303 `See Other' redirect status response is handled a bit differently.
Basically, the response content is located elsewhere, but the original
(POST) request is handled already.
*/
if (status == 303 && strcasecmp(req->method, "HEAD") != 0 &&
strcasecmp(req->method, "GET") != 0) {
// FIXME: should we provide the means to skip this step? The original
// request was already handled at this point.
req->method = p_strdup(req->pool, "GET");
/* drop payload */
if (req->payload_input != NULL)
i_stream_unref(&req->payload_input);
req->payload_size = 0;
req->payload_offset = 0;
}
/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
Section-7.4.4
-> A 303 `See Other' redirect status response is handled a bit differently.
Basically, the response content is located elsewhere, but the original
(POST) request is handled already.
*/
if (status == 303 && strcasecmp(req->method, "HEAD") != 0 &&
strcasecmp(req->method, "GET") != 0) {
// FIXME: should we provide the means to skip this step? The original
// request was already handled at this point.
req->method = p_strdup(req->pool, "GET");
/* drop payload */
if (req->payload_input != NULL)
i_stream_unref(&req->payload_input);
req->payload_size = 0;
req->payload_offset = 0;
}
/* resubmit */
req->client->pending_requests--;
req->state = HTTP_REQUEST_STATE_NEW;
http_client_request_submit(req);
}
void http_client_request_resubmit(struct http_client_request *req)
{
http_client_request_debug(req, "Resubmitting request");
/* rewind payload stream */
if (req->payload_input != NULL && req->payload_size > 0) {
if (req->payload_input->v_offset != req->payload_offset &&
!req->payload_input->seekable) {
http_client_request_error(req,
HTTP_CLIENT_REQUEST_ERROR_ABORTED,
"Resubmission failed: Cannot resend payload; stream is not seekable");
return;
} else {
i_stream_seek(req->payload_input, req->payload_offset);
}
}
/* rewind payload stream */
if (req->payload_input != NULL && req->payload_size > 0) {
if (req->payload_input->v_offset != req->payload_offset &&
!req->payload_input->seekable) {
http_client_request_error(req,
HTTP_CLIENT_REQUEST_ERROR_ABORTED,
"Resubmission failed: Cannot resend payload; stream is not seekable");
return;
} else {
i_stream_seek(req->payload_input, req->payload_offset);
}
}
req->conn = NULL;
req->peer = NULL;
req->state = HTTP_REQUEST_STATE_QUEUED;
http_client_host_submit_request(req->host, req);
}
void http_client_request_retry(struct http_client_request *req,
unsigned int status, const char *error)
{
/* limit the number of attempts for each request */
if (++req->attempts >= req->client->set.max_attempts) {
/* return error */
http_client_request_error(req, status, error);
return;
}
http_client_request_debug(req, "Retrying (attempts=%d)", req->attempts);
/* resubmit */
http_client_request_resubmit(req);
}