]> dgit.raspbian.org Git - dovecot.git/commitdiff
[PATCH 02/14] imap: Stream BODYSTRUCTURE to client without ostream memory duplication
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 17 Apr 2026 14:55:05 +0000 (16:55 +0200)
committerNoah Meyerhans <noahm@debian.org>
Wed, 16 Sep 2026 19:06:35 +0000 (15:06 -0400)
For huge BODYSTRUCTURE responses (messages with many MIME parts),
o_stream_send_str() caused the ostream to copy the entire string into
its ring buffer when the stream was corked, doubling memory usage.

Use the same o_stream_set_max_buffer_size(0) + o_stream_send_istream()
pattern as fetch_stream_continue() so the ostream buffer stays at 0
bytes and the I/O loop handles flow control via WAIT_OUTPUT.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gbp-Pq: Name 0002-imap-Stream-BODYSTRUCTURE-to-client-without-ostream-.patch

src/imap/imap-fetch.c

index ca4b3216cef8aedf798a1a032b4538bfe88900b1..b4a8a465d7b55492a4919b3376d8f82daa8fc1b7 100644 (file)
@@ -732,6 +732,34 @@ static bool fetch_body_init(struct imap_fetch_init_context *ctx)
        return imap_fetch_body_section_init(ctx);
 }
 
+static int fetch_bodystructure_stream_continue(struct imap_fetch_context *ctx)
+{
+       struct imap_fetch_state *state = &ctx->state;
+       enum ostream_send_istream_result res;
+
+       o_stream_set_max_buffer_size(ctx->client->output, 0);
+       res = o_stream_send_istream(ctx->client->output, state->cur_input);
+       o_stream_set_max_buffer_size(ctx->client->output, SIZE_MAX);
+
+       switch (res) {
+       case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
+               i_stream_unref(&state->cur_input);
+               state->cont_handler = NULL;
+               if (o_stream_send(ctx->client->output, ")", 1) < 0)
+                       return -1;
+               return 1;
+       case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
+               i_unreached();
+       case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
+               return 0;
+       case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
+               i_unreached();
+       case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
+               return -1;
+       }
+       i_unreached();
+}
+
 static int fetch_bodystructure(struct imap_fetch_context *ctx,
                               struct mail *mail, void *context ATTR_UNUSED)
 {
@@ -748,12 +776,13 @@ static int fetch_bodystructure(struct imap_fetch_context *ctx,
                        return -1;
        }
 
-       if (o_stream_send(ctx->client->output, "BODYSTRUCTURE (", 15) < 0 ||
-           o_stream_send_str(ctx->client->output, bodystructure) < 0 ||
-           o_stream_send(ctx->client->output, ")", 1) < 0)
+       if (o_stream_send(ctx->client->output, "BODYSTRUCTURE (", 15) < 0)
                return -1;
 
-       return 1;
+       ctx->state.cur_input =
+               i_stream_create_from_data(bodystructure, strlen(bodystructure));
+       ctx->state.cont_handler = fetch_bodystructure_stream_continue;
+       return fetch_bodystructure_stream_continue(ctx);
 }
 
 static bool fetch_bodystructure_init(struct imap_fetch_init_context *ctx)