mirror of https://github.com/grpc/grpc.git
[chttp2] New framing layer (#33692)
Building out a new framing layer for chttp2. The central idea here is to have the framing layer be solely responsible for serialization of frames, and their deserialization - the framing layer can reject frames that have invalid syntax - but the enacting of what that frame means is left to a higher layer. This class will become foundational for the promise conversion of chttp2 - by eliminating action from the parsing of frames we can reuse this sensitive code. Right now the new layer is inactive - there's a test that exercises it relatively well, and not much more. In the next PRs I'll add an experiments to enable using this layer or the existing code in the writing and reading paths. --------- Co-authored-by: ctiller <ctiller@users.noreply.github.com>pull/34179/head
parent
de98c1c9ad
commit
12c9748134
38 changed files with 2623 additions and 1355 deletions
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,500 @@ |
||||
// Copyright 2023 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/frame.h" |
||||
|
||||
#include <stddef.h> |
||||
|
||||
#include <cstdint> |
||||
#include <utility> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
#include <grpc/support/log.h> |
||||
|
||||
#include "src/core/lib/gprpp/crash.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace { |
||||
|
||||
constexpr uint8_t kFrameTypeData = 0; |
||||
constexpr uint8_t kFrameTypeHeader = 1; |
||||
constexpr uint8_t kFrameTypeContinuation = 9; |
||||
constexpr uint8_t kFrameTypeRstStream = 3; |
||||
constexpr uint8_t kFrameTypeSettings = 4; |
||||
constexpr uint8_t kFrameTypePing = 6; |
||||
constexpr uint8_t kFrameTypeGoaway = 7; |
||||
constexpr uint8_t kFrameTypeWindowUpdate = 8; |
||||
|
||||
constexpr uint8_t kFlagEndStream = 1; |
||||
constexpr uint8_t kFlagAck = 1; |
||||
constexpr uint8_t kFlagEndHeaders = 4; |
||||
constexpr uint8_t kFlagPadded = 8; |
||||
constexpr uint8_t kFlagPriority = 0x20; |
||||
|
||||
constexpr size_t kFrameHeaderSize = 9; |
||||
|
||||
void Write2b(uint16_t x, uint8_t* output) { |
||||
output[0] = static_cast<uint8_t>(x >> 8); |
||||
output[1] = static_cast<uint8_t>(x); |
||||
} |
||||
|
||||
uint16_t Read2b(const uint8_t* input) { |
||||
return static_cast<uint16_t>(input[0]) << 8 | static_cast<uint16_t>(input[1]); |
||||
} |
||||
|
||||
void Write3b(uint32_t x, uint8_t* output) { |
||||
GPR_ASSERT(x < 16777216); |
||||
output[0] = static_cast<uint8_t>(x >> 16); |
||||
output[1] = static_cast<uint8_t>(x >> 8); |
||||
output[2] = static_cast<uint8_t>(x); |
||||
} |
||||
|
||||
uint32_t Read3b(const uint8_t* input) { |
||||
return static_cast<uint32_t>(input[0]) << 16 | |
||||
static_cast<uint32_t>(input[1]) << 8 | static_cast<uint32_t>(input[2]); |
||||
} |
||||
|
||||
void Write4b(uint32_t x, uint8_t* output) { |
||||
output[0] = static_cast<uint8_t>(x >> 24); |
||||
output[1] = static_cast<uint8_t>(x >> 16); |
||||
output[2] = static_cast<uint8_t>(x >> 8); |
||||
output[3] = static_cast<uint8_t>(x); |
||||
} |
||||
|
||||
uint32_t Read4b(const uint8_t* input) { |
||||
return static_cast<uint32_t>(input[0]) << 24 | |
||||
static_cast<uint32_t>(input[1]) << 16 | |
||||
static_cast<uint32_t>(input[2]) << 8 | static_cast<uint32_t>(input[3]); |
||||
} |
||||
|
||||
void Write8b(uint64_t x, uint8_t* output) { |
||||
output[0] = static_cast<uint8_t>(x >> 56); |
||||
output[1] = static_cast<uint8_t>(x >> 48); |
||||
output[2] = static_cast<uint8_t>(x >> 40); |
||||
output[3] = static_cast<uint8_t>(x >> 32); |
||||
output[4] = static_cast<uint8_t>(x >> 24); |
||||
output[5] = static_cast<uint8_t>(x >> 16); |
||||
output[6] = static_cast<uint8_t>(x >> 8); |
||||
output[7] = static_cast<uint8_t>(x); |
||||
} |
||||
|
||||
uint64_t Read8b(const uint8_t* input) { |
||||
return static_cast<uint64_t>(input[0]) << 56 | |
||||
static_cast<uint64_t>(input[1]) << 48 | |
||||
static_cast<uint64_t>(input[2]) << 40 | |
||||
static_cast<uint64_t>(input[3]) << 32 | |
||||
static_cast<uint64_t>(input[4]) << 24 | |
||||
static_cast<uint64_t>(input[5]) << 16 | |
||||
static_cast<uint64_t>(input[6]) << 8 | static_cast<uint64_t>(input[7]); |
||||
} |
||||
|
||||
uint8_t MaybeFlag(bool condition, uint8_t flag_mask) { |
||||
return condition ? flag_mask : 0; |
||||
} |
||||
|
||||
bool ExtractFlag(uint8_t flags, uint8_t flag_mask) { |
||||
return (flags & flag_mask) != 0; |
||||
} |
||||
|
||||
class SerializeExtraBytesRequired { |
||||
public: |
||||
size_t operator()(const Http2DataFrame&) { return 0; } |
||||
size_t operator()(const Http2HeaderFrame&) { return 0; } |
||||
size_t operator()(const Http2ContinuationFrame&) { return 0; } |
||||
size_t operator()(const Http2RstStreamFrame&) { return 4; } |
||||
size_t operator()(const Http2SettingsFrame& f) { |
||||
return 6 * f.settings.size(); |
||||
} |
||||
size_t operator()(const Http2PingFrame&) { return 8; } |
||||
size_t operator()(const Http2GoawayFrame&) { return 8; } |
||||
size_t operator()(const Http2WindowUpdateFrame&) { return 4; } |
||||
size_t operator()(const Http2UnknownFrame&) { Crash("unreachable"); } |
||||
}; |
||||
|
||||
class SerializeHeaderAndPayload { |
||||
public: |
||||
SerializeHeaderAndPayload(size_t extra_bytes, SliceBuffer& out) |
||||
: out_(out), |
||||
extra_bytes_(MutableSlice::CreateUninitialized(extra_bytes)) {} |
||||
|
||||
void operator()(Http2DataFrame& frame) { |
||||
auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize); |
||||
Http2FrameHeader{ |
||||
static_cast<uint32_t>(frame.payload.Length()), kFrameTypeData, |
||||
MaybeFlag(frame.end_stream, kFlagEndStream), frame.stream_id} |
||||
.Serialize(hdr.begin()); |
||||
out_.AppendIndexed(Slice(std::move(hdr))); |
||||
out_.TakeAndAppend(frame.payload); |
||||
} |
||||
|
||||
void operator()(Http2HeaderFrame& frame) { |
||||
auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize); |
||||
Http2FrameHeader{ |
||||
static_cast<uint32_t>(frame.payload.Length()), kFrameTypeHeader, |
||||
static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders) | |
||||
MaybeFlag(frame.end_stream, kFlagEndStream)), |
||||
frame.stream_id} |
||||
.Serialize(hdr.begin()); |
||||
out_.AppendIndexed(Slice(std::move(hdr))); |
||||
out_.TakeAndAppend(frame.payload); |
||||
} |
||||
|
||||
void operator()(Http2ContinuationFrame& frame) { |
||||
auto hdr = extra_bytes_.TakeFirst(kFrameHeaderSize); |
||||
Http2FrameHeader{ |
||||
static_cast<uint32_t>(frame.payload.Length()), kFrameTypeContinuation, |
||||
static_cast<uint8_t>(MaybeFlag(frame.end_headers, kFlagEndHeaders)), |
||||
frame.stream_id} |
||||
.Serialize(hdr.begin()); |
||||
out_.AppendIndexed(Slice(std::move(hdr))); |
||||
out_.TakeAndAppend(frame.payload); |
||||
} |
||||
|
||||
void operator()(Http2RstStreamFrame& frame) { |
||||
auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4); |
||||
Http2FrameHeader{4, kFrameTypeRstStream, 0, frame.stream_id}.Serialize( |
||||
hdr_and_payload.begin()); |
||||
Write4b(frame.error_code, hdr_and_payload.begin() + kFrameHeaderSize); |
||||
out_.AppendIndexed(Slice(std::move(hdr_and_payload))); |
||||
} |
||||
|
||||
void operator()(Http2SettingsFrame& frame) { |
||||
const size_t payload_size = 6 * frame.settings.size(); |
||||
auto hdr_and_payload = |
||||
extra_bytes_.TakeFirst(kFrameHeaderSize + payload_size); |
||||
Http2FrameHeader{static_cast<uint32_t>(payload_size), kFrameTypeSettings, |
||||
MaybeFlag(frame.ack, kFlagAck), 0} |
||||
.Serialize(hdr_and_payload.begin()); |
||||
size_t offset = kFrameHeaderSize; |
||||
for (auto& setting : frame.settings) { |
||||
Write2b(setting.id, hdr_and_payload.begin() + offset); |
||||
Write4b(setting.value, hdr_and_payload.begin() + offset + 2); |
||||
offset += 6; |
||||
} |
||||
out_.AppendIndexed(Slice(std::move(hdr_and_payload))); |
||||
} |
||||
|
||||
void operator()(Http2PingFrame& frame) { |
||||
auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8); |
||||
Http2FrameHeader{8, kFrameTypePing, MaybeFlag(frame.ack, kFlagAck), 0} |
||||
.Serialize(hdr_and_payload.begin()); |
||||
Write8b(frame.opaque, hdr_and_payload.begin() + kFrameHeaderSize); |
||||
out_.AppendIndexed(Slice(std::move(hdr_and_payload))); |
||||
} |
||||
|
||||
void operator()(Http2GoawayFrame& frame) { |
||||
auto hdr_and_fixed_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 8); |
||||
Http2FrameHeader{static_cast<uint32_t>(8 + frame.debug_data.length()), |
||||
kFrameTypeGoaway, 0, 0} |
||||
.Serialize(hdr_and_fixed_payload.begin()); |
||||
Write4b(frame.last_stream_id, |
||||
hdr_and_fixed_payload.begin() + kFrameHeaderSize); |
||||
Write4b(frame.error_code, |
||||
hdr_and_fixed_payload.begin() + kFrameHeaderSize + 4); |
||||
out_.AppendIndexed(Slice(std::move(hdr_and_fixed_payload))); |
||||
out_.AppendIndexed(std::move(frame.debug_data)); |
||||
} |
||||
|
||||
void operator()(Http2WindowUpdateFrame& frame) { |
||||
auto hdr_and_payload = extra_bytes_.TakeFirst(kFrameHeaderSize + 4); |
||||
Http2FrameHeader{4, kFrameTypeWindowUpdate, 0, frame.stream_id}.Serialize( |
||||
hdr_and_payload.begin()); |
||||
Write4b(frame.increment, hdr_and_payload.begin() + kFrameHeaderSize); |
||||
out_.AppendIndexed(Slice(std::move(hdr_and_payload))); |
||||
} |
||||
|
||||
void operator()(Http2UnknownFrame&) { Crash("unreachable"); } |
||||
|
||||
private: |
||||
SliceBuffer& out_; |
||||
MutableSlice extra_bytes_; |
||||
}; |
||||
|
||||
absl::Status StripPadding(SliceBuffer& payload) { |
||||
if (payload.Length() < 1) { |
||||
return absl::InternalError("padding flag set but no padding byte"); |
||||
} |
||||
uint8_t padding_bytes; |
||||
payload.MoveFirstNBytesIntoBuffer(1, &padding_bytes); |
||||
if (payload.Length() < padding_bytes) { |
||||
return absl::InternalError("padding flag set but not enough padding bytes"); |
||||
} |
||||
payload.RemoveLastNBytes(padding_bytes); |
||||
return absl::OkStatus(); |
||||
} |
||||
|
||||
absl::StatusOr<Http2DataFrame> ParseDataFrame(const Http2FrameHeader& hdr, |
||||
SliceBuffer& payload) { |
||||
if (hdr.stream_id == 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid stream id: ", hdr.ToString())); |
||||
} |
||||
|
||||
if (hdr.flags & kFlagPadded) { |
||||
auto s = StripPadding(payload); |
||||
if (!s.ok()) return s; |
||||
} |
||||
|
||||
return Http2DataFrame{hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndStream), |
||||
std::move(payload)}; |
||||
} |
||||
|
||||
absl::StatusOr<Http2HeaderFrame> ParseHeaderFrame(const Http2FrameHeader& hdr, |
||||
SliceBuffer& payload) { |
||||
if (hdr.stream_id == 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid stream id: ", hdr.ToString())); |
||||
} |
||||
|
||||
if (hdr.flags & kFlagPadded) { |
||||
auto s = StripPadding(payload); |
||||
if (!s.ok()) return s; |
||||
} |
||||
|
||||
if (hdr.flags & kFlagPriority) { |
||||
if (payload.Length() < 5) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid priority payload: ", hdr.ToString())); |
||||
} |
||||
uint8_t trash[5]; |
||||
payload.MoveFirstNBytesIntoBuffer(5, trash); |
||||
} |
||||
|
||||
return Http2HeaderFrame{ |
||||
hdr.stream_id, ExtractFlag(hdr.flags, kFlagEndHeaders), |
||||
ExtractFlag(hdr.flags, kFlagEndStream), std::move(payload)}; |
||||
} |
||||
|
||||
absl::StatusOr<Http2ContinuationFrame> ParseContinuationFrame( |
||||
const Http2FrameHeader& hdr, SliceBuffer& payload) { |
||||
if (hdr.stream_id == 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid stream id: ", hdr.ToString())); |
||||
} |
||||
|
||||
return Http2ContinuationFrame{hdr.stream_id, |
||||
ExtractFlag(hdr.flags, kFlagEndHeaders), |
||||
std::move(payload)}; |
||||
} |
||||
|
||||
absl::StatusOr<Http2RstStreamFrame> ParseRstStreamFrame( |
||||
const Http2FrameHeader& hdr, SliceBuffer& payload) { |
||||
if (payload.Length() != 4) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid rst stream payload: ", hdr.ToString())); |
||||
} |
||||
|
||||
if (hdr.stream_id == 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid stream id: ", hdr.ToString())); |
||||
} |
||||
|
||||
uint8_t buffer[4]; |
||||
payload.CopyToBuffer(buffer); |
||||
|
||||
return Http2RstStreamFrame{hdr.stream_id, Read4b(buffer)}; |
||||
} |
||||
|
||||
absl::StatusOr<Http2SettingsFrame> ParseSettingsFrame( |
||||
const Http2FrameHeader& hdr, SliceBuffer& payload) { |
||||
if (hdr.stream_id != 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid stream id: ", hdr.ToString())); |
||||
} |
||||
if (hdr.flags == kFlagAck) { |
||||
if (payload.Length() != 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid settings ack length: ", hdr.ToString())); |
||||
} |
||||
return Http2SettingsFrame{true, {}}; |
||||
} |
||||
|
||||
if (payload.Length() % 6 != 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid settings payload: ", hdr.ToString(), |
||||
" -- settings must be multiples of 6 bytes long")); |
||||
} |
||||
|
||||
Http2SettingsFrame frame{false, {}}; |
||||
while (payload.Length() != 0) { |
||||
uint8_t buffer[6]; |
||||
payload.MoveFirstNBytesIntoBuffer(6, buffer); |
||||
frame.settings.push_back({ |
||||
Read2b(buffer), |
||||
Read4b(buffer + 2), |
||||
}); |
||||
} |
||||
return std::move(frame); |
||||
} |
||||
|
||||
absl::StatusOr<Http2PingFrame> ParsePingFrame(const Http2FrameHeader& hdr, |
||||
SliceBuffer& payload) { |
||||
if (payload.Length() != 8) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid ping payload: ", hdr.ToString())); |
||||
} |
||||
|
||||
if (hdr.stream_id != 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid ping stream id: ", hdr.ToString())); |
||||
} |
||||
|
||||
bool ack; |
||||
switch (hdr.flags) { |
||||
case 0: |
||||
ack = false; |
||||
break; |
||||
case kFlagAck: |
||||
ack = true; |
||||
break; |
||||
default: |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid ping flags: ", hdr.ToString())); |
||||
} |
||||
|
||||
uint8_t buffer[8]; |
||||
payload.CopyToBuffer(buffer); |
||||
|
||||
return Http2PingFrame{ack, Read8b(buffer)}; |
||||
} |
||||
|
||||
absl::StatusOr<Http2GoawayFrame> ParseGoawayFrame(const Http2FrameHeader& hdr, |
||||
SliceBuffer& payload) { |
||||
if (payload.Length() < 8) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid goaway payload: ", hdr.ToString(), |
||||
" -- must be at least 8 bytes")); |
||||
} |
||||
|
||||
if (hdr.stream_id != 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid goaway stream id: ", hdr.ToString())); |
||||
} |
||||
|
||||
if (hdr.flags != 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid goaway flags: ", hdr.ToString())); |
||||
} |
||||
|
||||
uint8_t buffer[8]; |
||||
payload.MoveFirstNBytesIntoBuffer(8, buffer); |
||||
return Http2GoawayFrame{Read4b(buffer), Read4b(buffer + 4), |
||||
payload.JoinIntoSlice()}; |
||||
} |
||||
|
||||
absl::StatusOr<Http2WindowUpdateFrame> ParseWindowUpdateFrame( |
||||
const Http2FrameHeader& hdr, SliceBuffer& payload) { |
||||
if (payload.Length() != 4) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid window update payload: ", hdr.ToString(), |
||||
" -- must be 4 bytes")); |
||||
} |
||||
|
||||
if (hdr.flags != 0) { |
||||
return absl::InternalError( |
||||
absl::StrCat("invalid window update flags: ", hdr.ToString())); |
||||
} |
||||
|
||||
uint8_t buffer[4]; |
||||
payload.CopyToBuffer(buffer); |
||||
return Http2WindowUpdateFrame{hdr.stream_id, Read4b(buffer)}; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void Http2FrameHeader::Serialize(uint8_t* output) const { |
||||
Write3b(length, output); |
||||
output[3] = type; |
||||
output[4] = flags; |
||||
Write4b(stream_id, output + 5); |
||||
} |
||||
|
||||
Http2FrameHeader Http2FrameHeader::Parse(const uint8_t* input) { |
||||
return Http2FrameHeader{Read3b(input), input[3], input[4], Read4b(input + 5)}; |
||||
} |
||||
|
||||
namespace { |
||||
std::string Http2FrameTypeString(uint8_t frame_type) { |
||||
switch (frame_type) { |
||||
case kFrameTypeData: |
||||
return "DATA"; |
||||
case kFrameTypeHeader: |
||||
return "HEADER"; |
||||
case kFrameTypeContinuation: |
||||
return "CONTINUATION"; |
||||
case kFrameTypeRstStream: |
||||
return "RST_STREAM"; |
||||
case kFrameTypeSettings: |
||||
return "SETTINGS"; |
||||
case kFrameTypeGoaway: |
||||
return "GOAWAY"; |
||||
case kFrameTypeWindowUpdate: |
||||
return "WINDOW_UPDATE"; |
||||
case kFrameTypePing: |
||||
return "PING"; |
||||
} |
||||
return absl::StrCat("UNKNOWN(", frame_type, ")"); |
||||
} |
||||
} // namespace
|
||||
|
||||
std::string Http2FrameHeader::ToString() const { |
||||
return absl::StrCat("{", Http2FrameTypeString(type), ": flags=", flags, |
||||
", stream_id=", stream_id, ", length=", length, "}"); |
||||
} |
||||
|
||||
void Serialize(absl::Span<Http2Frame> frames, SliceBuffer& out) { |
||||
size_t buffer_needed = 0; |
||||
for (auto& frame : frames) { |
||||
// Bytes needed for framing
|
||||
buffer_needed += kFrameHeaderSize; |
||||
// Bytes needed for unserialized payload
|
||||
buffer_needed += absl::visit(SerializeExtraBytesRequired(), frame); |
||||
} |
||||
SerializeHeaderAndPayload serialize(buffer_needed, out); |
||||
for (auto& frame : frames) { |
||||
absl::visit(serialize, frame); |
||||
} |
||||
} |
||||
|
||||
absl::StatusOr<Http2Frame> ParseFramePayload(const Http2FrameHeader& hdr, |
||||
SliceBuffer payload) { |
||||
GPR_ASSERT(payload.Length() == hdr.length); |
||||
switch (hdr.type) { |
||||
case kFrameTypeData: |
||||
return ParseDataFrame(hdr, payload); |
||||
case kFrameTypeHeader: |
||||
return ParseHeaderFrame(hdr, payload); |
||||
case kFrameTypeContinuation: |
||||
return ParseContinuationFrame(hdr, payload); |
||||
case kFrameTypeRstStream: |
||||
return ParseRstStreamFrame(hdr, payload); |
||||
case kFrameTypeSettings: |
||||
return ParseSettingsFrame(hdr, payload); |
||||
case kFrameTypePing: |
||||
return ParsePingFrame(hdr, payload); |
||||
case kFrameTypeGoaway: |
||||
return ParseGoawayFrame(hdr, payload); |
||||
case kFrameTypeWindowUpdate: |
||||
return ParseWindowUpdateFrame(hdr, payload); |
||||
default: |
||||
return Http2UnknownFrame{}; |
||||
} |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,43 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2015 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_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_LEGACY_FRAME_H |
||||
#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_LEGACY_FRAME_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
// defined in internal.h
|
||||
typedef struct grpc_chttp2_stream grpc_chttp2_stream; |
||||
typedef struct grpc_chttp2_transport grpc_chttp2_transport; |
||||
|
||||
#define GRPC_CHTTP2_FRAME_DATA 0 |
||||
#define GRPC_CHTTP2_FRAME_HEADER 1 |
||||
#define GRPC_CHTTP2_FRAME_CONTINUATION 9 |
||||
#define GRPC_CHTTP2_FRAME_RST_STREAM 3 |
||||
#define GRPC_CHTTP2_FRAME_SETTINGS 4 |
||||
#define GRPC_CHTTP2_FRAME_PING 6 |
||||
#define GRPC_CHTTP2_FRAME_GOAWAY 7 |
||||
#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8 |
||||
|
||||
#define GRPC_CHTTP2_DATA_FLAG_END_STREAM 1 |
||||
#define GRPC_CHTTP2_FLAG_ACK 1 |
||||
#define GRPC_CHTTP2_DATA_FLAG_END_HEADERS 4 |
||||
#define GRPC_CHTTP2_DATA_FLAG_PADDED 8 |
||||
#define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20 |
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_LEGACY_FRAME_H
|
@ -0,0 +1,393 @@ |
||||
// Copyright 2023 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/chttp2/transport/frame.h" |
||||
|
||||
#include <algorithm> |
||||
#include <initializer_list> |
||||
#include <utility> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
|
||||
#include "src/core/lib/transport/http2_errors.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace { |
||||
|
||||
MATCHER_P2(StatusIs, code, message, "") { |
||||
return arg.code() == code && arg.message() == message; |
||||
} |
||||
|
||||
void DoTheseThings(std::initializer_list<int>) {} |
||||
|
||||
template <typename... Frames> |
||||
std::vector<uint8_t> Serialize(Frames... f) { |
||||
std::vector<Http2Frame> frames; |
||||
DoTheseThings({(frames.emplace_back(std::move(f)), 1)...}); |
||||
SliceBuffer temp; |
||||
Serialize(absl::Span<Http2Frame>(frames), temp); |
||||
auto slice = temp.JoinIntoSlice(); |
||||
return std::vector<uint8_t>(slice.begin(), slice.end()); |
||||
} |
||||
|
||||
template <typename... I> |
||||
std::vector<uint8_t> ByteVec(I... i) { |
||||
return std::vector<uint8_t>{static_cast<uint8_t>(i)...}; |
||||
} |
||||
|
||||
SliceBuffer SliceBufferFromString(absl::string_view s) { |
||||
SliceBuffer temp; |
||||
temp.Append(Slice::FromCopiedString(s)); |
||||
return temp; |
||||
} |
||||
|
||||
std::vector<uint8_t> Serialize(const Http2FrameHeader& header) { |
||||
uint8_t temp[9]; |
||||
header.Serialize(temp); |
||||
return std::vector<uint8_t>(temp, temp + 9); |
||||
} |
||||
|
||||
Http2FrameHeader ParseHeader(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, |
||||
uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, |
||||
uint8_t b8) { |
||||
uint8_t temp[9] = {b0, b1, b2, b3, b4, b5, b6, b7, b8}; |
||||
return Http2FrameHeader::Parse(temp); |
||||
} |
||||
|
||||
template <typename... I> |
||||
Http2Frame ParseFrame(I... i) { |
||||
SliceBuffer buffer; |
||||
buffer.Append(Slice::FromCopiedBuffer(ByteVec(i...))); |
||||
uint8_t hdr[9]; |
||||
buffer.MoveFirstNBytesIntoBuffer(9, hdr); |
||||
auto frame_hdr = Http2FrameHeader::Parse(hdr); |
||||
EXPECT_EQ(frame_hdr.length, buffer.Length()) |
||||
<< "frame_hdr=" << frame_hdr.ToString(); |
||||
auto r = ParseFramePayload(frame_hdr, std::move(buffer)); |
||||
EXPECT_TRUE(r.ok()) << r.status(); |
||||
return std::move(r.value()); |
||||
} |
||||
|
||||
template <typename... I> |
||||
absl::Status ValidateFrame(I... i) { |
||||
SliceBuffer buffer; |
||||
buffer.Append(Slice::FromCopiedBuffer(ByteVec(i...))); |
||||
uint8_t hdr[9]; |
||||
buffer.MoveFirstNBytesIntoBuffer(9, hdr); |
||||
auto frame_hdr = Http2FrameHeader::Parse(hdr); |
||||
EXPECT_EQ(frame_hdr.length, buffer.Length()) |
||||
<< "frame_hdr=" << frame_hdr.ToString(); |
||||
return ParseFramePayload(frame_hdr, std::move(buffer)).status(); |
||||
} |
||||
|
||||
TEST(Header, Serialization) { |
||||
EXPECT_EQ(Serialize(Http2FrameHeader{0, 0, 0, 0}), |
||||
ByteVec(0, 0, 0, 0, 0, 0, 0, 0, 0)); |
||||
EXPECT_EQ(Serialize(Http2FrameHeader{0x123456, 0x9a, 0xbc, 0x12345678}), |
||||
ByteVec(0x12, 0x34, 0x56, 0x9a, 0xbc, 0x12, 0x34, 0x56, 0x78)); |
||||
} |
||||
|
||||
TEST(Header, Parse) { |
||||
EXPECT_EQ(ParseHeader(0, 0, 0, 0, 0, 0, 0, 0, 0), |
||||
(Http2FrameHeader{0, 0, 0, 0})); |
||||
EXPECT_EQ(ParseHeader(0x12, 0x34, 0x56, 0x9a, 0xbc, 0x12, 0x34, 0x56, 0x78), |
||||
(Http2FrameHeader{0x123456, 0x9a, 0xbc, 0x12345678})); |
||||
} |
||||
|
||||
TEST(Header, ToString) { |
||||
EXPECT_EQ((Http2FrameHeader{0, 0, 0, 0}).ToString(), |
||||
"{DATA: flags=0, stream_id=0, length=0}"); |
||||
EXPECT_EQ((Http2FrameHeader{0x123456, 0x9a, 0xbc, 0x12345678}).ToString(), |
||||
"{UNKNOWN(154): flags=188, stream_id=305419896, length=1193046}"); |
||||
} |
||||
|
||||
TEST(Frame, Serialization) { |
||||
EXPECT_EQ(Serialize(Http2DataFrame{1, false, SliceBufferFromString("hello")}), |
||||
ByteVec(0, 0, 5, 0, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2DataFrame{0x98381822, true, |
||||
SliceBufferFromString("kids")}), |
||||
ByteVec(0, 0, 4, 0, 1, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's')); |
||||
EXPECT_EQ(Serialize(Http2HeaderFrame{1, false, false, |
||||
SliceBufferFromString("hello")}), |
||||
ByteVec(0, 0, 5, 1, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2HeaderFrame{1, true, false, |
||||
SliceBufferFromString("hello")}), |
||||
ByteVec(0, 0, 5, 1, 4, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2HeaderFrame{1, false, true, |
||||
SliceBufferFromString("hello")}), |
||||
ByteVec(0, 0, 5, 1, 1, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2HeaderFrame{1, true, true, |
||||
SliceBufferFromString("hello")}), |
||||
ByteVec(0, 0, 5, 1, 5, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2ContinuationFrame{1, false, |
||||
SliceBufferFromString("hello")}), |
||||
ByteVec(0, 0, 5, 9, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2ContinuationFrame{1, true, |
||||
SliceBufferFromString("hello")}), |
||||
ByteVec(0, 0, 5, 9, 4, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2RstStreamFrame{1, GRPC_HTTP2_CONNECT_ERROR}), |
||||
ByteVec(0, 0, 4, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0x0a)); |
||||
EXPECT_EQ(Serialize(Http2SettingsFrame{}), |
||||
ByteVec(0, 0, 0, 4, 0, 0, 0, 0, 0)); |
||||
EXPECT_EQ( |
||||
Serialize(Http2SettingsFrame{false, {{0x1234, 0x9abcdef0}}}), |
||||
ByteVec(0, 0, 6, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde, 0xf0)); |
||||
EXPECT_EQ(Serialize(Http2SettingsFrame{ |
||||
false, {{0x1234, 0x9abcdef0}, {0x4321, 0x12345678}}}), |
||||
ByteVec(0, 0, 12, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde, |
||||
0xf0, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78)); |
||||
EXPECT_EQ(Serialize(Http2SettingsFrame{true, {}}), |
||||
ByteVec(0, 0, 0, 4, 1, 0, 0, 0, 0)); |
||||
EXPECT_EQ(Serialize(Http2PingFrame{false, 0x123456789abcdef0}), |
||||
ByteVec(0, 0, 8, 6, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a, |
||||
0xbc, 0xde, 0xf0)); |
||||
EXPECT_EQ(Serialize(Http2PingFrame{true, 0x123456789abcdef0}), |
||||
ByteVec(0, 0, 8, 6, 1, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a, |
||||
0xbc, 0xde, 0xf0)); |
||||
EXPECT_EQ(Serialize(Http2GoawayFrame{0x12345678, GRPC_HTTP2_ENHANCE_YOUR_CALM, |
||||
Slice::FromCopiedString("hello")}), |
||||
ByteVec(0, 0, 13, 7, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0, 0, 0, |
||||
0x0b, 'h', 'e', 'l', 'l', 'o')); |
||||
EXPECT_EQ(Serialize(Http2WindowUpdateFrame{1, 0x12345678}), |
||||
ByteVec(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78)); |
||||
} |
||||
|
||||
TEST(Frame, Parse) { |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 5, 0, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'), |
||||
Http2Frame(Http2DataFrame{1, false, SliceBufferFromString("hello")})); |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 4, 0, 1, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's'), |
||||
Http2Frame( |
||||
Http2DataFrame{0x98381822, true, SliceBufferFromString("kids")})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 5, 1, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'), |
||||
Http2Frame(Http2HeaderFrame{1, false, false, |
||||
SliceBufferFromString("hello")})); |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 4, 1, 4, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's'), |
||||
Http2Frame(Http2HeaderFrame{0x98381822, true, false, |
||||
SliceBufferFromString("kids")})); |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 4, 1, 1, 0x98, 0x38, 0x18, 0x22, 'k', 'i', 'd', 's'), |
||||
Http2Frame(Http2HeaderFrame{0x98381822, false, true, |
||||
SliceBufferFromString("kids")})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 5, 9, 0, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'), |
||||
Http2Frame(Http2ContinuationFrame{1, false, |
||||
SliceBufferFromString("hello")})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 5, 9, 4, 0, 0, 0, 1, 'h', 'e', 'l', 'l', 'o'), |
||||
Http2Frame(Http2ContinuationFrame{1, true, |
||||
SliceBufferFromString("hello")})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 4, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0x0a), |
||||
Http2Frame(Http2RstStreamFrame{1, GRPC_HTTP2_CONNECT_ERROR})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 0, 4, 0, 0, 0, 0, 0), |
||||
Http2Frame(Http2SettingsFrame{})); |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 6, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde, 0xf0), |
||||
Http2Frame(Http2SettingsFrame{false, {{0x1234, 0x9abcdef0}}})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 12, 4, 0, 0, 0, 0, 0, 0x12, 0x34, 0x9a, 0xbc, 0xde, |
||||
0xf0, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78), |
||||
Http2Frame(Http2SettingsFrame{ |
||||
false, {{0x1234, 0x9abcdef0}, {0x4321, 0x12345678}}})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 0, 4, 1, 0, 0, 0, 0), |
||||
Http2Frame(Http2SettingsFrame{true, {}})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 8, 6, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a, |
||||
0xbc, 0xde, 0xf0), |
||||
Http2Frame(Http2PingFrame{false, 0x123456789abcdef0})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 8, 6, 1, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0x9a, |
||||
0xbc, 0xde, 0xf0), |
||||
Http2Frame(Http2PingFrame{true, 0x123456789abcdef0})); |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 13, 7, 0, 0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0, 0, 0, |
||||
0x0b, 'h', 'e', 'l', 'l', 'o'), |
||||
Http2Frame(Http2GoawayFrame{0x12345678, GRPC_HTTP2_ENHANCE_YOUR_CALM, |
||||
Slice::FromCopiedString("hello")})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 4, 8, 0, 0, 0, 0, 1, 0x12, 0x34, 0x56, 0x78), |
||||
Http2Frame(Http2WindowUpdateFrame{1, 0x12345678})); |
||||
} |
||||
|
||||
TEST(Frame, ParsePadded) { |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 9, 0, 8, 0, 0, 0, 1, 3, 'h', 'e', 'l', 'l', 'o', 1, 2, |
||||
3), |
||||
Http2Frame(Http2DataFrame{1, false, SliceBufferFromString("hello")})); |
||||
EXPECT_EQ( |
||||
ParseFrame(0, 0, 8, 1, 8, 0, 0, 0, 1, 2, 'h', 'e', 'l', 'l', 'o', 1, 2), |
||||
Http2Frame( |
||||
Http2HeaderFrame{1, false, false, SliceBufferFromString("hello")})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 10, 1, 32, 0, 0, 0, 1, 1, 2, 3, 4, 5, 'h', 'e', |
||||
'l', 'l', 'o'), |
||||
Http2Frame(Http2HeaderFrame{1, false, false, |
||||
SliceBufferFromString("hello")})); |
||||
EXPECT_EQ(ParseFrame(0, 0, 13, 1, 40, 0, 0, 0, 1, 2, 1, 2, 3, 4, 5, 'h', 'e', |
||||
'l', 'l', 'o', 1, 2), |
||||
Http2Frame(Http2HeaderFrame{1, false, false, |
||||
SliceBufferFromString("hello")})); |
||||
} |
||||
|
||||
TEST(Frame, ParseRejects) { |
||||
EXPECT_THAT(ValidateFrame(0, 0, 0, 0, 0, 0, 0, 0, 0), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid stream id: {DATA: flags=0, " |
||||
"stream_id=0, length=0}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 0, 1, 0, 0, 0, 0, 0), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid stream id: {HEADER: flags=0, " |
||||
"stream_id=0, length=0}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 0, 9, 0, 0, 0, 0, 0), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid stream id: {CONTINUATION: flags=0, " |
||||
"stream_id=0, length=0}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 3, 3, 0, 0, 0, 0, 1, 100, 100, 100), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid rst stream payload: {RST_STREAM: flags=0, " |
||||
"stream_id=1, length=3}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 4, 3, 0, 0, 0, 0, 0, 100, 100, 100, 100), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid stream id: {RST_STREAM: flags=0, " |
||||
"stream_id=0, length=4}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 1, 4, 1, 0, 0, 0, 0, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid settings ack length: {SETTINGS: flags=1, " |
||||
"stream_id=0, length=1}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 1, 4, 0, 0, 0, 0, 0, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid settings payload: {SETTINGS: flags=0, " |
||||
"stream_id=0, length=1} -- settings must be multiples " |
||||
"of 6 bytes long")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 2, 4, 0, 0, 0, 0, 0, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid settings payload: {SETTINGS: flags=0, " |
||||
"stream_id=0, length=2} -- settings must be multiples " |
||||
"of 6 bytes long")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 3, 4, 0, 0, 0, 0, 0, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid settings payload: {SETTINGS: flags=0, " |
||||
"stream_id=0, length=3} -- settings must be multiples " |
||||
"of 6 bytes long")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 4, 4, 0, 0, 0, 0, 0, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid settings payload: {SETTINGS: flags=0, " |
||||
"stream_id=0, length=4} -- settings must be multiples " |
||||
"of 6 bytes long")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 5, 4, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid settings payload: {SETTINGS: flags=0, " |
||||
"stream_id=0, length=5} -- settings must be multiples " |
||||
"of 6 bytes long")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 7, 4, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid settings payload: {SETTINGS: flags=0, " |
||||
"stream_id=0, length=7} -- settings must be multiples " |
||||
"of 6 bytes long")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 0, 4, 0, 0, 0, 0, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid stream id: {SETTINGS: flags=0, " |
||||
"stream_id=1, length=0}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 0, 6, 0, 0, 0, 0, 0), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid ping payload: {PING: flags=0, " |
||||
"stream_id=0, length=0}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 8, 6, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid ping stream id: {PING: flags=0, " |
||||
"stream_id=1, length=8}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 8, 6, 2, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid ping flags: {PING: flags=2, " |
||||
"stream_id=0, length=8}")); |
||||
EXPECT_THAT( |
||||
ValidateFrame(0, 0, 8, 6, 255, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid ping flags: {PING: flags=255, " |
||||
"stream_id=0, length=8}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 0, 7, 0, 0, 0, 0, 0), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=0} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 1, 7, 0, 0, 0, 0, 0, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=1} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 2, 7, 0, 0, 0, 0, 0, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=2} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 3, 7, 0, 0, 0, 0, 0, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=3} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 4, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=4} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 5, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=5} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 6, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=6} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 7, 7, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway payload: {GOAWAY: flags=0, " |
||||
"stream_id=0, length=7} -- must be at least 8 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 8, 7, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway stream id: {GOAWAY: flags=0, " |
||||
"stream_id=1, length=8}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 8, 7, 1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway flags: {GOAWAY: flags=1, " |
||||
"stream_id=0, length=8}")); |
||||
EXPECT_THAT( |
||||
ValidateFrame(0, 0, 8, 7, 255, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid goaway flags: {GOAWAY: flags=255, " |
||||
"stream_id=0, length=8}")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 0, 8, 0, 0, 0, 0, 0), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid window update payload: {WINDOW_UPDATE: " |
||||
"flags=0, stream_id=0, length=0} -- must be 4 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 1, 8, 0, 0, 0, 0, 0, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid window update payload: {WINDOW_UPDATE: " |
||||
"flags=0, stream_id=0, length=1} -- must be 4 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 2, 8, 0, 0, 0, 0, 0, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid window update payload: {WINDOW_UPDATE: " |
||||
"flags=0, stream_id=0, length=2} -- must be 4 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 3, 8, 0, 0, 0, 0, 0, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid window update payload: {WINDOW_UPDATE: " |
||||
"flags=0, stream_id=0, length=3} -- must be 4 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 5, 8, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid window update payload: {WINDOW_UPDATE: " |
||||
"flags=0, stream_id=0, length=5} -- must be 4 bytes")); |
||||
EXPECT_THAT(ValidateFrame(0, 0, 4, 8, 1, 0, 0, 0, 0, 1, 1, 1, 1), |
||||
StatusIs(absl::StatusCode::kInternal, |
||||
"invalid window update flags: {WINDOW_UPDATE: flags=1, " |
||||
"stream_id=0, length=4}")); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue