|
|
|
@ -49,6 +49,102 @@ typedef struct { |
|
|
|
|
bool only_intern_key; |
|
|
|
|
} verify_params; |
|
|
|
|
|
|
|
|
|
/* verify that the output frames that are generated by encoding the stream
|
|
|
|
|
have sensible type and flags values */ |
|
|
|
|
static void verify_frames(grpc_slice_buffer& output, bool header_is_eof) { |
|
|
|
|
/* per the HTTP/2 spec:
|
|
|
|
|
All frames begin with a fixed 9-octet header followed by a |
|
|
|
|
variable-length payload. |
|
|
|
|
|
|
|
|
|
+-----------------------------------------------+ |
|
|
|
|
| Length (24) | |
|
|
|
|
+---------------+---------------+---------------+ |
|
|
|
|
| Type (8) | Flags (8) | |
|
|
|
|
+-+-------------+---------------+-------------------------------+ |
|
|
|
|
|R| Stream Identifier (31) | |
|
|
|
|
+=+=============================================================+ |
|
|
|
|
| Frame Payload (0...) ... |
|
|
|
|
+---------------------------------------------------------------+ |
|
|
|
|
*/ |
|
|
|
|
uint8_t type = 0xff, flags = 0xff; |
|
|
|
|
size_t i, merged_length, frame_size; |
|
|
|
|
bool first_frame = false; |
|
|
|
|
bool in_header = false; |
|
|
|
|
bool end_header = false; |
|
|
|
|
bool is_closed = false; |
|
|
|
|
for (i = 0; i < output.count;) { |
|
|
|
|
first_frame = i == 0; |
|
|
|
|
grpc_slice* slice = &output.slices[i++]; |
|
|
|
|
|
|
|
|
|
// Read gRPC frame header
|
|
|
|
|
uint8_t* p = GRPC_SLICE_START_PTR(*slice); |
|
|
|
|
frame_size = 0; |
|
|
|
|
frame_size |= static_cast<uint32_t>(p[0]) << 16; |
|
|
|
|
frame_size |= static_cast<uint32_t>(p[1]) << 8; |
|
|
|
|
frame_size |= static_cast<uint32_t>(p[2]); |
|
|
|
|
type = p[3]; |
|
|
|
|
flags = p[4]; |
|
|
|
|
|
|
|
|
|
// Read remainder of the gRPC frame
|
|
|
|
|
merged_length = GRPC_SLICE_LENGTH(*slice); |
|
|
|
|
while (merged_length < frame_size + 9) { // including 9 byte frame header
|
|
|
|
|
grpc_slice* slice = &output.slices[i++]; |
|
|
|
|
merged_length += GRPC_SLICE_LENGTH(*slice); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verifications
|
|
|
|
|
if (first_frame && type != GRPC_CHTTP2_FRAME_HEADER) { |
|
|
|
|
gpr_log(GPR_ERROR, "expected first frame to be of type header"); |
|
|
|
|
gpr_log(GPR_ERROR, "EXPECT: 0x%x", GRPC_CHTTP2_FRAME_HEADER); |
|
|
|
|
gpr_log(GPR_ERROR, "GOT: 0x%x", type); |
|
|
|
|
g_failure = 1; |
|
|
|
|
} else if (first_frame && header_is_eof && |
|
|
|
|
!(flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM)) { |
|
|
|
|
gpr_log(GPR_ERROR, "missing END_STREAM flag in HEADER frame"); |
|
|
|
|
g_failure = 1; |
|
|
|
|
} |
|
|
|
|
if (is_closed && |
|
|
|
|
(type == GRPC_CHTTP2_FRAME_DATA || type == GRPC_CHTTP2_FRAME_HEADER)) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"stream is closed; new frame headers and data are not allowed"); |
|
|
|
|
g_failure = 1; |
|
|
|
|
} |
|
|
|
|
if (end_header && (type == GRPC_CHTTP2_FRAME_HEADER || |
|
|
|
|
type == GRPC_CHTTP2_FRAME_CONTINUATION)) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"frame header is ended; new headers and continuations are not " |
|
|
|
|
"allowed"); |
|
|
|
|
g_failure = 1; |
|
|
|
|
} |
|
|
|
|
if (in_header && |
|
|
|
|
(type == GRPC_CHTTP2_FRAME_DATA || type == GRPC_CHTTP2_FRAME_HEADER)) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"parsing frame header; new headers and data are not allowed"); |
|
|
|
|
g_failure = 1; |
|
|
|
|
} |
|
|
|
|
if (flags & ~(GRPC_CHTTP2_DATA_FLAG_END_STREAM | |
|
|
|
|
GRPC_CHTTP2_DATA_FLAG_END_HEADERS)) { |
|
|
|
|
gpr_log(GPR_ERROR, "unexpected frame flags: 0x%x", flags); |
|
|
|
|
g_failure = 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update state
|
|
|
|
|
if (flags & GRPC_CHTTP2_DATA_FLAG_END_HEADERS) { |
|
|
|
|
in_header = false; |
|
|
|
|
end_header = true; |
|
|
|
|
} else if (type == GRPC_CHTTP2_DATA_FLAG_END_HEADERS) { |
|
|
|
|
in_header = true; |
|
|
|
|
} |
|
|
|
|
if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) { |
|
|
|
|
is_closed = true; |
|
|
|
|
if (type == GRPC_CHTTP2_FRAME_CONTINUATION) { |
|
|
|
|
gpr_log(GPR_ERROR, "unexpected END_STREAM flag in CONTINUATION frame"); |
|
|
|
|
g_failure = 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* verify that the output generated by encoding the stream matches the
|
|
|
|
|
hexstring passed in */ |
|
|
|
|
static void verify(const verify_params params, const char* expected, |
|
|
|
@ -106,6 +202,7 @@ static void verify(const verify_params params, const char* expected, |
|
|
|
|
&stats /* stats */ |
|
|
|
|
}; |
|
|
|
|
grpc_chttp2_encode_header(&g_compressor, nullptr, 0, &b, &hopt, &output); |
|
|
|
|
verify_frames(output, params.eof); |
|
|
|
|
merged = grpc_slice_merge(output.slices, output.count); |
|
|
|
|
grpc_slice_buffer_destroy_internal(&output); |
|
|
|
|
grpc_metadata_batch_destroy(&b); |
|
|
|
@ -151,6 +248,50 @@ static void test_basic_headers() { |
|
|
|
|
verify(params, "000004 0104 deadbeef 0f 2f 0176", 1, "a", "v"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void verify_continuation_headers(const char* key, const char* value, |
|
|
|
|
bool is_eof) { |
|
|
|
|
grpc_slice_buffer output; |
|
|
|
|
grpc_mdelem elem = grpc_mdelem_from_slices( |
|
|
|
|
grpc_slice_intern(grpc_slice_from_static_string(key)), |
|
|
|
|
grpc_slice_intern(grpc_slice_from_static_string(value))); |
|
|
|
|
grpc_linked_mdelem* e = |
|
|
|
|
static_cast<grpc_linked_mdelem*>(gpr_malloc(sizeof(*e))); |
|
|
|
|
grpc_metadata_batch b; |
|
|
|
|
grpc_metadata_batch_init(&b); |
|
|
|
|
e[0].md = elem; |
|
|
|
|
e[0].prev = nullptr; |
|
|
|
|
e[0].next = nullptr; |
|
|
|
|
b.list.head = &e[0]; |
|
|
|
|
b.list.tail = &e[0]; |
|
|
|
|
b.list.count = 1; |
|
|
|
|
grpc_slice_buffer_init(&output); |
|
|
|
|
|
|
|
|
|
grpc_transport_one_way_stats stats; |
|
|
|
|
stats = {}; |
|
|
|
|
grpc_encode_header_options hopt = {0xdeadbeef, /* stream_id */ |
|
|
|
|
is_eof, /* is_eof */ |
|
|
|
|
false, /* use_true_binary_metadata */ |
|
|
|
|
150, /* max_frame_size */ |
|
|
|
|
&stats /* stats */}; |
|
|
|
|
grpc_chttp2_encode_header(&g_compressor, nullptr, 0, &b, &hopt, &output); |
|
|
|
|
verify_frames(output, is_eof); |
|
|
|
|
grpc_slice_buffer_destroy_internal(&output); |
|
|
|
|
grpc_metadata_batch_destroy(&b); |
|
|
|
|
gpr_free(e); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void test_continuation_headers() { |
|
|
|
|
char value[200]; |
|
|
|
|
memset(value, 'a', 200); |
|
|
|
|
value[199] = 0; // null terminator
|
|
|
|
|
verify_continuation_headers("key", value, true); |
|
|
|
|
|
|
|
|
|
char value2[400]; |
|
|
|
|
memset(value2, 'b', 400); |
|
|
|
|
value2[399] = 0; // null terminator
|
|
|
|
|
verify_continuation_headers("key2", value2, true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void encode_int_to_str(int i, char* p) { |
|
|
|
|
p[0] = static_cast<char>('a' + i % 26); |
|
|
|
|
i /= 26; |
|
|
|
@ -225,6 +366,7 @@ static void verify_table_size_change_match_elem_size(const char* key, |
|
|
|
|
16384, /* max_frame_size */ |
|
|
|
|
&stats /* stats */}; |
|
|
|
|
grpc_chttp2_encode_header(&g_compressor, nullptr, 0, &b, &hopt, &output); |
|
|
|
|
verify_frames(output, false); |
|
|
|
|
grpc_slice_buffer_destroy_internal(&output); |
|
|
|
|
grpc_metadata_batch_destroy(&b); |
|
|
|
|
|
|
|
|
@ -267,6 +409,7 @@ int main(int argc, char** argv) { |
|
|
|
|
TEST(test_decode_table_overflow); |
|
|
|
|
TEST(test_encode_header_size); |
|
|
|
|
TEST(test_interned_key_indexed); |
|
|
|
|
TEST(test_continuation_headers); |
|
|
|
|
grpc_shutdown(); |
|
|
|
|
for (i = 0; i < num_to_delete; i++) { |
|
|
|
|
gpr_free(to_delete[i]); |
|
|
|
|