• 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 <grpc/compression.h>
22 #include <grpc/slice_buffer.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <memory>
28 
29 #include "absl/log/log.h"
30 #include "gtest/gtest.h"
31 #include "src/core/lib/iomgr/exec_ctx.h"
32 #include "src/core/util/useful.h"
33 #include "test/core/test_util/slice_splitter.h"
34 #include "test/core/test_util/test_config.h"
35 
36 typedef enum { ONE_A = 0, ONE_KB_A, ONE_MB_A, TEST_VALUE_COUNT } test_value;
37 
38 typedef enum {
39   SHOULD_NOT_COMPRESS,
40   SHOULD_COMPRESS,
41   MAYBE_COMPRESSES
42 } compressability;
43 
assert_passthrough(grpc_slice value,grpc_compression_algorithm algorithm,grpc_slice_split_mode uncompressed_split_mode,grpc_slice_split_mode compressed_split_mode,compressability compress_result_check)44 static void assert_passthrough(grpc_slice value,
45                                grpc_compression_algorithm algorithm,
46                                grpc_slice_split_mode uncompressed_split_mode,
47                                grpc_slice_split_mode compressed_split_mode,
48                                compressability compress_result_check) {
49   grpc_slice_buffer input;
50   grpc_slice_buffer compressed_raw;
51   grpc_slice_buffer compressed;
52   grpc_slice_buffer output;
53   grpc_slice final;
54   int was_compressed;
55   const char* algorithm_name;
56 
57   ASSERT_NE(grpc_compression_algorithm_name(algorithm, &algorithm_name), 0);
58   LOG(INFO) << "assert_passthrough: value_length=" << GRPC_SLICE_LENGTH(value)
59             << " algorithm='" << algorithm_name << "' uncompressed_split='"
60             << grpc_slice_split_mode_name(uncompressed_split_mode)
61             << "' compressed_split='"
62             << grpc_slice_split_mode_name(compressed_split_mode) << "'";
63 
64   grpc_slice_buffer_init(&input);
65   grpc_slice_buffer_init(&compressed_raw);
66   grpc_slice_buffer_init(&compressed);
67   grpc_slice_buffer_init(&output);
68 
69   grpc_split_slices_to_buffer(uncompressed_split_mode, &value, 1, &input);
70 
71   {
72     grpc_core::ExecCtx exec_ctx;
73     was_compressed = grpc_msg_compress(algorithm, &input, &compressed_raw);
74   }
75   ASSERT_GT(input.count, 0);
76 
77   switch (compress_result_check) {
78     case SHOULD_NOT_COMPRESS:
79       ASSERT_EQ(was_compressed, 0);
80       break;
81     case SHOULD_COMPRESS:
82       ASSERT_EQ(was_compressed, 1);
83       break;
84     case MAYBE_COMPRESSES:
85       // no check
86       break;
87   }
88 
89   grpc_split_slice_buffer(compressed_split_mode, &compressed_raw, &compressed);
90 
91   {
92     grpc_core::ExecCtx exec_ctx;
93     ASSERT_TRUE(grpc_msg_decompress(
94         was_compressed ? algorithm : GRPC_COMPRESS_NONE, &compressed, &output));
95   }
96 
97   final = grpc_slice_merge(output.slices, output.count);
98   ASSERT_TRUE(grpc_slice_eq(value, final));
99 
100   grpc_slice_buffer_destroy(&input);
101   grpc_slice_buffer_destroy(&compressed);
102   grpc_slice_buffer_destroy(&compressed_raw);
103   grpc_slice_buffer_destroy(&output);
104   grpc_slice_unref(final);
105 }
106 
repeated(char c,size_t length)107 static grpc_slice repeated(char c, size_t length) {
108   grpc_slice out = grpc_slice_malloc(length);
109   memset(GRPC_SLICE_START_PTR(out), c, length);
110   return out;
111 }
112 
get_compressability(test_value id,grpc_compression_algorithm algorithm)113 static compressability get_compressability(
114     test_value id, grpc_compression_algorithm algorithm) {
115   if (algorithm == GRPC_COMPRESS_NONE) return SHOULD_NOT_COMPRESS;
116   switch (id) {
117     case ONE_A:
118       return SHOULD_NOT_COMPRESS;
119     case ONE_KB_A:
120     case ONE_MB_A:
121       return SHOULD_COMPRESS;
122     case TEST_VALUE_COUNT:
123       abort();
124   }
125   return MAYBE_COMPRESSES;
126 }
127 
create_test_value(test_value id)128 static grpc_slice create_test_value(test_value id) {
129   switch (id) {
130     case ONE_A:
131       return grpc_slice_from_copied_string("a");
132     case ONE_KB_A:
133       return repeated('a', 1024);
134     case ONE_MB_A:
135       return repeated('a', 1024 * 1024);
136     case TEST_VALUE_COUNT:
137       abort();
138   }
139   return grpc_slice_from_copied_string("bad value");
140 }
141 
TEST(MessageCompressTest,TinyDataCompress)142 TEST(MessageCompressTest, TinyDataCompress) {
143   grpc_slice_buffer input;
144   grpc_slice_buffer output;
145 
146   grpc_slice_buffer_init(&input);
147   grpc_slice_buffer_init(&output);
148   grpc_slice_buffer_add(&input, create_test_value(ONE_A));
149 
150   for (int i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
151     if (i == GRPC_COMPRESS_NONE) continue;
152     grpc_core::ExecCtx exec_ctx;
153     ASSERT_EQ(0, grpc_msg_compress(static_cast<grpc_compression_algorithm>(i),
154                                    &input, &output));
155     ASSERT_EQ(1, output.count);
156   }
157 
158   grpc_slice_buffer_destroy(&input);
159   grpc_slice_buffer_destroy(&output);
160 }
161 
TEST(MessageCompressTest,BadDecompressionDataCrc)162 TEST(MessageCompressTest, BadDecompressionDataCrc) {
163   grpc_slice_buffer input;
164   grpc_slice_buffer corrupted;
165   grpc_slice_buffer output;
166   size_t idx;
167   const uint32_t bad = 0xdeadbeef;
168 
169   grpc_slice_buffer_init(&input);
170   grpc_slice_buffer_init(&corrupted);
171   grpc_slice_buffer_init(&output);
172   grpc_slice_buffer_add(&input, create_test_value(ONE_MB_A));
173 
174   grpc_core::ExecCtx exec_ctx;
175   // compress it
176   grpc_msg_compress(GRPC_COMPRESS_GZIP, &input, &corrupted);
177   // corrupt the output by smashing the CRC
178   ASSERT_GT(corrupted.count, 1);
179   ASSERT_GT(GRPC_SLICE_LENGTH(corrupted.slices[1]), 8);
180   idx = GRPC_SLICE_LENGTH(corrupted.slices[1]) - 8;
181   memcpy(GRPC_SLICE_START_PTR(corrupted.slices[1]) + idx, &bad, 4);
182 
183   // try (and fail) to decompress the corrupted compressed buffer
184   ASSERT_EQ(0, grpc_msg_decompress(GRPC_COMPRESS_GZIP, &corrupted, &output));
185 
186   grpc_slice_buffer_destroy(&input);
187   grpc_slice_buffer_destroy(&corrupted);
188   grpc_slice_buffer_destroy(&output);
189 }
190 
TEST(MessageCompressTest,BadDecompressionDataMissingTrailer)191 TEST(MessageCompressTest, BadDecompressionDataMissingTrailer) {
192   grpc_slice_buffer input;
193   grpc_slice_buffer decompressed;
194   grpc_slice_buffer garbage;
195   grpc_slice_buffer output;
196 
197   grpc_slice_buffer_init(&input);
198   grpc_slice_buffer_init(&decompressed);
199   grpc_slice_buffer_init(&garbage);
200   grpc_slice_buffer_init(&output);
201   grpc_slice_buffer_add(&input, create_test_value(ONE_MB_A));
202 
203   grpc_core::ExecCtx exec_ctx;
204   // compress it
205   grpc_msg_compress(GRPC_COMPRESS_GZIP, &input, &decompressed);
206   ASSERT_GT(decompressed.length, 8);
207   // Remove the footer from the decompressed message
208   grpc_slice_buffer_trim_end(&decompressed, 8, &garbage);
209   // try (and fail) to decompress the compressed buffer without the footer
210   ASSERT_EQ(0, grpc_msg_decompress(GRPC_COMPRESS_GZIP, &decompressed, &output));
211 
212   grpc_slice_buffer_destroy(&input);
213   grpc_slice_buffer_destroy(&decompressed);
214   grpc_slice_buffer_destroy(&garbage);
215   grpc_slice_buffer_destroy(&output);
216 }
217 
TEST(MessageCompressTest,BadDecompressionDataTrailingGarbage)218 TEST(MessageCompressTest, BadDecompressionDataTrailingGarbage) {
219   grpc_slice_buffer input;
220   grpc_slice_buffer output;
221 
222   grpc_slice_buffer_init(&input);
223   grpc_slice_buffer_init(&output);
224   // append 0x99 to the end of an otherwise valid stream
225   grpc_slice_buffer_add(
226       &input, grpc_slice_from_copied_buffer(
227                   "\x78\xda\x63\x60\x60\x60\x00\x00\x00\x04\x00\x01\x99", 13));
228 
229   // try (and fail) to decompress the invalid compressed buffer
230   grpc_core::ExecCtx exec_ctx;
231   ASSERT_EQ(0, grpc_msg_decompress(GRPC_COMPRESS_DEFLATE, &input, &output));
232 
233   grpc_slice_buffer_destroy(&input);
234   grpc_slice_buffer_destroy(&output);
235 }
236 
TEST(MessageCompressTest,BadDecompressionDataStream)237 TEST(MessageCompressTest, BadDecompressionDataStream) {
238   grpc_slice_buffer input;
239   grpc_slice_buffer output;
240 
241   grpc_slice_buffer_init(&input);
242   grpc_slice_buffer_init(&output);
243   grpc_slice_buffer_add(&input,
244                         grpc_slice_from_copied_buffer("\x78\xda\xff\xff", 4));
245 
246   // try (and fail) to decompress the invalid compressed buffer
247   grpc_core::ExecCtx exec_ctx;
248   ASSERT_EQ(0, grpc_msg_decompress(GRPC_COMPRESS_DEFLATE, &input, &output));
249 
250   grpc_slice_buffer_destroy(&input);
251   grpc_slice_buffer_destroy(&output);
252 }
253 
TEST(MessageCompressTest,BadCompressionAlgorithm)254 TEST(MessageCompressTest, BadCompressionAlgorithm) {
255   grpc_slice_buffer input;
256   grpc_slice_buffer output;
257   int was_compressed;
258 
259   grpc_slice_buffer_init(&input);
260   grpc_slice_buffer_init(&output);
261   grpc_slice_buffer_add(
262       &input, grpc_slice_from_copied_string("Never gonna give you up"));
263 
264   grpc_core::ExecCtx exec_ctx;
265   was_compressed =
266       grpc_msg_compress(GRPC_COMPRESS_ALGORITHMS_COUNT, &input, &output);
267   ASSERT_EQ(0, was_compressed);
268 
269   was_compressed = grpc_msg_compress(static_cast<grpc_compression_algorithm>(
270                                          GRPC_COMPRESS_ALGORITHMS_COUNT + 123),
271                                      &input, &output);
272   ASSERT_EQ(0, was_compressed);
273 
274   grpc_slice_buffer_destroy(&input);
275   grpc_slice_buffer_destroy(&output);
276 }
277 
TEST(MessageCompressTest,BadDecompressionAlgorithm)278 TEST(MessageCompressTest, BadDecompressionAlgorithm) {
279   grpc_slice_buffer input;
280   grpc_slice_buffer output;
281   int was_decompressed;
282 
283   grpc_slice_buffer_init(&input);
284   grpc_slice_buffer_init(&output);
285   grpc_slice_buffer_add(&input,
286                         grpc_slice_from_copied_string(
287                             "I'm not really compressed but it doesn't matter"));
288   grpc_core::ExecCtx exec_ctx;
289   was_decompressed =
290       grpc_msg_decompress(GRPC_COMPRESS_ALGORITHMS_COUNT, &input, &output);
291   ASSERT_EQ(0, was_decompressed);
292 
293   was_decompressed =
294       grpc_msg_decompress(static_cast<grpc_compression_algorithm>(
295                               GRPC_COMPRESS_ALGORITHMS_COUNT + 123),
296                           &input, &output);
297   ASSERT_EQ(0, was_decompressed);
298 
299   grpc_slice_buffer_destroy(&input);
300   grpc_slice_buffer_destroy(&output);
301 }
302 
main(int argc,char ** argv)303 int main(int argc, char** argv) {
304   grpc::testing::TestEnvironment env(&argc, argv);
305   ::testing::InitGoogleTest(&argc, argv);
306   grpc::testing::TestGrpcScope grpc_scope;
307 
308   unsigned i, j, k, m;
309   grpc_slice_split_mode uncompressed_split_modes[] = {
310       GRPC_SLICE_SPLIT_IDENTITY, GRPC_SLICE_SPLIT_ONE_BYTE};
311   grpc_slice_split_mode compressed_split_modes[] = {GRPC_SLICE_SPLIT_MERGE_ALL,
312                                                     GRPC_SLICE_SPLIT_IDENTITY,
313                                                     GRPC_SLICE_SPLIT_ONE_BYTE};
314   for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
315     for (j = 0; j < GPR_ARRAY_SIZE(uncompressed_split_modes); j++) {
316       for (k = 0; k < GPR_ARRAY_SIZE(compressed_split_modes); k++) {
317         for (m = 0; m < TEST_VALUE_COUNT; m++) {
318           grpc_slice slice = create_test_value(static_cast<test_value>(m));
319           assert_passthrough(
320               slice, static_cast<grpc_compression_algorithm>(i),
321               static_cast<grpc_slice_split_mode>(j),
322               static_cast<grpc_slice_split_mode>(k),
323               get_compressability(static_cast<test_value>(m),
324                                   static_cast<grpc_compression_algorithm>(i)));
325           grpc_slice_unref(slice);
326         }
327       }
328     }
329   }
330 
331   return RUN_ALL_TESTS();
332 }
333