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_trailing_garbage(void)199 static void test_bad_decompression_data_trailing_garbage(void) {
200 grpc_slice_buffer input;
201 grpc_slice_buffer output;
202
203 grpc_slice_buffer_init(&input);
204 grpc_slice_buffer_init(&output);
205 /* append 0x99 to the end of an otherwise valid stream */
206 grpc_slice_buffer_add(
207 &input, grpc_slice_from_copied_buffer(
208 "\x78\xda\x63\x60\x60\x60\x00\x00\x00\x04\x00\x01\x99", 13));
209
210 /* try (and fail) to decompress the invalid compresed buffer */
211 grpc_core::ExecCtx exec_ctx;
212 GPR_ASSERT(
213 0 == grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_DEFLATE, &input, &output));
214
215 grpc_slice_buffer_destroy(&input);
216 grpc_slice_buffer_destroy(&output);
217 }
218
test_bad_decompression_data_stream(void)219 static void test_bad_decompression_data_stream(void) {
220 grpc_slice_buffer input;
221 grpc_slice_buffer output;
222
223 grpc_slice_buffer_init(&input);
224 grpc_slice_buffer_init(&output);
225 grpc_slice_buffer_add(&input,
226 grpc_slice_from_copied_buffer("\x78\xda\xff\xff", 4));
227
228 /* try (and fail) to decompress the invalid compresed buffer */
229 grpc_core::ExecCtx exec_ctx;
230 GPR_ASSERT(
231 0 == grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_DEFLATE, &input, &output));
232
233 grpc_slice_buffer_destroy(&input);
234 grpc_slice_buffer_destroy(&output);
235 }
236
test_bad_compression_algorithm(void)237 static void test_bad_compression_algorithm(void) {
238 grpc_slice_buffer input;
239 grpc_slice_buffer output;
240 int was_compressed;
241
242 grpc_slice_buffer_init(&input);
243 grpc_slice_buffer_init(&output);
244 grpc_slice_buffer_add(
245 &input, grpc_slice_from_copied_string("Never gonna give you up"));
246
247 grpc_core::ExecCtx exec_ctx;
248 was_compressed = grpc_msg_compress(GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT,
249 &input, &output);
250 GPR_ASSERT(0 == was_compressed);
251
252 was_compressed =
253 grpc_msg_compress(static_cast<grpc_message_compression_algorithm>(
254 GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT + 123),
255 &input, &output);
256 GPR_ASSERT(0 == was_compressed);
257
258 grpc_slice_buffer_destroy(&input);
259 grpc_slice_buffer_destroy(&output);
260 }
261
test_bad_decompression_algorithm(void)262 static void test_bad_decompression_algorithm(void) {
263 grpc_slice_buffer input;
264 grpc_slice_buffer output;
265 int was_decompressed;
266
267 grpc_slice_buffer_init(&input);
268 grpc_slice_buffer_init(&output);
269 grpc_slice_buffer_add(&input,
270 grpc_slice_from_copied_string(
271 "I'm not really compressed but it doesn't matter"));
272 grpc_core::ExecCtx exec_ctx;
273 was_decompressed = grpc_msg_decompress(GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT,
274 &input, &output);
275 GPR_ASSERT(0 == was_decompressed);
276
277 was_decompressed =
278 grpc_msg_decompress(static_cast<grpc_message_compression_algorithm>(
279 GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT + 123),
280 &input, &output);
281 GPR_ASSERT(0 == was_decompressed);
282
283 grpc_slice_buffer_destroy(&input);
284 grpc_slice_buffer_destroy(&output);
285 }
286
main(int argc,char ** argv)287 int main(int argc, char** argv) {
288 unsigned i, j, k, m;
289 grpc_slice_split_mode uncompressed_split_modes[] = {
290 GRPC_SLICE_SPLIT_IDENTITY, GRPC_SLICE_SPLIT_ONE_BYTE};
291 grpc_slice_split_mode compressed_split_modes[] = {GRPC_SLICE_SPLIT_MERGE_ALL,
292 GRPC_SLICE_SPLIT_IDENTITY,
293 GRPC_SLICE_SPLIT_ONE_BYTE};
294
295 grpc_test_init(argc, argv);
296 grpc_init();
297
298 for (i = 0; i < GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT; i++) {
299 for (j = 0; j < GPR_ARRAY_SIZE(uncompressed_split_modes); j++) {
300 for (k = 0; k < GPR_ARRAY_SIZE(compressed_split_modes); k++) {
301 for (m = 0; m < TEST_VALUE_COUNT; m++) {
302 grpc_slice slice = create_test_value(static_cast<test_value>(m));
303 assert_passthrough(
304 slice, static_cast<grpc_message_compression_algorithm>(i),
305 static_cast<grpc_slice_split_mode>(j),
306 static_cast<grpc_slice_split_mode>(k),
307 get_compressability(
308 static_cast<test_value>(m),
309 static_cast<grpc_message_compression_algorithm>(i)));
310 grpc_slice_unref(slice);
311 }
312 }
313 }
314 }
315
316 test_tiny_data_compress();
317 test_bad_decompression_data_crc();
318 test_bad_decompression_data_stream();
319 test_bad_decompression_data_trailing_garbage();
320 test_bad_compression_algorithm();
321 test_bad_decompression_algorithm();
322 grpc_shutdown();
323
324 return 0;
325 }
326