TensorFlow Serving C++ API Documentation
gzip_zlib_test.cc
1 /* Copyright 2018 Google Inc. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7  http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow_serving/util/net_http/compression/gzip_zlib.h"
17 
18 #include <algorithm>
19 #include <random>
20 #include <string>
21 #include <vector>
22 
23 #include <gmock/gmock.h>
24 #include <gtest/gtest.h>
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/string_view.h"
27 
28 namespace tensorflow {
29 namespace serving {
30 namespace net_http {
31 namespace {
32 
33 std::random_device rd;
34 
35 typedef std::mt19937_64 RandomEngine;
36 
37 int GetUniformRand(RandomEngine* rng, int max) {
38  std::uniform_int_distribution<int> uniform(0, max);
39  return uniform(*rng);
40 }
41 
42 // Take some test headers and pass them to a GZipHeader, fragmenting
43 // the headers in many different random ways.
44 TEST(GzipHeader, FragmentTest) {
45  RandomEngine rng(rd());
46 
47  struct TestCase {
48  const char* str;
49  int len; // total length of the string
50  int cruft_len; // length of the gzip header part
51  };
52  TestCase tests[] = {
53  // Basic header:
54  {"\037\213\010\000\216\176\356\075\002\003", 10, 0},
55 
56  // Basic headers with crud on the end:
57  {"\037\213\010\000\216\176\356\075\002\003X", 11, 1},
58  {"\037\213\010\000\216\176\356\075\002\003XXX", 13, 3},
59 
60  {
61  "\037\213\010\010\321\135\265\100\000\003"
62  "emacs\000",
63  16, 0 // with an FNAME of "emacs"
64  },
65  {
66  "\037\213\010\010\321\135\265\100\000\003"
67  "\000",
68  11, 0 // with an FNAME of zero bytes
69  },
70  {
71  "\037\213\010\020\321\135\265\100\000\003"
72  "emacs\000",
73  16, 0, // with an FCOMMENT of "emacs"
74  },
75  {
76  "\037\213\010\020\321\135\265\100\000\003"
77  "\000",
78  11, 0, // with an FCOMMENT of zero bytes
79  },
80  {
81  "\037\213\010\002\321\135\265\100\000\003"
82  "\001\002",
83  12, 0 // with an FHCRC
84  },
85  {
86  "\037\213\010\004\321\135\265\100\000\003"
87  "\003\000foo",
88  15, 0 // with an extra of "foo"
89  },
90  {
91  "\037\213\010\004\321\135\265\100\000\003"
92  "\000\000",
93  12, 0 // with an extra of zero bytes
94  },
95  {
96  "\037\213\010\032\321\135\265\100\000\003"
97  "emacs\000"
98  "emacs\000"
99  "\001\002",
100  24, 0 // with an FNAME of "emacs", FCOMMENT of "emacs", and FHCRC
101  },
102  {
103  "\037\213\010\036\321\135\265\100\000\003"
104  "\003\000foo"
105  "emacs\000"
106  "emacs\000"
107  "\001\002",
108  29, 0 // with an FNAME of "emacs", FCOMMENT of "emacs", FHCRC, "foo"
109  },
110  {
111  "\037\213\010\036\321\135\265\100\000\003"
112  "\003\000foo"
113  "emacs\000"
114  "emacs\000"
115  "\001\002"
116  "XXX",
117  32, 3 // FNAME of "emacs", FCOMMENT of "emacs", FHCRC, "foo", crud
118  },
119  };
120 
121  // Test all the headers test cases.
122  for (auto test : tests) {
123  // Test many random ways they might be fragmented.
124  for (int j = 0; j < 1000; ++j) {
125  // Get the test case set up.
126  const char* p = test.str;
127  int bytes_left = test.len;
128  int bytes_read = 0;
129 
130  // Pick some random places to fragment the headers.
131  const int num_fragments = GetUniformRand(&rng, bytes_left);
132  std::vector<int> fragment_starts;
133  for (int frag_num = 0; frag_num < num_fragments; ++frag_num) {
134  fragment_starts.push_back(GetUniformRand(&rng, bytes_left));
135  }
136  sort(fragment_starts.begin(), fragment_starts.end());
137 
138  GZipHeader gzip_headers;
139  // Go through several fragments and pass them to the headers for parsing.
140  int frag_num = 0;
141  while (bytes_left > 0) {
142  const int fragment_len = (frag_num < num_fragments)
143  ? (fragment_starts[frag_num] - bytes_read)
144  : (test.len - bytes_read);
145  EXPECT_GE(fragment_len, 0);
146  const char* header_end = nullptr;
147  GZipHeader::Status status =
148  gzip_headers.ReadMore(p, fragment_len, &header_end);
149  bytes_read += fragment_len;
150  bytes_left -= fragment_len;
151  EXPECT_GE(bytes_left, 0);
152  p += fragment_len;
153  frag_num++;
154  if (bytes_left <= test.cruft_len) {
155  EXPECT_EQ(status, GZipHeader::COMPLETE_HEADER);
156  break;
157  } else {
158  EXPECT_EQ(status, GZipHeader::INCOMPLETE_HEADER);
159  }
160  } // while
161  } // for many fragmentations
162  } // for all test case headers
163 }
164 
165 // 1048576 == 2^20 == 1 MB
166 #define MAX_BUF_SIZE 1048500
167 #define MAX_BUF_FLEX 1048576
168 
169 void TestCompression(ZLib* zlib, const std::string& uncompbuf,
170  const char* msg) {
171  uLongf complen = ZLib::MinCompressbufSize(uncompbuf.size());
172  std::string compbuf(complen, '\0');
173  int err = zlib->Compress((Bytef*)compbuf.data(), &complen,
174  (Bytef*)uncompbuf.data(), uncompbuf.size());
175  EXPECT_EQ(Z_OK, err) << " " << uncompbuf.size() << " bytes down to "
176  << complen << " bytes.";
177 
178  // Output data size should match input data size.
179  uLongf uncomplen2 = uncompbuf.size();
180  std::string uncompbuf2(uncomplen2, '\0');
181  err = zlib->Uncompress((Bytef*)&uncompbuf2[0], &uncomplen2,
182  (Bytef*)compbuf.data(), complen);
183  EXPECT_EQ(Z_OK, err);
184 
185  if (msg != nullptr) {
186  printf("Orig: %7lu Compressed: %7lu %5.3f %s\n", uncomplen2, complen,
187  (float)complen / uncomplen2, msg);
188  }
189 
190  EXPECT_EQ(uncompbuf, absl::string_view(uncompbuf2.data(), uncomplen2))
191  << "Uncompression mismatch!";
192 }
193 
194 // Take some test inputs and pass them to zlib, fragmenting the input randomly.
195 void TestRandomGzipHeaderUncompress(ZLib* zlib) {
196  RandomEngine rng(rd());
197 
198  struct TestCase {
199  const char* str;
200  int len; // total length of the string
201  };
202  TestCase tests[] = {
203  {
204  // header, body ("hello, world!\n"), footer
205  "\037\213\010\000\216\176\356\075\002\003"
206  "\313\110\315\311\311\327\121\050\317\057\312\111\121\344\002\000"
207  "\300\337\061\266\016\000\000\000",
208  34,
209  },
210  };
211 
212  std::string uncompbuf2(MAX_BUF_FLEX, '\0');
213  // Test all the headers test cases.
214  for (uint32_t i = 0; i < ABSL_ARRAYSIZE(tests); ++i) {
215  // Test many random ways they might be fragmented.
216  for (int j = 0; j < 5 * 1000; ++j) {
217  // Get the test case set up.
218  const char* p = tests[i].str;
219  int bytes_left = tests[i].len;
220  int bytes_read = 0;
221  int bytes_uncompressed = 0;
222  zlib->Reset();
223 
224  // Pick some random places to fragment the headers.
225  const int num_fragments = GetUniformRand(&rng, bytes_left);
226  std::vector<int> fragment_starts;
227  for (int frag_num = 0; frag_num < num_fragments; ++frag_num) {
228  fragment_starts.push_back(GetUniformRand(&rng, bytes_left));
229  }
230  sort(fragment_starts.begin(), fragment_starts.end());
231 
232  // Go through several fragments and pass them in for parsing.
233  int frag_num = 0;
234  while (bytes_left > 0) {
235  const int fragment_len = (frag_num < num_fragments)
236  ? (fragment_starts[frag_num] - bytes_read)
237  : (tests[i].len - bytes_read);
238  ASSERT_GE(fragment_len, 0);
239  if (fragment_len != 0) { // zlib doesn't like 0-length buffers
240  uLongf uncomplen2 = uncompbuf2.size() - bytes_uncompressed;
241  auto complen_src = static_cast<uLongf>(fragment_len);
242  int err = zlib->UncompressAtMost(
243  (Bytef*)&uncompbuf2[0] + bytes_uncompressed, &uncomplen2,
244  (const Bytef*)p, &complen_src);
245  ASSERT_EQ(err, Z_OK);
246  bytes_uncompressed += uncomplen2;
247  bytes_read += fragment_len;
248  bytes_left -= fragment_len;
249  ASSERT_GE(bytes_left, 0);
250  p += fragment_len;
251  }
252  frag_num++;
253  } // while bytes left to uncompress
254 
255  ASSERT_TRUE(zlib->UncompressChunkDone());
256  EXPECT_EQ(sizeof("hello, world!\n") - 1, bytes_uncompressed);
257  EXPECT_EQ(
258  0, strncmp(uncompbuf2.data(), "hello, world!\n", bytes_uncompressed))
259  << "Uncompression mismatch, expected 'hello, world!\\n', "
260  << "got '" << absl::string_view(uncompbuf2.data(), bytes_uncompressed)
261  << "'";
262  } // for many fragmentations
263  } // for all test case headers
264 }
265 
266 constexpr int32_t kMaxSizeUncompressedData = 10 * 1024 * 1024; // 10MB
267 
268 void TestErrors(ZLib* zlib, const std::string& uncompbuf_str) {
269  const char* uncompbuf = uncompbuf_str.data();
270  const uLongf uncomplen = uncompbuf_str.size();
271  std::string compbuf(MAX_BUF_SIZE, '\0');
272  std::string uncompbuf2(MAX_BUF_FLEX, '\0');
273  int err;
274 
275  uLongf complen = 23; // don't give it enough space to compress
276  err = zlib->Compress((Bytef*)compbuf.data(), &complen, (Bytef*)uncompbuf,
277  uncomplen);
278  EXPECT_EQ(Z_BUF_ERROR, err);
279 
280  // OK, now successfully compress
281  complen = compbuf.size();
282  err = zlib->Compress((Bytef*)compbuf.data(), &complen, (Bytef*)uncompbuf,
283  uncomplen);
284  EXPECT_EQ(Z_OK, err) << " " << uncomplen << " bytes down to " << complen
285  << " bytes.";
286 
287  uLongf uncomplen2 = 10; // not enough space to uncompress
288  err = zlib->Uncompress((Bytef*)&uncompbuf2[0], &uncomplen2,
289  (Bytef*)compbuf.data(), complen);
290  EXPECT_EQ(Z_BUF_ERROR, err);
291 
292  // Here we check what happens when we don't try to uncompress enough bytes
293  uncomplen2 = uncompbuf2.size();
294  err = zlib->Uncompress((Bytef*)&uncompbuf2[0], &uncomplen2,
295  (Bytef*)compbuf.data(), 23);
296  EXPECT_EQ(Z_BUF_ERROR, err);
297 
298  uncomplen2 = uncompbuf2.size();
299  uLongf comlen2 = 23;
300  err = zlib->UncompressAtMost((Bytef*)&uncompbuf2[0], &uncomplen2,
301  (Bytef*)compbuf.data(), &comlen2);
302  EXPECT_EQ(Z_OK, err); // it's ok if a single chunk is too small
303  if (err == Z_OK) {
304  EXPECT_FALSE(zlib->UncompressChunkDone())
305  << "UncompresDone() was happy with its 3 bytes of compressed data";
306  }
307 
308  const int changepos = 0;
309  const char oldval = compbuf[changepos]; // corrupt the input
310  compbuf[changepos]++;
311  uncomplen2 = uncompbuf2.size();
312  err = zlib->Uncompress((Bytef*)&uncompbuf2[0], &uncomplen2,
313  (Bytef*)compbuf.data(), complen);
314  EXPECT_NE(Z_OK, err);
315 
316  compbuf[changepos] = oldval;
317 
318  // Make sure our memory-allocating uncompressor deals with problems gracefully
319  char* tmpbuf;
320  char tmp_compbuf[10] = "\255\255\255\255\255\255\255\255\255";
321  uncomplen2 = kMaxSizeUncompressedData;
322  err = zlib->UncompressGzipAndAllocate(
323  (Bytef**)&tmpbuf, &uncomplen2, (Bytef*)tmp_compbuf, sizeof(tmp_compbuf));
324  EXPECT_NE(Z_OK, err);
325  EXPECT_EQ(nullptr, tmpbuf);
326 }
327 
328 void TestBogusGunzipRequest(ZLib* zlib) {
329  const Bytef compbuf[] = "This is not compressed";
330  const uLongf complen = sizeof(compbuf);
331  Bytef* uncompbuf;
332  uLongf uncomplen = 0;
333  int err =
334  zlib->UncompressGzipAndAllocate(&uncompbuf, &uncomplen, compbuf, complen);
335  EXPECT_EQ(Z_DATA_ERROR, err);
336 }
337 
338 void TestGzip(ZLib* zlib, const std::string& uncompbuf_str) {
339  const char* uncompbuf = uncompbuf_str.data();
340  const uLongf uncomplen = uncompbuf_str.size();
341  std::string compbuf(MAX_BUF_SIZE, '\0');
342  std::string uncompbuf2(MAX_BUF_FLEX, '\0');
343 
344  uLongf complen = compbuf.size();
345  int err = zlib->Compress((Bytef*)compbuf.data(), &complen, (Bytef*)uncompbuf,
346  uncomplen);
347  EXPECT_EQ(Z_OK, err) << " " << uncomplen << " bytes down to " << complen
348  << " bytes.";
349 
350  uLongf uncomplen2 = uncompbuf2.size();
351  err = zlib->Uncompress((Bytef*)&uncompbuf2[0], &uncomplen2,
352  (Bytef*)compbuf.data(), complen);
353  EXPECT_EQ(Z_OK, err);
354  EXPECT_EQ(uncomplen, uncomplen2) << "Uncompression mismatch!";
355  EXPECT_EQ(0, memcmp(uncompbuf, uncompbuf2.data(), uncomplen))
356  << "Uncompression mismatch!";
357 
358  // Also try the auto-allocate uncompressor
359  char* tmpbuf;
360  err = zlib->UncompressGzipAndAllocate((Bytef**)&tmpbuf, &uncomplen2,
361  (Bytef*)compbuf.data(), complen);
362  EXPECT_EQ(Z_OK, err);
363  EXPECT_EQ(uncomplen, uncomplen2) << "Uncompression mismatch!";
364  EXPECT_EQ(0, memcmp(uncompbuf, uncompbuf2.data(), uncomplen))
365  << "Uncompression mismatch!";
366  if (tmpbuf) {
367  std::allocator<char>().deallocate(tmpbuf, uncomplen2);
368  }
369 }
370 
371 void TestChunkedGzip(ZLib* zlib, const std::string& uncompbuf_str,
372  int num_chunks) {
373  const char* uncompbuf = uncompbuf_str.data();
374  const uLongf uncomplen = uncompbuf_str.size();
375  std::string compbuf(MAX_BUF_SIZE, '\0');
376  std::string uncompbuf2(MAX_BUF_FLEX, '\0');
377  EXPECT_GT(num_chunks, 2);
378 
379  // uncompbuf2 is larger than uncompbuf to test for decoding too much
380  //
381  // Note that it is possible to receive num_chunks+1 total
382  // chunks, due to rounding error.
383  const int chunklen = uncomplen / num_chunks;
384  int chunknum, i, err;
385  int cum_len[100]; // cumulative compressed length, max to 100
386  cum_len[0] = 0;
387  for (chunknum = 0, i = 0; i < uncomplen; i += chunklen, chunknum++) {
388  uLongf complen = compbuf.size() - cum_len[chunknum];
389  // Make sure the last chunk gets the correct chunksize.
390  uLongf chunksize = (uncomplen - i) < chunklen ? (uncomplen - i) : chunklen;
391  err = zlib->CompressAtMost((Bytef*)compbuf.data() + cum_len[chunknum],
392  &complen, (Bytef*)uncompbuf + i, &chunksize);
393  ASSERT_EQ(Z_OK, err) << " " << uncomplen << " bytes down to " << complen
394  << " bytes.";
395  cum_len[chunknum + 1] = cum_len[chunknum] + complen;
396  }
397  uLongf complen = compbuf.size() - cum_len[chunknum];
398  err = zlib->CompressChunkDone((Bytef*)compbuf.data() + cum_len[chunknum],
399  &complen);
400  EXPECT_EQ(Z_OK, err);
401  cum_len[chunknum + 1] = cum_len[chunknum] + complen;
402 
403  for (chunknum = 0, i = 0; i < uncomplen; i += chunklen, chunknum++) {
404  uLongf uncomplen2 = uncomplen - i;
405  // Make sure the last chunk gets the correct chunksize.
406  int expected = uncomplen2 < chunklen ? uncomplen2 : chunklen;
407  uLongf complen_src = cum_len[chunknum + 1] - cum_len[chunknum];
408  err = zlib->UncompressAtMost((Bytef*)&uncompbuf2[0] + i, &uncomplen2,
409  (Bytef*)compbuf.data() + cum_len[chunknum],
410  &complen_src);
411  EXPECT_EQ(Z_OK, err);
412  EXPECT_EQ(expected, uncomplen2)
413  << "Uncompress size is " << uncomplen2 << ", not " << expected;
414  }
415  // There should be no further uncompressed bytes, after uncomplen bytes.
416  uLongf uncomplen2 = uncompbuf2.size() - uncomplen;
417  EXPECT_NE(0, uncomplen2);
418  uLongf complen_src = cum_len[chunknum + 1] - cum_len[chunknum];
419  err = zlib->UncompressAtMost((Bytef*)&uncompbuf2[0] + uncomplen, &uncomplen2,
420  (Bytef*)compbuf.data() + cum_len[chunknum],
421  &complen_src);
422  EXPECT_EQ(Z_OK, err);
423  EXPECT_EQ(0, uncomplen2);
424  EXPECT_TRUE(zlib->UncompressChunkDone());
425 
426  // Those uncomplen bytes should match.
427  EXPECT_EQ(0, memcmp(uncompbuf, uncompbuf2.data(), uncomplen))
428  << "Uncompression mismatch!";
429 
430  // Now test to make sure resetting works properly
431  // (1) First, uncompress the first chunk and make sure it's ok
432  uncomplen2 = uncompbuf2.size();
433  complen_src = cum_len[1];
434  err = zlib->UncompressAtMost((Bytef*)&uncompbuf2[0], &uncomplen2,
435  (Bytef*)compbuf.data(), &complen_src);
436  EXPECT_EQ(Z_OK, err);
437  EXPECT_EQ(chunklen, uncomplen2) << "Uncompression mismatch!";
438  // The first uncomplen2 bytes should match, where uncomplen2 is the number of
439  // successfully uncompressed bytes by the most recent UncompressChunk call.
440  // The remaining (uncomplen - uncomplen2) bytes would still match if the
441  // uncompression guaranteed not to modify the buffer other than those first
442  // uncomplen2 bytes, but there is no such guarantee.
443  EXPECT_EQ(0, memcmp(uncompbuf, uncompbuf2.data(), uncomplen2))
444  << "Uncompression mismatch!";
445 
446  // (2) Now, try the first chunk again and see that there's an error
447  uncomplen2 = uncompbuf2.size();
448  complen_src = cum_len[1];
449  err = zlib->UncompressAtMost((Bytef*)&uncompbuf2[0], &uncomplen2,
450  (Bytef*)compbuf.data(), &complen_src);
451  EXPECT_EQ(Z_DATA_ERROR, err);
452 
453  // (3) Now reset it and try again, and see that it's ok
454  zlib->Reset();
455  uncomplen2 = uncompbuf2.size();
456  complen_src = cum_len[1];
457  err = zlib->UncompressAtMost((Bytef*)&uncompbuf2[0], &uncomplen2,
458  (Bytef*)compbuf.data(), &complen_src);
459  EXPECT_EQ(Z_OK, err);
460  EXPECT_EQ(chunklen, uncomplen2) << "Uncompression mismatch!";
461  EXPECT_EQ(0, memcmp(uncompbuf, uncompbuf2.data(), uncomplen2))
462  << "Uncompression mismatch!";
463 
464  // (4) Make sure we can tackle output buffers that are too small
465  // with the *AtMost() interfaces.
466  uLong source_len = cum_len[2] - cum_len[1];
467  EXPECT_GT(source_len, 1);
468  // uncomplen2 = source_len/2;
469  uncomplen2 = 2; // fixed as we use fixed strings now
470  err = zlib->UncompressAtMost((Bytef*)&uncompbuf2[0], &uncomplen2,
471  (Bytef*)(compbuf.data() + cum_len[1]),
472  &source_len);
473  EXPECT_EQ(Z_BUF_ERROR, err);
474 
475  EXPECT_EQ(0, memcmp(uncompbuf + chunklen, uncompbuf2.data(), uncomplen2))
476  << "Uncompression mismatch!";
477 
478  const int saveuncomplen2 = uncomplen2;
479  uncomplen2 = uncompbuf2.size() - uncomplen2;
480  // Uncompress the rest of the chunk.
481  err = zlib->UncompressAtMost(
482  (Bytef*)&uncompbuf2[0], &uncomplen2,
483  (Bytef*)(compbuf.data() + cum_len[2] - source_len), &source_len);
484 
485  EXPECT_EQ(Z_OK, err);
486 
487  EXPECT_EQ(0, memcmp(uncompbuf + chunklen + saveuncomplen2, uncompbuf2.data(),
488  uncomplen2))
489  << "Uncompression mismatch!";
490 
491  // (5) Finally, reset again
492  zlib->Reset();
493 }
494 
495 void TestFooterBufferTooSmall(ZLib* zlib) {
496  uLongf footer_len = zlib->MinFooterSize() - 1;
497  ASSERT_EQ(9, footer_len);
498  Bytef footer_buffer[9];
499  int err = zlib->CompressChunkDone(footer_buffer, &footer_len);
500  ASSERT_EQ(Z_BUF_ERROR, err);
501  ASSERT_EQ(0, footer_len);
502 }
503 
504 TEST(ZLibTest, HugeCompression) {
505  // Just big enough to trigger 32 bit overflow in MinCompressbufSize()
506  // calculation.
507  const uLong HUGE_DATA_SIZE = 0x81000000;
508 
509  // Construct an easily compressible huge buffer.
510  std::string uncompbuf(HUGE_DATA_SIZE, 'A');
511 
512  ZLib zlib;
513  zlib.SetCompressionLevel(1); // as fast as possible
514  TestCompression(&zlib, uncompbuf, nullptr);
515 }
516 
517 // TODO(wenboz): random size randm data
518 const char kText[] = "1234567890abcdefghijklmnopqrstuvwxyz";
519 
520 TEST(ZLibTest, Compression) {
521  const std::string uncompbuf = kText;
522  ZLib zlib;
523  zlib.SetCompressionLevel(6);
524 
525  TestCompression(&zlib, uncompbuf, "fixed size");
526 }
527 
528 TEST(ZLibTest, OtherErrors) {
529  const std::string uncompbuf = kText;
530  ZLib zlib;
531 
532  TestErrors(&zlib, uncompbuf);
533 
534  TestBogusGunzipRequest(&zlib);
535 }
536 
537 TEST(ZLibTest, UncompressChunkedHeaders) {
538  // TestGzipHeaderUncompress(&zlib);
539 
540  ZLib zlib;
541  TestRandomGzipHeaderUncompress(&zlib);
542 }
543 
544 TEST(ZLibTest, GzipCompression) {
545  const std::string uncompbuf = kText;
546  ZLib zlib;
547 
548  TestGzip(&zlib, uncompbuf);
549 
550  // Try compressing again using the same ZLib
551  TestGzip(&zlib, uncompbuf);
552 }
553 
554 TEST(ZLibTest, ChunkedCompression) {
555  const std::string uncompbuf = kText;
556  ZLib zlib;
557 
558  TestChunkedGzip(&zlib, uncompbuf, 5);
559 
560  // Try compressing again using the same ZLib
561  TestChunkedGzip(&zlib, uncompbuf, 6);
562 
563  // In theory we can mix and match the type of compression we do
564  TestGzip(&zlib, uncompbuf);
565  TestChunkedGzip(&zlib, uncompbuf, 8);
566 
567  // Test writing final chunk and footer into buffer that's too small.
568  TestFooterBufferTooSmall(&zlib);
569 
570  TestGzip(&zlib, uncompbuf);
571 }
572 
573 TEST(ZLibTest, BytewiseRead) {
574  std::string text =
575  "v nedrah tundry vydra v getrah tyrit v vedrah yadra kedra";
576  size_t text_len = text.size();
577  size_t archive_len = ZLib::MinCompressbufSize(text_len);
578  std::string archive(archive_len, '\0');
579  size_t decompressed_len = text_len + 1;
580  std::string decompressed(decompressed_len, '\0');
581  size_t decompressed_offset = 0;
582 
583  ZLib compressor;
584  int rc = compressor.Compress((Bytef*)archive.data(), &archive_len,
585  (Bytef*)text.data(), text_len);
586  ASSERT_EQ(rc, Z_OK);
587 
588  ZLib zlib;
589  for (size_t i = 0; i < archive_len; ++i) {
590  size_t source_len = 1;
591  size_t dest_len = decompressed_len - decompressed_offset;
592  rc = zlib.UncompressAtMost(
593  (Bytef*)decompressed.data() + decompressed_offset, &dest_len,
594  (Bytef*)archive.data() + i, &source_len);
595  ASSERT_EQ(rc, Z_OK);
596  ASSERT_EQ(source_len, 0);
597  decompressed_offset += dest_len;
598  }
599 
600  ASSERT_TRUE(zlib.IsGzipFooterValid());
601  ASSERT_EQ(decompressed_offset, text_len);
602 
603  std::string truncated_output(decompressed.data(), text_len);
604  ASSERT_EQ(truncated_output, text);
605 }
606 
607 TEST(ZLibTest, TruncatedData) {
608  const int kBufferLen = 64;
609  std::string uncompressed = "Hello, World!";
610  std::string compressed(
611  "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xf3\x48\xcd\xc9\xc9"
612  "\xd7\x51\x08\xcf\x2f\xca\x49\x51\x04\x00\xd0\xc3\x4a\xec\x0d"
613  "\x00\x00\x00",
614  33);
615 
616  // Verify that "compressed" contains valid gzip data.
617  {
618  ZLib zlib;
619  // zlib.SetGzipHeaderMode();
620  char uncompbuf[kBufferLen];
621  bzero(uncompbuf, kBufferLen);
622  uLongf uncomplen = kBufferLen;
623  int err = zlib.Uncompress(
624  reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
625  reinterpret_cast<const Bytef*>(compressed.c_str()), compressed.size());
626  ASSERT_EQ(err, Z_OK);
627  ASSERT_EQ(uncompressed, absl::string_view(uncompbuf, uncomplen));
628  }
629 
630  // Test truncated data with ZLib::Uncompress().
631  for (int len = compressed.size() - 1; len > 0; len--) {
632  SCOPED_TRACE(absl::StrCat("Decompressing first ", len, " out of ",
633  compressed.size(), " bytes"));
634  ZLib zlib;
635  // zlib.SetGzipHeaderMode();
636  char uncompbuf[kBufferLen];
637  bzero(uncompbuf, kBufferLen);
638  uLongf uncomplen = kBufferLen;
639  int err = zlib.Uncompress(
640  reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
641  reinterpret_cast<const Bytef*>(compressed.c_str()), len);
642  ASSERT_NE(err, Z_OK);
643  }
644 
645  // Test truncated data with ZLib::UncompressAtMost() and
646  // ZLib::UncompressDone().
647  for (int len = compressed.size() - 1; len > 0; len--) {
648  SCOPED_TRACE(absl::StrCat("Decompressing first ", len, " out of ",
649  compressed.size(), " bytes"));
650  ZLib zlib;
651  // zlib.SetGzipHeaderMode();
652  char uncompbuf[kBufferLen];
653  bzero(uncompbuf, kBufferLen);
654  uLongf uncomplen = kBufferLen;
655  uLongf complen = len;
656  int err = zlib.UncompressAtMost(
657  reinterpret_cast<Bytef*>(uncompbuf), &uncomplen,
658  reinterpret_cast<const Bytef*>(compressed.c_str()), &complen);
659  ASSERT_EQ(err, Z_OK);
660  ASSERT_EQ(complen, 0);
661  if (uncomplen > 0) {
662  EXPECT_THAT(
663  uncompressed,
664  testing::StartsWith(std::string(uncompbuf).substr(0, uncomplen)));
665  }
666  ASSERT_FALSE(zlib.UncompressChunkDone());
667  }
668 }
669 
670 TEST(ZLibTest, GzipUncompressedLength) {
671  ZLib zlib;
672 
673  // "Hello, World!", compressed.
674  std::string hello_world(
675  "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xf3\x48\xcd\xc9\xc9"
676  "\xd7\x51\x08\xcf\x2f\xca\x49\x51\x04\x00\xd0\xc3\x4a\xec\x0d"
677  "\x00\x00\x00",
678  33);
679  EXPECT_EQ(13, zlib.GzipUncompressedLength(
680  reinterpret_cast<const Bytef*>(hello_world.c_str()),
681  hello_world.size()));
682 
683  // Empty string, "", compressed.
684  std::string empty(
685  "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\x03\x00\x00\x00\x00"
686  "\x00\x00\x00\x00\x00",
687  20);
688  EXPECT_EQ(0,
689  zlib.GzipUncompressedLength(
690  reinterpret_cast<const Bytef*>(empty.c_str()), empty.size()));
691 
692  std::string bad_data("\x01\x01\x01\x01", 4);
693  for (int len = 0; len <= bad_data.size(); len++) {
694  EXPECT_EQ(0, zlib.GzipUncompressedLength(
695  reinterpret_cast<const Bytef*>(bad_data.c_str()), len));
696  }
697 }
698 
699 } // namespace
700 } // namespace net_http
701 } // namespace serving
702 } // namespace tensorflow