[chaotic-good] Reland basic frame serialization (#31631)
* Revert "Revert "[chaotic-good] Basic frame serialization/deserialization (#31257)" (#31630)"
This reverts commit e8ac147311
.
* fix build
pull/31635/head
parent
250f9fc026
commit
571e98f6d5
57 changed files with 2849 additions and 350 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,307 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chaotic_good/frame.h" |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <limits> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include "absl/status/status.h" |
||||||
|
#include "absl/status/statusor.h" |
||||||
|
|
||||||
|
#include <grpc/slice.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/lib/gprpp/bitset.h" |
||||||
|
#include "src/core/lib/gprpp/no_destruct.h" |
||||||
|
#include "src/core/lib/gprpp/status_helper.h" |
||||||
|
#include "src/core/lib/promise/context.h" |
||||||
|
#include "src/core/lib/slice/slice.h" |
||||||
|
#include "src/core/lib/slice/slice_buffer.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace chaotic_good { |
||||||
|
|
||||||
|
namespace { |
||||||
|
const NoDestruct<Slice> kZeroSlice{[] { |
||||||
|
auto slice = GRPC_SLICE_MALLOC(64); |
||||||
|
memset(GRPC_SLICE_START_PTR(slice), 0, 64); |
||||||
|
return slice; |
||||||
|
}()}; |
||||||
|
|
||||||
|
class FrameSerializer { |
||||||
|
public: |
||||||
|
explicit FrameSerializer(FrameType type, uint32_t stream_id) |
||||||
|
: header_{type, {}, stream_id, 0, 0, 0} { |
||||||
|
output_.AppendIndexed(kZeroSlice->Copy()); |
||||||
|
} |
||||||
|
// If called, must be called before AddMessage, AddTrailers, Finish
|
||||||
|
SliceBuffer& AddHeaders() { |
||||||
|
GPR_ASSERT(last_added_ == nullptr); |
||||||
|
header_.flags.set(0); |
||||||
|
return Start(&header_.header_length); |
||||||
|
} |
||||||
|
// If called, must be called before AddTrailers, Finish
|
||||||
|
SliceBuffer& AddMessage() { |
||||||
|
MaybeCommitLast(); |
||||||
|
header_.flags.set(1); |
||||||
|
return Start(&header_.message_length); |
||||||
|
} |
||||||
|
// If called, must be called before Finish
|
||||||
|
SliceBuffer& AddTrailers() { |
||||||
|
MaybeCommitLast(); |
||||||
|
header_.flags.set(2); |
||||||
|
return Start(&header_.trailer_length); |
||||||
|
} |
||||||
|
|
||||||
|
SliceBuffer Finish() { |
||||||
|
MaybeCommitLast(); |
||||||
|
header_.Serialize( |
||||||
|
GRPC_SLICE_START_PTR(output_.c_slice_buffer()->slices[0])); |
||||||
|
return std::move(output_); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
SliceBuffer& Start(uint32_t* length_field) { |
||||||
|
last_added_ = length_field; |
||||||
|
length_at_last_added_ = output_.Length(); |
||||||
|
return output_; |
||||||
|
} |
||||||
|
|
||||||
|
void MaybeCommitLast() { |
||||||
|
if (last_added_ == nullptr) return; |
||||||
|
*last_added_ = output_.Length() - length_at_last_added_; |
||||||
|
if (output_.Length() % 64 != 0) { |
||||||
|
output_.Append(kZeroSlice->RefSubSlice(0, 64 - output_.Length() % 64)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FrameHeader header_; |
||||||
|
|
||||||
|
uint32_t* last_added_ = nullptr; |
||||||
|
size_t length_at_last_added_; |
||||||
|
SliceBuffer output_; |
||||||
|
}; |
||||||
|
|
||||||
|
class FrameDeserializer { |
||||||
|
public: |
||||||
|
FrameDeserializer(const FrameHeader& header, SliceBuffer& input) |
||||||
|
: header_(header), input_(input) {} |
||||||
|
const FrameHeader& header() const { return header_; } |
||||||
|
// If called, must be called before ReceiveMessage, ReceiveTrailers
|
||||||
|
absl::StatusOr<SliceBuffer> ReceiveHeaders() { |
||||||
|
return Take(header_.header_length); |
||||||
|
} |
||||||
|
// If called, must be called before ReceiveTrailers
|
||||||
|
absl::StatusOr<SliceBuffer> ReceiveMessage() { |
||||||
|
return Take(header_.message_length); |
||||||
|
} |
||||||
|
// If called, must be called before Finish
|
||||||
|
absl::StatusOr<SliceBuffer> ReceiveTrailers() { |
||||||
|
return Take(header_.trailer_length); |
||||||
|
} |
||||||
|
|
||||||
|
absl::Status Finish() { return absl::OkStatus(); } |
||||||
|
|
||||||
|
private: |
||||||
|
absl::StatusOr<SliceBuffer> Take(uint32_t length) { |
||||||
|
if (length == 0) return SliceBuffer{}; |
||||||
|
if (input_.Length() < length) { |
||||||
|
return absl::InvalidArgumentError( |
||||||
|
"Frame too short (insufficient payload)"); |
||||||
|
} |
||||||
|
SliceBuffer out; |
||||||
|
input_.MoveFirstNBytesIntoSliceBuffer(length, out); |
||||||
|
if (length % 64 != 0) { |
||||||
|
const uint32_t padding_length = 64 - length % 64; |
||||||
|
if (input_.Length() < padding_length) { |
||||||
|
return absl::InvalidArgumentError( |
||||||
|
"Frame too short (insufficient padding)"); |
||||||
|
} |
||||||
|
uint8_t padding[64]; |
||||||
|
input_.MoveFirstNBytesIntoBuffer(padding_length, padding); |
||||||
|
for (uint32_t i = 0; i < padding_length; i++) { |
||||||
|
if (padding[i] != 0) { |
||||||
|
return absl::InvalidArgumentError("Frame padding not zero"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return std::move(out); |
||||||
|
} |
||||||
|
FrameHeader header_; |
||||||
|
SliceBuffer& input_; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Metadata> |
||||||
|
absl::StatusOr<Arena::PoolPtr<Metadata>> ReadMetadata( |
||||||
|
HPackParser* parser, absl::StatusOr<SliceBuffer> maybe_slices, |
||||||
|
uint32_t stream_id, bool is_header, bool is_client) { |
||||||
|
if (!maybe_slices.ok()) return maybe_slices.status(); |
||||||
|
auto& slices = *maybe_slices; |
||||||
|
Arena::PoolPtr<Metadata> metadata; |
||||||
|
parser->BeginFrame( |
||||||
|
metadata.get(), std::numeric_limits<uint32_t>::max(), |
||||||
|
is_header ? HPackParser::Boundary::EndOfHeaders |
||||||
|
: HPackParser::Boundary::EndOfStream, |
||||||
|
HPackParser::Priority::None, |
||||||
|
HPackParser::LogInfo{stream_id, |
||||||
|
is_header ? HPackParser::LogInfo::Type::kHeaders |
||||||
|
: HPackParser::LogInfo::Type::kTrailers, |
||||||
|
is_client}); |
||||||
|
for (size_t i = 0; i < slices.Count(); i++) { |
||||||
|
GRPC_RETURN_IF_ERROR( |
||||||
|
parser->Parse(slices.c_slice_at(i), i == slices.Count() - 1)); |
||||||
|
} |
||||||
|
parser->FinishFrame(); |
||||||
|
return std::move(metadata); |
||||||
|
} |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
absl::Status SettingsFrame::Deserialize(HPackParser*, const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) { |
||||||
|
if (header.type != FrameType::kSettings) { |
||||||
|
return absl::InvalidArgumentError("Expected settings frame"); |
||||||
|
} |
||||||
|
if (header.flags.any()) { |
||||||
|
return absl::InvalidArgumentError("Unexpected flags"); |
||||||
|
} |
||||||
|
FrameDeserializer deserializer(header, slice_buffer); |
||||||
|
return deserializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
SliceBuffer SettingsFrame::Serialize(HPackCompressor*) const { |
||||||
|
FrameSerializer serializer(FrameType::kSettings, 0); |
||||||
|
return serializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
absl::Status ClientFragmentFrame::Deserialize(HPackParser* parser, |
||||||
|
const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) { |
||||||
|
if (header.stream_id == 0) { |
||||||
|
return absl::InvalidArgumentError("Expected non-zero stream id"); |
||||||
|
} |
||||||
|
stream_id = header.stream_id; |
||||||
|
if (header.type != FrameType::kFragment) { |
||||||
|
return absl::InvalidArgumentError("Expected fragment frame"); |
||||||
|
} |
||||||
|
FrameDeserializer deserializer(header, slice_buffer); |
||||||
|
if (header.flags.is_set(0)) { |
||||||
|
auto r = ReadMetadata<ClientMetadata>(parser, deserializer.ReceiveHeaders(), |
||||||
|
header.stream_id, true, true); |
||||||
|
if (!r.ok()) return r.status(); |
||||||
|
} |
||||||
|
if (header.flags.is_set(1)) { |
||||||
|
message = GetContext<Arena>()->MakePooled<Message>(); |
||||||
|
auto r = deserializer.ReceiveMessage(); |
||||||
|
if (!r.ok()) return r.status(); |
||||||
|
r->Swap(message->payload()); |
||||||
|
} |
||||||
|
if (header.flags.is_set(2)) { |
||||||
|
if (header.trailer_length != 0) { |
||||||
|
return absl::InvalidArgumentError("Unexpected trailer length"); |
||||||
|
} |
||||||
|
end_of_stream = true; |
||||||
|
} else { |
||||||
|
end_of_stream = false; |
||||||
|
} |
||||||
|
return deserializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
SliceBuffer ClientFragmentFrame::Serialize(HPackCompressor* encoder) const { |
||||||
|
GPR_ASSERT(stream_id != 0); |
||||||
|
FrameSerializer serializer(FrameType::kFragment, stream_id); |
||||||
|
if (headers.get() != nullptr) { |
||||||
|
encoder->EncodeRawHeaders(*headers.get(), serializer.AddHeaders()); |
||||||
|
} |
||||||
|
if (message.get() != nullptr) { |
||||||
|
serializer.AddMessage().Append(*message->payload()); |
||||||
|
} |
||||||
|
if (end_of_stream) { |
||||||
|
serializer.AddTrailers(); |
||||||
|
} |
||||||
|
return serializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
absl::Status ServerFragmentFrame::Deserialize(HPackParser* parser, |
||||||
|
const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) { |
||||||
|
if (header.stream_id == 0) { |
||||||
|
return absl::InvalidArgumentError("Expected non-zero stream id"); |
||||||
|
} |
||||||
|
stream_id = header.stream_id; |
||||||
|
if (header.type != FrameType::kFragment) { |
||||||
|
return absl::InvalidArgumentError("Expected fragment frame"); |
||||||
|
} |
||||||
|
FrameDeserializer deserializer(header, slice_buffer); |
||||||
|
if (header.flags.is_set(0)) { |
||||||
|
auto r = ReadMetadata<ServerMetadata>(parser, deserializer.ReceiveHeaders(), |
||||||
|
header.stream_id, true, false); |
||||||
|
if (!r.ok()) return r.status(); |
||||||
|
} |
||||||
|
if (header.flags.is_set(1)) { |
||||||
|
message = GetContext<Arena>()->MakePooled<Message>(); |
||||||
|
auto r = deserializer.ReceiveMessage(); |
||||||
|
if (!r.ok()) return r.status(); |
||||||
|
r->Swap(message->payload()); |
||||||
|
} |
||||||
|
if (header.flags.is_set(2)) { |
||||||
|
auto r = ReadMetadata<ServerMetadata>( |
||||||
|
parser, deserializer.ReceiveTrailers(), header.stream_id, false, false); |
||||||
|
} |
||||||
|
return deserializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
SliceBuffer ServerFragmentFrame::Serialize(HPackCompressor* encoder) const { |
||||||
|
GPR_ASSERT(stream_id != 0); |
||||||
|
FrameSerializer serializer(FrameType::kFragment, stream_id); |
||||||
|
if (headers.get() != nullptr) { |
||||||
|
encoder->EncodeRawHeaders(*headers.get(), serializer.AddHeaders()); |
||||||
|
} |
||||||
|
if (message.get() != nullptr) { |
||||||
|
serializer.AddMessage().Append(*message->payload()); |
||||||
|
} |
||||||
|
if (trailers.get() != nullptr) { |
||||||
|
encoder->EncodeRawHeaders(*trailers.get(), serializer.AddTrailers()); |
||||||
|
} |
||||||
|
return serializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
absl::Status CancelFrame::Deserialize(HPackParser*, const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) { |
||||||
|
if (header.type != FrameType::kCancel) { |
||||||
|
return absl::InvalidArgumentError("Expected cancel frame"); |
||||||
|
} |
||||||
|
if (header.flags.any()) { |
||||||
|
return absl::InvalidArgumentError("Unexpected flags"); |
||||||
|
} |
||||||
|
if (header.stream_id == 0) { |
||||||
|
return absl::InvalidArgumentError("Expected non-zero stream id"); |
||||||
|
} |
||||||
|
FrameDeserializer deserializer(header, slice_buffer); |
||||||
|
stream_id = header.stream_id; |
||||||
|
return deserializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
SliceBuffer CancelFrame::Serialize(HPackCompressor*) const { |
||||||
|
GPR_ASSERT(stream_id != 0); |
||||||
|
FrameSerializer serializer(FrameType::kCancel, stream_id); |
||||||
|
return serializer.Finish(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace chaotic_good
|
||||||
|
} // namespace grpc_core
|
@ -0,0 +1,122 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef GRPC_CORE_EXT_TRANSPORT_CHAOTIC_GOOD_FRAME_H |
||||||
|
#define GRPC_CORE_EXT_TRANSPORT_CHAOTIC_GOOD_FRAME_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "absl/status/status.h" |
||||||
|
#include "absl/types/variant.h" |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chaotic_good/frame_header.h" |
||||||
|
#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" |
||||||
|
#include "src/core/ext/transport/chttp2/transport/hpack_parser.h" |
||||||
|
#include "src/core/lib/resource_quota/arena.h" |
||||||
|
#include "src/core/lib/slice/slice_buffer.h" |
||||||
|
#include "src/core/lib/transport/metadata_batch.h" |
||||||
|
#include "src/core/lib/transport/transport.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace chaotic_good { |
||||||
|
|
||||||
|
class FrameInterface { |
||||||
|
public: |
||||||
|
virtual absl::Status Deserialize(HPackParser* parser, |
||||||
|
const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) = 0; |
||||||
|
virtual SliceBuffer Serialize(HPackCompressor* encoder) const = 0; |
||||||
|
|
||||||
|
protected: |
||||||
|
static bool EqVal(const Message& a, const Message& b) { |
||||||
|
return a.payload()->JoinIntoString() == b.payload()->JoinIntoString() && |
||||||
|
a.flags() == b.flags(); |
||||||
|
} |
||||||
|
static bool EqVal(const grpc_metadata_batch& a, |
||||||
|
const grpc_metadata_batch& b) { |
||||||
|
return a.DebugString() == b.DebugString(); |
||||||
|
} |
||||||
|
template <typename T> |
||||||
|
static bool EqHdl(const Arena::PoolPtr<T>& a, const Arena::PoolPtr<T>& b) { |
||||||
|
if (a == nullptr && b == nullptr) return true; |
||||||
|
if (a == nullptr || b == nullptr) return false; |
||||||
|
return EqVal(*a, *b); |
||||||
|
} |
||||||
|
~FrameInterface() = default; |
||||||
|
}; |
||||||
|
|
||||||
|
struct SettingsFrame final : public FrameInterface { |
||||||
|
absl::Status Deserialize(HPackParser* parser, const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) override; |
||||||
|
SliceBuffer Serialize(HPackCompressor* encoder) const override; |
||||||
|
|
||||||
|
bool operator==(const SettingsFrame&) const { return true; } |
||||||
|
}; |
||||||
|
|
||||||
|
struct ClientFragmentFrame final : public FrameInterface { |
||||||
|
absl::Status Deserialize(HPackParser* parser, const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) override; |
||||||
|
SliceBuffer Serialize(HPackCompressor* encoder) const override; |
||||||
|
|
||||||
|
uint32_t stream_id; |
||||||
|
ClientMetadataHandle headers; |
||||||
|
MessageHandle message; |
||||||
|
bool end_of_stream = false; |
||||||
|
|
||||||
|
bool operator==(const ClientFragmentFrame& other) const { |
||||||
|
return stream_id == other.stream_id && EqHdl(headers, other.headers) && |
||||||
|
EqHdl(message, other.message) && |
||||||
|
end_of_stream == other.end_of_stream; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct ServerFragmentFrame final : public FrameInterface { |
||||||
|
absl::Status Deserialize(HPackParser* parser, const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) override; |
||||||
|
SliceBuffer Serialize(HPackCompressor* encoder) const override; |
||||||
|
|
||||||
|
uint32_t stream_id; |
||||||
|
ServerMetadataHandle headers; |
||||||
|
MessageHandle message; |
||||||
|
ServerMetadataHandle trailers; |
||||||
|
|
||||||
|
bool operator==(const ServerFragmentFrame& other) const { |
||||||
|
return stream_id == other.stream_id && EqHdl(headers, other.headers) && |
||||||
|
EqHdl(message, other.message) && EqHdl(trailers, other.trailers); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct CancelFrame final : public FrameInterface { |
||||||
|
absl::Status Deserialize(HPackParser* parser, const FrameHeader& header, |
||||||
|
SliceBuffer& slice_buffer) override; |
||||||
|
SliceBuffer Serialize(HPackCompressor* encoder) const override; |
||||||
|
|
||||||
|
uint32_t stream_id; |
||||||
|
|
||||||
|
bool operator==(const CancelFrame& other) const { |
||||||
|
return stream_id == other.stream_id; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
using ClientFrame = absl::variant<ClientFragmentFrame, CancelFrame>; |
||||||
|
using ServerFrame = absl::variant<ServerFragmentFrame>; |
||||||
|
|
||||||
|
} // namespace chaotic_good
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_CORE_EXT_TRANSPORT_CHAOTIC_GOOD_FRAME_H
|
@ -0,0 +1,87 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chaotic_good/frame_header.h" |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
#include "absl/status/status.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace chaotic_good { |
||||||
|
|
||||||
|
namespace { |
||||||
|
void WriteLittleEndianUint32(uint32_t value, uint8_t* data) { |
||||||
|
data[0] = static_cast<uint8_t>(value); |
||||||
|
data[1] = static_cast<uint8_t>(value >> 8); |
||||||
|
data[2] = static_cast<uint8_t>(value >> 16); |
||||||
|
data[3] = static_cast<uint8_t>(value >> 24); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t ReadLittleEndianUint32(const uint8_t* data) { |
||||||
|
return static_cast<uint32_t>(data[0]) | |
||||||
|
(static_cast<uint32_t>(data[1]) << 8) | |
||||||
|
(static_cast<uint32_t>(data[2]) << 16) | |
||||||
|
(static_cast<uint32_t>(data[3]) << 24); |
||||||
|
} |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void FrameHeader::Serialize(uint8_t* data) const { |
||||||
|
WriteLittleEndianUint32( |
||||||
|
static_cast<uint32_t>(type) | (flags.ToInt<uint32_t>() << 8), data); |
||||||
|
WriteLittleEndianUint32(stream_id, data + 4); |
||||||
|
WriteLittleEndianUint32(header_length, data + 8); |
||||||
|
WriteLittleEndianUint32(message_length, data + 12); |
||||||
|
WriteLittleEndianUint32(trailer_length, data + 16); |
||||||
|
memset(data + 20, 0, 44); |
||||||
|
} |
||||||
|
|
||||||
|
absl::StatusOr<FrameHeader> FrameHeader::Parse(const uint8_t* data) { |
||||||
|
FrameHeader header; |
||||||
|
const uint32_t type_and_flags = ReadLittleEndianUint32(data); |
||||||
|
header.type = static_cast<FrameType>(type_and_flags & 0xff); |
||||||
|
const uint32_t flags = type_and_flags >> 8; |
||||||
|
if (flags > 7) return absl::InvalidArgumentError("Invalid flags"); |
||||||
|
header.flags = BitSet<3>::FromInt(flags); |
||||||
|
header.stream_id = ReadLittleEndianUint32(data + 4); |
||||||
|
header.header_length = ReadLittleEndianUint32(data + 8); |
||||||
|
header.message_length = ReadLittleEndianUint32(data + 12); |
||||||
|
header.trailer_length = ReadLittleEndianUint32(data + 16); |
||||||
|
for (int i = 0; i < 44; i++) { |
||||||
|
if (data[20 + i] != 0) return absl::InvalidArgumentError("Invalid padding"); |
||||||
|
} |
||||||
|
return header; |
||||||
|
} |
||||||
|
|
||||||
|
namespace { |
||||||
|
uint64_t RoundUp(uint64_t x) { |
||||||
|
if (x % 64 == 0) return x; |
||||||
|
return x + 64 - (x % 64); |
||||||
|
} |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
FrameSizes FrameHeader::ComputeFrameSizes() const { |
||||||
|
FrameSizes sizes; |
||||||
|
sizes.message_offset = RoundUp(header_length); |
||||||
|
sizes.trailer_offset = sizes.message_offset + RoundUp(message_length); |
||||||
|
sizes.frame_length = sizes.trailer_offset + RoundUp(trailer_length); |
||||||
|
return sizes; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace chaotic_good
|
||||||
|
} // namespace grpc_core
|
@ -0,0 +1,73 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef GRPC_CORE_EXT_TRANSPORT_CHAOTIC_GOOD_FRAME_HEADER_H |
||||||
|
#define GRPC_CORE_EXT_TRANSPORT_CHAOTIC_GOOD_FRAME_HEADER_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
#include "absl/status/statusor.h" |
||||||
|
|
||||||
|
#include "src/core/lib/gprpp/bitset.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace chaotic_good { |
||||||
|
|
||||||
|
enum class FrameType : uint8_t { |
||||||
|
kSettings = 0x00, |
||||||
|
kFragment = 0x80, |
||||||
|
kCancel = 0x81, |
||||||
|
}; |
||||||
|
|
||||||
|
struct FrameSizes { |
||||||
|
uint64_t message_offset; |
||||||
|
uint64_t trailer_offset; |
||||||
|
uint64_t frame_length; |
||||||
|
|
||||||
|
bool operator==(const FrameSizes& other) const { |
||||||
|
return message_offset == other.message_offset && |
||||||
|
trailer_offset == other.trailer_offset && |
||||||
|
frame_length == other.frame_length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct FrameHeader { |
||||||
|
FrameType type; |
||||||
|
BitSet<3> flags; |
||||||
|
uint32_t stream_id; |
||||||
|
uint32_t header_length; |
||||||
|
uint32_t message_length; |
||||||
|
uint32_t trailer_length; |
||||||
|
|
||||||
|
// Parses a frame header from a buffer of 64 bytes. All 64 bytes are consumed.
|
||||||
|
static absl::StatusOr<FrameHeader> Parse(const uint8_t* data); |
||||||
|
// Serializes a frame header into a buffer of 64 bytes.
|
||||||
|
void Serialize(uint8_t* data) const; |
||||||
|
// Compute frame sizes from the header.
|
||||||
|
FrameSizes ComputeFrameSizes() const; |
||||||
|
|
||||||
|
bool operator==(const FrameHeader& h) const { |
||||||
|
return type == h.type && flags == h.flags && stream_id == h.stream_id && |
||||||
|
header_length == h.header_length && |
||||||
|
message_length == h.message_length && |
||||||
|
trailer_length == h.trailer_length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace chaotic_good
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif // GRPC_CORE_EXT_TRANSPORT_CHAOTIC_GOOD_FRAME_HEADER_H
|
@ -0,0 +1,19 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chttp2/transport/http_trace.h" |
||||||
|
|
||||||
|
grpc_core::TraceFlag grpc_http_trace(false, "http"); |
@ -0,0 +1,24 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP_TRACE_H |
||||||
|
#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP_TRACE_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/debug/trace.h" |
||||||
|
|
||||||
|
extern grpc_core::TraceFlag grpc_http_trace; |
||||||
|
|
||||||
|
#endif // GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP_TRACE_H
|
@ -0,0 +1,78 @@ |
|||||||
|
# Copyright 2021 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package") |
||||||
|
load("//test/core/util:grpc_fuzzer.bzl", "grpc_fuzzer") |
||||||
|
|
||||||
|
licenses(["notice"]) |
||||||
|
|
||||||
|
grpc_package( |
||||||
|
name = "test/core/transport/chaotic_good", |
||||||
|
visibility = "tests", |
||||||
|
) |
||||||
|
|
||||||
|
grpc_cc_test( |
||||||
|
name = "frame_header_test", |
||||||
|
srcs = ["frame_header_test.cc"], |
||||||
|
external_deps = [ |
||||||
|
"absl/status", |
||||||
|
"gtest", |
||||||
|
], |
||||||
|
deps = ["//src/core:chaotic_good_frame_header"], |
||||||
|
) |
||||||
|
|
||||||
|
grpc_fuzzer( |
||||||
|
name = "frame_header_fuzzer", |
||||||
|
srcs = ["frame_header_fuzzer.cc"], |
||||||
|
corpus = "frame_header_fuzzer_corpus", |
||||||
|
external_deps = ["absl/status:statusor"], |
||||||
|
language = "C++", |
||||||
|
tags = ["no_windows"], |
||||||
|
deps = ["//src/core:chaotic_good_frame_header"], |
||||||
|
) |
||||||
|
|
||||||
|
grpc_cc_test( |
||||||
|
name = "frame_test", |
||||||
|
srcs = ["frame_test.cc"], |
||||||
|
external_deps = [ |
||||||
|
"absl/status", |
||||||
|
"absl/status:statusor", |
||||||
|
"gtest", |
||||||
|
], |
||||||
|
deps = ["//src/core:chaotic_good_frame"], |
||||||
|
) |
||||||
|
|
||||||
|
grpc_fuzzer( |
||||||
|
name = "frame_fuzzer", |
||||||
|
srcs = ["frame_fuzzer.cc"], |
||||||
|
corpus = "frame_fuzzer_corpus", |
||||||
|
external_deps = ["absl/status:statusor"], |
||||||
|
language = "C++", |
||||||
|
tags = ["no_windows"], |
||||||
|
deps = [ |
||||||
|
"//:gpr", |
||||||
|
"//:hpack_encoder", |
||||||
|
"//:hpack_parser", |
||||||
|
"//:ref_counted_ptr", |
||||||
|
"//src/core:arena", |
||||||
|
"//src/core:chaotic_good_frame", |
||||||
|
"//src/core:chaotic_good_frame_header", |
||||||
|
"//src/core:event_engine_memory_allocator", |
||||||
|
"//src/core:memory_quota", |
||||||
|
"//src/core:resource_quota", |
||||||
|
"//src/core:slice", |
||||||
|
"//src/core:slice_buffer", |
||||||
|
"//test/core/promise:test_context", |
||||||
|
], |
||||||
|
) |
@ -0,0 +1,112 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include "absl/status/statusor.h" |
||||||
|
|
||||||
|
#include <grpc/event_engine/memory_allocator.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chaotic_good/frame.h" |
||||||
|
#include "src/core/ext/transport/chaotic_good/frame_header.h" |
||||||
|
#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" |
||||||
|
#include "src/core/ext/transport/chttp2/transport/hpack_parser.h" |
||||||
|
#include "src/core/lib/gprpp/ref_counted_ptr.h" |
||||||
|
#include "src/core/lib/resource_quota/arena.h" |
||||||
|
#include "src/core/lib/resource_quota/memory_quota.h" |
||||||
|
#include "src/core/lib/resource_quota/resource_quota.h" |
||||||
|
#include "src/core/lib/slice/slice.h" |
||||||
|
#include "src/core/lib/slice/slice_buffer.h" |
||||||
|
#include "test/core/promise/test_context.h" |
||||||
|
|
||||||
|
bool squelch = false; |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace chaotic_good { |
||||||
|
|
||||||
|
static auto* g_memory_allocator = new MemoryAllocator( |
||||||
|
ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator("test")); |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
void AssertRoundTrips(const T& input, FrameType expected_frame_type) { |
||||||
|
HPackCompressor hpack_compressor; |
||||||
|
auto serialized = input.Serialize(&hpack_compressor); |
||||||
|
GPR_ASSERT(serialized.Length() >= 64); |
||||||
|
GPR_ASSERT(serialized.Length() % 64 == 0); |
||||||
|
uint8_t header_bytes[64]; |
||||||
|
serialized.MoveFirstNBytesIntoBuffer(64, header_bytes); |
||||||
|
auto header = FrameHeader::Parse(header_bytes); |
||||||
|
GPR_ASSERT(header.ok()); |
||||||
|
GPR_ASSERT(header->type == expected_frame_type); |
||||||
|
T output; |
||||||
|
HPackParser hpack_parser; |
||||||
|
auto deser = output.Deserialize(&hpack_parser, header.value(), serialized); |
||||||
|
GPR_ASSERT(deser.ok()); |
||||||
|
GPR_ASSERT(output == input); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
void FinishParseAndChecks(const FrameHeader& header, const uint8_t* data, |
||||||
|
size_t size) { |
||||||
|
T parsed; |
||||||
|
HPackParser hpack_parser; |
||||||
|
SliceBuffer serialized; |
||||||
|
serialized.Append(Slice::FromCopiedBuffer(data, size)); |
||||||
|
auto deser = parsed.Deserialize(&hpack_parser, header, serialized); |
||||||
|
if (!deser.ok()) return; |
||||||
|
AssertRoundTrips(parsed, header.type); |
||||||
|
} |
||||||
|
|
||||||
|
int Run(const uint8_t* data, size_t size) { |
||||||
|
if (size < 1) return 0; |
||||||
|
const bool is_server = (data[0] & 1) != 0; |
||||||
|
size--; |
||||||
|
data++; |
||||||
|
if (size < 64) return 0; |
||||||
|
auto r = FrameHeader::Parse(data); |
||||||
|
if (!r.ok()) return 0; |
||||||
|
size -= 64; |
||||||
|
data += 64; |
||||||
|
auto arena = MakeScopedArena(1024, g_memory_allocator); |
||||||
|
TestContext<Arena> ctx(arena.get()); |
||||||
|
switch (r->type) { |
||||||
|
default: |
||||||
|
return 0; // We don't know how to parse this frame type.
|
||||||
|
case FrameType::kSettings: |
||||||
|
FinishParseAndChecks<SettingsFrame>(*r, data, size); |
||||||
|
break; |
||||||
|
case FrameType::kFragment: |
||||||
|
if (is_server) { |
||||||
|
FinishParseAndChecks<ServerFragmentFrame>(*r, data, size); |
||||||
|
} else { |
||||||
|
FinishParseAndChecks<ClientFragmentFrame>(*r, data, size); |
||||||
|
} |
||||||
|
break; |
||||||
|
case FrameType::kCancel: |
||||||
|
FinishParseAndChecks<CancelFrame>(*r, data, size); |
||||||
|
break; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace chaotic_good
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
||||||
|
return grpc_core::chaotic_good::Run(data, size); |
||||||
|
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,34 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#include "absl/status/statusor.h" |
||||||
|
|
||||||
|
#include "src/core/ext/transport/chaotic_good/frame_header.h" |
||||||
|
|
||||||
|
bool squelch = false; |
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
||||||
|
if (size != 64) return 0; |
||||||
|
auto r = grpc_core::chaotic_good::FrameHeader::Parse(data); |
||||||
|
if (!r.ok()) return 0; |
||||||
|
uint8_t reserialized[64]; |
||||||
|
r->Serialize(reserialized); |
||||||
|
// If it parses, we insist that the bytes reserialize to the same thing.
|
||||||
|
if (memcmp(data, reserialized, 64) != 0) abort(); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/core/ext/transport/chaotic_good/frame_header.h" |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <cstdint> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "absl/status/status.h" |
||||||
|
#include "gtest/gtest.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace chaotic_good { |
||||||
|
namespace { |
||||||
|
|
||||||
|
std::vector<uint8_t> Serialize(FrameHeader h) { |
||||||
|
uint8_t buffer[64]; |
||||||
|
h.Serialize(buffer); |
||||||
|
return std::vector<uint8_t>(buffer, buffer + 64); |
||||||
|
} |
||||||
|
|
||||||
|
absl::StatusOr<FrameHeader> Deserialize(std::vector<uint8_t> data) { |
||||||
|
if (data.size() != 64) return absl::InvalidArgumentError("bad length"); |
||||||
|
return FrameHeader::Parse(data.data()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FrameHeaderTest, SimpleSerialize) { |
||||||
|
EXPECT_EQ( |
||||||
|
Serialize(FrameHeader{FrameType::kCancel, BitSet<3>::FromInt(0), |
||||||
|
0x01020304, 0x05060708, 0x090a0b0c, 0x0d0e0f10}), |
||||||
|
std::vector<uint8_t>({0x81, 0, 0, 0, // type, flags
|
||||||
|
0x04, 0x03, 0x02, 0x01, // stream_id
|
||||||
|
0x08, 0x07, 0x06, 0x05, // header_length
|
||||||
|
0x0c, 0x0b, 0x0a, 0x09, // message_length
|
||||||
|
0x10, 0x0f, 0x0e, 0x0d, // trailer_length
|
||||||
|
// padding
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FrameHeaderTest, SimpleDeserialize) { |
||||||
|
EXPECT_EQ( |
||||||
|
Deserialize(std::vector<uint8_t>( |
||||||
|
{0x81, 0, 0, 0, // type, flags
|
||||||
|
0x04, 0x03, 0x02, 0x01, // stream_id
|
||||||
|
0x08, 0x07, 0x06, 0x05, // header_length
|
||||||
|
0x0c, 0x0b, 0x0a, 0x09, // message_length
|
||||||
|
0x10, 0x0f, 0x0e, 0x0d, // trailer_length
|
||||||
|
// padding
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})), |
||||||
|
absl::StatusOr<FrameHeader>( |
||||||
|
FrameHeader{FrameType::kCancel, BitSet<3>::FromInt(0), 0x01020304, |
||||||
|
0x05060708, 0x090a0b0c, 0x0d0e0f10})); |
||||||
|
EXPECT_EQ(Deserialize(std::vector<uint8_t>( |
||||||
|
{0x81, 88, 88, 88, // type, flags
|
||||||
|
0x04, 0x03, 0x02, 0x01, // stream_id
|
||||||
|
0x08, 0x07, 0x06, 0x05, // header_length
|
||||||
|
0x0c, 0x0b, 0x0a, 0x09, // message_length
|
||||||
|
0x10, 0x0f, 0x0e, 0x0d, // trailer_length
|
||||||
|
// garbage padding
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0})) |
||||||
|
.status(), |
||||||
|
absl::InvalidArgumentError("Invalid flags")); |
||||||
|
EXPECT_EQ(Deserialize(std::vector<uint8_t>( |
||||||
|
{0x81, 0, 0, 0, // type, flags
|
||||||
|
0x04, 0x03, 0x02, 0x01, // stream_id
|
||||||
|
0x08, 0x07, 0x06, 0x05, // header_length
|
||||||
|
0x0c, 0x0b, 0x0a, 0x09, // message_length
|
||||||
|
0x10, 0x0f, 0x0e, 0x0d, // trailer_length
|
||||||
|
// garbage padding
|
||||||
|
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0})) |
||||||
|
.status(), |
||||||
|
absl::InvalidArgumentError("Invalid padding")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FrameHeaderTest, ComputeFrameSizes) { |
||||||
|
EXPECT_EQ( |
||||||
|
(FrameHeader{FrameType::kFragment, BitSet<3>::FromInt(7), 1, 0, 0, 0}) |
||||||
|
.ComputeFrameSizes(), |
||||||
|
(FrameSizes{0, 0, 0})); |
||||||
|
EXPECT_EQ( |
||||||
|
(FrameHeader{FrameType::kFragment, BitSet<3>::FromInt(7), 1, 14, 0, 0}) |
||||||
|
.ComputeFrameSizes(), |
||||||
|
(FrameSizes{64, 64, 64})); |
||||||
|
EXPECT_EQ( |
||||||
|
(FrameHeader{FrameType::kFragment, BitSet<3>::FromInt(7), 1, 0, 14, 0}) |
||||||
|
.ComputeFrameSizes(), |
||||||
|
(FrameSizes{0, 64, 64})); |
||||||
|
EXPECT_EQ( |
||||||
|
(FrameHeader{FrameType::kFragment, BitSet<3>::FromInt(7), 1, 0, 0, 14}) |
||||||
|
.ComputeFrameSizes(), |
||||||
|
(FrameSizes{0, 0, 64})); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace chaotic_good
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
// Copyright 2022 gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "src/core/ext/transport/chaotic_good/frame.h" |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
#include "absl/status/status.h" |
||||||
|
#include "absl/status/statusor.h" |
||||||
|
#include "gtest/gtest.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
namespace chaotic_good { |
||||||
|
namespace { |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
void AssertRoundTrips(const T input, FrameType expected_frame_type) { |
||||||
|
HPackCompressor hpack_compressor; |
||||||
|
auto serialized = input.Serialize(&hpack_compressor); |
||||||
|
EXPECT_GE(serialized.Length(), 64); |
||||||
|
EXPECT_EQ(serialized.Length() % 64, 0); |
||||||
|
uint8_t header_bytes[64]; |
||||||
|
serialized.MoveFirstNBytesIntoBuffer(64, header_bytes); |
||||||
|
auto header = FrameHeader::Parse(header_bytes); |
||||||
|
EXPECT_TRUE(header.ok()) << header.status(); |
||||||
|
EXPECT_EQ(header->type, expected_frame_type); |
||||||
|
T output; |
||||||
|
HPackParser hpack_parser; |
||||||
|
auto deser = output.Deserialize(&hpack_parser, header.value(), serialized); |
||||||
|
EXPECT_TRUE(deser.ok()) << deser; |
||||||
|
EXPECT_EQ(output, input); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(FrameTest, SettingsFrameRoundTrips) { |
||||||
|
AssertRoundTrips(SettingsFrame{}, FrameType::kSettings); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace chaotic_good
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
int r = RUN_ALL_TESTS(); |
||||||
|
return r; |
||||||
|
} |
Loading…
Reference in new issue