/* * * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "src/core/transport/chttp2/stream_encoder.h" #include #include "src/core/support/string.h" #include "src/core/transport/chttp2/hpack_parser.h" #include #include #include #include "test/core/util/parse_hexstring.h" #include "test/core/util/slice_splitter.h" #include "test/core/util/test_config.h" #define TEST(x) run_test(x, #x) grpc_mdctx *g_mdctx; grpc_chttp2_hpack_compressor g_compressor; int g_failure = 0; grpc_stream_op_buffer g_sopb; void **to_delete = NULL; int num_to_delete = 0; int cap_to_delete = 0; static gpr_slice create_test_slice(size_t length) { gpr_slice slice = gpr_slice_malloc(length); size_t i; for (i = 0; i < length; i++) { GPR_SLICE_START_PTR(slice)[i] = i; } return slice; } /* verify that the output generated by encoding the stream matches the hexstring passed in */ static void verify_sopb(size_t window_available, int eof, size_t expect_window_used, const char *expected) { gpr_slice_buffer output; grpc_stream_op_buffer encops; gpr_slice merged; gpr_slice expect = parse_hexstring(expected); gpr_slice_buffer_init(&output); grpc_sopb_init(&encops); GPR_ASSERT(expect_window_used == grpc_chttp2_preencode(g_sopb.ops, &g_sopb.nops, window_available, &encops)); grpc_chttp2_encode(encops.ops, encops.nops, eof, 0xdeadbeef, &g_compressor, &output); encops.nops = 0; merged = grpc_slice_merge(output.slices, output.count); gpr_slice_buffer_destroy(&output); grpc_sopb_destroy(&encops); if (0 != gpr_slice_cmp(merged, expect)) { char *expect_str = gpr_hexdump((char *)GPR_SLICE_START_PTR(expect), GPR_SLICE_LENGTH(expect), GPR_HEXDUMP_PLAINTEXT); char *got_str = gpr_hexdump((char *)GPR_SLICE_START_PTR(merged), GPR_SLICE_LENGTH(merged), GPR_HEXDUMP_PLAINTEXT); 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); g_failure = 1; } gpr_slice_unref(merged); gpr_slice_unref(expect); } static void test_small_data_framing(void) { grpc_sopb_add_no_op(&g_sopb); verify_sopb(10, 0, 0, ""); grpc_sopb_add_slice(&g_sopb, create_test_slice(3)); verify_sopb(10, 0, 3, "000003 0000 deadbeef 000102"); grpc_sopb_add_slice(&g_sopb, create_test_slice(4)); verify_sopb(10, 0, 4, "000004 0000 deadbeef 00010203"); grpc_sopb_add_slice(&g_sopb, create_test_slice(3)); grpc_sopb_add_slice(&g_sopb, create_test_slice(4)); verify_sopb(10, 0, 7, "000007 0000 deadbeef 000102 00010203"); grpc_sopb_add_slice(&g_sopb, create_test_slice(0)); grpc_sopb_add_slice(&g_sopb, create_test_slice(0)); grpc_sopb_add_slice(&g_sopb, create_test_slice(0)); grpc_sopb_add_slice(&g_sopb, create_test_slice(0)); grpc_sopb_add_slice(&g_sopb, create_test_slice(3)); verify_sopb(10, 0, 3, "000003 0000 deadbeef 000102"); verify_sopb(10, 1, 0, "000000 0001 deadbeef"); grpc_sopb_add_begin_message(&g_sopb, 255, 0); verify_sopb(10, 0, 5, "000005 0000 deadbeef 00000000ff"); } static void add_sopb_headers(int n, ...) { int i; grpc_metadata_batch b; va_list l; grpc_linked_mdelem *e = gpr_malloc(sizeof(*e) * n); grpc_metadata_batch_init(&b); va_start(l, n); for (i = 0; i < n; i++) { char *key = va_arg(l, char *); char *value = va_arg(l, char *); if (i) { e[i - 1].next = &e[i]; e[i].prev = &e[i - 1]; } e[i].md = grpc_mdelem_from_strings(g_mdctx, key, value); } e[0].prev = NULL; e[n - 1].next = NULL; va_end(l); b.list.head = &e[0]; b.list.tail = &e[n - 1]; if (cap_to_delete == num_to_delete) { cap_to_delete = GPR_MAX(2 * cap_to_delete, 1000); to_delete = gpr_realloc(to_delete, sizeof(*to_delete) * cap_to_delete); } to_delete[num_to_delete++] = e; grpc_sopb_add_metadata(&g_sopb, b); } static void test_basic_headers(void) { int i; add_sopb_headers(1, "a", "a"); verify_sopb(0, 0, 0, "000005 0104 deadbeef 40 0161 0161"); add_sopb_headers(1, "a", "a"); verify_sopb(0, 0, 0, "000001 0104 deadbeef be"); add_sopb_headers(1, "a", "a"); verify_sopb(0, 0, 0, "000001 0104 deadbeef be"); add_sopb_headers(2, "a", "a", "b", "c"); verify_sopb(0, 0, 0, "000006 0104 deadbeef be 40 0162 0163"); add_sopb_headers(2, "a", "a", "b", "c"); verify_sopb(0, 0, 0, "000002 0104 deadbeef bf be"); add_sopb_headers(1, "a", "d"); verify_sopb(0, 0, 0, "000004 0104 deadbeef 7f 00 0164"); /* flush out what's there to make a few values look very popular */ for (i = 0; i < 350; i++) { add_sopb_headers(3, "a", "a", "b", "c", "a", "d"); verify_sopb(0, 0, 0, "000003 0104 deadbeef c0 bf be"); } add_sopb_headers(2, "a", "a", "k", "v"); verify_sopb(0, 0, 0, "000006 0104 deadbeef c0 00 016b 0176"); add_sopb_headers(1, "a", "v"); /* this could be 000004 0104 deadbeef 0f 30 0176 also */ verify_sopb(0, 0, 0, "000004 0104 deadbeef 0f 2f 0176"); } static void encode_int_to_str(int i, char *p) { p[0] = 'a' + i % 26; i /= 26; GPR_ASSERT(i < 26); p[1] = 'a' + i; p[2] = 0; } static void test_decode_table_overflow(void) { int i; char key[3], value[3]; char *expect; for (i = 0; i < 114; i++) { if (i > 0) { add_sopb_headers(1, "aa", "ba"); } encode_int_to_str(i, key); encode_int_to_str(i + 1, value); if (i + 61 >= 127) { gpr_asprintf(&expect, "000002 0104 deadbeef ff%02x 000007 0104 deadbeef 40 " "02%02x%02x 02%02x%02x", i + 61 - 127, key[0], key[1], value[0], value[1]); } else if (i > 0) { gpr_asprintf(&expect, "000001 0104 deadbeef %02x 000007 0104 deadbeef 40 " "02%02x%02x 02%02x%02x", 0x80 + 61 + i, key[0], key[1], value[0], value[1]); } else { gpr_asprintf(&expect, "000007 0104 deadbeef 40 02%02x%02x 02%02x%02x", key[0], key[1], value[0], value[1]); } add_sopb_headers(1, key, value); verify_sopb(0, 0, 0, expect); gpr_free(expect); } /* if the above passes, then we must have just knocked this pair out of the decoder stack, and so we'll be forced to re-encode it */ add_sopb_headers(1, "aa", "ba"); verify_sopb(0, 0, 0, "000007 0104 deadbeef 40 026161 026261"); } static void randstr(char *p, int bufsz) { int i; int len = 1 + rand() % bufsz; for (i = 0; i < len; i++) { p[i] = 'a' + rand() % 26; } p[len] = 0; } typedef struct { char key[300]; char value[300]; int got_hdr; } test_decode_random_header_state; static void chk_hdr(void *p, grpc_mdelem *el) { test_decode_random_header_state *st = p; GPR_ASSERT(0 == gpr_slice_str_cmp(el->key->slice, st->key)); GPR_ASSERT(0 == gpr_slice_str_cmp(el->value->slice, st->value)); st->got_hdr = 1; grpc_mdelem_unref(el); } static void test_decode_random_headers_inner(int max_len) { int i; test_decode_random_header_state st; gpr_slice_buffer output; gpr_slice merged; grpc_stream_op_buffer encops; grpc_chttp2_hpack_parser parser; grpc_chttp2_hpack_parser_init(&parser, g_mdctx); grpc_sopb_init(&encops); gpr_log(GPR_INFO, "max_len = %d", max_len); for (i = 0; i < 10000; i++) { randstr(st.key, max_len); randstr(st.value, max_len); add_sopb_headers(1, st.key, st.value); gpr_slice_buffer_init(&output); GPR_ASSERT(0 == grpc_chttp2_preencode(g_sopb.ops, &g_sopb.nops, 0, &encops)); grpc_chttp2_encode(encops.ops, encops.nops, 0, 0xdeadbeef, &g_compressor, &output); encops.nops = 0; merged = grpc_slice_merge(output.slices, output.count); gpr_slice_buffer_destroy(&output); st.got_hdr = 0; parser.on_header = chk_hdr; parser.on_header_user_data = &st; grpc_chttp2_hpack_parser_parse(&parser, GPR_SLICE_START_PTR(merged) + 9, GPR_SLICE_END_PTR(merged)); GPR_ASSERT(st.got_hdr); gpr_slice_unref(merged); } grpc_chttp2_hpack_parser_destroy(&parser); grpc_sopb_destroy(&encops); } #define DECL_TEST_DECODE_RANDOM_HEADERS(n) \ static void test_decode_random_headers_##n(void) { \ test_decode_random_headers_inner(n); \ } \ int keeps_formatting_correct_##n DECL_TEST_DECODE_RANDOM_HEADERS(1); DECL_TEST_DECODE_RANDOM_HEADERS(2); DECL_TEST_DECODE_RANDOM_HEADERS(3); DECL_TEST_DECODE_RANDOM_HEADERS(5); DECL_TEST_DECODE_RANDOM_HEADERS(8); DECL_TEST_DECODE_RANDOM_HEADERS(13); DECL_TEST_DECODE_RANDOM_HEADERS(21); DECL_TEST_DECODE_RANDOM_HEADERS(34); DECL_TEST_DECODE_RANDOM_HEADERS(55); DECL_TEST_DECODE_RANDOM_HEADERS(89); DECL_TEST_DECODE_RANDOM_HEADERS(144); static void run_test(void (*test)(), const char *name) { gpr_log(GPR_INFO, "RUN TEST: %s", name); g_mdctx = grpc_mdctx_create_with_seed(0); grpc_chttp2_hpack_compressor_init(&g_compressor, g_mdctx); grpc_sopb_init(&g_sopb); test(); grpc_chttp2_hpack_compressor_destroy(&g_compressor); grpc_mdctx_unref(g_mdctx); grpc_sopb_destroy(&g_sopb); } int main(int argc, char **argv) { int i; grpc_test_init(argc, argv); TEST(test_small_data_framing); TEST(test_basic_headers); TEST(test_decode_table_overflow); TEST(test_decode_random_headers_1); TEST(test_decode_random_headers_2); TEST(test_decode_random_headers_3); TEST(test_decode_random_headers_5); TEST(test_decode_random_headers_8); TEST(test_decode_random_headers_13); TEST(test_decode_random_headers_21); TEST(test_decode_random_headers_34); TEST(test_decode_random_headers_55); TEST(test_decode_random_headers_89); TEST(test_decode_random_headers_144); for (i = 0; i < num_to_delete; i++) { gpr_free(to_delete[i]); } return g_failure; }