• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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