diff options
Diffstat (limited to 'test/test_buckets.c')
-rw-r--r-- | test/test_buckets.c | 332 |
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; } |