1 #include "quiche/binary_http/binary_http_message.h"
2
3 #include <cstdint>
4 #include <functional>
5 #include <iterator>
6 #include <memory>
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "absl/container/flat_hash_map.h"
12 #include "absl/status/status.h"
13 #include "absl/status/statusor.h"
14 #include "absl/strings/ascii.h"
15 #include "absl/strings/str_cat.h"
16 #include "absl/strings/str_join.h"
17 #include "absl/strings/string_view.h"
18 #include "quiche/common/quiche_callbacks.h"
19 #include "quiche/common/quiche_data_reader.h"
20 #include "quiche/common/quiche_data_writer.h"
21
22 namespace quiche {
23 namespace {
24
25 constexpr uint8_t kKnownLengthRequestFraming = 0;
26 constexpr uint8_t kKnownLengthResponseFraming = 1;
27
ReadStringValue(quiche::QuicheDataReader & reader,std::string & data)28 bool ReadStringValue(quiche::QuicheDataReader& reader, std::string& data) {
29 absl::string_view data_view;
30 if (!reader.ReadStringPieceVarInt62(&data_view)) {
31 return false;
32 }
33 data = std::string(data_view);
34 return true;
35 }
36
IsValidPadding(absl::string_view data)37 bool IsValidPadding(absl::string_view data) {
38 return std::all_of(data.begin(), data.end(),
39 [](char c) { return c == '\0'; });
40 }
41
DecodeControlData(quiche::QuicheDataReader & reader)42 absl::StatusOr<BinaryHttpRequest::ControlData> DecodeControlData(
43 quiche::QuicheDataReader& reader) {
44 BinaryHttpRequest::ControlData control_data;
45 if (!ReadStringValue(reader, control_data.method)) {
46 return absl::InvalidArgumentError("Failed to read method.");
47 }
48 if (!ReadStringValue(reader, control_data.scheme)) {
49 return absl::InvalidArgumentError("Failed to read scheme.");
50 }
51 if (!ReadStringValue(reader, control_data.authority)) {
52 return absl::InvalidArgumentError("Failed to read authority.");
53 }
54 if (!ReadStringValue(reader, control_data.path)) {
55 return absl::InvalidArgumentError("Failed to read path.");
56 }
57 return control_data;
58 }
59
DecodeFields(quiche::QuicheDataReader & reader,quiche::UnretainedCallback<void (absl::string_view name,absl::string_view value)> callback)60 absl::Status DecodeFields(quiche::QuicheDataReader& reader,
61 quiche::UnretainedCallback<void(
62 absl::string_view name, absl::string_view value)>
63 callback) {
64 absl::string_view fields;
65 if (!reader.ReadStringPieceVarInt62(&fields)) {
66 return absl::InvalidArgumentError("Failed to read fields.");
67 }
68 quiche::QuicheDataReader fields_reader(fields);
69 while (!fields_reader.IsDoneReading()) {
70 absl::string_view name;
71 if (!fields_reader.ReadStringPieceVarInt62(&name)) {
72 return absl::InvalidArgumentError("Failed to read field name.");
73 }
74 absl::string_view value;
75 if (!fields_reader.ReadStringPieceVarInt62(&value)) {
76 return absl::InvalidArgumentError("Failed to read field value.");
77 }
78 callback(name, value);
79 }
80 return absl::OkStatus();
81 }
82
DecodeFieldsAndBody(quiche::QuicheDataReader & reader,BinaryHttpMessage & message)83 absl::Status DecodeFieldsAndBody(quiche::QuicheDataReader& reader,
84 BinaryHttpMessage& message) {
85 if (const absl::Status status = DecodeFields(
86 reader,
87 [&message](absl::string_view name, absl::string_view value) {
88 message.AddHeaderField({std::string(name), std::string(value)});
89 });
90 !status.ok()) {
91 return status;
92 }
93 // Exit early if message has been truncated.
94 // https://www.rfc-editor.org/rfc/rfc9292#section-3.8
95 if (reader.IsDoneReading()) {
96 return absl::OkStatus();
97 }
98
99 absl::string_view body;
100 if (!reader.ReadStringPieceVarInt62(&body)) {
101 return absl::InvalidArgumentError("Failed to read body.");
102 }
103 message.set_body(std::string(body));
104 // TODO(bschneider): Check for / read-in any trailer-fields
105 return absl::OkStatus();
106 }
107
DecodeKnownLengthRequest(quiche::QuicheDataReader & reader)108 absl::StatusOr<BinaryHttpRequest> DecodeKnownLengthRequest(
109 quiche::QuicheDataReader& reader) {
110 const auto control_data = DecodeControlData(reader);
111 if (!control_data.ok()) {
112 return control_data.status();
113 }
114 BinaryHttpRequest request(std::move(*control_data));
115 if (const absl::Status status = DecodeFieldsAndBody(reader, request);
116 !status.ok()) {
117 return status;
118 }
119 if (!IsValidPadding(reader.PeekRemainingPayload())) {
120 return absl::InvalidArgumentError("Non-zero padding.");
121 }
122 request.set_num_padding_bytes(reader.BytesRemaining());
123 return request;
124 }
125
DecodeKnownLengthResponse(quiche::QuicheDataReader & reader)126 absl::StatusOr<BinaryHttpResponse> DecodeKnownLengthResponse(
127 quiche::QuicheDataReader& reader) {
128 std::vector<std::pair<uint16_t, std::vector<BinaryHttpMessage::Field>>>
129 informational_responses;
130 uint64_t status_code;
131 bool reading_response_control_data = true;
132 while (reading_response_control_data) {
133 if (!reader.ReadVarInt62(&status_code)) {
134 return absl::InvalidArgumentError("Failed to read status code.");
135 }
136 if (status_code >= 100 && status_code <= 199) {
137 std::vector<BinaryHttpMessage::Field> fields;
138 if (const absl::Status status = DecodeFields(
139 reader,
140 [&fields](absl::string_view name, absl::string_view value) {
141 fields.push_back({std::string(name), std::string(value)});
142 });
143 !status.ok()) {
144 return status;
145 }
146 informational_responses.emplace_back(status_code, std::move(fields));
147 } else {
148 reading_response_control_data = false;
149 }
150 }
151 BinaryHttpResponse response(status_code);
152 for (const auto& informational_response : informational_responses) {
153 if (const absl::Status status = response.AddInformationalResponse(
154 informational_response.first,
155 std::move(informational_response.second));
156 !status.ok()) {
157 return status;
158 }
159 }
160 if (const absl::Status status = DecodeFieldsAndBody(reader, response);
161 !status.ok()) {
162 return status;
163 }
164 if (!IsValidPadding(reader.PeekRemainingPayload())) {
165 return absl::InvalidArgumentError("Non-zero padding.");
166 }
167 response.set_num_padding_bytes(reader.BytesRemaining());
168 return response;
169 }
170
StringPieceVarInt62Len(absl::string_view s)171 uint64_t StringPieceVarInt62Len(absl::string_view s) {
172 return quiche::QuicheDataWriter::GetVarInt62Len(s.length()) + s.length();
173 }
174 } // namespace
175
AddField(BinaryHttpMessage::Field field)176 void BinaryHttpMessage::Fields::AddField(BinaryHttpMessage::Field field) {
177 fields_.push_back(std::move(field));
178 }
179
180 // Encode fields in the order they were initially inserted.
181 // Updates do not change order.
Encode(quiche::QuicheDataWriter & writer) const182 absl::Status BinaryHttpMessage::Fields::Encode(
183 quiche::QuicheDataWriter& writer) const {
184 if (!writer.WriteVarInt62(EncodedFieldsSize())) {
185 return absl::InvalidArgumentError("Failed to write encoded field size.");
186 }
187 for (const BinaryHttpMessage::Field& field : fields_) {
188 if (!writer.WriteStringPieceVarInt62(field.name)) {
189 return absl::InvalidArgumentError("Failed to write field name.");
190 }
191 if (!writer.WriteStringPieceVarInt62(field.value)) {
192 return absl::InvalidArgumentError("Failed to write field value.");
193 }
194 }
195 return absl::OkStatus();
196 }
197
EncodedSize() const198 size_t BinaryHttpMessage::Fields::EncodedSize() const {
199 const size_t size = EncodedFieldsSize();
200 return size + quiche::QuicheDataWriter::GetVarInt62Len(size);
201 }
202
EncodedFieldsSize() const203 size_t BinaryHttpMessage::Fields::EncodedFieldsSize() const {
204 size_t size = 0;
205 for (const BinaryHttpMessage::Field& field : fields_) {
206 size += StringPieceVarInt62Len(field.name) +
207 StringPieceVarInt62Len(field.value);
208 }
209 return size;
210 }
211
AddHeaderField(BinaryHttpMessage::Field field)212 BinaryHttpMessage* BinaryHttpMessage::AddHeaderField(
213 BinaryHttpMessage::Field field) {
214 const std::string lower_name = absl::AsciiStrToLower(field.name);
215 if (lower_name == "host") {
216 has_host_ = true;
217 }
218 header_fields_.AddField({std::move(lower_name), std::move(field.value)});
219 return this;
220 }
221
222 // Appends the encoded fields and body to data.
EncodeKnownLengthFieldsAndBody(quiche::QuicheDataWriter & writer) const223 absl::Status BinaryHttpMessage::EncodeKnownLengthFieldsAndBody(
224 quiche::QuicheDataWriter& writer) const {
225 if (const absl::Status status = header_fields_.Encode(writer); !status.ok()) {
226 return status;
227 }
228 if (!writer.WriteStringPieceVarInt62(body_)) {
229 return absl::InvalidArgumentError("Failed to encode body.");
230 }
231 // TODO(bschneider): Consider support for trailer fields on known-length
232 // requests. Trailers are atypical for a known-length request.
233 return absl::OkStatus();
234 }
235
EncodedKnownLengthFieldsAndBodySize() const236 size_t BinaryHttpMessage::EncodedKnownLengthFieldsAndBodySize() const {
237 return header_fields_.EncodedSize() + StringPieceVarInt62Len(body_);
238 }
239
AddInformationalResponse(uint16_t status_code,std::vector<Field> header_fields)240 absl::Status BinaryHttpResponse::AddInformationalResponse(
241 uint16_t status_code, std::vector<Field> header_fields) {
242 if (status_code < 100) {
243 return absl::InvalidArgumentError("status code < 100");
244 }
245 if (status_code > 199) {
246 return absl::InvalidArgumentError("status code > 199");
247 }
248 InformationalResponse data(status_code);
249 for (Field& header : header_fields) {
250 data.AddField(header.name, std::move(header.value));
251 }
252 informational_response_control_data_.push_back(std::move(data));
253 return absl::OkStatus();
254 }
255
Serialize() const256 absl::StatusOr<std::string> BinaryHttpResponse::Serialize() const {
257 // Only supporting known length requests so far.
258 return EncodeAsKnownLength();
259 }
260
EncodeAsKnownLength() const261 absl::StatusOr<std::string> BinaryHttpResponse::EncodeAsKnownLength() const {
262 std::string data;
263 data.resize(EncodedSize());
264 quiche::QuicheDataWriter writer(data.size(), data.data());
265 if (!writer.WriteUInt8(kKnownLengthResponseFraming)) {
266 return absl::InvalidArgumentError("Failed to write framing indicator");
267 }
268 // Informational response
269 for (const auto& informational : informational_response_control_data_) {
270 if (const absl::Status status = informational.Encode(writer);
271 !status.ok()) {
272 return status;
273 }
274 }
275 if (!writer.WriteVarInt62(status_code_)) {
276 return absl::InvalidArgumentError("Failed to write status code");
277 }
278 if (const absl::Status status = EncodeKnownLengthFieldsAndBody(writer);
279 !status.ok()) {
280 return status;
281 }
282 QUICHE_DCHECK_EQ(writer.remaining(), num_padding_bytes());
283 writer.WritePadding();
284 return data;
285 }
286
EncodedSize() const287 size_t BinaryHttpResponse::EncodedSize() const {
288 size_t size = sizeof(kKnownLengthResponseFraming);
289 for (const auto& informational : informational_response_control_data_) {
290 size += informational.EncodedSize();
291 }
292 return size + quiche::QuicheDataWriter::GetVarInt62Len(status_code_) +
293 EncodedKnownLengthFieldsAndBodySize() + num_padding_bytes();
294 }
295
AddField(absl::string_view name,std::string value)296 void BinaryHttpResponse::InformationalResponse::AddField(absl::string_view name,
297 std::string value) {
298 fields_.AddField({absl::AsciiStrToLower(name), std::move(value)});
299 }
300
301 // Appends the encoded fields and body to data.
Encode(quiche::QuicheDataWriter & writer) const302 absl::Status BinaryHttpResponse::InformationalResponse::Encode(
303 quiche::QuicheDataWriter& writer) const {
304 writer.WriteVarInt62(status_code_);
305 return fields_.Encode(writer);
306 }
307
EncodedSize() const308 size_t BinaryHttpResponse::InformationalResponse::EncodedSize() const {
309 return quiche::QuicheDataWriter::GetVarInt62Len(status_code_) +
310 fields_.EncodedSize();
311 }
312
Serialize() const313 absl::StatusOr<std::string> BinaryHttpRequest::Serialize() const {
314 // Only supporting known length requests so far.
315 return EncodeAsKnownLength();
316 }
317
318 // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-request-control-data
EncodeControlData(quiche::QuicheDataWriter & writer) const319 absl::Status BinaryHttpRequest::EncodeControlData(
320 quiche::QuicheDataWriter& writer) const {
321 if (!writer.WriteStringPieceVarInt62(control_data_.method)) {
322 return absl::InvalidArgumentError("Failed to encode method.");
323 }
324 if (!writer.WriteStringPieceVarInt62(control_data_.scheme)) {
325 return absl::InvalidArgumentError("Failed to encode scheme.");
326 }
327 // the Host header field is not replicated in the :authority field, as is
328 // required for ensuring that the request is reproduced accurately; see
329 // Section 8.1.2.3 of [H2].
330 if (!has_host()) {
331 if (!writer.WriteStringPieceVarInt62(control_data_.authority)) {
332 return absl::InvalidArgumentError("Failed to encode authority.");
333 }
334 } else {
335 if (!writer.WriteStringPieceVarInt62("")) {
336 return absl::InvalidArgumentError("Failed to encode authority.");
337 }
338 }
339 if (!writer.WriteStringPieceVarInt62(control_data_.path)) {
340 return absl::InvalidArgumentError("Failed to encode path.");
341 }
342 return absl::OkStatus();
343 }
344
EncodedControlDataSize() const345 size_t BinaryHttpRequest::EncodedControlDataSize() const {
346 size_t size = StringPieceVarInt62Len(control_data_.method) +
347 StringPieceVarInt62Len(control_data_.scheme) +
348 StringPieceVarInt62Len(control_data_.path);
349 if (!has_host()) {
350 size += StringPieceVarInt62Len(control_data_.authority);
351 } else {
352 size += StringPieceVarInt62Len("");
353 }
354 return size;
355 }
356
EncodedSize() const357 size_t BinaryHttpRequest::EncodedSize() const {
358 return sizeof(kKnownLengthRequestFraming) + EncodedControlDataSize() +
359 EncodedKnownLengthFieldsAndBodySize() + num_padding_bytes();
360 }
361
362 // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-known-length-messages
EncodeAsKnownLength() const363 absl::StatusOr<std::string> BinaryHttpRequest::EncodeAsKnownLength() const {
364 std::string data;
365 data.resize(EncodedSize());
366 quiche::QuicheDataWriter writer(data.size(), data.data());
367 if (!writer.WriteUInt8(kKnownLengthRequestFraming)) {
368 return absl::InvalidArgumentError("Failed to encode framing indicator.");
369 }
370 if (const absl::Status status = EncodeControlData(writer); !status.ok()) {
371 return status;
372 }
373 if (const absl::Status status = EncodeKnownLengthFieldsAndBody(writer);
374 !status.ok()) {
375 return status;
376 }
377 QUICHE_DCHECK_EQ(writer.remaining(), num_padding_bytes());
378 writer.WritePadding();
379 return data;
380 }
381
Create(absl::string_view data)382 absl::StatusOr<BinaryHttpRequest> BinaryHttpRequest::Create(
383 absl::string_view data) {
384 quiche::QuicheDataReader reader(data);
385 uint8_t framing;
386 if (!reader.ReadUInt8(&framing)) {
387 return absl::InvalidArgumentError("Missing framing indicator.");
388 }
389 if (framing == kKnownLengthRequestFraming) {
390 return DecodeKnownLengthRequest(reader);
391 }
392 return absl::UnimplementedError(
393 absl::StrCat("Unsupported framing type ", framing));
394 }
395
Create(absl::string_view data)396 absl::StatusOr<BinaryHttpResponse> BinaryHttpResponse::Create(
397 absl::string_view data) {
398 quiche::QuicheDataReader reader(data);
399 uint8_t framing;
400 if (!reader.ReadUInt8(&framing)) {
401 return absl::InvalidArgumentError("Missing framing indicator.");
402 }
403 if (framing == kKnownLengthResponseFraming) {
404 return DecodeKnownLengthResponse(reader);
405 }
406 return absl::UnimplementedError(
407 absl::StrCat("Unsupported framing type ", framing));
408 }
409
DebugString() const410 std::string BinaryHttpMessage::DebugString() const {
411 std::vector<std::string> headers;
412 for (const auto& field : GetHeaderFields()) {
413 headers.emplace_back(field.DebugString());
414 }
415 return absl::StrCat("BinaryHttpMessage{Headers{", absl::StrJoin(headers, ";"),
416 "}Body{", body(), "}}");
417 }
418
DebugString() const419 std::string BinaryHttpMessage::Field::DebugString() const {
420 return absl::StrCat("Field{", name, "=", value, "}");
421 }
422
DebugString() const423 std::string BinaryHttpResponse::InformationalResponse::DebugString() const {
424 std::vector<std::string> fs;
425 for (const auto& field : fields()) {
426 fs.emplace_back(field.DebugString());
427 }
428 return absl::StrCat("InformationalResponse{", absl::StrJoin(fs, ";"), "}");
429 }
430
DebugString() const431 std::string BinaryHttpResponse::DebugString() const {
432 std::vector<std::string> irs;
433 for (const auto& ir : informational_responses()) {
434 irs.emplace_back(ir.DebugString());
435 }
436 return absl::StrCat("BinaryHttpResponse(", status_code_, "){",
437 BinaryHttpMessage::DebugString(), absl::StrJoin(irs, ";"),
438 "}");
439 }
440
DebugString() const441 std::string BinaryHttpRequest::DebugString() const {
442 return absl::StrCat("BinaryHttpRequest{", BinaryHttpMessage::DebugString(),
443 "}");
444 }
445
PrintTo(const BinaryHttpRequest & msg,std::ostream * os)446 void PrintTo(const BinaryHttpRequest& msg, std::ostream* os) {
447 *os << msg.DebugString();
448 }
449
PrintTo(const BinaryHttpResponse & msg,std::ostream * os)450 void PrintTo(const BinaryHttpResponse& msg, std::ostream* os) {
451 *os << msg.DebugString();
452 }
453
PrintTo(const BinaryHttpMessage::Field & msg,std::ostream * os)454 void PrintTo(const BinaryHttpMessage::Field& msg, std::ostream* os) {
455 *os << msg.DebugString();
456 }
457
458 } // namespace quiche
459