• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/core/ext/transport/chttp2/transport/frame.h"
16 
17 #include <grpc/support/port_platform.h>
18 #include <stddef.h>
19 
20 #include <cstdint>
21 #include <utility>
22 
23 #include "absl/log/check.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "src/core/util/crash.h"
27 
28 namespace grpc_core {
29 
30 namespace {
31 
32 constexpr uint8_t kFrameTypeData = 0;
33 constexpr uint8_t kFrameTypeHeader = 1;
34 constexpr uint8_t kFrameTypeContinuation = 9;
35 constexpr uint8_t kFrameTypeRstStream = 3;
36 constexpr uint8_t kFrameTypeSettings = 4;
37 constexpr uint8_t kFrameTypePing = 6;
38 constexpr uint8_t kFrameTypeGoaway = 7;
39 constexpr uint8_t kFrameTypeWindowUpdate = 8;
40 constexpr uint8_t kFrameTypePushPromise = 5;
41 constexpr uint8_t kFrameTypeSecurity = 200;
42 
43 constexpr uint8_t kFlagEndStream = 1;
44 constexpr uint8_t kFlagAck = 1;
45 constexpr uint8_t kFlagEndHeaders = 4;
46 constexpr uint8_t kFlagPadded = 8;
47 constexpr uint8_t kFlagPriority = 0x20;
48 
49 constexpr size_t kFrameHeaderSize = 9;
50 
Write2b(uint16_t x,uint8_t * output)51 void Write2b(uint16_t x, uint8_t* output) {
52   output[0] = static_cast<uint8_t>(x >> 8);
53   output[1] = static_cast<uint8_t>(x);
54 }
55 
Read2b(const uint8_t * input)56 uint16_t Read2b(const uint8_t* input) {
57   return static_cast<uint16_t>(input[0]) << 8 | static_cast<uint16_t>(input[1]);
58 }
59 
Write3b(uint32_t x,uint8_t * output)60 void Write3b(uint32_t x, uint8_t* output) {
61   CHECK_LT(x, 16777216u);
62   output[0] = static_cast<uint8_t>(x >> 16);
63   output[1] = static_cast<uint8_t>(x >> 8);
64   output[2] = static_cast<uint8_t>(x);
65 }
66 
Read3b(const uint8_t * input)67 uint32_t Read3b(const uint8_t* input) {
68   return static_cast<uint32_t>(input[0]) << 16 |
69          static_cast<uint32_t>(input[1]) << 8 | static_cast<uint32_t>(input[2]);
70 }
71 
Write4b(uint32_t x,uint8_t * output)72 void Write4b(uint32_t x, uint8_t* output) {
73   output[0] = static_cast<uint8_t>(x >> 24);
74   output[1] = static_cast<uint8_t>(x >> 16);
75   output[2] = static_cast<uint8_t>(x >> 8);
76   output[3] = static_cast<uint8_t>(x);
77 }
78 
Read4b(const uint8_t * input)79 uint32_t Read4b(const uint8_t* input) {
80   return static_cast<uint32_t>(input[0]) << 24 |
81          static_cast<uint32_t>(input[1]) << 16 |
82          static_cast<uint32_t>(input[2]) << 8 | static_cast<uint32_t>(input[3]);
83 }
84 
Write8b(uint64_t x,uint8_t * output)85 void Write8b(uint64_t x, uint8_t* output) {
86   output[0] = static_cast<uint8_t>(x >> 56);
87   output[1] = static_cast<uint8_t>(x >> 48);
88   output[2] = static_cast<uint8_t>(x >> 40);
89   output[3] = static_cast<uint8_t>(x >> 32);
90   output[4] = static_cast<uint8_t>(x >> 24);
91   output[5] = static_cast<uint8_t>(x >> 16);
92   output[6] = static_cast<uint8_t>(x >> 8);
93   output[7] = static_cast<uint8_t>(x);
94 }
95 
Read8b(const uint8_t * input)96 uint64_t Read8b(const uint8_t* input) {
97   return static_cast<uint64_t>(input[0]) << 56 |
98          static_cast<uint64_t>(input[1]) << 48 |
99          static_cast<uint64_t>(input[2]) << 40 |
100          static_cast<uint64_t>(input[3]) << 32 |
101          static_cast<uint64_t>(input[4]) << 24 |
102          static_cast<uint64_t>(input[5]) << 16 |
103          static_cast<uint64_t>(input[6]) << 8 | static_cast<uint64_t>(input[7]);
104 }
105 
MaybeFlag(bool condition,uint8_t flag_mask)106 uint8_t MaybeFlag(bool condition, uint8_t flag_mask) {
107   return condition ? flag_mask : 0;
108 }
109 
ExtractFlag(uint8_t flags,uint8_t flag_mask)110 bool ExtractFlag(uint8_t flags, uint8_t flag_mask) {
111   return (flags & flag_mask) != 0;
112 }
113 
114 class SerializeExtraBytesRequired {
115  public:
operator ()(const Http2DataFrame &)116   size_t operator()(const Http2DataFrame&) { return 0; }
operator ()(const Http2HeaderFrame &)117   size_t operator()(const Http2HeaderFrame&) { return 0; }
operator ()(const Http2ContinuationFrame &)118   size_t operator()(const Http2ContinuationFrame&) { return 0; }
operator ()(const Http2RstStreamFrame &)119   size_t operator()(const Http2RstStreamFrame&) { return 4; }
operator ()(const Http2SettingsFrame & f)120   size_t operator()(const Http2SettingsFrame& f) {
121     return 6 * f.settings.size();
122   }
operator ()(const Http2PingFrame &)123   size_t operator()(const Http2PingFrame&) { return 8; }
operator ()(const Http2GoawayFrame &)124   size_t operator()(const Http2GoawayFrame&) { return 8; }
operator ()(const Http2WindowUpdateFrame &)125   size_t operator()(const Http2WindowUpdateFrame&) { return 4; }
operator ()(const Http2SecurityFrame &)126   size_t operator()(const Http2SecurityFrame&) { return 0; }
operator ()(const Http2UnknownFrame &)127   size_t operator()(const Http2UnknownFrame&) { Crash("unreachable"); }
128 };
129 
130 class SerializeHeaderAndPayload {
131  public:
SerializeHeaderAndPayload(size_t extra_bytes,SliceBuffer & out)132   SerializeHeaderAndPayload(size_t extra_bytes, SliceBuffer& out)
133       : out_(out),
134         extra_bytes_(MutableSlice::CreateUninitialized(extra_bytes)) {}
135 
operator ()(Http2DataFrame & frame)136   void operator()(Http2DataFrame& frame) {
137     auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
138     Http2FrameHeader{
139         static_cast<uint32_t>(frame.payload.Length()), kFrameTypeData,
140         MaybeFlag(frame.end_stream, kFlagEndStream), frame.stream_id}
141         .Serialize(hdr.begin());
142     out_.AppendIndexed(Slice(std::move(hdr)));
143     out_.TakeAndAppend(frame.payload);
144   }
145 
operator ()(Http2HeaderFrame & frame)146   void operator()(Http2HeaderFrame& frame) {
147     auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
148     Http2FrameHeader{
149         static_cast<uint32_t>(frame.payload.Length()), kFrameTypeHeader,
150         static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders) |
151                              MaybeFlag(frame.end_stream, kFlagEndStream)),
152         frame.stream_id}
153         .Serialize(hdr.begin());
154     out_.AppendIndexed(Slice(std::move(hdr)));
155     out_.TakeAndAppend(frame.payload);
156   }
157 
operator ()(Http2ContinuationFrame & frame)158   void operator()(Http2ContinuationFrame& frame) {
159     auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
160     Http2FrameHeader{
161         static_cast<uint32_t>(frame.payload.Length()), kFrameTypeContinuation,
162         static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders)),
163         frame.stream_id}
164         .Serialize(hdr.begin());
165     out_.AppendIndexed(Slice(std::move(hdr)));
166     out_.TakeAndAppend(frame.payload);
167   }
168 
operator ()(Http2RstStreamFrame & frame)169   void operator()(Http2RstStreamFrame& frame) {
170     auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4);
171     Http2FrameHeader{4, kFrameTypeRstStream, 0, frame.stream_id}.Serialize(
172         hdr_and_payload.begin());
173     Write4b(frame.error_code, hdr_and_payload.begin() + kFrameHeaderSize);
174     out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
175   }
176 
operator ()(Http2SettingsFrame & frame)177   void operator()(Http2SettingsFrame& frame) {
178     // Six bytes per setting (u16 id, u32 value)
179     const size_t payload_size = 6 * frame.settings.size();
180     auto hdr_and_payload =
181         extra_bytes_.TakeFirst(kFrameHeaderSize + payload_size);
182     Http2FrameHeader{static_cast<uint32_t>(payload_size), kFrameTypeSettings,
183                      MaybeFlag(frame.ack, kFlagAck), 0}
184         .Serialize(hdr_and_payload.begin());
185     size_t offset = kFrameHeaderSize;
186     for (auto& setting : frame.settings) {
187       Write2b(setting.id, hdr_and_payload.begin() + offset);
188       Write4b(setting.value, hdr_and_payload.begin() + offset + 2);
189       offset += 6;
190     }
191     out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
192   }
193 
operator ()(Http2PingFrame & frame)194   void operator()(Http2PingFrame& frame) {
195     auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8);
196     Http2FrameHeader{8, kFrameTypePing, MaybeFlag(frame.ack, kFlagAck), 0}
197         .Serialize(hdr_and_payload.begin());
198     Write8b(frame.opaque, hdr_and_payload.begin() + kFrameHeaderSize);
199     out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
200   }
201 
operator ()(Http2GoawayFrame & frame)202   void operator()(Http2GoawayFrame& frame) {
203     auto hdr_and_fixed_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8);
204     Http2FrameHeader{static_cast<uint32_t>(8 + frame.debug_data.length()),
205                      kFrameTypeGoaway, 0, 0}
206         .Serialize(hdr_and_fixed_payload.begin());
207     Write4b(frame.last_stream_id,
208             hdr_and_fixed_payload.begin() + kFrameHeaderSize);
209     Write4b(frame.error_code,
210             hdr_and_fixed_payload.begin() + kFrameHeaderSize + 4);
211     out_.AppendIndexed(Slice(std::move(hdr_and_fixed_payload)));
212     out_.AppendIndexed(std::move(frame.debug_data));
213   }
214 
operator ()(Http2WindowUpdateFrame & frame)215   void operator()(Http2WindowUpdateFrame& frame) {
216     auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4);
217     Http2FrameHeader{4, kFrameTypeWindowUpdate, 0, frame.stream_id}.Serialize(
218         hdr_and_payload.begin());
219     Write4b(frame.increment, hdr_and_payload.begin() + kFrameHeaderSize);
220     out_.AppendIndexed(Slice(std::move(hdr_and_payload)));
221   }
222 
operator ()(Http2SecurityFrame & frame)223   void operator()(Http2SecurityFrame& frame) {
224     auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize);
225     Http2FrameHeader{static_cast<uint32_t>(frame.payload.Length()),
226                      kFrameTypeSecurity, 0, 0}
227         .Serialize(hdr.begin());
228     out_.AppendIndexed(Slice(std::move(hdr)));
229     out_.TakeAndAppend(frame.payload);
230   }
231 
operator ()(Http2UnknownFrame &)232   void operator()(Http2UnknownFrame&) { Crash("unreachable"); }
233 
234  private:
235   SliceBuffer& out_;
236   MutableSlice extra_bytes_;
237 };
238 
StripPadding(SliceBuffer & payload)239 absl::Status StripPadding(SliceBuffer& payload) {
240   if (payload.Length() < 1) {
241     return absl::InternalError("padding flag set but no padding byte");
242   }
243   uint8_t padding_bytes;
244   payload.MoveFirstNBytesIntoBuffer(1, &padding_bytes);
245   if (payload.Length() < padding_bytes) {
246     return absl::InternalError("padding flag set but not enough padding bytes");
247   }
248   payload.RemoveLastNBytes(padding_bytes);
249   return absl::OkStatus();
250 }
251 
ParseDataFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)252 absl::StatusOr<Http2DataFrame> ParseDataFrame(const Http2FrameHeader& hdr,
253                                               SliceBuffer& payload) {
254   if (hdr.stream_id == 0) {
255     return absl::InternalError(
256         absl::StrCat("invalid stream id: ", hdr.ToString()));
257   }
258 
259   if (hdr.flags & kFlagPadded) {
260     auto s = StripPadding(payload);
261     if (!s.ok()) return s;
262   }
263 
264   return Http2DataFrame{hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndStream),
265                         std::move(payload)};
266 }
267 
ParseHeaderFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)268 absl::StatusOr<Http2HeaderFrame> ParseHeaderFrame(const Http2FrameHeader& hdr,
269                                                   SliceBuffer& payload) {
270   if (hdr.stream_id == 0) {
271     return absl::InternalError(
272         absl::StrCat("invalid stream id: ", hdr.ToString()));
273   }
274 
275   if (hdr.flags & kFlagPadded) {
276     auto s = StripPadding(payload);
277     if (!s.ok()) return s;
278   }
279 
280   if (hdr.flags & kFlagPriority) {
281     if (payload.Length() < 5) {
282       return absl::InternalError(
283           absl::StrCat("invalid priority payload: ", hdr.ToString()));
284     }
285     uint8_t trash[5];
286     payload.MoveFirstNBytesIntoBuffer(5, trash);
287   }
288 
289   return Http2HeaderFrame{
290       hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndHeaders),
291       ExtractFlag(hdr.flags, kFlagEndStream), std::move(payload)};
292 }
293 
ParseContinuationFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)294 absl::StatusOr<Http2ContinuationFrame> ParseContinuationFrame(
295     const Http2FrameHeader& hdr, SliceBuffer& payload) {
296   if (hdr.stream_id == 0) {
297     return absl::InternalError(
298         absl::StrCat("invalid stream id: ", hdr.ToString()));
299   }
300 
301   return Http2ContinuationFrame{hdr.stream_id,
302                                 ExtractFlag(hdr.flags, kFlagEndHeaders),
303                                 std::move(payload)};
304 }
305 
ParseRstStreamFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)306 absl::StatusOr<Http2RstStreamFrame> ParseRstStreamFrame(
307     const Http2FrameHeader& hdr, SliceBuffer& payload) {
308   if (payload.Length() != 4) {
309     return absl::InternalError(
310         absl::StrCat("invalid rst stream payload: ", hdr.ToString()));
311   }
312 
313   if (hdr.stream_id == 0) {
314     return absl::InternalError(
315         absl::StrCat("invalid stream id: ", hdr.ToString()));
316   }
317 
318   uint8_t buffer[4];
319   payload.CopyToBuffer(buffer);
320 
321   return Http2RstStreamFrame{hdr.stream_id, Read4b(buffer)};
322 }
323 
ParseSettingsFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)324 absl::StatusOr<Http2SettingsFrame> ParseSettingsFrame(
325     const Http2FrameHeader& hdr, SliceBuffer& payload) {
326   if (hdr.stream_id != 0) {
327     return absl::InternalError(
328         absl::StrCat("invalid stream id: ", hdr.ToString()));
329   }
330   if (hdr.flags == kFlagAck) {
331     if (payload.Length() != 0) {
332       return absl::InternalError(
333           absl::StrCat("invalid settings ack length: ", hdr.ToString()));
334     }
335     return Http2SettingsFrame{true, {}};
336   }
337 
338   if (payload.Length() % 6 != 0) {
339     return absl::InternalError(
340         absl::StrCat("invalid settings payload: ", hdr.ToString(),
341                      " -- settings must be multiples of 6 bytes long"));
342   }
343 
344   Http2SettingsFrame frame{false, {}};
345   while (payload.Length() != 0) {
346     uint8_t buffer[6];
347     payload.MoveFirstNBytesIntoBuffer(6, buffer);
348     frame.settings.push_back({
349         Read2b(buffer),
350         Read4b(buffer + 2),
351     });
352   }
353   return std::move(frame);
354 }
355 
ParsePingFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)356 absl::StatusOr<Http2PingFrame> ParsePingFrame(const Http2FrameHeader& hdr,
357                                               SliceBuffer& payload) {
358   if (payload.Length() != 8) {
359     return absl::InternalError(
360         absl::StrCat("invalid ping payload: ", hdr.ToString()));
361   }
362 
363   if (hdr.stream_id != 0) {
364     return absl::InternalError(
365         absl::StrCat("invalid ping stream id: ", hdr.ToString()));
366   }
367 
368   bool ack;
369   switch (hdr.flags) {
370     case 0:
371       ack = false;
372       break;
373     case kFlagAck:
374       ack = true;
375       break;
376     default:
377       return absl::InternalError(
378           absl::StrCat("invalid ping flags: ", hdr.ToString()));
379   }
380 
381   uint8_t buffer[8];
382   payload.CopyToBuffer(buffer);
383 
384   return Http2PingFrame{ack, Read8b(buffer)};
385 }
386 
ParseGoawayFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)387 absl::StatusOr<Http2GoawayFrame> ParseGoawayFrame(const Http2FrameHeader& hdr,
388                                                   SliceBuffer& payload) {
389   if (payload.Length() < 8) {
390     return absl::InternalError(
391         absl::StrCat("invalid goaway payload: ", hdr.ToString(),
392                      " -- must be at least 8 bytes"));
393   }
394 
395   if (hdr.stream_id != 0) {
396     return absl::InternalError(
397         absl::StrCat("invalid goaway stream id: ", hdr.ToString()));
398   }
399 
400   if (hdr.flags != 0) {
401     return absl::InternalError(
402         absl::StrCat("invalid goaway flags: ", hdr.ToString()));
403   }
404 
405   uint8_t buffer[8];
406   payload.MoveFirstNBytesIntoBuffer(8, buffer);
407   return Http2GoawayFrame{Read4b(buffer), Read4b(buffer + 4),
408                           payload.JoinIntoSlice()};
409 }
410 
ParseWindowUpdateFrame(const Http2FrameHeader & hdr,SliceBuffer & payload)411 absl::StatusOr<Http2WindowUpdateFrame> ParseWindowUpdateFrame(
412     const Http2FrameHeader& hdr, SliceBuffer& payload) {
413   if (payload.Length() != 4) {
414     return absl::InternalError(
415         absl::StrCat("invalid window update payload: ", hdr.ToString(),
416                      " -- must be 4 bytes"));
417   }
418 
419   if (hdr.flags != 0) {
420     return absl::InternalError(
421         absl::StrCat("invalid window update flags: ", hdr.ToString()));
422   }
423 
424   uint8_t buffer[4];
425   payload.CopyToBuffer(buffer);
426   return Http2WindowUpdateFrame{hdr.stream_id, Read4b(buffer)};
427 }
428 
ParseSecurityFrame(const Http2FrameHeader &,SliceBuffer & payload)429 absl::StatusOr<Http2SecurityFrame> ParseSecurityFrame(
430     const Http2FrameHeader& /*hdr*/, SliceBuffer& payload) {
431   return Http2SecurityFrame{std::move(payload)};
432 }
433 
434 }  // namespace
435 
Serialize(uint8_t * output) const436 void Http2FrameHeader::Serialize(uint8_t* output) const {
437   Write3b(length, output);
438   output[3] = type;
439   output[4] = flags;
440   Write4b(stream_id, output + 5);
441 }
442 
Parse(const uint8_t * input)443 Http2FrameHeader Http2FrameHeader::Parse(const uint8_t* input) {
444   return Http2FrameHeader{Read3b(input), input[3], input[4], Read4b(input + 5)};
445 }
446 
447 namespace {
Http2FrameTypeString(uint8_t frame_type)448 std::string Http2FrameTypeString(uint8_t frame_type) {
449   switch (frame_type) {
450     case kFrameTypeData:
451       return "DATA";
452     case kFrameTypeHeader:
453       return "HEADER";
454     case kFrameTypeContinuation:
455       return "CONTINUATION";
456     case kFrameTypeRstStream:
457       return "RST_STREAM";
458     case kFrameTypeSettings:
459       return "SETTINGS";
460     case kFrameTypeGoaway:
461       return "GOAWAY";
462     case kFrameTypeWindowUpdate:
463       return "WINDOW_UPDATE";
464     case kFrameTypePing:
465       return "PING";
466     case kFrameTypeSecurity:
467       return "SECURITY";
468   }
469   return absl::StrCat("UNKNOWN(", frame_type, ")");
470 }
471 }  // namespace
472 
ToString() const473 std::string Http2FrameHeader::ToString() const {
474   return absl::StrCat("{", Http2FrameTypeString(type), ": flags=", flags,
475                       ", stream_id=", stream_id, ", length=", length, "}");
476 }
477 
Serialize(absl::Span<Http2Frame> frames,SliceBuffer & out)478 void Serialize(absl::Span<Http2Frame> frames, SliceBuffer& out) {
479   size_t buffer_needed = 0;
480   for (auto& frame : frames) {
481     // Bytes needed for framing
482     buffer_needed += kFrameHeaderSize;
483     // Bytes needed for frame payload
484     buffer_needed += absl::visit(SerializeExtraBytesRequired(), frame);
485   }
486   SerializeHeaderAndPayload serialize(buffer_needed, out);
487   for (auto& frame : frames) {
488     absl::visit(serialize, frame);
489   }
490 }
491 
ParseFramePayload(const Http2FrameHeader & hdr,SliceBuffer payload)492 absl::StatusOr<Http2Frame> ParseFramePayload(const Http2FrameHeader& hdr,
493                                              SliceBuffer payload) {
494   CHECK(payload.Length() == hdr.length);
495   switch (hdr.type) {
496     case kFrameTypeData:
497       return ParseDataFrame(hdr, payload);
498     case kFrameTypeHeader:
499       return ParseHeaderFrame(hdr, payload);
500     case kFrameTypeContinuation:
501       return ParseContinuationFrame(hdr, payload);
502     case kFrameTypeRstStream:
503       return ParseRstStreamFrame(hdr, payload);
504     case kFrameTypeSettings:
505       return ParseSettingsFrame(hdr, payload);
506     case kFrameTypePing:
507       return ParsePingFrame(hdr, payload);
508     case kFrameTypeGoaway:
509       return ParseGoawayFrame(hdr, payload);
510     case kFrameTypeWindowUpdate:
511       return ParseWindowUpdateFrame(hdr, payload);
512     case kFrameTypePushPromise:
513       return absl::InternalError(
514           "push promise not supported (and SETTINGS_ENABLE_PUSH explicitly "
515           "disabled).");
516     case kFrameTypeSecurity:
517       return ParseSecurityFrame(hdr, payload);
518     default:
519       return Http2UnknownFrame{};
520   }
521 }
522 
523 }  // namespace grpc_core
524