changeset 19636:994625f360af

lib: When closing istream-chain, make sure parent stream is seeked to correct offset. We were only seeking it earlier if it ended at EOF.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Tue, 26 Jan 2016 13:51:47 +0200
parents 3712091cf4d0
children 1c25fe4c74a0
files src/lib/Makefile.am src/lib/istream-chain.c src/lib/test-istream-chain.c src/lib/test-lib.c src/lib/test-lib.h
diffstat 5 files changed, 133 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/Makefile.am	Mon Jan 25 22:34:24 2016 +0200
+++ b/src/lib/Makefile.am	Tue Jan 26 13:51:47 2016 +0200
@@ -312,6 +312,7 @@
 	test-istream.c \
 	test-istream-base64-decoder.c \
 	test-istream-base64-encoder.c \
+	test-istream-chain.c \
 	test-istream-concat.c \
 	test-istream-crlf.c \
 	test-istream-failure-at.c \
--- a/src/lib/istream-chain.c	Mon Jan 25 22:34:24 2016 +0200
+++ b/src/lib/istream-chain.c	Tue Jan 26 13:51:47 2016 +0200
@@ -153,19 +153,11 @@
 	i_stream_unref(&prev_input);
 }
 
-static ssize_t i_stream_chain_read(struct istream_private *stream)
+static bool i_stream_chain_skip(struct chain_istream *cstream)
 {
-	struct chain_istream *cstream = (struct chain_istream *)stream;
+	struct istream_private *stream = &cstream->istream;
 	struct istream_chain_link *link = cstream->chain.head;
-	const unsigned char *data;
-	size_t size, data_size, cur_data_pos, new_pos, bytes_skipped;
-	size_t new_bytes_count;
-	ssize_t ret;
-
-	if (link != NULL && link->eof) {
-		stream->istream.eof = TRUE;
-		return -1;
-	}
+	size_t bytes_skipped;
 
 	i_assert(stream->skip >= cstream->prev_skip);
 	bytes_skipped = stream->skip - cstream->prev_skip;
@@ -185,12 +177,30 @@
 	stream->skip -= bytes_skipped;
 	stream->buffer += bytes_skipped;
 	cstream->prev_skip = stream->skip;
-
-	if (link == NULL) {
+	if (link == NULL || link->eof) {
 		i_assert(bytes_skipped == 0);
-		return 0;
+		return FALSE;
 	}
 	i_stream_skip(link->stream, bytes_skipped);
+	return TRUE;
+}
+
+static ssize_t i_stream_chain_read(struct istream_private *stream)
+{
+	struct chain_istream *cstream = (struct chain_istream *)stream;
+	struct istream_chain_link *link = cstream->chain.head;
+	const unsigned char *data;
+	size_t size, data_size, cur_data_pos, new_pos;
+	size_t new_bytes_count;
+	ssize_t ret;
+
+	if (link != NULL && link->eof) {
+		stream->istream.eof = TRUE;
+		return -1;
+	}
+
+	if (!i_stream_chain_skip(cstream))
+		return 0;
 
 	i_assert(stream->pos >= stream->skip + cstream->prev_stream_left);
 	cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left);
@@ -260,6 +270,24 @@
 	return ret;
 }
 
+static void i_stream_chain_close(struct iostream_private *stream,
+				 bool close_parent)
+{
+	struct chain_istream *cstream = (struct chain_istream *)stream;
+
+	/* seek to the correct position in parent stream in case it didn't
+	   end with EOF */
+	(void)i_stream_chain_skip(cstream);
+
+	if (close_parent) {
+		struct istream_chain_link *link = cstream->chain.head;
+		while (link != NULL) {
+			i_stream_close(link->stream);
+			link = link->next;
+		}
+	}
+}
+
 struct istream *i_stream_create_chain(struct istream_chain **chain_r)
 {
 	struct chain_istream *cstream;
@@ -268,6 +296,7 @@
 	cstream->chain.stream = cstream;
 	cstream->istream.max_buffer_size = 256;
 
+	cstream->istream.iostream.close = i_stream_chain_close;
 	cstream->istream.iostream.destroy = i_stream_chain_destroy;
 	cstream->istream.iostream.set_max_buffer_size =
 		i_stream_chain_set_max_buffer_size;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/test-istream-chain.c	Tue Jan 26 13:51:47 2016 +0200
@@ -0,0 +1,87 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "istream-private.h"
+#include "istream-chain.h"
+
+static void test_istream_chain_basic(void)
+{
+	struct istream *input, *test_input, *test_input2;
+	struct istream_chain *chain;
+	const unsigned char *data;
+	size_t size;
+
+	test_begin("istream chain");
+
+	test_input = test_istream_create("stream1");
+	test_input2 = test_istream_create("STREAM2");
+
+	input = i_stream_create_chain(&chain);
+	/* no input */
+	test_assert(i_stream_read(input) == 0);
+	/* stream1 input */
+	i_stream_chain_append(chain, test_input);
+	test_assert(i_stream_read(input) == 7);
+	data = i_stream_get_data(input, &size);
+	test_assert(size == 7 && memcmp(data, "stream1", 7) == 0);
+	test_assert(i_stream_read(input) == 0);
+	data = i_stream_get_data(input, &size);
+	test_assert(size == 7 && memcmp(data, "stream1", 7) == 0);
+	/* STREAM2 input */
+	i_stream_chain_append(chain, test_input2);
+	test_assert(i_stream_read(input) == 7);
+	data = i_stream_get_data(input, &size);
+	test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0);
+	test_assert(i_stream_read(input) == 0);
+	data = i_stream_get_data(input, &size);
+	test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0);
+	/* EOF */
+	i_stream_chain_append_eof(chain);
+	test_assert(i_stream_read(input) == -1 &&
+		    input->eof && input->stream_errno == 0);
+	data = i_stream_get_data(input, &size);
+	test_assert(size == 14 && memcmp(data, "stream1STREAM2", 14) == 0);
+
+	i_stream_unref(&input);
+
+	test_assert(i_stream_is_eof(test_input));
+	test_assert(i_stream_is_eof(test_input2));
+
+	i_stream_unref(&test_input);
+	i_stream_unref(&test_input2);
+	test_end();
+}
+
+static void test_istream_chain_early_end(void)
+{
+	struct istream *input, *test_input;
+	struct istream_chain *chain;
+
+	test_begin("istream chain early end");
+
+	test_input = test_istream_create("string");
+	test_istream_set_size(test_input, 3);
+	test_istream_set_allow_eof(test_input, FALSE);
+
+	input = i_stream_create_chain(&chain);
+	i_stream_chain_append(chain, test_input);
+	test_assert(i_stream_read(input) == 3);
+	test_istream_set_size(test_input, 5);
+	test_assert(i_stream_read(input) == 2);
+	/* with current implementation we could skip less than 5 and have
+	   v_offset<5, but I don't think that can work in all situations.
+	   the normal case is anyway that we'll read everything up until some
+	   point and skip over all the data up to there. */
+	i_stream_skip(input, 5);
+	i_stream_unref(&input);
+
+	test_assert(test_input->v_offset == 5);
+	i_stream_unref(&test_input);
+	test_end();
+}
+
+void test_istream_chain(void)
+{
+	test_istream_chain_basic();
+	test_istream_chain_early_end();
+}
--- a/src/lib/test-lib.c	Mon Jan 25 22:34:24 2016 +0200
+++ b/src/lib/test-lib.c	Tue Jan 26 13:51:47 2016 +0200
@@ -25,6 +25,7 @@
 		test_istream,
 		test_istream_base64_decoder,
 		test_istream_base64_encoder,
+		test_istream_chain,
 		test_istream_concat,
 		test_istream_crlf,
 		test_istream_failure_at,
--- a/src/lib/test-lib.h	Mon Jan 25 22:34:24 2016 +0200
+++ b/src/lib/test-lib.h	Tue Jan 26 13:51:47 2016 +0200
@@ -26,6 +26,7 @@
 void test_istream(void);
 void test_istream_base64_decoder(void);
 void test_istream_base64_encoder(void);
+void test_istream_chain(void);
 void test_istream_concat(void);
 void test_istream_crlf(void);
 void test_istream_failure_at(void);