| 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 |