|
|
|
@ -21,8 +21,10 @@ |
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
#include <memory> |
|
|
|
|
#include <string> |
|
|
|
|
|
|
|
|
|
#include <gmock/gmock.h> |
|
|
|
|
#include <gtest/gtest.h> |
|
|
|
|
|
|
|
|
|
#include "absl/strings/str_cat.h" |
|
|
|
@ -152,73 +154,155 @@ static void CrashOnAppendError(absl::string_view, const grpc_core::Slice&) { |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* verify that the output generated by encoding the stream matches the
|
|
|
|
|
hexstring passed in */ |
|
|
|
|
static void verify(const verify_params params, const char* expected, |
|
|
|
|
size_t nheaders, ...) { |
|
|
|
|
grpc_slice_buffer output; |
|
|
|
|
grpc_slice merged; |
|
|
|
|
grpc_slice expect = parse_hexstring(expected); |
|
|
|
|
size_t i; |
|
|
|
|
va_list l; |
|
|
|
|
grpc_slice EncodeHeaderIntoBytes( |
|
|
|
|
bool is_eof, |
|
|
|
|
const std::vector<std::pair<std::string, std::string>>& header_fields) { |
|
|
|
|
std::unique_ptr<grpc_core::HPackCompressor> compressor = |
|
|
|
|
std::make_unique<grpc_core::HPackCompressor>(); |
|
|
|
|
|
|
|
|
|
auto arena = grpc_core::MakeScopedArena(1024, g_memory_allocator); |
|
|
|
|
grpc_metadata_batch b(arena.get()); |
|
|
|
|
|
|
|
|
|
va_start(l, nheaders); |
|
|
|
|
for (i = 0; i < nheaders; i++) { |
|
|
|
|
char* key = va_arg(l, char*); |
|
|
|
|
char* value = va_arg(l, char*); |
|
|
|
|
b.Append(key, grpc_core::Slice::FromStaticString(value), |
|
|
|
|
for (const auto& field : header_fields) { |
|
|
|
|
b.Append(field.first, |
|
|
|
|
grpc_core::Slice::FromStaticString(field.second.c_str()), |
|
|
|
|
CrashOnAppendError); |
|
|
|
|
} |
|
|
|
|
va_end(l); |
|
|
|
|
|
|
|
|
|
grpc_slice_buffer_init(&output); |
|
|
|
|
|
|
|
|
|
grpc_transport_one_way_stats stats; |
|
|
|
|
stats = {}; |
|
|
|
|
grpc_transport_one_way_stats stats = {}; |
|
|
|
|
grpc_core::HPackCompressor::EncodeHeaderOptions hopt{ |
|
|
|
|
0xdeadbeef, /* stream_id */ |
|
|
|
|
params.eof, /* is_eof */ |
|
|
|
|
params.use_true_binary_metadata, /* use_true_binary_metadata */ |
|
|
|
|
16384, /* max_frame_size */ |
|
|
|
|
&stats /* stats */ |
|
|
|
|
0xdeadbeef, /* stream_id */ |
|
|
|
|
is_eof, /* is_eof */ |
|
|
|
|
false, /* use_true_binary_metadata */ |
|
|
|
|
16384, /* max_frame_size */ |
|
|
|
|
&stats /* stats */ |
|
|
|
|
}; |
|
|
|
|
g_compressor->EncodeHeaders(hopt, b, &output); |
|
|
|
|
verify_frames(output, params.eof); |
|
|
|
|
merged = grpc_slice_merge(output.slices, output.count); |
|
|
|
|
grpc_slice_buffer output; |
|
|
|
|
grpc_slice_buffer_init(&output); |
|
|
|
|
|
|
|
|
|
compressor->EncodeHeaders(hopt, b, &output); |
|
|
|
|
verify_frames(output, is_eof); |
|
|
|
|
|
|
|
|
|
grpc_slice ret = grpc_slice_merge(output.slices, output.count); |
|
|
|
|
grpc_slice_buffer_destroy_internal(&output); |
|
|
|
|
|
|
|
|
|
if (!grpc_slice_eq(merged, expect)) { |
|
|
|
|
char* expect_str = grpc_dump_slice(expect, GPR_DUMP_HEX | GPR_DUMP_ASCII); |
|
|
|
|
char* got_str = grpc_dump_slice(merged, GPR_DUMP_HEX | GPR_DUMP_ASCII); |
|
|
|
|
gpr_log(GPR_ERROR, "mismatched output for %s", expected); |
|
|
|
|
gpr_log(GPR_ERROR, "EXPECT: %s", expect_str); |
|
|
|
|
gpr_log(GPR_ERROR, "GOT: %s", got_str); |
|
|
|
|
gpr_free(expect_str); |
|
|
|
|
gpr_free(got_str); |
|
|
|
|
EXPECT_TRUE(false); |
|
|
|
|
} |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_slice_unref_internal(merged); |
|
|
|
|
grpc_slice_unref_internal(expect); |
|
|
|
|
/* verify that the output generated by encoding the stream matches the
|
|
|
|
|
hexstring passed in */ |
|
|
|
|
static void verify( |
|
|
|
|
bool is_eof, const char* expected, |
|
|
|
|
const std::vector<std::pair<std::string, std::string>>& header_fields) { |
|
|
|
|
const grpc_core::Slice merged(EncodeHeaderIntoBytes(is_eof, header_fields)); |
|
|
|
|
const grpc_core::Slice expect(parse_hexstring(expected)); |
|
|
|
|
|
|
|
|
|
EXPECT_EQ(merged, expect); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(HpackEncoderTest, TestBasicHeaders) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
g_compressor = new grpc_core::HPackCompressor(); |
|
|
|
|
|
|
|
|
|
verify_params params = { |
|
|
|
|
false, |
|
|
|
|
false, |
|
|
|
|
}; |
|
|
|
|
verify(params, "000005 0104 deadbeef 00 0161 0161", 1, "a", "a"); |
|
|
|
|
verify(params, "00000a 0104 deadbeef 00 0161 0161 00 0162 0163", 2, "a", "a", |
|
|
|
|
"b", "c"); |
|
|
|
|
verify(false, "000005 0104 deadbeef 00 0161 0161", {{"a", "a"}}); |
|
|
|
|
verify(false, "00000a 0104 deadbeef 00 0161 0161 00 0162 0163", |
|
|
|
|
{{"a", "a"}, {"b", "c"}}); |
|
|
|
|
|
|
|
|
|
delete g_compressor; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
MATCHER(HasLiteralHeaderFieldNewNameFlagIncrementalIndexing, "") { |
|
|
|
|
constexpr size_t kHttp2FrameHeaderSize = 9u; |
|
|
|
|
/// Reference: https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.1
|
|
|
|
|
/// The first byte of a literal header field with incremental indexing should
|
|
|
|
|
/// be 0x40.
|
|
|
|
|
constexpr uint8_t kLiteralHeaderFieldNewNameFlagIncrementalIndexing = 0x40; |
|
|
|
|
return (GRPC_SLICE_START_PTR(arg)[kHttp2FrameHeaderSize] == |
|
|
|
|
kLiteralHeaderFieldNewNameFlagIncrementalIndexing); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
MATCHER(HasLiteralHeaderFieldNewNameFlagNoIndexing, "") { |
|
|
|
|
constexpr size_t kHttp2FrameHeaderSize = 9u; |
|
|
|
|
/// Reference: https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.2
|
|
|
|
|
/// The first byte of a literal header field without indexing should be 0x0.
|
|
|
|
|
constexpr uint8_t kLiteralHeaderFieldNewNameFlagNoIndexing = 0x00; |
|
|
|
|
return (GRPC_SLICE_START_PTR(arg)[kHttp2FrameHeaderSize] == |
|
|
|
|
kLiteralHeaderFieldNewNameFlagNoIndexing); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(HpackEncoderTest, GrpcTraceBinMetadataIndexing) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
|
|
|
|
|
const grpc_slice encoded_header = EncodeHeaderIntoBytes( |
|
|
|
|
false, {{grpc_core::GrpcTraceBinMetadata::key().data(), "value"}}); |
|
|
|
|
EXPECT_THAT(encoded_header, |
|
|
|
|
HasLiteralHeaderFieldNewNameFlagIncrementalIndexing()); |
|
|
|
|
|
|
|
|
|
grpc_slice_unref_internal(encoded_header); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(HpackEncoderTest, GrpcTraceBinMetadataNoIndexing) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
|
|
|
|
|
/// needs to be greater than `HPackEncoderTable::MaxEntrySize()`
|
|
|
|
|
constexpr size_t long_value_size = 70000u; |
|
|
|
|
const grpc_slice encoded_header = EncodeHeaderIntoBytes( |
|
|
|
|
false, {{grpc_core::GrpcTraceBinMetadata::key().data(), |
|
|
|
|
std::string(long_value_size, 'a')}}); |
|
|
|
|
EXPECT_THAT(encoded_header, HasLiteralHeaderFieldNewNameFlagNoIndexing()); |
|
|
|
|
|
|
|
|
|
grpc_slice_unref_internal(encoded_header); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(HpackEncoderTest, TestGrpcTagsBinMetadataIndexing) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
|
|
|
|
|
const grpc_slice encoded_header = EncodeHeaderIntoBytes( |
|
|
|
|
false, |
|
|
|
|
{{grpc_core::GrpcTagsBinMetadata::key().data(), std::string("value")}}); |
|
|
|
|
EXPECT_THAT(encoded_header, |
|
|
|
|
HasLiteralHeaderFieldNewNameFlagIncrementalIndexing()); |
|
|
|
|
|
|
|
|
|
grpc_slice_unref_internal(encoded_header); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(HpackEncoderTest, TestGrpcTagsBinMetadataNoIndexing) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
|
|
|
|
|
/// needs to be greater than `HPackEncoderTable::MaxEntrySize()`
|
|
|
|
|
constexpr size_t long_value_size = 70000u; |
|
|
|
|
const grpc_slice encoded_header = EncodeHeaderIntoBytes( |
|
|
|
|
false, {{grpc_core::GrpcTagsBinMetadata::key().data(), |
|
|
|
|
std::string(long_value_size, 'a')}}); |
|
|
|
|
EXPECT_THAT(encoded_header, HasLiteralHeaderFieldNewNameFlagNoIndexing()); |
|
|
|
|
|
|
|
|
|
grpc_slice_unref_internal(encoded_header); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(HpackEncoderTest, UserAgentMetadataIndexing) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
|
|
|
|
|
const grpc_slice encoded_header = EncodeHeaderIntoBytes( |
|
|
|
|
false, {{grpc_core::UserAgentMetadata::key().data(), "value"}}); |
|
|
|
|
EXPECT_THAT(encoded_header, |
|
|
|
|
HasLiteralHeaderFieldNewNameFlagIncrementalIndexing()); |
|
|
|
|
|
|
|
|
|
grpc_slice_unref_internal(encoded_header); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(HpackEncoderTest, UserAgentMetadataNoIndexing) { |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
|
|
|
|
|
/// needs to be greater than `HPackEncoderTable::MaxEntrySize()`
|
|
|
|
|
constexpr size_t long_value_size = 70000u; |
|
|
|
|
const grpc_slice encoded_header = |
|
|
|
|
EncodeHeaderIntoBytes(false, {{grpc_core::UserAgentMetadata::key().data(), |
|
|
|
|
std::string(long_value_size, 'a')}}); |
|
|
|
|
EXPECT_THAT(encoded_header, HasLiteralHeaderFieldNewNameFlagNoIndexing()); |
|
|
|
|
|
|
|
|
|
grpc_slice_unref_internal(encoded_header); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void verify_continuation_headers(const char* key, const char* value, |
|
|
|
|
bool is_eof) { |
|
|
|
|
auto arena = grpc_core::MakeScopedArena(1024, g_memory_allocator); |
|
|
|
|