1 // Copyright 2022 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/chaotic_good/frame.h"
18
19 #include <string.h>
20
21 #include <cstdint>
22 #include <limits>
23 #include <utility>
24
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27
28 #include <grpc/slice.h>
29 #include <grpc/support/log.h>
30
31 #include "src/core/lib/gprpp/bitset.h"
32 #include "src/core/lib/gprpp/no_destruct.h"
33 #include "src/core/lib/gprpp/status_helper.h"
34 #include "src/core/lib/slice/slice.h"
35 #include "src/core/lib/slice/slice_buffer.h"
36
37 namespace grpc_core {
38 namespace chaotic_good {
39
40 namespace {
__anon8c38c9af0202null41 const NoDestruct<Slice> kZeroSlice{[] {
42 // Frame header size is fixed to 24 bytes.
43 auto slice = GRPC_SLICE_MALLOC(24);
44 memset(GRPC_SLICE_START_PTR(slice), 0, 24);
45 return slice;
46 }()};
47
48 class FrameSerializer {
49 public:
FrameSerializer(FrameType type,uint32_t stream_id)50 explicit FrameSerializer(FrameType type, uint32_t stream_id)
51 : header_{type, {}, stream_id, 0, 0, 0, 0} {
52 output_.AppendIndexed(kZeroSlice->Copy());
53 }
54 // If called, must be called before AddTrailers, Finish.
AddHeaders()55 SliceBuffer& AddHeaders() {
56 header_.flags.set(0);
57 return output_;
58 }
59 // If called, must be called before Finish.
AddTrailers()60 SliceBuffer& AddTrailers() {
61 header_.flags.set(1);
62 return output_;
63 }
64
Finish()65 SliceBuffer Finish() {
66 header_.Serialize(
67 GRPC_SLICE_START_PTR(output_.c_slice_buffer()->slices[0]));
68 return std::move(output_);
69 }
70
71 private:
72 FrameHeader header_;
73 SliceBuffer output_;
74 };
75
76 class FrameDeserializer {
77 public:
FrameDeserializer(const FrameHeader & header,SliceBuffer & input)78 FrameDeserializer(const FrameHeader& header, SliceBuffer& input)
79 : header_(header), input_(input) {}
header() const80 const FrameHeader& header() const { return header_; }
81 // If called, must be called before ReceiveTrailers, Finish.
ReceiveHeaders()82 absl::StatusOr<SliceBuffer> ReceiveHeaders() {
83 return Take(header_.header_length);
84 }
85 // If called, must be called before Finish.
ReceiveTrailers()86 absl::StatusOr<SliceBuffer> ReceiveTrailers() {
87 return Take(header_.trailer_length);
88 }
89
90 // Return message length to get payload size in data plane.
GetMessageLength() const91 uint32_t GetMessageLength() const { return header_.message_length; }
92 // Return message padding to get padding size in data plane.
GetMessagePadding() const93 uint32_t GetMessagePadding() const { return header_.message_padding; }
94
Finish()95 absl::Status Finish() { return absl::OkStatus(); }
96
97 private:
Take(uint32_t length)98 absl::StatusOr<SliceBuffer> Take(uint32_t length) {
99 if (length == 0) return SliceBuffer{};
100 if (input_.Length() < length) {
101 return absl::InvalidArgumentError(
102 "Frame too short (insufficient payload)");
103 }
104 SliceBuffer out;
105 input_.MoveFirstNBytesIntoSliceBuffer(length, out);
106 return std::move(out);
107 }
108 FrameHeader header_;
109 SliceBuffer& input_;
110 };
111
112 template <typename Metadata>
ReadMetadata(HPackParser * parser,absl::StatusOr<SliceBuffer> maybe_slices,uint32_t stream_id,bool is_header,bool is_client)113 absl::StatusOr<Arena::PoolPtr<Metadata>> ReadMetadata(
114 HPackParser* parser, absl::StatusOr<SliceBuffer> maybe_slices,
115 uint32_t stream_id, bool is_header, bool is_client) {
116 if (!maybe_slices.ok()) return maybe_slices.status();
117 auto& slices = *maybe_slices;
118 Arena::PoolPtr<Metadata> metadata;
119 parser->BeginFrame(
120 metadata.get(), std::numeric_limits<uint32_t>::max(),
121 std::numeric_limits<uint32_t>::max(),
122 is_header ? HPackParser::Boundary::EndOfHeaders
123 : HPackParser::Boundary::EndOfStream,
124 HPackParser::Priority::None,
125 HPackParser::LogInfo{stream_id,
126 is_header ? HPackParser::LogInfo::Type::kHeaders
127 : HPackParser::LogInfo::Type::kTrailers,
128 is_client});
129 for (size_t i = 0; i < slices.Count(); i++) {
130 GRPC_RETURN_IF_ERROR(
131 parser->Parse(slices.c_slice_at(i), i == slices.Count() - 1));
132 }
133 parser->FinishFrame();
134 return std::move(metadata);
135 }
136 } // namespace
137
Deserialize(HPackParser *,const FrameHeader & header,SliceBuffer & slice_buffer)138 absl::Status SettingsFrame::Deserialize(HPackParser*, const FrameHeader& header,
139 SliceBuffer& slice_buffer) {
140 if (header.type != FrameType::kSettings) {
141 return absl::InvalidArgumentError("Expected settings frame");
142 }
143 if (header.flags.any()) {
144 return absl::InvalidArgumentError("Unexpected flags");
145 }
146 FrameDeserializer deserializer(header, slice_buffer);
147 return deserializer.Finish();
148 }
149
Serialize(HPackCompressor *) const150 SliceBuffer SettingsFrame::Serialize(HPackCompressor*) const {
151 FrameSerializer serializer(FrameType::kSettings, 0);
152 return serializer.Finish();
153 }
154
Deserialize(HPackParser * parser,const FrameHeader & header,SliceBuffer & slice_buffer)155 absl::Status ClientFragmentFrame::Deserialize(HPackParser* parser,
156 const FrameHeader& header,
157 SliceBuffer& slice_buffer) {
158 if (header.stream_id == 0) {
159 return absl::InvalidArgumentError("Expected non-zero stream id");
160 }
161 stream_id = header.stream_id;
162 if (header.type != FrameType::kFragment) {
163 return absl::InvalidArgumentError("Expected fragment frame");
164 }
165 FrameDeserializer deserializer(header, slice_buffer);
166 if (header.flags.is_set(0)) {
167 auto r = ReadMetadata<ClientMetadata>(parser, deserializer.ReceiveHeaders(),
168 header.stream_id, true, true);
169 if (!r.ok()) return r.status();
170 }
171 if (header.flags.is_set(1)) {
172 if (header.trailer_length != 0) {
173 return absl::InvalidArgumentError("Unexpected trailer length");
174 }
175 end_of_stream = true;
176 } else {
177 end_of_stream = false;
178 }
179 return deserializer.Finish();
180 }
181
Serialize(HPackCompressor * encoder) const182 SliceBuffer ClientFragmentFrame::Serialize(HPackCompressor* encoder) const {
183 GPR_ASSERT(stream_id != 0);
184 FrameSerializer serializer(FrameType::kFragment, stream_id);
185 if (headers.get() != nullptr) {
186 encoder->EncodeRawHeaders(*headers.get(), serializer.AddHeaders());
187 }
188 if (end_of_stream) {
189 serializer.AddTrailers();
190 }
191 return serializer.Finish();
192 }
193
Deserialize(HPackParser * parser,const FrameHeader & header,SliceBuffer & slice_buffer)194 absl::Status ServerFragmentFrame::Deserialize(HPackParser* parser,
195 const FrameHeader& header,
196 SliceBuffer& slice_buffer) {
197 if (header.stream_id == 0) {
198 return absl::InvalidArgumentError("Expected non-zero stream id");
199 }
200 stream_id = header.stream_id;
201 if (header.type != FrameType::kFragment) {
202 return absl::InvalidArgumentError("Expected fragment frame");
203 }
204 FrameDeserializer deserializer(header, slice_buffer);
205 if (header.flags.is_set(0)) {
206 auto r = ReadMetadata<ServerMetadata>(parser, deserializer.ReceiveHeaders(),
207 header.stream_id, true, false);
208 if (!r.ok()) return r.status();
209 }
210 if (header.flags.is_set(1)) {
211 auto r = ReadMetadata<ServerMetadata>(
212 parser, deserializer.ReceiveTrailers(), header.stream_id, false, false);
213 }
214 return deserializer.Finish();
215 }
216
Serialize(HPackCompressor * encoder) const217 SliceBuffer ServerFragmentFrame::Serialize(HPackCompressor* encoder) const {
218 GPR_ASSERT(stream_id != 0);
219 FrameSerializer serializer(FrameType::kFragment, stream_id);
220 if (headers.get() != nullptr) {
221 encoder->EncodeRawHeaders(*headers.get(), serializer.AddHeaders());
222 }
223 if (trailers.get() != nullptr) {
224 encoder->EncodeRawHeaders(*trailers.get(), serializer.AddTrailers());
225 }
226 return serializer.Finish();
227 }
228
Deserialize(HPackParser *,const FrameHeader & header,SliceBuffer & slice_buffer)229 absl::Status CancelFrame::Deserialize(HPackParser*, const FrameHeader& header,
230 SliceBuffer& slice_buffer) {
231 if (header.type != FrameType::kCancel) {
232 return absl::InvalidArgumentError("Expected cancel frame");
233 }
234 if (header.flags.any()) {
235 return absl::InvalidArgumentError("Unexpected flags");
236 }
237 if (header.stream_id == 0) {
238 return absl::InvalidArgumentError("Expected non-zero stream id");
239 }
240 FrameDeserializer deserializer(header, slice_buffer);
241 stream_id = header.stream_id;
242 return deserializer.Finish();
243 }
244
Serialize(HPackCompressor *) const245 SliceBuffer CancelFrame::Serialize(HPackCompressor*) const {
246 GPR_ASSERT(stream_id != 0);
247 FrameSerializer serializer(FrameType::kCancel, stream_id);
248 return serializer.Finish();
249 }
250
251 } // namespace chaotic_good
252 } // namespace grpc_core
253