1 /*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include "src/core/lib/compression/message_compress.h"
20
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <grpc/grpc.h>
25 #include <grpc/support/log.h>
26
27 #include "src/core/lib/gpr/murmur_hash.h"
28 #include "src/core/lib/gpr/useful.h"
29 #include "src/core/lib/iomgr/exec_ctx.h"
30 #include "test/core/util/slice_splitter.h"
31 #include "test/core/util/test_config.h"
32
33 typedef enum { ONE_A = 0, ONE_KB_A, ONE_MB_A, TEST_VALUE_COUNT } test_value;
34
35 typedef enum {
36 SHOULD_NOT_COMPRESS,
37 SHOULD_COMPRESS,
38 MAYBE_COMPRESSES
39 } compressability;
40
assert_passthrough(grpc_slice value,grpc_message_compression_algorithm algorithm,grpc_slice_split_mode uncompressed_split_mode,grpc_slice_split_mode compressed_split_mode,compressability compress_result_check)41 static void assert_passthrough(grpc_slice value,
42 grpc_message_compression_algorithm algorithm,
43 grpc_slice_split_mode uncompressed_split_mode,
44 grpc_slice_split_mode compressed_split_mode,
45 compressability compress_result_check) {
46 grpc_slice_buffer input;
47 grpc_slice_buffer compressed_raw;
48 grpc_slice_buffer compressed;
49 grpc_slice_buffer output;
50 grpc_slice final;
51 int was_compressed;
52 const char* algorithm_name;
53
54 GPR_ASSERT(
55 grpc_message_compression_algorithm_name(algorithm, &algorithm_name) != 0);
56 gpr_log(GPR_INFO,
57 "assert_passthrough: value_length=%" PRIuPTR
58 " value_hash=0x%08x "
59 "algorithm='%s' uncompressed_split='%s' compressed_split='%s'",
60 GRPC_SLICE_LENGTH(value),
61 gpr_murmur_hash3(GRPC_SLICE_START_PTR(value),
62 GRPC_SLICE_LENGTH(value), 0),
63 algorithm_name, grpc_slice_split_mode_name(uncompressed_split_mode),
64 grpc_slice_split_mode_name(compressed_split_mode));
65
66 grpc_slice_buffer_init(&input);
67 grpc_slice_buffer_init(&compressed_raw);
68 grpc_slice_buffer_init(&compressed);
69 grpc_slice_buffer_init(&output);
70
71 grpc_split_slices_to_buffer(uncompressed_split_mode, &value, 1, &input);
72
73 {
74 grpc_core::ExecCtx exec_ctx;
75 was_compressed = grpc_msg_compress(algorithm, &input, &compressed_raw);
76 }
77 GPR_ASSERT(input.count > 0);
78
79 switch (compress_result_check) {
80 case SHOULD_NOT_COMPRESS:
81 GPR_ASSERT(was_compressed == 0);
82 break;
83 case SHOULD_COMPRESS:
84 GPR_ASSERT(was_compressed == 1);
85 break;
86 case MAYBE_COMPRESSES:
87 /* no check */
88 break;
89 }
90
91 grpc_split_slice_buffer(compressed_split_mode, &compressed_raw, &compressed);
92
93 {
94 grpc_core::ExecCtx exec_ctx;
95 GPR_ASSERT(grpc_msg_decompress(
96 was_compressed ? algorithm : GRPC_MESSAGE_COMPRESS_NONE, &compressed,
97 &output));
98 }
99
100 final = grpc_slice_merge(output.slices, output.count);
101 GPR_ASSERT(grpc_slice_eq(value, final));
102
103 grpc_slice_buffer_destroy(&input);
104 grpc_slice_buffer_destroy(&compressed);
105 grpc_slice_buffer_destroy(&compressed_raw);
106 grpc_slice_buffer_destroy(&output);
107 grpc_slice_unref(final);
108 }
109
repeated(char c,size_t length)110 static grpc_slice repeated(char c, size_t length) {
111 grpc_slice out = grpc_slice_malloc(length);
112 memset(GRPC_SLICE_START_PTR(out), c, length);
113 return out;
114 }
115
get_compressability(test_value id,grpc_message_compression_algorithm algorithm)116 static compressability get_compressability(
117 test_value id, grpc_message_compression_algorithm algorithm) {
118 if (algorithm == GRPC_MESSAGE_COMPRESS_NONE) return SHOULD_NOT_COMPRESS;
119 switch (id) {
120 case ONE_A:
121 return SHOULD_NOT_COMPRESS;
122 case ONE_KB_A:
123 case ONE_MB_A:
124 return SHOULD_COMPRESS;
125 case TEST_VALUE_COUNT:
126 abort();
127 break;
128 }
129 return MAYBE_COMPRESSES;
130 }
131
create_test_value(test_value id)132 static grpc_slice create_test_value(test_value id) {
133 switch (id) {
134 case ONE_A:
135 return grpc_slice_from_copied_string("a");
136 case ONE_KB_A:
137 return repeated('a', 1024);
138 case ONE_MB_A:
139 return repeated('a', 1024 * 1024);
140 case TEST_VALUE_COUNT:
141 abort();
142 break;
143 }
144 return grpc_slice_from_copied_string("bad value");
145 }
146
test_tiny_data_compress(void)147 static void test_tiny_data_compress(void) {
148 grpc_slice_buffer input;
149 grpc_slice_buffer output;
150
151 grpc_slice_buffer_init(&input);
152 grpc_slice_buffer_init(&output);
153 grpc_slice_buffer_add(&input, create_test_value(ONE_A));
154
155 for (int i = 0; i < GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT; i++) {
156 if (i == GRPC_MESSAGE_COMPRESS_NONE) continue;
157 grpc_core::ExecCtx exec_ctx;
158 GPR_ASSERT(0 == grpc_msg_compress(
159
160 static_cast<grpc_message_compression_algorithm>(i),
161 &input, &output));
162 GPR_ASSERT(1 == output.count);
163 }
164
165 grpc_slice_buffer_destroy(&input);
166 grpc_slice_buffer_destroy(&output);
167 }
168
test_bad_decompression_data_crc(void)169 static void test_bad_decompression_data_crc(void) {
170 grpc_slice_buffer input;
171 grpc_slice_buffer corrupted;
172 grpc_slice_buffer output;
173 size_t idx;
174 const uint32_t bad = 0xdeadbeef;
175
176 grpc_slice_buffer_init(&input);
177 grpc_slice_buffer_init(&corrupted);
178 grpc_slice_buffer_init(&output);
179 grpc_slice_buffer_add(&input, create_test_value(ONE_MB_A));
180
181 grpc_core::ExecCtx exec_ctx;
182 /* compress it */
183 grpc_msg_compress(GRPC_MESSAGE_COMPRESS_GZIP, &input, &corrupted);
184 /* corrupt the output by smashing the CRC */
185 GPR_ASSERT(corrupted.count > 1);
186 GPR_ASSERT(GRPC_SLICE_LENGTH(corrupted.slices[1]) > 8);
187 idx = GRPC_SLICE_LENGTH(corrupted.slices[1]) - 8;
188 memcpy(GRPC_SLICE_START_PTR(corrupted.slices[1]) + idx, &bad, 4);
189
190 /* try (and fail) to decompress the corrupted compresed buffer */
191 GPR_ASSERT(0 == grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_GZIP, &corrupted,
192 &output));
193
194 grpc_slice_buffer_destroy(&input);
195 grpc_slice_buffer_destroy(&corrupted);
196 grpc_slice_buffer_destroy(&output);
197 }
198
test_bad_decompression_data_missing_trailer(void)199 static void test_bad_decompression_data_missing_trailer(void) {
200 grpc_slice_buffer input;
201 grpc_slice_buffer decompressed;
202 grpc_slice_buffer garbage;
203 grpc_slice_buffer output;
204
205 grpc_slice_buffer_init(&input);
206 grpc_slice_buffer_init(&decompressed);
207 grpc_slice_buffer_init(&garbage);
208 grpc_slice_buffer_init(&output);
209 grpc_slice_buffer_add(&input, create_test_value(ONE_MB_A));
210
211 grpc_core::ExecCtx exec_ctx;
212 /* compress it */
213 grpc_msg_compress(GRPC_MESSAGE_COMPRESS_GZIP, &input, &decompressed);
214 GPR_ASSERT(decompressed.length > 8);
215 /* Remove the footer from the decompressed message */
216 grpc_slice_buffer_trim_end(&decompressed, 8, &garbage);
217 /* try (and fail) to decompress the compressed buffer without the footer */
218 GPR_ASSERT(0 == grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_GZIP, &decompressed,
219 &output));
220
221 grpc_slice_buffer_destroy(&input);
222 grpc_slice_buffer_destroy(&decompressed);
223 grpc_slice_buffer_destroy(&garbage);
224 grpc_slice_buffer_destroy(&output);
225 }
226
test_bad_decompression_data_trailing_garbage(void)227 static void test_bad_decompression_data_trailing_garbage(void) {
228 grpc_slice_buffer input;
229 grpc_slice_buffer output;
230
231 grpc_slice_buffer_init(&input);
232 grpc_slice_buffer_init(&output);
233 /* append 0x99 to the end of an otherwise valid stream */
234 grpc_slice_buffer_add(
235 &input, grpc_slice_from_copied_buffer(
236 "\x78\xda\x63\x60\x60\x60\x00\x00\x00\x04\x00\x01\x99", 13));
237
238 /* try (and fail) to decompress the invalid compresed buffer */
239 grpc_core::ExecCtx exec_ctx;
240 GPR_ASSERT(
241 0 == grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_DEFLATE, &input, &output));
242
243 grpc_slice_buffer_destroy(&input);
244 grpc_slice_buffer_destroy(&output);
245 }
246
test_bad_decompression_data_stream(void)247 static void test_bad_decompression_data_stream(void) {
248 grpc_slice_buffer input;
249 grpc_slice_buffer output;
250
251 grpc_slice_buffer_init(&input);
252 grpc_slice_buffer_init(&output);
253 grpc_slice_buffer_add(&input,
254 grpc_slice_from_copied_buffer("\x78\xda\xff\xff", 4));
255
256 /* try (and fail) to decompress the invalid compresed buffer */
257 grpc_core::ExecCtx exec_ctx;
258 GPR_ASSERT(
259 0 == grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_DEFLATE, &input, &output));
260
261 grpc_slice_buffer_destroy(&input);
262 grpc_slice_buffer_destroy(&output);
263 }
264
test_bad_compression_algorithm(void)265 static void test_bad_compression_algorithm(void) {
266 grpc_slice_buffer input;
267 grpc_slice_buffer output;
268 int was_compressed;
269
270 grpc_slice_buffer_init(&input);
271 grpc_slice_buffer_init(&output);
272 grpc_slice_buffer_add(
273 &input, grpc_slice_from_copied_string("Never gonna give you up"));
274
275 grpc_core::ExecCtx exec_ctx;
276 was_compressed = grpc_msg_compress(GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT,
277 &input, &output);
278 GPR_ASSERT(0 == was_compressed);
279
280 was_compressed =
281 grpc_msg_compress(static_cast<grpc_message_compression_algorithm>(
282 GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT + 123),
283 &input, &output);
284 GPR_ASSERT(0 == was_compressed);
285
286 grpc_slice_buffer_destroy(&input);
287 grpc_slice_buffer_destroy(&output);
288 }
289
test_bad_decompression_algorithm(void)290 static void test_bad_decompression_algorithm(void) {
291 grpc_slice_buffer input;
292 grpc_slice_buffer output;
293 int was_decompressed;
294
295 grpc_slice_buffer_init(&input);
296 grpc_slice_buffer_init(&output);
297 grpc_slice_buffer_add(&input,
298 grpc_slice_from_copied_string(
299 "I'm not really compressed but it doesn't matter"));
300 grpc_core::ExecCtx exec_ctx;
301 was_decompressed = grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT,
302 &input, &output);
303 GPR_ASSERT(0 == was_decompressed);
304
305 was_decompressed =
306 grpc_msg_decompress(static_cast<grpc_message_compression_algorithm>(
307 GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT + 123),
308 &input, &output);
309 GPR_ASSERT(0 == was_decompressed);
310
311 grpc_slice_buffer_destroy(&input);
312 grpc_slice_buffer_destroy(&output);
313 }
314
main(int argc,char ** argv)315 int main(int argc, char** argv) {
316 unsigned i, j, k, m;
317 grpc_slice_split_mode uncompressed_split_modes[] = {
318 GRPC_SLICE_SPLIT_IDENTITY, GRPC_SLICE_SPLIT_ONE_BYTE};
319 grpc_slice_split_mode compressed_split_modes[] = {GRPC_SLICE_SPLIT_MERGE_ALL,
320 GRPC_SLICE_SPLIT_IDENTITY,
321 GRPC_SLICE_SPLIT_ONE_BYTE};
322
323 grpc::testing::TestEnvironment env(argc, argv);
324 grpc_init();
325
326 for (i = 0; i < GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT; i++) {
327 for (j = 0; j < GPR_ARRAY_SIZE(uncompressed_split_modes); j++) {
328 for (k = 0; k < GPR_ARRAY_SIZE(compressed_split_modes); k++) {
329 for (m = 0; m < TEST_VALUE_COUNT; m++) {
330 grpc_slice slice = create_test_value(static_cast<test_value>(m));
331 assert_passthrough(
332 slice, static_cast<grpc_message_compression_algorithm>(i),
333 static_cast<grpc_slice_split_mode>(j),
334 static_cast<grpc_slice_split_mode>(k),
335 get_compressability(
336 static_cast<test_value>(m),
337 static_cast<grpc_message_compression_algorithm>(i)));
338 grpc_slice_unref(slice);
339 }
340 }
341 }
342 }
343
344 test_tiny_data_compress();
345 test_bad_decompression_data_crc();
346 test_bad_decompression_data_missing_trailer();
347 test_bad_decompression_data_stream();
348 test_bad_decompression_data_trailing_garbage();
349 test_bad_compression_algorithm();
350 test_bad_decompression_algorithm();
351 grpc_shutdown();
352
353 return 0;
354 }
355