summaryrefslogtreecommitdiffstats
path: root/src/lib-http/test-http-transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-http/test-http-transfer.c')
-rw-r--r--src/lib-http/test-http-transfer.c347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/lib-http/test-http-transfer.c b/src/lib-http/test-http-transfer.c
new file mode 100644
index 0000000..39b08a6
--- /dev/null
+++ b/src/lib-http/test-http-transfer.c
@@ -0,0 +1,347 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "buffer.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "str-sanitize.h"
+#include "istream.h"
+#include "ostream.h"
+#include "test-common.h"
+#include "http-transfer.h"
+
+#include <time.h>
+
+struct http_transfer_chunked_input_test {
+ const char *in;
+ const char *out;
+};
+
+/* Valid transfer_chunked input tests */
+static struct http_transfer_chunked_input_test
+valid_transfer_chunked_input_tests[] = {
+ { .in = "1E\r\n"
+ "This is a simple test payload."
+ "\r\n"
+ "0\r\n"
+ "\r\n",
+ .out =
+ "This is a simple test payload."
+ },
+ { .in = "20\r\n"
+ "This is a longer test payload..."
+ "\r\n"
+ "23\r\n"
+ "...spread over two separate chunks."
+ "\r\n"
+ "0\r\n"
+ "\r\n",
+ .out =
+ "This is a longer test payload..."
+ "...spread over two separate chunks."
+ },
+ { .in = "26\r\n"
+ "This is an even longer test payload..."
+ "\r\n"
+ "27\r\n"
+ "...spread over three separate chunks..."
+ "\r\n"
+ "1F\r\n"
+ "...and also includes a trailer."
+ "\r\n"
+ "0\r\n"
+ "Checksum: adgfef3fdaf3daf3dfaf3ff3fdag\r\n"
+ "X-Dovecot: Whatever\r\n"
+ "\r\n",
+ .out =
+ "This is an even longer test payload..."
+ "...spread over three separate chunks..."
+ "...and also includes a trailer."
+ },
+ { .in = "26\n"
+ "This is an even longer test payload..."
+ "\n"
+ "27\n"
+ "...spread over three separate chunks..."
+ "\n"
+ "1F\n"
+ "...and also includes a trailer."
+ "\n"
+ "0\n"
+ "Checksum: adgfef3fdaf3daf3dfaf3ff3fdag\n"
+ "X-Dovecot: Whatever\n"
+ "\n",
+ .out =
+ "This is an even longer test payload..."
+ "...spread over three separate chunks..."
+ "...and also includes a trailer."
+ }
+};
+
+static unsigned int valid_transfer_chunked_input_test_count =
+ N_ELEMENTS(valid_transfer_chunked_input_tests);
+
+static void test_http_transfer_chunked_input_valid(void)
+{
+ struct istream *input, *chunked;
+ struct ostream *output;
+ buffer_t *payload_buffer;
+ unsigned int i;
+
+ payload_buffer = buffer_create_dynamic(default_pool, 1024);
+
+ for (i = 0; i < valid_transfer_chunked_input_test_count; i++) T_BEGIN {
+ const char *in, *out, *stream_out;
+
+ in = valid_transfer_chunked_input_tests[i].in;
+ out = valid_transfer_chunked_input_tests[i].out;
+
+ test_begin(t_strdup_printf("http transfer_chunked input valid [%d]", i));
+
+ input = i_stream_create_from_data(in, strlen(in));
+ chunked = http_transfer_chunked_istream_create(input, 0);
+ i_stream_unref(&input);
+
+ buffer_set_used_size(payload_buffer, 0);
+ output = o_stream_create_buffer(payload_buffer);
+ test_out("payload read", o_stream_send_istream(output, chunked) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED
+ && chunked->stream_errno == 0);
+ o_stream_destroy(&output);
+ i_stream_unref(&chunked);
+ stream_out = str_c(payload_buffer);
+
+ test_out(t_strdup_printf("response->payload = %s",
+ str_sanitize(stream_out, 80)),
+ strcmp(stream_out, out) == 0);
+ test_end();
+ } T_END;
+
+ buffer_free(&payload_buffer);
+}
+
+/* Invalid transfer_chunked input tests */
+static const char *
+invalid_transfer_chunked_input_tests[] = {
+ // invalid size
+ "1X\r\n"
+ "This is a simple test payload."
+ "\r\n"
+ "0\r\n"
+ "\r\n",
+ // invalid end
+ "1E\r\n"
+ "This is a simple test payload."
+ "\r\n"
+ "0\r\n"
+ "ah\r\n",
+ // invalid size
+ "20\r\n"
+ "This is a longer test payload..."
+ "\r\n"
+ "2q\r\n"
+ "...spread over two separate chunks."
+ "\r\n"
+ "0\r\n"
+ "\r\n",
+ // invalid end
+ "20\r\n"
+ "This is a longer test payload..."
+ "\r\n"
+ "23\r\n"
+ "...spread over two separate chunks."
+ "\r\n"
+ "0\r\n",
+ // invalid last chunk
+ "20\r\n"
+ "This is a longer test payload..."
+ "\r\n"
+ "23\r\n"
+ "...spread over two separate chunks."
+ "\r\n"
+ "4\r\n"
+ "\r\n",
+ // invalid trailer
+ "26\r\n"
+ "This is an even longer test payload..."
+ "\r\n"
+ "27\r\n"
+ "...spread over three separate chunks..."
+ "\r\n"
+ "1F\r\n"
+ "...and also includes a trailer."
+ "\r\n"
+ "0\r\n"
+ "Checksum adgfef3fdaf3daf3dfaf3ff3fdag\r\n"
+ "\r\n"
+};
+
+static unsigned int invalid_transfer_chunked_input_test_count =
+ N_ELEMENTS(invalid_transfer_chunked_input_tests);
+
+static void test_http_transfer_chunked_input_invalid(void)
+{
+ struct istream *input, *chunked;
+ struct ostream *output;
+ buffer_t *payload_buffer;
+ unsigned int i;
+
+ payload_buffer = buffer_create_dynamic(default_pool, 1024);
+
+ for (i = 0; i < invalid_transfer_chunked_input_test_count; i++) T_BEGIN {
+ const char *in;
+
+ in = invalid_transfer_chunked_input_tests[i];
+
+ test_begin(t_strdup_printf("http transfer_chunked input invalid [%d]", i));
+
+ input = i_stream_create_from_data(in, strlen(in));
+ chunked = http_transfer_chunked_istream_create(input, 0);
+ i_stream_unref(&input);
+
+ buffer_set_used_size(payload_buffer, 0);
+ output = o_stream_create_buffer(payload_buffer);
+ o_stream_nsend_istream(output, chunked);
+ test_out("payload read failure", chunked->stream_errno != 0);
+ i_stream_unref(&chunked);
+ o_stream_destroy(&output);
+
+ test_end();
+ } T_END;
+
+ buffer_free(&payload_buffer);
+}
+
+/* Valid transfer_chunked output tests */
+static const char *valid_transfer_chunked_output_tests[] = {
+ /* The maximum chunk size is set to 16. These tests are tuned to some border
+ cases
+ */
+ "A small payload", // 15 bytes
+ "A longer payload", // 16 bytes
+ "A lengthy payload", // 17 bytes
+ /* Others */
+ "This is a test payload with lots of nonsense.",
+ "Yet another payload.",
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+ "This a very long repetitive payload. This a very long repetitive payload. "
+};
+
+static unsigned int valid_transfer_chunked_output_test_count =
+ N_ELEMENTS(valid_transfer_chunked_output_tests);
+
+static void test_http_transfer_chunked_output_valid(void)
+{
+ struct istream *input, *ichunked;
+ struct ostream *output, *ochunked;
+ buffer_t *chunked_buffer, *plain_buffer;
+ unsigned int i;
+
+ chunked_buffer = buffer_create_dynamic(default_pool, 1024);
+ plain_buffer = buffer_create_dynamic(default_pool, 1024);
+
+ for (i = 0; i < valid_transfer_chunked_output_test_count; i++) T_BEGIN {
+ const char *data, *stream_out;
+ const unsigned char *rdata;
+ size_t rsize;
+ ssize_t ret;
+
+ data = valid_transfer_chunked_output_tests[i];
+
+ test_begin(t_strdup_printf("http transfer_chunked output valid [%d]", i));
+
+ /* create input stream */
+ input = i_stream_create_from_data(data, strlen(data));
+
+ /* create buffer output stream */
+ buffer_set_used_size(chunked_buffer, 0);
+ output = o_stream_create_buffer(chunked_buffer);
+
+ /* create chunked output stream */
+ ochunked = http_transfer_chunked_ostream_create(output);
+
+ /* send input through chunked stream; chunk size is limited */
+ for (;;) {
+ ret = i_stream_read_more(input, &rdata, &rsize);
+ if (ret < 0) {
+ if (input->eof)
+ ret = 1;
+ break;
+ }
+ if (rsize == 0)
+ break;
+ if (rsize > 16)
+ rsize = 16;
+
+ ret = o_stream_send(ochunked, rdata, rsize);
+ if (ret < 0)
+ break;
+
+ if ((size_t)ret != rsize) {
+ ret = -1;
+ break;
+ }
+
+ i_stream_skip(input, ret);
+ }
+
+ /* cleanup streams */
+ test_out("payload chunk", ret > 0);
+ test_assert(o_stream_finish(ochunked) > 0);
+ o_stream_destroy(&ochunked);
+ o_stream_destroy(&output);
+ i_stream_destroy(&input);
+
+ /* create chunked input stream */
+ input = i_stream_create_from_data
+ (chunked_buffer->data, chunked_buffer->used);
+ ichunked = http_transfer_chunked_istream_create(input, 0);
+
+ /* read back chunk */
+ buffer_set_used_size(plain_buffer, 0);
+ output = o_stream_create_buffer(plain_buffer);
+ test_out("payload unchunk",
+ o_stream_send_istream(output, ichunked) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED
+ && ichunked->stream_errno == 0);
+ o_stream_destroy(&output);
+ i_stream_destroy(&ichunked);
+ i_stream_destroy(&input);
+
+ /* test output */
+ stream_out = str_c(plain_buffer);
+ test_out(t_strdup_printf("response->payload = %s",
+ str_sanitize(stream_out, 80)),
+ strcmp(stream_out, data) == 0);
+ test_end();
+ } T_END;
+
+ buffer_free(&chunked_buffer);
+ buffer_free(&plain_buffer);
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_http_transfer_chunked_input_valid,
+ test_http_transfer_chunked_input_invalid,
+ test_http_transfer_chunked_output_valid,
+ NULL
+ };
+ return test_run(test_functions);
+}