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