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