http-server-response.c revision 95e0b82fdff1bb511067d703bb8b67c22f242c38
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic inline void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenhttp_server_response_debug(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic inline void
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenhttp_server_response_debug(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *format, ...)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("http-server: request %s; %u response: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_request_label(resp->request), resp->status,
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainenhttp_server_response_create(struct http_server_request *req,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp = req->response = p_new(req->pool, struct http_server_response, 1);
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenvoid http_server_response_free(struct http_server_response *resp)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenvoid http_server_response_add_header(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* mark presence of special headers */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen switch (key[0]) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strcasecmp(key, "Content-Length") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(key, "Transfer-Encoding") == 0)
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen str_printfa(resp->headers, "%s: %s\r\n", key, value);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_set_date(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_set_payload(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = i_stream_get_size(input, TRUE, &resp->payload_size)) <= 0) {
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainenvoid http_server_response_set_payload_data(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned char *payload_data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen payload_data = p_malloc(resp->request->pool, size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen input = i_stream_create_from_data(payload_data, size);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_set_payload(resp, input);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainenstatic void http_server_response_do_submit(struct http_server_response *resp,
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen http_server_request_submit_response(resp->request);
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainenvoid http_server_response_submit(struct http_server_response *resp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Submitted");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_submit_close(struct http_server_response *resp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Submitted");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid http_server_response_submit_tunnel(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_tunnel_callback_t callback, void *context)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Started tunnelling");
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainenhttp_server_response_finish_payload_out(struct http_server_response *resp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_debug(resp, "Finished sending payload");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenhttp_server_response_payload_input(struct http_server_response *resp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct http_server_connection *conn = resp->request->conn;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint http_server_response_send_more(struct http_server_response *resp,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct http_server_connection *conn = resp->request->conn;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ostream *output = resp->payload_output;
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);
9047d770bfbb93ab6af5363dedb2d01363877243Timo Sirainen *error_r = t_strdup_printf("read(%s) failed: %m",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("write(%s) failed: %m",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret < 0 || i_stream_is_eof(resp->payload_input)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen resp->payload_input->v_offset - resp->payload_offset != resp->payload_size) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_finish_payload_out(resp);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (i_stream_get_data_size(resp->payload_input) > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* output is blocking */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen //http_server_response_debug(resp, "Partially sent payload");
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* input is blocking */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen (fd, IO_READ, http_server_response_payload_input, resp);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainenstatic int http_server_response_send_real(struct http_server_response *resp,
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen const char **error_r)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct http_server_request *req = resp->request;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct http_server_connection *conn = req->conn;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* create status line */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen /* create special headers implicitly if not set explicitly using
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen http_server_response_add_header() */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, http_date_create(resp->date));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (http_server_request_version_equals(req, 1, 0)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* cannot use Transfer-Encoding */
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen /* connection close marks end of payload */
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen str_append(rtext, "Transfer-Encoding: chunked\r\n");
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen /* send Content-Length if we have specified a payload,
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen even if it's 0 bytes. */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen } else if (resp->tunnel_callback == NULL && resp->status / 100 != 1
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen && !http_request_method_is(&req->req, "HEAD")) {
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen /* RFC 7230, Section 3.3: Message Body
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 RFC 7230, Section 3.3.2: Content-Length
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 -> Create empty body if it is missing.
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (resp->close && resp->tunnel_callback == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (http_server_request_version_equals(req, 1, 0))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(rtext, "Connection: Keep-Alive\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* status line + implicit headers */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* explicit headers */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* end of header */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen req->state = HTTP_SERVER_REQUEST_STATE_PAYLOAD_OUT;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (o_stream_sendv(output, iov, N_ELEMENTS(iov)) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("write(%s) failed: %m",
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen http_server_response_debug(resp, "Sent header");
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen if (ret >= 0 && resp->payload_output != NULL) {
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen if (http_server_response_send_more(resp, error_r) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint http_server_response_send(struct http_server_response *resp,
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen const char **error_r)