Create a backport branch for r1805301 (Fix for an endless loop in the
deflate bucket with the truncated input).

Since the deflate bucket has been heavily tweaked in trunk, the fix
required adjustment.  The test required a couple of tweaks as well.

* buckets/deflate_buckets.c
  (serf_deflate_read): Handle a case when we hit the end of input
   stream and zlib can't continue, even though there's enough output
   space.  If that happens, return an error.

* test/test_buckets.c
  (test_deflate_bucket_truncated_data): New test, fails without the fix.
  (test_buckets): Add new test.


git-svn-id: https://svn.apache.org/repos/asf/serf/branches/1.3.x-r1805301@1805336 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/buckets/deflate_buckets.c b/buckets/deflate_buckets.c
index 6d43256..52f8b81 100644
--- a/buckets/deflate_buckets.c
+++ b/buckets/deflate_buckets.c
@@ -281,9 +281,17 @@
 
                 zRC = inflate(&ctx->zstream, Z_NO_FLUSH);
 
-                /* We're full or zlib requires more space. Either case, clear
-                   out our buffer, reset, and return. */
-                if (zRC == Z_BUF_ERROR || ctx->zstream.avail_out == 0) {
+                if (zRC == Z_BUF_ERROR && APR_STATUS_IS_EOF(ctx->stream_status) &&
+                    ctx->zstream.avail_out > 0) {
+                    /* Zlib can't continue, although there's still space in the
+                       output buffer.  This can happen either if the stream is
+                       truncated or corrupted.  As we don't know for sure,
+                       return a generic error. */
+                    return SERF_ERROR_DECOMPRESSION_FAILED;
+                }
+                else if (zRC == Z_BUF_ERROR || ctx->zstream.avail_out == 0) {
+                    /* We're full or zlib requires more space. Either case, clear
+                       out our buffer, reset, and return. */
                     serf_bucket_t *tmp;
                     ctx->zstream.next_out = ctx->buffer;
                     private_len = ctx->bufferSize - ctx->zstream.avail_out;
diff --git a/test/test_buckets.c b/test/test_buckets.c
index 7d9d473..26679e3 100644
--- a/test/test_buckets.c
+++ b/test/test_buckets.c
@@ -1599,6 +1599,35 @@
 #undef BUFSIZE
 }
 
+static void test_deflate_bucket_truncated_data(CuTest *tc)
+{
+    test_baton_t *tb = tc->testBaton;
+    serf_bucket_t *input;
+    serf_bucket_t *bkt;
+    serf_bucket_t *chunk;
+    serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(tb->pool, NULL,
+                                                              NULL);
+
+    /* This is a valid, but truncated gzip data (in two chunks). */
+    input = serf_bucket_aggregate_create(alloc);
+    chunk = SERF_BUCKET_SIMPLE_STRING_LEN("\x1F\x8B\x08\x00\x00", 5, alloc);
+    serf_bucket_aggregate_append(input, chunk);
+    chunk = SERF_BUCKET_SIMPLE_STRING_LEN("\x00\x00\x00\x00\x03", 5, alloc);
+    serf_bucket_aggregate_append(input, chunk);
+
+    bkt = serf_bucket_deflate_create(input, alloc, SERF_DEFLATE_GZIP);
+    {
+        char buf[1024];
+        apr_size_t len;
+        apr_status_t status;
+
+        status = read_all(bkt, buf, sizeof(buf), &len);
+        CuAssertIntEquals(tc, SERF_ERROR_DECOMPRESSION_FAILED, status);
+    }
+
+    serf_bucket_destroy(bkt);
+}
+
 CuSuite *test_buckets(void)
 {
     CuSuite *suite = CuSuiteNew();
@@ -1631,6 +1660,7 @@
        data so it's disabled by default. */
     SUITE_ADD_TEST(suite, test_deflate_4GBplus_buckets);
 #endif
+    SUITE_ADD_TEST(suite, test_deflate_bucket_truncated_data);
 
 #if 0
     SUITE_ADD_TEST(suite, test_serf_default_read_iovec);