| #include "quiche/binary_http/binary_http_message.h" | 
 |  | 
 | #include <cstdint> | 
 | #include <functional> | 
 | #include <iterator> | 
 | #include <memory> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "absl/container/flat_hash_map.h" | 
 | #include "absl/status/status.h" | 
 | #include "absl/status/statusor.h" | 
 | #include "absl/strings/ascii.h" | 
 | #include "absl/strings/str_cat.h" | 
 | #include "absl/strings/str_join.h" | 
 | #include "absl/strings/string_view.h" | 
 | #include "quiche/common/quiche_callbacks.h" | 
 | #include "quiche/common/quiche_data_reader.h" | 
 | #include "quiche/common/quiche_data_writer.h" | 
 |  | 
 | namespace quiche { | 
 | namespace { | 
 |  | 
 | constexpr uint8_t kKnownLengthRequestFraming = 0; | 
 | constexpr uint8_t kKnownLengthResponseFraming = 1; | 
 |  | 
 | bool ReadStringValue(quiche::QuicheDataReader& reader, std::string& data) { | 
 |   absl::string_view data_view; | 
 |   if (!reader.ReadStringPieceVarInt62(&data_view)) { | 
 |     return false; | 
 |   } | 
 |   data = std::string(data_view); | 
 |   return true; | 
 | } | 
 |  | 
 | bool IsValidPadding(absl::string_view data) { | 
 |   return std::all_of(data.begin(), data.end(), | 
 |                      [](char c) { return c == '\0'; }); | 
 | } | 
 |  | 
 | absl::StatusOr<BinaryHttpRequest::ControlData> DecodeControlData( | 
 |     quiche::QuicheDataReader& reader) { | 
 |   BinaryHttpRequest::ControlData control_data; | 
 |   if (!ReadStringValue(reader, control_data.method)) { | 
 |     return absl::InvalidArgumentError("Failed to read method."); | 
 |   } | 
 |   if (!ReadStringValue(reader, control_data.scheme)) { | 
 |     return absl::InvalidArgumentError("Failed to read scheme."); | 
 |   } | 
 |   if (!ReadStringValue(reader, control_data.authority)) { | 
 |     return absl::InvalidArgumentError("Failed to read authority."); | 
 |   } | 
 |   if (!ReadStringValue(reader, control_data.path)) { | 
 |     return absl::InvalidArgumentError("Failed to read path."); | 
 |   } | 
 |   return control_data; | 
 | } | 
 |  | 
 | absl::Status DecodeFields(quiche::QuicheDataReader& reader, | 
 |                           quiche::UnretainedCallback<void( | 
 |                               absl::string_view name, absl::string_view value)> | 
 |                               callback) { | 
 |   absl::string_view fields; | 
 |   if (!reader.ReadStringPieceVarInt62(&fields)) { | 
 |     return absl::InvalidArgumentError("Failed to read fields."); | 
 |   } | 
 |   quiche::QuicheDataReader fields_reader(fields); | 
 |   while (!fields_reader.IsDoneReading()) { | 
 |     absl::string_view name; | 
 |     if (!fields_reader.ReadStringPieceVarInt62(&name)) { | 
 |       return absl::InvalidArgumentError("Failed to read field name."); | 
 |     } | 
 |     absl::string_view value; | 
 |     if (!fields_reader.ReadStringPieceVarInt62(&value)) { | 
 |       return absl::InvalidArgumentError("Failed to read field value."); | 
 |     } | 
 |     callback(name, value); | 
 |   } | 
 |   return absl::OkStatus(); | 
 | } | 
 |  | 
 | absl::Status DecodeFieldsAndBody(quiche::QuicheDataReader& reader, | 
 |                                  BinaryHttpMessage& message) { | 
 |   if (const absl::Status status = DecodeFields( | 
 |           reader, | 
 |           [&message](absl::string_view name, absl::string_view value) { | 
 |             message.AddHeaderField({std::string(name), std::string(value)}); | 
 |           }); | 
 |       !status.ok()) { | 
 |     return status; | 
 |   } | 
 |   // Exit early if message has been truncated. | 
 |   // https://www.rfc-editor.org/rfc/rfc9292#section-3.8 | 
 |   if (reader.IsDoneReading()) { | 
 |     return absl::OkStatus(); | 
 |   } | 
 |  | 
 |   absl::string_view body; | 
 |   if (!reader.ReadStringPieceVarInt62(&body)) { | 
 |     return absl::InvalidArgumentError("Failed to read body."); | 
 |   } | 
 |   message.set_body(std::string(body)); | 
 |   // TODO(bschneider): Check for / read-in any trailer-fields | 
 |   return absl::OkStatus(); | 
 | } | 
 |  | 
 | absl::StatusOr<BinaryHttpRequest> DecodeKnownLengthRequest( | 
 |     quiche::QuicheDataReader& reader) { | 
 |   const auto control_data = DecodeControlData(reader); | 
 |   if (!control_data.ok()) { | 
 |     return control_data.status(); | 
 |   } | 
 |   BinaryHttpRequest request(std::move(*control_data)); | 
 |   if (const absl::Status status = DecodeFieldsAndBody(reader, request); | 
 |       !status.ok()) { | 
 |     return status; | 
 |   } | 
 |   if (!IsValidPadding(reader.PeekRemainingPayload())) { | 
 |     return absl::InvalidArgumentError("Non-zero padding."); | 
 |   } | 
 |   request.set_num_padding_bytes(reader.BytesRemaining()); | 
 |   return request; | 
 | } | 
 |  | 
 | absl::StatusOr<BinaryHttpResponse> DecodeKnownLengthResponse( | 
 |     quiche::QuicheDataReader& reader) { | 
 |   std::vector<std::pair<uint16_t, std::vector<BinaryHttpMessage::Field>>> | 
 |       informational_responses; | 
 |   uint64_t status_code; | 
 |   bool reading_response_control_data = true; | 
 |   while (reading_response_control_data) { | 
 |     if (!reader.ReadVarInt62(&status_code)) { | 
 |       return absl::InvalidArgumentError("Failed to read status code."); | 
 |     } | 
 |     if (status_code >= 100 && status_code <= 199) { | 
 |       std::vector<BinaryHttpMessage::Field> fields; | 
 |       if (const absl::Status status = DecodeFields( | 
 |               reader, | 
 |               [&fields](absl::string_view name, absl::string_view value) { | 
 |                 fields.push_back({std::string(name), std::string(value)}); | 
 |               }); | 
 |           !status.ok()) { | 
 |         return status; | 
 |       } | 
 |       informational_responses.emplace_back(status_code, std::move(fields)); | 
 |     } else { | 
 |       reading_response_control_data = false; | 
 |     } | 
 |   } | 
 |   BinaryHttpResponse response(status_code); | 
 |   for (const auto& informational_response : informational_responses) { | 
 |     if (const absl::Status status = response.AddInformationalResponse( | 
 |             informational_response.first, | 
 |             std::move(informational_response.second)); | 
 |         !status.ok()) { | 
 |       return status; | 
 |     } | 
 |   } | 
 |   if (const absl::Status status = DecodeFieldsAndBody(reader, response); | 
 |       !status.ok()) { | 
 |     return status; | 
 |   } | 
 |   if (!IsValidPadding(reader.PeekRemainingPayload())) { | 
 |     return absl::InvalidArgumentError("Non-zero padding."); | 
 |   } | 
 |   response.set_num_padding_bytes(reader.BytesRemaining()); | 
 |   return response; | 
 | } | 
 |  | 
 | uint64_t StringPieceVarInt62Len(absl::string_view s) { | 
 |   return quiche::QuicheDataWriter::GetVarInt62Len(s.length()) + s.length(); | 
 | } | 
 | }  // namespace | 
 |  | 
 | void BinaryHttpMessage::Fields::AddField(BinaryHttpMessage::Field field) { | 
 |   fields_.push_back(std::move(field)); | 
 | } | 
 |  | 
 | // Encode fields in the order they were initially inserted. | 
 | // Updates do not change order. | 
 | absl::Status BinaryHttpMessage::Fields::Encode( | 
 |     quiche::QuicheDataWriter& writer) const { | 
 |   if (!writer.WriteVarInt62(EncodedFieldsSize())) { | 
 |     return absl::InvalidArgumentError("Failed to write encoded field size."); | 
 |   } | 
 |   for (const BinaryHttpMessage::Field& field : fields_) { | 
 |     if (!writer.WriteStringPieceVarInt62(field.name)) { | 
 |       return absl::InvalidArgumentError("Failed to write field name."); | 
 |     } | 
 |     if (!writer.WriteStringPieceVarInt62(field.value)) { | 
 |       return absl::InvalidArgumentError("Failed to write field value."); | 
 |     } | 
 |   } | 
 |   return absl::OkStatus(); | 
 | } | 
 |  | 
 | size_t BinaryHttpMessage::Fields::EncodedSize() const { | 
 |   const size_t size = EncodedFieldsSize(); | 
 |   return size + quiche::QuicheDataWriter::GetVarInt62Len(size); | 
 | } | 
 |  | 
 | size_t BinaryHttpMessage::Fields::EncodedFieldsSize() const { | 
 |   size_t size = 0; | 
 |   for (const BinaryHttpMessage::Field& field : fields_) { | 
 |     size += StringPieceVarInt62Len(field.name) + | 
 |             StringPieceVarInt62Len(field.value); | 
 |   } | 
 |   return size; | 
 | } | 
 |  | 
 | BinaryHttpMessage* BinaryHttpMessage::AddHeaderField( | 
 |     BinaryHttpMessage::Field field) { | 
 |   const std::string lower_name = absl::AsciiStrToLower(field.name); | 
 |   if (lower_name == "host") { | 
 |     has_host_ = true; | 
 |   } | 
 |   header_fields_.AddField({std::move(lower_name), std::move(field.value)}); | 
 |   return this; | 
 | } | 
 |  | 
 | // Appends the encoded fields and body to data. | 
 | absl::Status BinaryHttpMessage::EncodeKnownLengthFieldsAndBody( | 
 |     quiche::QuicheDataWriter& writer) const { | 
 |   if (const absl::Status status = header_fields_.Encode(writer); !status.ok()) { | 
 |     return status; | 
 |   } | 
 |   if (!writer.WriteStringPieceVarInt62(body_)) { | 
 |     return absl::InvalidArgumentError("Failed to encode body."); | 
 |   } | 
 |   // TODO(bschneider): Consider support for trailer fields on known-length | 
 |   // requests. Trailers are atypical for a known-length request. | 
 |   return absl::OkStatus(); | 
 | } | 
 |  | 
 | size_t BinaryHttpMessage::EncodedKnownLengthFieldsAndBodySize() const { | 
 |   return header_fields_.EncodedSize() + StringPieceVarInt62Len(body_); | 
 | } | 
 |  | 
 | absl::Status BinaryHttpResponse::AddInformationalResponse( | 
 |     uint16_t status_code, std::vector<Field> header_fields) { | 
 |   if (status_code < 100) { | 
 |     return absl::InvalidArgumentError("status code < 100"); | 
 |   } | 
 |   if (status_code > 199) { | 
 |     return absl::InvalidArgumentError("status code > 199"); | 
 |   } | 
 |   InformationalResponse data(status_code); | 
 |   for (Field& header : header_fields) { | 
 |     data.AddField(header.name, std::move(header.value)); | 
 |   } | 
 |   informational_response_control_data_.push_back(std::move(data)); | 
 |   return absl::OkStatus(); | 
 | } | 
 |  | 
 | absl::StatusOr<std::string> BinaryHttpResponse::Serialize() const { | 
 |   // Only supporting known length requests so far. | 
 |   return EncodeAsKnownLength(); | 
 | } | 
 |  | 
 | absl::StatusOr<std::string> BinaryHttpResponse::EncodeAsKnownLength() const { | 
 |   std::string data; | 
 |   data.resize(EncodedSize()); | 
 |   quiche::QuicheDataWriter writer(data.size(), data.data()); | 
 |   if (!writer.WriteUInt8(kKnownLengthResponseFraming)) { | 
 |     return absl::InvalidArgumentError("Failed to write framing indicator"); | 
 |   } | 
 |   // Informational response | 
 |   for (const auto& informational : informational_response_control_data_) { | 
 |     if (const absl::Status status = informational.Encode(writer); | 
 |         !status.ok()) { | 
 |       return status; | 
 |     } | 
 |   } | 
 |   if (!writer.WriteVarInt62(status_code_)) { | 
 |     return absl::InvalidArgumentError("Failed to write status code"); | 
 |   } | 
 |   if (const absl::Status status = EncodeKnownLengthFieldsAndBody(writer); | 
 |       !status.ok()) { | 
 |     return status; | 
 |   } | 
 |   QUICHE_DCHECK_EQ(writer.remaining(), num_padding_bytes()); | 
 |   writer.WritePadding(); | 
 |   return data; | 
 | } | 
 |  | 
 | size_t BinaryHttpResponse::EncodedSize() const { | 
 |   size_t size = sizeof(kKnownLengthResponseFraming); | 
 |   for (const auto& informational : informational_response_control_data_) { | 
 |     size += informational.EncodedSize(); | 
 |   } | 
 |   return size + quiche::QuicheDataWriter::GetVarInt62Len(status_code_) + | 
 |          EncodedKnownLengthFieldsAndBodySize() + num_padding_bytes(); | 
 | } | 
 |  | 
 | void BinaryHttpResponse::InformationalResponse::AddField(absl::string_view name, | 
 |                                                          std::string value) { | 
 |   fields_.AddField({absl::AsciiStrToLower(name), std::move(value)}); | 
 | } | 
 |  | 
 | // Appends the encoded fields and body to data. | 
 | absl::Status BinaryHttpResponse::InformationalResponse::Encode( | 
 |     quiche::QuicheDataWriter& writer) const { | 
 |   writer.WriteVarInt62(status_code_); | 
 |   return fields_.Encode(writer); | 
 | } | 
 |  | 
 | size_t BinaryHttpResponse::InformationalResponse::EncodedSize() const { | 
 |   return quiche::QuicheDataWriter::GetVarInt62Len(status_code_) + | 
 |          fields_.EncodedSize(); | 
 | } | 
 |  | 
 | absl::StatusOr<std::string> BinaryHttpRequest::Serialize() const { | 
 |   // Only supporting known length requests so far. | 
 |   return EncodeAsKnownLength(); | 
 | } | 
 |  | 
 | // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-request-control-data | 
 | absl::Status BinaryHttpRequest::EncodeControlData( | 
 |     quiche::QuicheDataWriter& writer) const { | 
 |   if (!writer.WriteStringPieceVarInt62(control_data_.method)) { | 
 |     return absl::InvalidArgumentError("Failed to encode method."); | 
 |   } | 
 |   if (!writer.WriteStringPieceVarInt62(control_data_.scheme)) { | 
 |     return absl::InvalidArgumentError("Failed to encode scheme."); | 
 |   } | 
 |   // the Host header field is not replicated in the :authority field, as is | 
 |   // required for ensuring that the request is reproduced accurately; see | 
 |   // Section 8.1.2.3 of [H2]. | 
 |   if (!has_host()) { | 
 |     if (!writer.WriteStringPieceVarInt62(control_data_.authority)) { | 
 |       return absl::InvalidArgumentError("Failed to encode authority."); | 
 |     } | 
 |   } else { | 
 |     if (!writer.WriteStringPieceVarInt62("")) { | 
 |       return absl::InvalidArgumentError("Failed to encode authority."); | 
 |     } | 
 |   } | 
 |   if (!writer.WriteStringPieceVarInt62(control_data_.path)) { | 
 |     return absl::InvalidArgumentError("Failed to encode path."); | 
 |   } | 
 |   return absl::OkStatus(); | 
 | } | 
 |  | 
 | size_t BinaryHttpRequest::EncodedControlDataSize() const { | 
 |   size_t size = StringPieceVarInt62Len(control_data_.method) + | 
 |                 StringPieceVarInt62Len(control_data_.scheme) + | 
 |                 StringPieceVarInt62Len(control_data_.path); | 
 |   if (!has_host()) { | 
 |     size += StringPieceVarInt62Len(control_data_.authority); | 
 |   } else { | 
 |     size += StringPieceVarInt62Len(""); | 
 |   } | 
 |   return size; | 
 | } | 
 |  | 
 | size_t BinaryHttpRequest::EncodedSize() const { | 
 |   return sizeof(kKnownLengthRequestFraming) + EncodedControlDataSize() + | 
 |          EncodedKnownLengthFieldsAndBodySize() + num_padding_bytes(); | 
 | } | 
 |  | 
 | // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-known-length-messages | 
 | absl::StatusOr<std::string> BinaryHttpRequest::EncodeAsKnownLength() const { | 
 |   std::string data; | 
 |   data.resize(EncodedSize()); | 
 |   quiche::QuicheDataWriter writer(data.size(), data.data()); | 
 |   if (!writer.WriteUInt8(kKnownLengthRequestFraming)) { | 
 |     return absl::InvalidArgumentError("Failed to encode framing indicator."); | 
 |   } | 
 |   if (const absl::Status status = EncodeControlData(writer); !status.ok()) { | 
 |     return status; | 
 |   } | 
 |   if (const absl::Status status = EncodeKnownLengthFieldsAndBody(writer); | 
 |       !status.ok()) { | 
 |     return status; | 
 |   } | 
 |   QUICHE_DCHECK_EQ(writer.remaining(), num_padding_bytes()); | 
 |   writer.WritePadding(); | 
 |   return data; | 
 | } | 
 |  | 
 | absl::StatusOr<BinaryHttpRequest> BinaryHttpRequest::Create( | 
 |     absl::string_view data) { | 
 |   quiche::QuicheDataReader reader(data); | 
 |   uint8_t framing; | 
 |   if (!reader.ReadUInt8(&framing)) { | 
 |     return absl::InvalidArgumentError("Missing framing indicator."); | 
 |   } | 
 |   if (framing == kKnownLengthRequestFraming) { | 
 |     return DecodeKnownLengthRequest(reader); | 
 |   } | 
 |   return absl::UnimplementedError( | 
 |       absl::StrCat("Unsupported framing type ", framing)); | 
 | } | 
 |  | 
 | absl::StatusOr<BinaryHttpResponse> BinaryHttpResponse::Create( | 
 |     absl::string_view data) { | 
 |   quiche::QuicheDataReader reader(data); | 
 |   uint8_t framing; | 
 |   if (!reader.ReadUInt8(&framing)) { | 
 |     return absl::InvalidArgumentError("Missing framing indicator."); | 
 |   } | 
 |   if (framing == kKnownLengthResponseFraming) { | 
 |     return DecodeKnownLengthResponse(reader); | 
 |   } | 
 |   return absl::UnimplementedError( | 
 |       absl::StrCat("Unsupported framing type ", framing)); | 
 | } | 
 |  | 
 | std::string BinaryHttpMessage::DebugString() const { | 
 |   std::vector<std::string> headers; | 
 |   for (const auto& field : GetHeaderFields()) { | 
 |     headers.emplace_back(field.DebugString()); | 
 |   } | 
 |   return absl::StrCat("BinaryHttpMessage{Headers{", absl::StrJoin(headers, ";"), | 
 |                       "}Body{", body(), "}}"); | 
 | } | 
 |  | 
 | std::string BinaryHttpMessage::Field::DebugString() const { | 
 |   return absl::StrCat("Field{", name, "=", value, "}"); | 
 | } | 
 |  | 
 | std::string BinaryHttpResponse::InformationalResponse::DebugString() const { | 
 |   std::vector<std::string> fs; | 
 |   for (const auto& field : fields()) { | 
 |     fs.emplace_back(field.DebugString()); | 
 |   } | 
 |   return absl::StrCat("InformationalResponse{", absl::StrJoin(fs, ";"), "}"); | 
 | } | 
 |  | 
 | std::string BinaryHttpResponse::DebugString() const { | 
 |   std::vector<std::string> irs; | 
 |   for (const auto& ir : informational_responses()) { | 
 |     irs.emplace_back(ir.DebugString()); | 
 |   } | 
 |   return absl::StrCat("BinaryHttpResponse(", status_code_, "){", | 
 |                       BinaryHttpMessage::DebugString(), absl::StrJoin(irs, ";"), | 
 |                       "}"); | 
 | } | 
 |  | 
 | std::string BinaryHttpRequest::DebugString() const { | 
 |   return absl::StrCat("BinaryHttpRequest{", BinaryHttpMessage::DebugString(), | 
 |                       "}"); | 
 | } | 
 |  | 
 | void PrintTo(const BinaryHttpRequest& msg, std::ostream* os) { | 
 |   *os << msg.DebugString(); | 
 | } | 
 |  | 
 | void PrintTo(const BinaryHttpResponse& msg, std::ostream* os) { | 
 |   *os << msg.DebugString(); | 
 | } | 
 |  | 
 | void PrintTo(const BinaryHttpMessage::Field& msg, std::ostream* os) { | 
 |   *os << msg.DebugString(); | 
 | } | 
 |  | 
 | }  // namespace quiche |