Author: kotkov Date: Thu Aug 3 15:01:21 2017 New Revision: 1804005 URL: http://svn.apache.org/viewvc?rev=1804005&view=rev Log: Fix an edge case in the dechunk bucket where it could erroneously report APR_EOF instead of an error in presence of a truncated response with CRLF in place of the next chunk length.
* buckets/dechunk_buckets.c (wait_for_chunk): Check if we got an empty chunk size line before trying to parse it with apr_strtoi64(). Otherwise this function would report 0, which we would then incorrectly interpret as the final zero-sized chunk. * test/test_buckets.c (test_response_body_chunked_truncated_with_crlf): New test, modelled after what httpd + mod_dav + mod_dav_svn can actually send when aborting connection due to an internal error. (test_linebuf_crlf_split): Update the expectation in this test, as the response in it didn't include the final zero-sized chunk. That's ill- formed per RFC7230 3.3.3 and 4.1. (test_buckets): Add new test. Modified: serf/trunk/buckets/dechunk_buckets.c serf/trunk/test/test_buckets.c Modified: serf/trunk/buckets/dechunk_buckets.c URL: http://svn.apache.org/viewvc/serf/trunk/buckets/dechunk_buckets.c?rev=1804005&r1=1804004&r2=1804005&view=diff ============================================================================== --- serf/trunk/buckets/dechunk_buckets.c (original) +++ serf/trunk/buckets/dechunk_buckets.c Thu Aug 3 15:01:21 2017 @@ -83,6 +83,11 @@ static apr_status_t wait_for_chunk(serf_ /* if a line was read, then parse it. */ if (ctx->linebuf.state == SERF_LINEBUF_READY) { + /* Do we have the chunk length? */ + if (ctx->linebuf.line[0] == '\0') { + return SERF_ERROR_TRUNCATED_HTTP_RESPONSE; + } + /* Convert from HEX digits. The linebuffer ensures a '\0' */ ctx->body_left = apr_strtoi64(ctx->linebuf.line, NULL, 16); if (errno == ERANGE) { Modified: serf/trunk/test/test_buckets.c URL: http://svn.apache.org/viewvc/serf/trunk/test/test_buckets.c?rev=1804005&r1=1804004&r2=1804005&view=diff ============================================================================== --- serf/trunk/test/test_buckets.c (original) +++ serf/trunk/test/test_buckets.c Thu Aug 3 15:01:21 2017 @@ -1122,6 +1122,40 @@ static void test_response_body_chunked_g serf_bucket_destroy(bkt); } +/* Test for issue: the server aborts the connection and also sends + a bogus CRLF in place of the expected chunk size. Test that we get + a decent error code from the response bucket instead of APR_EOF. */ +static void test_response_body_chunked_truncated_with_crlf(CuTest *tc) +{ + test_baton_t *tb = tc->testBaton; + serf_bucket_t *bkt, *tmp; + serf_bucket_alloc_t *alloc = test__create_bucket_allocator(tc, tb->pool); + + tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF + "Content-Type: text/plain" CRLF + "Transfer-Encoding: chunked" CRLF + CRLF + "2" CRLF + "AB" CRLF + CRLF, + 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); + } + + /* This will also destroy response stream bucket. */ + serf_bucket_destroy(bkt); +} + static void test_response_bucket_peek_at_headers(CuTest *tc) { test_baton_t *tb = tc->testBaton; @@ -1298,7 +1332,7 @@ static void test_linebuf_crlf_split(CuTe CRLF, APR_SUCCESS }, { 1, "6" CR, APR_SUCCESS }, { 1, "", APR_EAGAIN }, - { 1, LF "blabla" CRLF CRLF, APR_SUCCESS }, }; + { 1, LF "blabla" CRLF "0" CRLF CRLF, APR_SUCCESS }, }; apr_status_t status; const char *expected = "blabla"; @@ -3184,6 +3218,7 @@ CuSuite *test_buckets(void) 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_body_chunked_truncated_with_crlf); SUITE_ADD_TEST(suite, test_response_bucket_peek_at_headers); SUITE_ADD_TEST(suite, test_response_bucket_iis_status_code); SUITE_ADD_TEST(suite, test_response_bucket_no_reason);