• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2021 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <grpc/event_engine/memory_allocator.h>
18 #include <stdlib.h>
19 
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 #include "absl/strings/match.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/str_split.h"
27 #include "absl/types/optional.h"
28 #include "gtest/gtest.h"
29 #include "src/core/lib/resource_quota/arena.h"
30 #include "src/core/lib/resource_quota/memory_quota.h"
31 #include "src/core/lib/resource_quota/resource_quota.h"
32 #include "src/core/lib/slice/slice.h"
33 #include "src/core/lib/transport/metadata_batch.h"
34 #include "src/core/util/ref_counted_ptr.h"
35 #include "src/core/util/time.h"
36 #include "test/core/test_util/test_config.h"
37 
38 namespace grpc_core {
39 namespace testing {
40 
41 struct EmptyMetadataMap : public MetadataMap<EmptyMetadataMap> {
42   using MetadataMap<EmptyMetadataMap>::MetadataMap;
43 };
44 
45 struct TimeoutOnlyMetadataMap
46     : public MetadataMap<TimeoutOnlyMetadataMap, GrpcTimeoutMetadata> {
47   using MetadataMap<TimeoutOnlyMetadataMap, GrpcTimeoutMetadata>::MetadataMap;
48 };
49 
50 struct StreamNetworkStateMetadataMap
51     : public MetadataMap<StreamNetworkStateMetadataMap,
52                          GrpcStreamNetworkState> {
53   using MetadataMap<StreamNetworkStateMetadataMap,
54                     GrpcStreamNetworkState>::MetadataMap;
55 };
56 
TEST(MetadataMapTest,Noop)57 TEST(MetadataMapTest, Noop) { EmptyMetadataMap(); }
58 
TEST(MetadataMapTest,NoopWithDeadline)59 TEST(MetadataMapTest, NoopWithDeadline) { TimeoutOnlyMetadataMap(); }
60 
TEST(MetadataMapTest,SimpleOps)61 TEST(MetadataMapTest, SimpleOps) {
62   TimeoutOnlyMetadataMap map;
63   EXPECT_EQ(map.get_pointer(GrpcTimeoutMetadata()), nullptr);
64   EXPECT_EQ(map.get(GrpcTimeoutMetadata()), absl::nullopt);
65   map.Set(GrpcTimeoutMetadata(),
66           Timestamp::FromMillisecondsAfterProcessEpoch(1234));
67   EXPECT_NE(map.get_pointer(GrpcTimeoutMetadata()), nullptr);
68   EXPECT_EQ(*map.get_pointer(GrpcTimeoutMetadata()),
69             Timestamp::FromMillisecondsAfterProcessEpoch(1234));
70   EXPECT_EQ(map.get(GrpcTimeoutMetadata()),
71             Timestamp::FromMillisecondsAfterProcessEpoch(1234));
72   map.Remove(GrpcTimeoutMetadata());
73   EXPECT_EQ(map.get_pointer(GrpcTimeoutMetadata()), nullptr);
74   EXPECT_EQ(map.get(GrpcTimeoutMetadata()), absl::nullopt);
75 }
76 
77 // Target for MetadataMap::Encode.
78 // Writes down some string representation of what it receives, so we can
79 // EXPECT_EQ it later.
80 class FakeEncoder {
81  public:
output()82   const std::string& output() { return output_; }
83 
Encode(const Slice & key,const Slice & value)84   void Encode(const Slice& key, const Slice& value) {
85     output_ += absl::StrCat("UNKNOWN METADATUM: key=", key.as_string_view(),
86                             " value=", value.as_string_view(), "\n");
87   }
88 
Encode(GrpcTimeoutMetadata,Timestamp deadline)89   void Encode(GrpcTimeoutMetadata, Timestamp deadline) {
90     output_ += absl::StrCat("grpc-timeout: deadline=",
91                             deadline.milliseconds_after_process_epoch(), "\n");
92   }
93 
94  private:
95   std::string output_;
96 };
97 
TEST(MetadataMapTest,EmptyEncodeTest)98 TEST(MetadataMapTest, EmptyEncodeTest) {
99   FakeEncoder encoder;
100   TimeoutOnlyMetadataMap map;
101   map.Encode(&encoder);
102   EXPECT_EQ(encoder.output(), "");
103 }
104 
TEST(MetadataMapTest,TimeoutEncodeTest)105 TEST(MetadataMapTest, TimeoutEncodeTest) {
106   FakeEncoder encoder;
107   TimeoutOnlyMetadataMap map;
108   map.Set(GrpcTimeoutMetadata(),
109           Timestamp::FromMillisecondsAfterProcessEpoch(1234));
110   map.Encode(&encoder);
111   EXPECT_EQ(encoder.output(), "grpc-timeout: deadline=1234\n");
112 }
113 
TEST(MetadataMapTest,NonEncodableTrait)114 TEST(MetadataMapTest, NonEncodableTrait) {
115   struct EncoderWithNoTraitEncodeFunctions {
116     void Encode(const Slice&, const Slice&) {
117       abort();  // should not be called
118     }
119   };
120   StreamNetworkStateMetadataMap map;
121   map.Set(GrpcStreamNetworkState(), GrpcStreamNetworkState::kNotSentOnWire);
122   EXPECT_EQ(map.get(GrpcStreamNetworkState()),
123             GrpcStreamNetworkState::kNotSentOnWire);
124   EncoderWithNoTraitEncodeFunctions encoder;
125   map.Encode(&encoder);
126   EXPECT_EQ(map.DebugString(), "GrpcStreamNetworkState: not sent on wire");
127 }
128 
TEST(MetadataMapTest,NonTraitKeyWithMultipleValues)129 TEST(MetadataMapTest, NonTraitKeyWithMultipleValues) {
130   FakeEncoder encoder;
131   TimeoutOnlyMetadataMap map;
132   const absl::string_view kKey = "key";
133   map.Append(kKey, Slice::FromStaticString("value1"),
134              [](absl::string_view error, const Slice& value) {
135                LOG(ERROR) << error << " value:" << value.as_string_view();
136              });
137   map.Append(kKey, Slice::FromStaticString("value2"),
138              [](absl::string_view error, const Slice& value) {
139                LOG(ERROR) << error << " value:" << value.as_string_view();
140              });
141   map.Encode(&encoder);
142   EXPECT_EQ(encoder.output(),
143             "UNKNOWN METADATUM: key=key value=value1\n"
144             "UNKNOWN METADATUM: key=key value=value2\n");
145   std::string buffer;
146   EXPECT_EQ(map.GetStringValue(kKey, &buffer), "value1,value2");
147 }
148 
TEST(DebugStringBuilderTest,OneAddAfterRedaction)149 TEST(DebugStringBuilderTest, OneAddAfterRedaction) {
150   metadata_detail::DebugStringBuilder b;
151   b.AddAfterRedaction(ContentTypeMetadata::key(), "AddValue01");
152   EXPECT_EQ(b.TakeOutput(),
153             absl::StrCat(ContentTypeMetadata::key(), ": AddValue01"));
154 }
155 
GetAllowList()156 std::vector<std::string> GetAllowList() {
157   return {
158       // clang-format off
159           std::string(ContentTypeMetadata::key()),
160           std::string(EndpointLoadMetricsBinMetadata::key()),
161           std::string(GrpcAcceptEncodingMetadata::key()),
162           std::string(GrpcEncodingMetadata::key()),
163           std::string(GrpcInternalEncodingRequest::key()),
164           std::string(GrpcLbClientStatsMetadata::key()),
165           std::string(GrpcMessageMetadata::key()),
166           std::string(GrpcPreviousRpcAttemptsMetadata::key()),
167           std::string(GrpcRetryPushbackMsMetadata::key()),
168           std::string(GrpcServerStatsBinMetadata::key()),
169           std::string(GrpcStatusMetadata::key()),
170           std::string(GrpcTagsBinMetadata::key()),
171           std::string(GrpcTimeoutMetadata::key()),
172           std::string(GrpcTraceBinMetadata::key()),
173           std::string(HostMetadata::key()),
174           std::string(HttpAuthorityMetadata::key()),
175           std::string(HttpMethodMetadata::key()),
176           std::string(HttpPathMetadata::key()),
177           std::string(HttpSchemeMetadata::key()),
178           std::string(HttpStatusMetadata::key()),
179           std::string(LbCostBinMetadata::key()),
180           std::string(LbTokenMetadata::key()),
181           std::string(TeMetadata::key()),
182           std::string(UserAgentMetadata::key()),
183           std::string(XEnvoyPeerMetadata::key()),
184           std::string(GrpcCallWasCancelled::DebugKey()),
185           std::string(GrpcRegisteredMethod::DebugKey()),
186           std::string(GrpcStatusContext::DebugKey()),
187           std::string(GrpcStatusFromWire::DebugKey()),
188           std::string(GrpcStreamNetworkState::DebugKey()),
189           std::string(GrpcTarPit::DebugKey()),
190           std::string(GrpcTrailersOnly::DebugKey()),
191           std::string(PeerString::DebugKey()),
192           std::string(WaitForReady::DebugKey())
193       // clang-format on
194   };
195 }
196 
TEST(DebugStringBuilderTest,TestAllAllowListed)197 TEST(DebugStringBuilderTest, TestAllAllowListed) {
198   metadata_detail::DebugStringBuilder builder_add_allow_list;
199   const std::vector<std::string> allow_list_keys = GetAllowList();
200 
201   for (const std::string& curr_key : allow_list_keys) {
202     builder_add_allow_list.AddAfterRedaction(curr_key, curr_key);
203   }
204 
205   // All values which are allow listed should be added as is.
206   EXPECT_EQ(builder_add_allow_list.TakeOutput(),
207             "content-type: content-type, "
208             "endpoint-load-metrics-bin: endpoint-load-metrics-bin, "
209             "grpc-accept-encoding: grpc-accept-encoding, "
210             "grpc-encoding: grpc-encoding, "
211             "grpc-internal-encoding-request: grpc-internal-encoding-request, "
212             "grpclb_client_stats: grpclb_client_stats, "
213             "grpc-message: grpc-message, "
214             "grpc-previous-rpc-attempts: grpc-previous-rpc-attempts, "
215             "grpc-retry-pushback-ms: grpc-retry-pushback-ms, "
216             "grpc-server-stats-bin: grpc-server-stats-bin, "
217             "grpc-status: grpc-status, "
218             "grpc-tags-bin: grpc-tags-bin, "
219             "grpc-timeout: grpc-timeout, "
220             "grpc-trace-bin: grpc-trace-bin, "
221             "host: host, :authority: :authority, "
222             ":method: :method, "
223             ":path: :path, "
224             ":scheme: :scheme, "
225             ":status: :status, "
226             "lb-cost-bin: lb-cost-bin, "
227             "lb-token: lb-token, "
228             "te: te, "
229             "user-agent: user-agent, "
230             "x-envoy-peer-metadata: x-envoy-peer-metadata, "
231             "GrpcCallWasCancelled: GrpcCallWasCancelled, "
232             "GrpcRegisteredMethod: GrpcRegisteredMethod, "
233             "GrpcStatusContext: GrpcStatusContext, "
234             "GrpcStatusFromWire: GrpcStatusFromWire, "
235             "GrpcStreamNetworkState: GrpcStreamNetworkState, "
236             "GrpcTarPit: GrpcTarPit, "
237             "GrpcTrailersOnly: GrpcTrailersOnly, "
238             "PeerString: PeerString, "
239             "WaitForReady: WaitForReady");
240 }
241 
TEST(DebugStringBuilderTest,TestAllRedacted)242 TEST(DebugStringBuilderTest, TestAllRedacted) {
243   metadata_detail::DebugStringBuilder builder_add_redacted;
244   const std::vector<std::string> allow_list_keys = GetAllowList();
245 
246   for (const std::string& curr_key : allow_list_keys) {
247     builder_add_redacted.AddAfterRedaction(curr_key + "1234", curr_key);
248   }
249 
250   // All values which are not allow listed should be redacted
251   std::vector<std::string> redacted_output =
252       absl::StrSplit(builder_add_redacted.TakeOutput(), ',');
253   int i = 0;
254   for (std::string& curr_row : redacted_output) {
255     std::string redacted_str = absl::StrCat(
256         allow_list_keys[i++].size(), " bytes redacted for security reasons.");
257     EXPECT_EQ(absl::StrContains(curr_row, redacted_str), true);
258   }
259 }
260 
GetEncodableHeaders()261 std::vector<std::string> GetEncodableHeaders() {
262   return {
263       // clang-format off
264           std::string(ContentTypeMetadata::key()),
265           std::string(EndpointLoadMetricsBinMetadata::key()),
266           std::string(GrpcAcceptEncodingMetadata::key()),
267           std::string(GrpcEncodingMetadata::key()),
268           std::string(GrpcInternalEncodingRequest::key()),
269           std::string(GrpcLbClientStatsMetadata::key()),
270           std::string(GrpcMessageMetadata::key()),
271           std::string(GrpcPreviousRpcAttemptsMetadata::key()),
272           std::string(GrpcRetryPushbackMsMetadata::key()),
273           std::string(GrpcServerStatsBinMetadata::key()),
274           std::string(GrpcStatusMetadata::key()),
275           std::string(GrpcTagsBinMetadata::key()),
276           std::string(GrpcTimeoutMetadata::key()),
277           std::string(GrpcTraceBinMetadata::key()),
278           std::string(HostMetadata::key()),
279           std::string(HttpAuthorityMetadata::key()),
280           std::string(HttpMethodMetadata::key()),
281           std::string(HttpPathMetadata::key()),
282           std::string(HttpSchemeMetadata::key()),
283           std::string(HttpStatusMetadata::key()),
284           std::string(LbCostBinMetadata::key()),
285           std::string(LbTokenMetadata::key()),
286           std::string(TeMetadata::key()),
287       // clang-format on
288   };
289 }
290 
291 template <typename NonEncodableHeader, typename Value>
AddNonEncodableHeader(grpc_metadata_batch & md,Value value)292 void AddNonEncodableHeader(grpc_metadata_batch& md, Value value) {
293   md.Set(NonEncodableHeader(), value);
294 }
295 
296 template <bool filter_unknown>
297 class HeaderFilter {
298  public:
299   template <typename Key>
operator ()(Key)300   bool operator()(Key) {
301     return filter_unknown;
302   }
operator ()(absl::string_view)303   bool operator()(absl::string_view /*key*/) { return !filter_unknown; }
304 };
305 
TEST(MetadataMapTest,FilterTest)306 TEST(MetadataMapTest, FilterTest) {
307   grpc_metadata_batch map;
308   std::vector<std::string> allow_list_keys = GetEncodableHeaders();
309   std::vector<std::string> unknown_keys = {"unknown_key_1", "unknown_key_2"};
310   allow_list_keys.insert(allow_list_keys.end(), unknown_keys.begin(),
311                          unknown_keys.end());
312   // Add some encodable and unknown headers
313   for (const std::string& curr_key : allow_list_keys) {
314     map.Append(curr_key, Slice::FromStaticString("value1"),
315                [](absl::string_view /*error*/, const Slice& /*value*/) {});
316   }
317 
318   // Add 5 non-encodable headers
319   constexpr int kNumNonEncodableHeaders = 5;
320   AddNonEncodableHeader<GrpcCallWasCancelled, bool>(map, true);
321   AddNonEncodableHeader<GrpcRegisteredMethod, void*>(map, nullptr);
322   AddNonEncodableHeader<GrpcStatusContext, std::string>(map, "value1");
323   AddNonEncodableHeader<GrpcStatusFromWire>(map, "value1");
324   AddNonEncodableHeader<GrpcStreamNetworkState,
325                         GrpcStreamNetworkState::ValueType>(
326       map, GrpcStreamNetworkState::kNotSentOnWire);
327 
328   EXPECT_EQ(map.count(), allow_list_keys.size() + kNumNonEncodableHeaders);
329   // Remove all unknown headers
330   map.Filter(HeaderFilter<true>());
331   EXPECT_EQ(map.count(), allow_list_keys.size() + kNumNonEncodableHeaders -
332                              unknown_keys.size());
333   // Remove all encodable headers
334   map.Filter(HeaderFilter<false>());
335   EXPECT_EQ(map.count(), kNumNonEncodableHeaders);
336 }
337 
338 }  // namespace testing
339 }  // namespace grpc_core
340 
main(int argc,char ** argv)341 int main(int argc, char** argv) {
342   testing::InitGoogleTest(&argc, argv);
343   grpc::testing::TestEnvironment env(&argc, argv);
344   return RUN_ALL_TESTS();
345 };
346