]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: http-server-response - Restructure initialization of response payload sending.
authorStephan Bosch <stephan.bosch@open-xchange.com>
Sat, 9 Nov 2019 11:52:41 +0000 (12:52 +0100)
committermartti.rannanjarvi <martti.rannanjarvi@open-xchange.com>
Sat, 18 Apr 2020 14:55:11 +0000 (14:55 +0000)
Divided it into three phases: determination of the type of payload, the
initialization of the payload output stream and the construction of the message
header.

The new structure allows for adding new payload APIs more easily.

src/lib-http/http-server-response.c

index e10594dad69e8d287e663557dea468f00c948f1f..5c244ef9df5a28071b1ff5cbe3542eeb2ccf591a 100644 (file)
@@ -591,63 +591,31 @@ static int http_server_response_send_real(struct http_server_response *resp)
        struct http_server *server = req->server;
        string_t *rtext = t_str_new(256);
        struct const_iovec iov[3];
+       uoff_t content_length = 0;
+       bool chunked = FALSE, send_content_length = FALSE, close = FALSE;
        bool is_head = http_request_method_is(&req->req, "HEAD");
-       bool close = FALSE;
 
        i_assert(!conn->output_locked);
 
-       /* Create status line */
-       str_append(rtext, "HTTP/1.1 ");
-       str_printfa(rtext, "%u", resp->status);
-       str_append(rtext, " ");
-       str_append(rtext, resp->reason);
-
-       /* Create special headers implicitly if not set explicitly using
-          http_server_response_add_header() */
-       if (!resp->have_hdr_date) {
-               str_append(rtext, "\r\nDate: ");
-               str_append(rtext, http_date_create(resp->date));
-               str_append(rtext, "\r\n");
-       }
-       if (array_is_created(&resp->auth_challenges)) {
-               str_append(rtext, "WWW-Authenticate: ");
-               http_auth_create_challenges(rtext, &resp->auth_challenges);
-               str_append(rtext, "\r\n");
-       }
+       /* Determine response payload to send */
        if (resp->payload_input != NULL || resp->payload_direct) {
                i_assert(resp->tunnel_callback == NULL &&
                         resp->status / 100 != 1 &&
                         resp->status != 204 && resp->status != 304);
                if (resp->payload_chunked) {
                        if (http_server_request_version_equals(req, 1, 0)) {
-                               if (!is_head) {
-                                       /* Cannot use Transfer-Encoding */
-                                       resp->payload_output = conn->conn.output;
-                                       o_stream_ref(conn->conn.output);
-                                       /* Connection close marks end of payload
-                                        */
-                                       close = TRUE;
-                               }
+                               /* Connection close marks end of payload
+                                */
+                               close = TRUE;
                        } else {
-                               if (!resp->have_hdr_body_spec)
-                                       str_append(rtext, "Transfer-Encoding: chunked\r\n");
-                               if (!is_head) {
-                                       resp->payload_output =
-                                               http_transfer_chunked_ostream_create(conn->conn.output);
-                                       o_stream_set_finish_also_parent(resp->payload_output, FALSE);
-                               }
+                               /* Input stream with unknown size */
+                               chunked = TRUE;
                        }
                } else {
                        /* Send Content-Length if we have specified a payload,
                           even if it's 0 bytes. */
-                       if (!resp->have_hdr_body_spec) {
-                               str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
-                                           resp->payload_size);
-                       }
-                       if (!is_head) {
-                               resp->payload_output = conn->conn.output;
-                               o_stream_ref(conn->conn.output);
-                       }
+                       content_length = resp->payload_size;
+                       send_content_length = TRUE;
                }
        } else if (resp->tunnel_callback == NULL && resp->status / 100 != 1 &&
                   resp->status != 204 && resp->status != 304 && !is_head) {
@@ -674,8 +642,70 @@ static int http_server_response_send_real(struct http_server_response *resp)
 
                   -> Create empty body if it is missing.
                 */
+               send_content_length = TRUE;
+       }
+
+       /* Initialize output payload stream if needed */
+       if (is_head) {
+               e_debug(resp->event, "A HEAD response has no payload");
+       } else if (chunked) {
+               i_assert(resp->payload_input != NULL || resp->payload_direct);
+
+               e_debug(resp->event, "Will send payload in chunks");
+
+               resp->payload_output =
+                       http_transfer_chunked_ostream_create(conn->conn.output);
+       } else if (send_content_length) {
+               i_assert(resp->payload_input != NULL || content_length == 0 ||
+                        resp->payload_direct);
+
+               e_debug(resp->event,
+                       "Will send payload with explicit size %"PRIuUOFF_T,
+                       content_length);
+
+               if (content_length > 0) {
+                       resp->payload_output = conn->conn.output;
+                       o_stream_ref(conn->conn.output);
+               }
+       } else if (close) {
+               i_assert(resp->payload_input != NULL || resp->payload_direct);
+
+               e_debug(resp->event,
+                       "Will close connection after sending payload "
+                       "(HTTP/1.0)");
+
+               resp->payload_output = conn->conn.output;
+               o_stream_ref(conn->conn.output);
+       } else {
+               e_debug(resp->event, "Response has no payload");
+       }
+
+       /* Create status line */
+       str_append(rtext, "HTTP/1.1 ");
+       str_printfa(rtext, "%u", resp->status);
+       str_append(rtext, " ");
+       str_append(rtext, resp->reason);
+
+       /* Create special headers implicitly if not set explicitly using
+          http_server_response_add_header() */
+       if (!resp->have_hdr_date) {
+               str_append(rtext, "\r\nDate: ");
+               str_append(rtext, http_date_create(resp->date));
+               str_append(rtext, "\r\n");
+       }
+       if (array_is_created(&resp->auth_challenges)) {
+               str_append(rtext, "WWW-Authenticate: ");
+               http_auth_create_challenges(rtext, &resp->auth_challenges);
+               str_append(rtext, "\r\n");
+       }
+       if (chunked) {
                if (!resp->have_hdr_body_spec)
-                       str_append(rtext, "Content-Length: 0\r\n");
+                       str_append(rtext, "Transfer-Encoding: chunked\r\n");
+       } else if (send_content_length) {
+               if (!resp->have_hdr_body_spec) {
+                       str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
+                                   content_length);
+               }
        }
        if (!resp->have_hdr_connection) {
                close = (close || req->req.connection_close ||