http-server-response.c revision 95e0b82fdff1bb511067d703bb8b67c22f242c38
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ostream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "http-date.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "http-transfer.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "http-server-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/*
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen * Logging
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic inline void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenhttp_server_response_debug(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *format, ...) ATTR_FORMAT(2, 3);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic inline void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenhttp_server_response_debug(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *format, ...)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen va_list args;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (resp->request->server->set.debug) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen va_start(args, format);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("http-server: request %s; %u response: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_request_label(resp->request), resp->status,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t_strdup_vprintf(format, args));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen va_end(args);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/*
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen * Response
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct http_server_response *
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainenhttp_server_response_create(struct http_server_request *req,
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen unsigned int status, const char *reason)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct http_server_response *resp;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp = req->response = p_new(req->pool, struct http_server_response, 1);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen resp->request = req;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen resp->status = status;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen resp->reason = p_strdup(req->pool, reason);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen resp->headers = str_new(default_pool, 256);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen resp->date = (time_t)-1;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen return resp;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen}
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenvoid http_server_response_free(struct http_server_response *resp)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen{
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen http_server_response_debug(resp, "Destroy");
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen if (resp->payload_input != NULL)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen i_stream_unref(&resp->payload_input);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (resp->payload_output != NULL)
714c6a150480112eb1a5f309d0cc39b60613a719Timo Sirainen o_stream_unref(&resp->payload_output);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen str_free(&resp->headers);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen}
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenvoid http_server_response_add_header(struct http_server_response *resp,
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen const char *key, const char *value)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!resp->submitted);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* mark presence of special headers */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen switch (key[0]) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case 'c': case 'C':
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(key, "Connection") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->have_hdr_connection = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strcasecmp(key, "Content-Length") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->have_hdr_body_spec = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case 'd': case 'D':
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(key, "Date") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->have_hdr_date = TRUE;
9a02317c852face76737763fa6ec43b444688de5Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case 't': case 'T':
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(key, "Transfer-Encoding") == 0)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen resp->have_hdr_body_spec = TRUE;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen break;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen }
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen str_printfa(resp->headers, "%s: %s\r\n", key, value);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen}
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_set_date(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen time_t date)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen i_assert(!resp->submitted);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->date = date;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_set_payload(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct istream *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!resp->submitted);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(resp->payload_input == NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_ref(input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->payload_input = input;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = i_stream_get_size(input, TRUE, &resp->payload_size)) <= 0) {
e2eac5bb5637c2d4aaf453389750740931822b92Timo Sirainen if (ret < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("i_stream_get_size(%s) failed: %m",
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen i_stream_get_name(input));
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen }
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen resp->payload_size = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->payload_chunked = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->payload_offset = input->v_offset;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainenvoid http_server_response_set_payload_data(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const unsigned char *data, size_t size)
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen{
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen struct istream *input;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned char *payload_data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (size == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen payload_data = p_malloc(resp->request->pool, size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(payload_data, data, size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen input = i_stream_create_from_data(payload_data, size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_set_payload(resp, input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_unref(&input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainenstatic void http_server_response_do_submit(struct http_server_response *resp,
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen bool close)
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen{
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen if (resp->date == (time_t)-1)
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen resp->date = ioloop_time;
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen resp->close = close;
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen resp->submitted = TRUE;
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen http_server_request_submit_response(resp->request);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen}
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainenvoid http_server_response_submit(struct http_server_response *resp)
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!resp->submitted);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Submitted");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen http_server_response_do_submit(resp, FALSE);
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_submit_close(struct http_server_response *resp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!resp->submitted);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Submitted");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_do_submit(resp, TRUE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_submit_tunnel(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_tunnel_callback_t callback, void *context)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!resp->submitted);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Started tunnelling");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->tunnel_callback = callback;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->tunnel_context = context;
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen http_server_response_do_submit(resp, TRUE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainenhttp_server_response_finish_payload_out(struct http_server_response *resp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (resp->payload_output != NULL) {
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen o_stream_unref(&resp->payload_output);
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen resp->payload_output = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->request->conn->output_locked = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Finished sending payload");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_request_finished(resp->request);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenhttp_server_response_payload_input(struct http_server_response *resp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct http_server_connection *conn = resp->request->conn;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (conn->io_resp_payload != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&conn->io_resp_payload);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)http_server_connection_output(conn);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint http_server_response_send_more(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct http_server_connection *conn = resp->request->conn;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ostream *output = resp->payload_output;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen off_t ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int fd;
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = NULL;
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(resp->payload_input != NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(resp->payload_output != NULL);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (conn->io_resp_payload != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&conn->io_resp_payload);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen /* chunked ostream needs to write to the parent stream's buffer */
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen ret = o_stream_send_istream(output, resp->payload_input);
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen o_stream_set_max_buffer_size(output, (size_t)-1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (resp->payload_input->stream_errno != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen errno = resp->payload_input->stream_errno;
9047d770bfbb93ab6af5363dedb2d01363877243Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %m",
836e57b1e7817d008f8ae05cd4b506f420fed80dTimo Sirainen i_stream_get_name(resp->payload_input));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (output->stream_errno != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen errno = output->stream_errno;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (errno != EPIPE && errno != ECONNRESET) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("write(%s) failed: %m",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_get_name(output));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(ret >= 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret < 0 || i_stream_is_eof(resp->payload_input)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!resp->payload_chunked &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->payload_input->v_offset - resp->payload_offset != resp->payload_size) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("stream input size changed"); //FIXME
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_finish_payload_out(resp);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (i_stream_get_data_size(resp->payload_input) > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* output is blocking */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen conn->output_locked = TRUE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen o_stream_set_flush_pending(output, TRUE);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen //http_server_response_debug(resp, "Partially sent payload");
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen } else {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* input is blocking */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen fd = i_stream_get_fd(resp->payload_input);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen conn->output_locked = TRUE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen i_assert(fd >= 0);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen conn->io_resp_payload = io_add
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen (fd, IO_READ, http_server_response_payload_input, resp);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen return ret < 0 ? -1 : 0;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen}
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainenstatic int http_server_response_send_real(struct http_server_response *resp,
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen const char **error_r)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen{
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct http_server_request *req = resp->request;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct http_server_connection *conn = req->conn;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct ostream *output = conn->conn.output;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen string_t *rtext = t_str_new(256);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct const_iovec iov[3];
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen int ret = 0;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen *error_r = NULL;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen i_assert(!conn->output_locked);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* create status line */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen str_append(rtext, "HTTP/1.1 ");
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen str_printfa(rtext, "%u", resp->status);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen str_append(rtext, " ");
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen str_append(rtext, resp->reason);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* create special headers implicitly if not set explicitly using
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_add_header() */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!resp->have_hdr_date) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, "\r\nDate: ");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, http_date_create(resp->date));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, "\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (resp->payload_chunked) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (http_server_request_version_equals(req, 1, 0)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* cannot use Transfer-Encoding */
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen resp->payload_output = output;
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen o_stream_ref(output);
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen /* connection close marks end of payload */
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen resp->close = TRUE;
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen } else {
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen if (!resp->have_hdr_body_spec)
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen str_append(rtext, "Transfer-Encoding: chunked\r\n");
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen resp->payload_output =
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen http_transfer_chunked_ostream_create(output);
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen }
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen } else if (resp->payload_input != NULL) {
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen /* send Content-Length if we have specified a payload,
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen even if it's 0 bytes. */
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen if (!resp->have_hdr_body_spec) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen resp->payload_size);
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen }
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen resp->payload_output = output;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen o_stream_ref(output);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen } else if (resp->tunnel_callback == NULL && resp->status / 100 != 1
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen && resp->status != 204 && resp->status != 304
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen && !http_request_method_is(&req->req, "HEAD")) {
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen /* RFC 7230, Section 3.3: Message Body
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen Responses to the HEAD request method (Section 4.3.2 of [RFC7231])
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen never include a message body because the associated response header
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen fields (e.g., Transfer-Encoding, Content-Length, etc.), if present,
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen indicate only what their values would have been if the request method
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen had been GET (Section 4.3.1 of [RFC7231]). 2xx (Successful) responses
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen to a CONNECT request method (Section 4.3.6 of [RFC7231]) switch to
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen tunnel mode instead of having a message body. All 1xx (Informational),
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen 204 (No Content), and 304 (Not Modified) responses do not include a
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen message body. All other responses do include a message body, although
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen the body might be of zero length.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen RFC 7230, Section 3.3.2: Content-Length
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen A server MUST NOT send a Content-Length header field in any 2xx
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen (Successful) response to a CONNECT request (Section 4.3.6 of [RFC7231]).
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen -> Create empty body if it is missing.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!resp->have_hdr_body_spec)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, "Content-Length: 0\r\n");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen }
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (!resp->have_hdr_connection) {
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (resp->close && resp->tunnel_callback == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, "Connection: close\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (http_server_request_version_equals(req, 1, 0))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, "Connection: Keep-Alive\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* status line + implicit headers */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[0].iov_base = str_data(rtext);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[0].iov_len = str_len(rtext);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* explicit headers */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[1].iov_base = str_data(resp->headers);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[1].iov_len = str_len(resp->headers);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* end of header */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen iov[2].iov_base = "\r\n";
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen iov[2].iov_len = 2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen req->state = HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen o_stream_cork(output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (o_stream_sendv(output, iov, N_ELEMENTS(iov)) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (errno != EPIPE && errno != ECONNRESET) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("write(%s) failed: %m",
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen o_stream_get_name(output));
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen }
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen ret = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen http_server_response_debug(resp, "Sent header");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen if (ret >= 0 && resp->payload_output != NULL) {
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen if (http_server_response_send_more(resp, error_r) < 0)
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen ret = -1;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen } else {
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen conn->output_locked = FALSE;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen http_server_request_finished(resp->request);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen o_stream_uncork(output);
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen return ret;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen}
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint http_server_response_send(struct http_server_response *resp,
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen const char **error_r)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen{
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen int ret;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen T_BEGIN {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen ret = http_server_response_send_real(resp, error_r);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } T_END;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen