summaryrefslogtreecommitdiff
path: root/test/test_buckets.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_buckets.c')
-rw-r--r--test/test_buckets.c332
1 files changed, 331 insertions, 1 deletions
diff --git a/test/test_buckets.c b/test/test_buckets.c
index 7d4f7ac..4707d70 100644
--- a/test/test_buckets.c
+++ b/test/test_buckets.c
@@ -20,8 +20,44 @@
#include "serf.h"
#include "test_serf.h"
+/* test case has access to internal functions. */
+#include "serf_private.h"
+
#define CRLF "\r\n"
+static apr_status_t read_all(serf_bucket_t *bkt,
+ char *buf,
+ apr_size_t buf_len,
+ apr_size_t *read_len)
+{
+ const char *data;
+ apr_size_t data_len;
+ apr_status_t status;
+ apr_size_t read;
+
+ read = 0;
+
+ do
+ {
+ status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &data_len);
+
+ if (!SERF_BUCKET_READ_ERROR(status))
+ {
+ if (data_len > buf_len - read)
+ {
+ /* Buffer is not large enough to read all data */
+ data_len = buf_len - read;
+ status = APR_EGENERAL;
+ }
+ memcpy(buf + read, data, data_len);
+ read += data_len;
+ }
+ } while(status == APR_SUCCESS);
+
+ *read_len = read;
+ return status;
+}
+
static void test_simple_bucket_readline(CuTest *tc)
{
apr_status_t status;
@@ -201,7 +237,7 @@ static void test_bucket_header_set(CuTest *tc)
CuAssertStrEquals(tc, "bar,baz,test", serf_bucket_headers_get(hdrs, "Foo"));
- // headers are case insensitive.
+ /* headers are case insensitive. */
CuAssertStrEquals(tc, "bar,baz,test", serf_bucket_headers_get(hdrs, "fOo"));
test_teardown(test_pool);
}
@@ -384,6 +420,44 @@ static void test_iovec_buckets(CuTest *tc)
test_teardown(test_pool);
}
+/* Construct a header bucket with some headers, and then read from it. */
+static void test_header_buckets(CuTest *tc)
+{
+ apr_status_t status;
+ apr_pool_t *test_pool = test_setup();
+ serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+ NULL);
+ const char *cur;
+
+ serf_bucket_t *hdrs = serf_bucket_headers_create(alloc);
+ CuAssertTrue(tc, hdrs != NULL);
+
+ serf_bucket_headers_set(hdrs, "Content-Type", "text/plain");
+ serf_bucket_headers_set(hdrs, "Content-Length", "100");
+
+ /* Note: order not guaranteed, assume here that it's fifo. */
+ cur = "Content-Type: text/plain" CRLF
+ "Content-Length: 100" CRLF
+ CRLF
+ CRLF;
+ while (1) {
+ const char *data;
+ apr_size_t len;
+
+ status = serf_bucket_read(hdrs, SERF_READ_ALL_AVAIL, &data, &len);
+ CuAssert(tc, "Unexpected error when waiting for response headers",
+ !SERF_BUCKET_READ_ERROR(status));
+ if (SERF_BUCKET_READ_ERROR(status) ||
+ APR_STATUS_IS_EOF(status))
+ break;
+
+ /* Check that the bytes read match with expected at current position. */
+ CuAssertStrnEquals(tc, cur, len, data);
+ cur += len;
+ }
+ CuAssertIntEquals(tc, APR_EOF, status);
+}
+
static void test_aggregate_buckets(CuTest *tc)
{
apr_status_t status;
@@ -409,6 +483,255 @@ static void test_aggregate_buckets(CuTest *tc)
test_teardown(test_pool);
}
+/* Test for issue: the server aborts the connection in the middle of
+ streaming the body of the response, where the length was set with the
+ Content-Length header. Test that we get a decent error code from the
+ response bucket instead of APR_EOF. */
+static void test_response_body_too_small_cl(CuTest *tc)
+{
+ serf_bucket_t *bkt, *tmp;
+ apr_pool_t *test_pool = test_setup();
+ serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+ NULL);
+
+ /* Make a response of 60 bytes, but set the Content-Length to 100. */
+#define BODY "12345678901234567890"\
+ "12345678901234567890"\
+ "12345678901234567890"
+
+ tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF
+ "Content-Type: text/plain" CRLF
+ "Content-Length: 100" CRLF
+ CRLF
+ BODY,
+ alloc);
+
+ bkt = serf_bucket_response_create(tmp, alloc);
+
+ {
+ const char *data;
+ apr_size_t len;
+ apr_status_t status;
+
+ status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &len);
+
+ CuAssert(tc, "Read more data than expected.",
+ strlen(BODY) >= len);
+ CuAssert(tc, "Read data is not equal to expected.",
+ strncmp(BODY, data, len) == 0);
+ CuAssert(tc, "Error expected due to response body too short!",
+ SERF_BUCKET_READ_ERROR(status));
+ CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status);
+ }
+}
+#undef BODY
+
+/* Test for issue: the server aborts the connection in the middle of
+ streaming the body of the response, using chunked encoding. Test that we get
+ a decent error code from the response bucket instead of APR_EOF. */
+static void test_response_body_too_small_chunked(CuTest *tc)
+{
+ serf_bucket_t *bkt, *tmp;
+ apr_pool_t *test_pool = test_setup();
+ serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+ NULL);
+
+ /* Make a response of 60 bytes, but set the chunk size to 60 and don't end
+ with chunk of length 0. */
+#define BODY "12345678901234567890"\
+"12345678901234567890"\
+"12345678901234567890"
+
+ tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF
+ "Content-Type: text/plain" CRLF
+ "Transfer-Encoding: chunked" CRLF
+ CRLF
+ "64" CRLF BODY,
+ alloc);
+
+ bkt = serf_bucket_response_create(tmp, alloc);
+
+ {
+ const char *data;
+ apr_size_t len;
+ apr_status_t status;
+
+ status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &len);
+
+ CuAssert(tc, "Read more data than expected.",
+ strlen(BODY) >= len);
+ CuAssert(tc, "Read data is not equal to expected.",
+ strncmp(BODY, data, len) == 0);
+ CuAssert(tc, "Error expected due to response body too short!",
+ SERF_BUCKET_READ_ERROR(status));
+ CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status);
+ }
+}
+#undef BODY
+
+/* Test for issue: the server aborts the connection in the middle of
+ streaming trailing CRLF after body chunk. Test that we get
+ a decent error code from the response bucket instead of APR_EOF. */
+static void test_response_body_chunked_no_crlf(CuTest *tc)
+{
+ serf_bucket_t *bkt, *tmp;
+ apr_pool_t *test_pool = test_setup();
+ serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+ NULL);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF
+ "Content-Type: text/plain" CRLF
+ "Transfer-Encoding: chunked" CRLF
+ CRLF
+ "2" CRLF
+ "AB",
+ alloc);
+
+ bkt = serf_bucket_response_create(tmp, alloc);
+
+ {
+ char buf[1024];
+ apr_size_t len;
+ apr_status_t status;
+
+ status = read_all(bkt, buf, sizeof(buf), &len);
+
+ CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status);
+ }
+ test_teardown(test_pool);
+}
+
+/* Test for issue: the server aborts the connection in the middle of
+ streaming trailing CRLF after body chunk. Test that we get
+ a decent error code from the response bucket instead of APR_EOF. */
+static void test_response_body_chunked_incomplete_crlf(CuTest *tc)
+{
+ serf_bucket_t *bkt, *tmp;
+ apr_pool_t *test_pool = test_setup();
+ serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+ NULL);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF
+ "Content-Type: text/plain" CRLF
+ "Transfer-Encoding: chunked" CRLF
+ CRLF
+ "2" CRLF
+ "AB"
+ "\r",
+ alloc);
+
+ bkt = serf_bucket_response_create(tmp, alloc);
+
+ {
+ char buf[1024];
+ apr_size_t len;
+ apr_status_t status;
+
+ status = read_all(bkt, buf, sizeof(buf), &len);
+
+ CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status);
+ }
+ test_teardown(test_pool);
+}
+
+static void test_response_body_chunked_gzip_small(CuTest *tc)
+{
+ serf_bucket_t *bkt, *tmp;
+ apr_pool_t *test_pool = test_setup();
+ serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+ NULL);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF
+ "Content-Type: text/plain" CRLF
+ "Transfer-Encoding: chunked" CRLF
+ "Content-Encoding: gzip" CRLF
+ CRLF
+ "2" CRLF
+ "A",
+ alloc);
+
+ bkt = serf_bucket_response_create(tmp, alloc);
+
+ {
+ char buf[1024];
+ apr_size_t len;
+ apr_status_t status;
+
+ status = read_all(bkt, buf, sizeof(buf), &len);
+
+ CuAssertIntEquals(tc, SERF_ERROR_TRUNCATED_HTTP_RESPONSE, status);
+ }
+ test_teardown(test_pool);
+}
+
+static void test_response_bucket_peek_at_headers(CuTest *tc)
+{
+ apr_pool_t *test_pool = test_setup();
+ serf_bucket_t *resp_bkt1, *tmp, *hdrs;
+ serf_status_line sl;
+ serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
+ NULL);
+ const char *hdr_val, *cur;
+ apr_status_t status;
+
+#define EXP_RESPONSE "HTTP/1.1 200 OK" CRLF\
+ "Content-Type: text/plain" CRLF\
+ "Content-Length: 100" CRLF\
+ CRLF\
+ "12345678901234567890"\
+ "12345678901234567890"\
+ "12345678901234567890"
+
+ tmp = SERF_BUCKET_SIMPLE_STRING(EXP_RESPONSE,
+ alloc);
+
+ resp_bkt1 = serf_bucket_response_create(tmp, alloc);
+
+ status = serf_bucket_response_status(resp_bkt1, &sl);
+ CuAssertIntEquals(tc, 200, sl.code);
+ CuAssertStrEquals(tc, "OK", sl.reason);
+ CuAssertIntEquals(tc, SERF_HTTP_11, sl.version);
+
+ /* Ensure that the status line & headers are read in the response_bucket. */
+ status = serf_bucket_response_wait_for_headers(resp_bkt1);
+ CuAssert(tc, "Unexpected error when waiting for response headers",
+ !SERF_BUCKET_READ_ERROR(status));
+
+ hdrs = serf_bucket_response_get_headers(resp_bkt1);
+ CuAssertPtrNotNull(tc, hdrs);
+
+ hdr_val = serf_bucket_headers_get(hdrs, "Content-Type");
+ CuAssertStrEquals(tc, "text/plain", hdr_val);
+ hdr_val = serf_bucket_headers_get(hdrs, "Content-Length");
+ CuAssertStrEquals(tc, "100", hdr_val);
+
+ /* Create a new bucket for the response which still has the original
+ status line & headers. */
+
+ status = serf_response_full_become_aggregate(resp_bkt1);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+ cur = EXP_RESPONSE;
+
+ while (1) {
+ const char *data;
+ apr_size_t len;
+ apr_status_t status;
+
+ status = serf_bucket_read(resp_bkt1, SERF_READ_ALL_AVAIL, &data, &len);
+ CuAssert(tc, "Unexpected error when waiting for response headers",
+ !SERF_BUCKET_READ_ERROR(status));
+ if (SERF_BUCKET_READ_ERROR(status) ||
+ APR_STATUS_IS_EOF(status))
+ break;
+
+ /* Check that the bytes read match with expected at current position. */
+ CuAssertStrnEquals(tc, cur, len, data);
+ cur += len;
+ }
+
+}
+#undef EXP_RESPONSE
+
CuSuite *test_buckets(void)
{
CuSuite *suite = CuSuiteNew();
@@ -417,9 +740,16 @@ CuSuite *test_buckets(void)
SUITE_ADD_TEST(suite, test_response_bucket_read);
SUITE_ADD_TEST(suite, test_response_bucket_headers);
SUITE_ADD_TEST(suite, test_response_bucket_chunked_read);
+ SUITE_ADD_TEST(suite, test_response_body_too_small_cl);
+ SUITE_ADD_TEST(suite, test_response_body_too_small_chunked);
+ SUITE_ADD_TEST(suite, test_response_body_chunked_no_crlf);
+ SUITE_ADD_TEST(suite, test_response_body_chunked_incomplete_crlf);
+ SUITE_ADD_TEST(suite, test_response_body_chunked_gzip_small);
+ SUITE_ADD_TEST(suite, test_response_bucket_peek_at_headers);
SUITE_ADD_TEST(suite, test_bucket_header_set);
SUITE_ADD_TEST(suite, test_iovec_buckets);
SUITE_ADD_TEST(suite, test_aggregate_buckets);
+ SUITE_ADD_TEST(suite, test_header_buckets);
return suite;
}