OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Crashpad Authors. 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 #include "util/net/http_body_gzip.h" |
| 16 |
| 17 #include <string.h> |
| 18 |
| 19 #include <algorithm> |
| 20 #include <memory> |
| 21 #include <string> |
| 22 #include <utility> |
| 23 |
| 24 #include "base/macros.h" |
| 25 #include "base/rand_util.h" |
| 26 #include "base/numerics/safe_conversions.h" |
| 27 #include "gtest/gtest.h" |
| 28 #include "third_party/zlib/zlib_crashpad.h" |
| 29 #include "util/misc/zlib.h" |
| 30 #include "util/net/http_body.h" |
| 31 |
| 32 namespace crashpad { |
| 33 namespace test { |
| 34 namespace { |
| 35 |
| 36 class ScopedZlibInflateStream { |
| 37 public: |
| 38 explicit ScopedZlibInflateStream(z_stream* zlib) : zlib_(zlib) {} |
| 39 ~ScopedZlibInflateStream() { |
| 40 int zr = inflateEnd(zlib_); |
| 41 EXPECT_EQ(Z_OK, zr) << "inflateEnd: " << ZlibErrorString(zr); |
| 42 } |
| 43 |
| 44 private: |
| 45 z_stream* zlib_; // weak |
| 46 DISALLOW_COPY_AND_ASSIGN(ScopedZlibInflateStream); |
| 47 }; |
| 48 |
| 49 void GzipInflate(const std::string& compressed, |
| 50 std::string* decompressed, |
| 51 size_t buf_size) { |
| 52 decompressed->clear(); |
| 53 |
| 54 // There’s got to be at least a small buffer. |
| 55 buf_size = std::max(buf_size, static_cast<size_t>(1)); |
| 56 |
| 57 std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size]); |
| 58 z_stream zlib = {}; |
| 59 zlib.zalloc = Z_NULL; |
| 60 zlib.zfree = Z_NULL; |
| 61 zlib.opaque = Z_NULL; |
| 62 zlib.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(&compressed[0])); |
| 63 zlib.avail_in = base::checked_cast<uInt>(compressed.size()); |
| 64 zlib.next_out = buf.get(); |
| 65 zlib.avail_out = base::checked_cast<uInt>(buf_size); |
| 66 |
| 67 int zr = inflateInit2(&zlib, ZlibWindowBitsWithGzipWrapper(0)); |
| 68 ASSERT_EQ(Z_OK, zr) << "inflateInit2: " << ZlibErrorString(zr); |
| 69 ScopedZlibInflateStream zlib_inflate(&zlib); |
| 70 |
| 71 zr = inflate(&zlib, Z_FINISH); |
| 72 ASSERT_EQ(Z_STREAM_END, zr) << "inflate: " << ZlibErrorString(zr); |
| 73 |
| 74 ASSERT_LE(zlib.avail_out, buf_size); |
| 75 decompressed->assign(reinterpret_cast<char*>(buf.get()), |
| 76 buf_size - zlib.avail_out); |
| 77 } |
| 78 |
| 79 void TestGzipDeflateInflate(const std::string& string) { |
| 80 std::unique_ptr<HTTPBodyStream> string_stream( |
| 81 new StringHTTPBodyStream(string)); |
| 82 GzipHTTPBodyStream gzip_stream(std::move(string_stream)); |
| 83 |
| 84 // The minimum size of a gzip wrapper per RFC 1952: a 10-byte header and an |
| 85 // 8-byte trailer. |
| 86 const size_t kGzipHeaderSize = 18; |
| 87 |
| 88 // Per http://www.zlib.net/zlib_tech.html, in the worst case, zlib will store |
| 89 // uncompressed data as-is, at an overhead of 5 bytes per 16384-byte block. |
| 90 // Zero-length input will “compress” to a 2-byte zlib stream. Add the overhead |
| 91 // of the gzip wrapper, assuming no optional fields are present. |
| 92 size_t buf_size = |
| 93 string.size() + kGzipHeaderSize + |
| 94 (string.empty() ? 2 : (((string.size() + 16383) / 16384) * 5)); |
| 95 std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size]); |
| 96 FileOperationResult compressed_bytes = |
| 97 gzip_stream.GetBytesBuffer(buf.get(), buf_size); |
| 98 ASSERT_NE(compressed_bytes, -1); |
| 99 ASSERT_LE(static_cast<size_t>(compressed_bytes), buf_size); |
| 100 |
| 101 // Make sure that the stream is really at EOF. |
| 102 uint8_t eof_buf[16]; |
| 103 ASSERT_EQ(0, gzip_stream.GetBytesBuffer(eof_buf, sizeof(eof_buf))); |
| 104 |
| 105 std::string compressed(reinterpret_cast<char*>(buf.get()), compressed_bytes); |
| 106 |
| 107 ASSERT_GE(compressed.size(), kGzipHeaderSize); |
| 108 EXPECT_EQ('\37', compressed[0]); |
| 109 EXPECT_EQ('\213', compressed[1]); |
| 110 EXPECT_EQ(Z_DEFLATED, compressed[2]); |
| 111 |
| 112 std::string decompressed; |
| 113 ASSERT_NO_FATAL_FAILURE( |
| 114 GzipInflate(compressed, &decompressed, string.size())); |
| 115 |
| 116 EXPECT_EQ(string, decompressed); |
| 117 |
| 118 // In block mode, compression should be identical. |
| 119 string_stream.reset(new StringHTTPBodyStream(string)); |
| 120 GzipHTTPBodyStream block_gzip_stream(std::move(string_stream)); |
| 121 uint8_t block_buf[4096]; |
| 122 std::string block_compressed; |
| 123 FileOperationResult block_compressed_bytes; |
| 124 while ((block_compressed_bytes = block_gzip_stream.GetBytesBuffer( |
| 125 block_buf, sizeof(block_buf))) > 0) { |
| 126 block_compressed.append(reinterpret_cast<char*>(block_buf), |
| 127 block_compressed_bytes); |
| 128 } |
| 129 ASSERT_EQ(0, block_compressed_bytes); |
| 130 EXPECT_EQ(compressed, block_compressed); |
| 131 } |
| 132 |
| 133 std::string MakeString(size_t size) { |
| 134 std::string string; |
| 135 for (size_t i = 0; i < size; ++i) { |
| 136 string.append(1, (i % 256) ^ ((i >> 8) % 256)); |
| 137 } |
| 138 return string; |
| 139 } |
| 140 |
| 141 constexpr size_t kFourKBytes = 4096; |
| 142 constexpr size_t kManyBytes = 375017; |
| 143 |
| 144 TEST(GzipHTTPBodyStream, Empty) { |
| 145 TestGzipDeflateInflate(std::string()); |
| 146 } |
| 147 |
| 148 TEST(GzipHTTPBodyStream, OneByte) { |
| 149 TestGzipDeflateInflate(std::string("Z")); |
| 150 } |
| 151 |
| 152 TEST(GzipHTTPBodyStream, FourKBytes_NUL) { |
| 153 TestGzipDeflateInflate(std::string(kFourKBytes, '\0')); |
| 154 } |
| 155 |
| 156 TEST(GzipHTTPBodyStream, ManyBytes_NUL) { |
| 157 TestGzipDeflateInflate(std::string(kManyBytes, '\0')); |
| 158 } |
| 159 |
| 160 TEST(GzipHTTPBodyStream, FourKBytes_Deterministic) { |
| 161 TestGzipDeflateInflate(MakeString(kFourKBytes)); |
| 162 } |
| 163 |
| 164 TEST(GzipHTTPBodyStream, ManyBytes_Deterministic) { |
| 165 TestGzipDeflateInflate(MakeString(kManyBytes)); |
| 166 } |
| 167 |
| 168 TEST(GzipHTTPBodyStream, FourKBytes_Random) { |
| 169 TestGzipDeflateInflate(base::RandBytesAsString(kFourKBytes)); |
| 170 } |
| 171 |
| 172 TEST(GzipHTTPBodyStream, ManyBytes_Random) { |
| 173 TestGzipDeflateInflate(base::RandBytesAsString(kManyBytes)); |
| 174 } |
| 175 |
| 176 } // namespace |
| 177 } // namespace test |
| 178 } // namespace crashpad |
OLD | NEW |