blob: 4224cfc3186200378cba94386ab53d374e20d24a [file] [log] [blame]
#include "quiche/binary_http/binary_http_message.h"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/attributes.h"
#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_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.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 uint64_t kKnownLengthRequestFraming = 0;
constexpr uint64_t kKnownLengthResponseFraming = 1;
constexpr uint64_t kIndeterminateLengthRequestFraming = 2;
constexpr uint64_t kIndeterminateLengthResponseFraming = 3;
constexpr uint64_t kContentTerminator = 0;
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;
}
// Decodes a header/trailer name and value. This takes a length which represents
// only the name length.
absl::StatusOr<BinaryHttpMessage::FieldView> DecodeField(
QuicheDataReader& reader, uint64_t name_length) {
absl::string_view name;
if (!reader.ReadStringPiece(&name, name_length)) {
return absl::OutOfRangeError("Not enough data to read field name.");
}
absl::string_view value;
if (!reader.ReadStringPieceVarInt62(&value)) {
return absl::OutOfRangeError("Not enough data to read field value.");
}
return BinaryHttpMessage::FieldView{name, value};
}
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 (reader.IsDoneReading()) {
// Per RFC 9292, Section 3.8, "Decoders MUST treat missing truncated fields
// as equivalent to having been sent with the length field set to zero."
// If we've run out of payload, stop parsing and return the request.
return request;
}
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.WriteVarInt62(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 = QuicheDataWriter::GetVarInt62Len(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 QuicheDataWriter::GetVarInt62Len(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.WriteVarInt62(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);
uint64_t framing;
if (!reader.ReadVarInt62(&framing)) {
return absl::InvalidArgumentError("Missing framing indicator.");
}
if (framing == kKnownLengthRequestFraming) {
return DecodeKnownLengthRequest(reader);
}
return absl::UnimplementedError(
absl::StrCat("Unsupported framing type ", framing));
}
absl::Status
BinaryHttpRequest::IndeterminateLengthDecoder::DecodeContentTerminatedSection(
QuicheDataReader& reader) {
uint64_t length_or_content_terminator;
do {
if (!reader.ReadVarInt62(&length_or_content_terminator)) {
return absl::OutOfRangeError("Not enough data to read section.");
}
if (length_or_content_terminator != kContentTerminator) {
switch (current_section_) {
case MessageSection::kHeader: {
const absl::StatusOr<FieldView> field =
DecodeField(reader, length_or_content_terminator);
if (!field.ok()) {
return field.status();
}
const absl::Status section_status =
message_section_handler_.OnHeader(field->name, field->value);
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat("Failed to handle header: ",
section_status.message()));
}
break;
}
case MessageSection::kBody: {
absl::string_view body_chunk;
if (!reader.ReadStringPiece(&body_chunk,
length_or_content_terminator)) {
return absl::OutOfRangeError("Failed to read body chunk.");
}
const absl::Status section_status =
message_section_handler_.OnBodyChunk(body_chunk);
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle body chunk: ", section_status.message()));
}
break;
}
case MessageSection::kTrailer: {
const absl::StatusOr<FieldView> field =
DecodeField(reader, length_or_content_terminator);
if (!field.ok()) {
return field.status();
}
const absl::Status section_status =
message_section_handler_.OnTrailer(field->name, field->value);
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle trailer: ", section_status.message()));
}
break;
}
default:
return absl::InternalError(
"Unexpected section in DecodeContentTerminatedSection.");
}
}
// Either a section was successfully decoded or a content terminator was
// encountered, save the checkpoint.
SaveCheckpoint(reader);
} while (length_or_content_terminator != kContentTerminator);
return absl::OkStatus();
}
// Returns Ok status only if the decoding processes the Padding section
// successfully or if the message is truncated properly. All other points of
// return are errors.
absl::Status
BinaryHttpRequest::IndeterminateLengthDecoder::DecodeCheckpointData(
bool end_stream) {
QuicheDataReader reader(checkpoint_view_);
switch (current_section_) {
case MessageSection::kEnd:
return absl::InternalError("Decoder is invalid.");
case MessageSection::kControlData: {
uint64_t framing;
if (!reader.ReadVarInt62(&framing)) {
return absl::OutOfRangeError("Failed to read framing.");
}
if (framing != kIndeterminateLengthRequestFraming) {
return absl::InvalidArgumentError(
absl::StrFormat("Unsupported framing type: 0x%02x", framing));
}
const absl::StatusOr<BinaryHttpRequest::ControlData> control_data =
DecodeControlData(reader);
// Only fails if there is not enough data to read the entire control data.
if (!control_data.ok()) {
return absl::OutOfRangeError("Failed to read control data.");
}
const absl::Status section_status =
message_section_handler_.OnControlData(control_data.value());
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle control data: ", section_status.message()));
}
SaveCheckpoint(reader);
current_section_ = MessageSection::kHeader;
}
ABSL_FALLTHROUGH_INTENDED;
case MessageSection::kHeader: {
const absl::Status status = DecodeContentTerminatedSection(reader);
if (!status.ok()) {
return status;
}
const absl::Status section_status =
message_section_handler_.OnHeadersDone();
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle headers done: ", section_status.message()));
}
current_section_ = MessageSection::kBody;
}
ABSL_FALLTHROUGH_INTENDED;
case MessageSection::kBody: {
if (!reader.IsDoneReading()) {
maybe_truncated_ = false;
}
// Body and trailers truncation is valid only if:
// 1. There is no data to read after the headers section.
// 2. This is signaled as the last piece of data (end_stream).
if (maybe_truncated_ && end_stream) {
absl::Status section_status =
message_section_handler_.OnBodyChunksDone();
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle body chunks done: ", section_status.message()));
}
section_status = message_section_handler_.OnTrailersDone();
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle trailers done: ", section_status.message()));
}
return absl::OkStatus();
}
absl::Status section_status = DecodeContentTerminatedSection(reader);
if (!section_status.ok()) {
return section_status;
}
section_status = message_section_handler_.OnBodyChunksDone();
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle body chunks done: ", section_status.message()));
}
current_section_ = MessageSection::kTrailer;
// Reset the truncation flag before entering the trailers section.
maybe_truncated_ = true;
}
ABSL_FALLTHROUGH_INTENDED;
case MessageSection::kTrailer: {
if (!reader.IsDoneReading()) {
maybe_truncated_ = false;
}
// Trailers truncation is valid only if:
// 1. There is no data to read after the body section.
// 2. This is signaled as the last piece of data (end_stream).
if (maybe_truncated_ && end_stream) {
const absl::Status section_status =
message_section_handler_.OnTrailersDone();
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle trailers done: ", section_status.message()));
}
return absl::OkStatus();
}
absl::Status section_status = DecodeContentTerminatedSection(reader);
if (!section_status.ok()) {
return section_status;
}
section_status = message_section_handler_.OnTrailersDone();
if (!section_status.ok()) {
return absl::InternalError(absl::StrCat(
"Failed to handle trailers done: ", section_status.message()));
}
current_section_ = MessageSection::kPadding;
}
ABSL_FALLTHROUGH_INTENDED;
case MessageSection::kPadding: {
if (!IsValidPadding(reader.PeekRemainingPayload())) {
return absl::InvalidArgumentError("Non-zero padding.");
}
return absl::OkStatus();
}
}
}
void BinaryHttpRequest::IndeterminateLengthDecoder::InitializeCheckpoint(
absl::string_view data) {
checkpoint_view_ = data;
// Prepend buffered data if present. This is the data from a previous call to
// Decode that could not finish because it needed this new data.
if (!buffer_.empty()) {
absl::StrAppend(&buffer_, data);
checkpoint_view_ = buffer_;
}
}
absl::Status BinaryHttpRequest::IndeterminateLengthDecoder::Decode(
absl::string_view data, bool end_stream) {
if (current_section_ == MessageSection::kEnd) {
return absl::InternalError("Decoder is invalid.");
}
InitializeCheckpoint(data);
absl::Status status = DecodeCheckpointData(end_stream);
if (end_stream) {
current_section_ = MessageSection::kEnd;
buffer_.clear();
// Out of range errors shall be treated as invalid argument errors when the
// stream is ending.
if (absl::IsOutOfRange(status)) {
return absl::InvalidArgumentError(status.message());
}
return status;
}
if (absl::IsOutOfRange(status)) {
BufferCheckpoint();
return absl::OkStatus();
}
if (!status.ok()) {
current_section_ = MessageSection::kEnd;
}
buffer_.clear();
return status;
}
absl::StatusOr<BinaryHttpResponse> BinaryHttpResponse::Create(
absl::string_view data) {
quiche::QuicheDataReader reader(data);
uint64_t framing;
if (!reader.ReadVarInt62(&framing)) {
return absl::InvalidArgumentError("Missing framing indicator.");
}
if (framing == kKnownLengthResponseFraming) {
return DecodeKnownLengthResponse(reader);
}
return absl::UnimplementedError(
absl::StrCat("Unsupported framing type ", framing));
}
absl::StatusOr<std::string>
BinaryHttpResponse::IndeterminateLengthEncoder::EncodeFieldSection(
std::optional<uint16_t> status_code, absl::Span<FieldView> fields) {
uint64_t total_length = 0;
if (!framing_indicator_encoded_) {
total_length += quiche::QuicheDataWriter::GetVarInt62Len(
kIndeterminateLengthResponseFraming);
}
if (status_code.has_value()) {
total_length += QuicheDataWriter::GetVarInt62Len(*status_code);
}
for (const auto& field : fields) {
uint8_t length = QuicheDataWriter::GetVarInt62Len(field.name.size());
if (length == 0) {
return absl::InvalidArgumentError("Field name exceeds maximum length.");
}
total_length += length + field.name.size();
length = QuicheDataWriter::GetVarInt62Len(field.value.size());
if (length == 0) {
return absl::InvalidArgumentError("Field value exceeds maximum length.");
}
total_length += length + field.value.size();
}
total_length += quiche::QuicheDataWriter::GetVarInt62Len(kContentTerminator);
std::string data(total_length, '\0');
QuicheDataWriter writer(total_length, data.data());
if (!framing_indicator_encoded_) {
if (!writer.WriteVarInt62(kIndeterminateLengthResponseFraming)) {
return absl::InternalError("Failed to write framing indicator.");
}
framing_indicator_encoded_ = true;
}
if (status_code.has_value() && !writer.WriteVarInt62(*status_code)) {
return absl::InternalError("Failed to write status code.");
}
for (const auto& field : fields) {
if (!writer.WriteStringPieceVarInt62(absl::AsciiStrToLower(field.name))) {
return absl::InternalError("Failed to write field name.");
}
if (!writer.WriteStringPieceVarInt62(field.value)) {
return absl::InternalError("Failed to write field value.");
}
}
if (!writer.WriteVarInt62(kContentTerminator)) {
return absl::InternalError("Failed to write content terminator.");
}
if (writer.remaining() != 0) {
return absl::InternalError("Failed to write all data.");
}
return data;
}
std::string
BinaryHttpResponse::IndeterminateLengthEncoder::GetMessageSectionString(
MessageSection section) const {
switch (section) {
case MessageSection::kInformationalResponseOrHeader:
return "InformationalResponseOrHeader";
case MessageSection::kBody:
return "Body";
case MessageSection::kTrailer:
return "Trailer";
case MessageSection::kEnd:
return "End";
default:
return "Unknown";
}
}
absl::StatusOr<std::string>
BinaryHttpResponse::IndeterminateLengthEncoder::EncodeInformationalResponse(
uint16_t status_code, absl::Span<FieldView> fields) {
if (current_section_ != MessageSection::kInformationalResponseOrHeader) {
current_section_ = MessageSection::kEnd;
return absl::InvalidArgumentError(absl::StrCat(
"EncodeInformationalResponse called in incorrect section: ",
GetMessageSectionString(current_section_)));
}
if (status_code < 100 || status_code > 199) {
current_section_ = MessageSection::kEnd;
return absl::InvalidArgumentError(absl::StrCat(
"Invalid informational response status code: ", status_code));
}
absl::StatusOr<std::string> data = EncodeFieldSection(status_code, fields);
if (!data.ok()) {
current_section_ = MessageSection::kEnd;
}
return data;
}
absl::StatusOr<std::string>
BinaryHttpResponse::IndeterminateLengthEncoder::EncodeHeaders(
uint16_t status_code, absl::Span<FieldView> headers) {
if (current_section_ != MessageSection::kInformationalResponseOrHeader) {
current_section_ = MessageSection::kEnd;
return absl::InvalidArgumentError(
absl::StrCat("EncodeHeaders called in incorrect section: ",
GetMessageSectionString(current_section_)));
}
if (status_code < 200 || status_code > 599) {
current_section_ = MessageSection::kEnd;
return absl::InvalidArgumentError(
absl::StrCat("Invalid response status code: ", status_code));
}
absl::StatusOr<std::string> data = EncodeFieldSection(status_code, headers);
if (!data.ok()) {
current_section_ = MessageSection::kEnd;
return data;
}
current_section_ = MessageSection::kBody;
return data;
}
absl::StatusOr<std::string>
BinaryHttpResponse::IndeterminateLengthEncoder::EncodeBodyChunks(
absl::Span<absl::string_view> body_chunks, bool body_chunks_done) {
if (current_section_ != MessageSection::kBody) {
current_section_ = MessageSection::kEnd;
return absl::InvalidArgumentError(
absl::StrCat("EncodeBodyChunks called in incorrect section: ",
GetMessageSectionString(current_section_)));
}
uint64_t total_length = 0;
for (const auto& body_chunk : body_chunks) {
uint8_t body_chunk_var_int_length =
QuicheDataWriter::GetVarInt62Len(body_chunk.size());
if (body_chunk_var_int_length == 0) {
current_section_ = MessageSection::kEnd;
return absl::InvalidArgumentError(
"Body chunk size exceeds maximum length.");
}
total_length += body_chunk_var_int_length + body_chunk.size();
}
if (body_chunks_done) {
total_length +=
quiche::QuicheDataWriter::GetVarInt62Len(kContentTerminator);
}
std::string data(total_length, '\0');
QuicheDataWriter writer(total_length, data.data());
for (const auto& body_chunk : body_chunks) {
if (!writer.WriteStringPieceVarInt62(body_chunk)) {
current_section_ = MessageSection::kEnd;
return absl::InternalError("Failed to write body chunk.");
}
}
if (body_chunks_done) {
if (!writer.WriteVarInt62(kContentTerminator)) {
current_section_ = MessageSection::kEnd;
return absl::InternalError("Failed to write content terminator.");
}
current_section_ = MessageSection::kTrailer;
}
if (writer.remaining() != 0) {
current_section_ = MessageSection::kEnd;
return absl::InternalError("Failed to write all data.");
}
return data;
}
absl::StatusOr<std::string>
BinaryHttpResponse::IndeterminateLengthEncoder::EncodeTrailers(
absl::Span<FieldView> trailers) {
if (current_section_ != MessageSection::kTrailer) {
current_section_ = MessageSection::kEnd;
return absl::InvalidArgumentError(
absl::StrCat("EncodeTrailers called in incorrect section: ",
GetMessageSectionString(current_section_)));
}
absl::StatusOr<std::string> data =
EncodeFieldSection(/*status_code=*/std::nullopt, trailers);
current_section_ = MessageSection::kEnd;
return data;
}
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