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