xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/binary_http/binary_http_message.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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