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 |