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