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