Repository: trafficserver Updated Branches: refs/heads/master 02fe08ee9 -> df48fade2
TS-4001: Separate Regression Tests for HPACK from HTTP2.cc This closes #328 Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/df48fade Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/df48fade Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/df48fade Branch: refs/heads/master Commit: df48fade2526b71aa6def113cfc307e883544a73 Parents: 02fe08e Author: Masaori Koshiba <[email protected]> Authored: Mon Nov 9 19:51:52 2015 -0800 Committer: Bryan Call <[email protected]> Committed: Mon Nov 9 19:51:52 2015 -0800 ---------------------------------------------------------------------- proxy/http2/HTTP2.cc | 387 +-------------------------------- proxy/http2/Makefile.am | 5 + proxy/http2/RegressionHPACK.cc | 416 ++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+), 382 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/df48fade/proxy/http2/HTTP2.cc ---------------------------------------------------------------------- diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc index ef33e6b..ce10d62 100644 --- a/proxy/http2/HTTP2.cc +++ b/proxy/http2/HTTP2.cc @@ -727,390 +727,14 @@ Http2::init() #if TS_HAS_TESTS -#include "ts/TestBox.h" - -// Constants for regression test -const static int BUFSIZE_FOR_REGRESSION_TEST = 128; -const static int MAX_TEST_FIELD_NUM = 8; - -/*********************************************************************************** - * * - * Regression test for HPACK * - * * - * Some test cases are based on examples of specification. * - * - https://tools.ietf.org/html/rfc7541#appendix-C * - * * - ***********************************************************************************/ - -// [RFC 7541] C.1. Integer Representation Examples -const static struct { - uint32_t raw_integer; - uint8_t *encoded_field; - int encoded_field_len; - int prefix; -} integer_test_case[] = {{10, (uint8_t *) "\x0A", 1, 5}, {1337, (uint8_t *) "\x1F\x9A\x0A", 3, 5}, {42, (uint8_t *) "\x2A", 1, 8}}; - -// Example: custom-key: custom-header -const static struct { - char *raw_string; - uint32_t raw_string_len; - uint8_t *encoded_field; - int encoded_field_len; -} string_test_case[] = {{(char *)"custom-key", 10, (uint8_t *) "\xA" - "custom-key", - 11}, - {(char *)"custom-key", 10, (uint8_t *) "\x88" - "\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f", - 9}}; - -// [RFC 7541] C.2.4. Indexed Header Field -const static struct { - int index; - char *raw_name; - char *raw_value; - uint8_t *encoded_field; - int encoded_field_len; -} indexed_test_case[] = {{2, (char *) ":method", (char *) "GET", (uint8_t *) "\x82", 1}}; - -// [RFC 7541] C.2. Header Field Representation Examples -const static struct { - char *raw_name; - char *raw_value; - int index; - HpackFieldType type; - uint8_t *encoded_field; - int encoded_field_len; -} literal_test_case[] = { - {(char *)"custom-key", (char *) "custom-header", 0, HPACK_FIELD_INDEXED_LITERAL, (uint8_t *) "\x40\x0a" - "custom-key\x0d" - "custom-header", - 26}, - {(char *)"custom-key", (char *) "custom-header", 0, HPACK_FIELD_NOINDEX_LITERAL, (uint8_t *) "\x00\x0a" - "custom-key\x0d" - "custom-header", - 26}, - {(char *)"custom-key", (char *) "custom-header", 0, HPACK_FIELD_NEVERINDEX_LITERAL, (uint8_t *) "\x10\x0a" - "custom-key\x0d" - "custom-header", - 26}, - {(char *)":path", (char *) "/sample/path", 4, HPACK_FIELD_INDEXED_LITERAL, (uint8_t *) "\x44\x0c" - "/sample/path", - 14}, - {(char *)":path", (char *) "/sample/path", 4, HPACK_FIELD_NOINDEX_LITERAL, (uint8_t *) "\x04\x0c" - "/sample/path", - 14}, - {(char *)":path", (char *) "/sample/path", 4, HPACK_FIELD_NEVERINDEX_LITERAL, (uint8_t *) "\x14\x0c" - "/sample/path", - 14}, - {(char *)"password", (char *) "secret", 0, HPACK_FIELD_INDEXED_LITERAL, (uint8_t *) "\x40\x08" - "password\x06" - "secret", - 17}, - {(char *)"password", (char *) "secret", 0, HPACK_FIELD_NOINDEX_LITERAL, (uint8_t *) "\x00\x08" - "password\x06" - "secret", - 17}, - {(char *)"password", (char *) "secret", 0, HPACK_FIELD_NEVERINDEX_LITERAL, (uint8_t *) "\x10\x08" - "password\x06" - "secret", - 17}}; - -// [RFC 7541] C.3. Request Examples without Huffman Coding - C.3.1. First Request -const static struct { - char *raw_name; - char *raw_value; -} raw_field_test_case[][MAX_TEST_FIELD_NUM] = {{ - {(char *)":method", (char *) "GET"}, - {(char *)":scheme", (char *) "http"}, - {(char *)":path", (char *) "/"}, - {(char *)":authority", (char *) "www.example.com"}, - {(char *)"", (char *) ""} // End of this test case -}}; -const static struct { - uint8_t *encoded_field; - int encoded_field_len; -} encoded_field_test_case[] = {{(uint8_t *)"\x40" - "\x7:method" - "\x3GET" - "\x40" - "\x7:scheme" - "\x4http" - "\x40" - "\x5:path" - "\x1/" - "\x40" - "\xa:authority" - "\xfwww.example.com", - 64}}; - -/*********************************************************************************** - * * - * Regression test codes * - * * - ***********************************************************************************/ - -REGRESSION_TEST(HPACK_EncodeInteger)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; - - for (unsigned int i = 0; i < sizeof(integer_test_case) / sizeof(integer_test_case[0]); i++) { - memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); - - int len = encode_integer(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, integer_test_case[i].raw_integer, integer_test_case[i].prefix); - - box.check(len == integer_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, - integer_test_case[i].encoded_field_len); - box.check(len > 0 && memcmp(buf, integer_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); - } -} - -REGRESSION_TEST(HPACK_EncodeString)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; - int len; - - // FIXME Current encoder don't support huffman conding. - for (unsigned int i = 0; i < 1; i++) { - memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); - - len = encode_string(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, string_test_case[i].raw_string, string_test_case[i].raw_string_len); - - box.check(len == string_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, - integer_test_case[i].encoded_field_len); - box.check(len > 0 && memcmp(buf, string_test_case[i].encoded_field, len) == 0, "encoded string was invalid"); - } -} - -REGRESSION_TEST(HPACK_EncodeIndexedHeaderField)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; - - for (unsigned int i = 0; i < sizeof(indexed_test_case) / sizeof(indexed_test_case[0]); i++) { - memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); - - int len = encode_indexed_header_field(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, indexed_test_case[i].index); - - box.check(len == indexed_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, - indexed_test_case[i].encoded_field_len); - box.check(len > 0 && memcmp(buf, indexed_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); - } -} - -REGRESSION_TEST(HPACK_EncodeLiteralHeaderField)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; - int len; - - for (unsigned int i = 0; i < sizeof(literal_test_case) / sizeof(literal_test_case[0]); i++) { - memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); - - ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); - headers->create(HTTP_TYPE_RESPONSE); - MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); - MIMEFieldWrapper header(field, headers->m_heap, headers->m_http->m_fields_impl); - - header.value_set(literal_test_case[i].raw_value, strlen(literal_test_case[i].raw_value)); - if (literal_test_case[i].index > 0) { - len = encode_literal_header_field(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, header, literal_test_case[i].index, - literal_test_case[i].type); - } else { - header.name_set(literal_test_case[i].raw_name, strlen(literal_test_case[i].raw_name)); - len = encode_literal_header_field(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, header, literal_test_case[i].type); - } - - box.check(len == literal_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, - literal_test_case[i].encoded_field_len); - box.check(len > 0 && memcmp(buf, literal_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); - } -} - -REGRESSION_TEST(HPACK_Encode)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; - Http2DynamicTable dynamic_table; - - // FIXME Current encoder don't support indexing. - for (unsigned int i = 0; i < sizeof(encoded_field_test_case) / sizeof(encoded_field_test_case[0]); i++) { - ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); - headers->create(HTTP_TYPE_REQUEST); - - for (unsigned int j = 0; j < sizeof(raw_field_test_case[i]) / sizeof(raw_field_test_case[i][0]); j++) { - const char *expected_name = raw_field_test_case[i][j].raw_name; - const char *expected_value = raw_field_test_case[i][j].raw_value; - if (strlen(expected_name) == 0) - break; - - MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); - mime_field_name_value_set(headers->m_heap, headers->m_http->m_fields_impl, field, -1, expected_name, strlen(expected_name), - expected_value, strlen(expected_value), 1, strlen(expected_name) + strlen(expected_value), true); - mime_hdr_field_attach(headers->m_http->m_fields_impl, field, 1, NULL); - } - - memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); - uint64_t buf_len = BUFSIZE_FOR_REGRESSION_TEST; - int64_t len = http2_write_psuedo_headers(headers, buf, buf_len, dynamic_table); - buf_len -= len; - - MIMEFieldIter field_iter; - bool cont = false; - len += http2_write_header_fragment(headers, field_iter, buf, buf_len, dynamic_table, cont); - - box.check(len == encoded_field_test_case[i].encoded_field_len, "encoded length was %" PRId64 ", expecting %d", len, - encoded_field_test_case[i].encoded_field_len); - box.check(len > 0 && memcmp(buf, encoded_field_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); - } -} - -REGRESSION_TEST(HPACK_DecodeInteger)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - uint32_t actual; - - for (unsigned int i = 0; i < sizeof(integer_test_case) / sizeof(integer_test_case[0]); i++) { - int len = - decode_integer(actual, integer_test_case[i].encoded_field, - integer_test_case[i].encoded_field + integer_test_case[i].encoded_field_len, integer_test_case[i].prefix); - - box.check(len == integer_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, - integer_test_case[i].encoded_field_len); - box.check(actual == integer_test_case[i].raw_integer, "decoded value was %d, expected %d", actual, - integer_test_case[i].raw_integer); - } -} - -REGRESSION_TEST(HPACK_DecodeString)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - Arena arena; - char *actual = NULL; - uint32_t actual_len = 0; - - hpack_huffman_init(); - - for (unsigned int i = 0; i < sizeof(string_test_case) / sizeof(string_test_case[0]); i++) { - int len = decode_string(arena, &actual, actual_len, string_test_case[i].encoded_field, - string_test_case[i].encoded_field + string_test_case[i].encoded_field_len); - - box.check(len == string_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, - string_test_case[i].encoded_field_len); - box.check(actual_len == string_test_case[i].raw_string_len, "length of decoded string was %d, expecting %d", actual_len, - string_test_case[i].raw_string_len); - box.check(memcmp(actual, string_test_case[i].raw_string, actual_len) == 0, "decoded string was invalid"); - } -} - -REGRESSION_TEST(HPACK_DecodeIndexedHeaderField)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - Http2DynamicTable dynamic_table; - - for (unsigned int i = 0; i < sizeof(indexed_test_case) / sizeof(indexed_test_case[0]); i++) { - ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); - headers->create(HTTP_TYPE_REQUEST); - MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); - MIMEFieldWrapper header(field, headers->m_heap, headers->m_http->m_fields_impl); - - int len = - decode_indexed_header_field(header, indexed_test_case[i].encoded_field, - indexed_test_case[i].encoded_field + indexed_test_case[i].encoded_field_len, dynamic_table); - - box.check(len == indexed_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, - indexed_test_case[i].encoded_field_len); - - int name_len; - const char *name = header.name_get(&name_len); - box.check(len > 0 && memcmp(name, indexed_test_case[i].raw_name, name_len) == 0, "decoded header name was invalid"); - - int actual_value_len; - const char *actual_value = header.value_get(&actual_value_len); - box.check(memcmp(actual_value, indexed_test_case[i].raw_value, actual_value_len) == 0, "decoded header value was invalid"); - } -} - -REGRESSION_TEST(HPACK_DecodeLiteralHeaderField)(RegressionTest *t, int, int *pstatus) +void forceLinkRegressionHPACK(); +void +forceLinkRegressionHPACKCaller() { - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - Http2DynamicTable dynamic_table; - - for (unsigned int i = 0; i < sizeof(literal_test_case) / sizeof(literal_test_case[0]); i++) { - ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); - headers->create(HTTP_TYPE_REQUEST); - MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); - MIMEFieldWrapper header(field, headers->m_heap, headers->m_http->m_fields_impl); - - int len = - decode_literal_header_field(header, literal_test_case[i].encoded_field, - literal_test_case[i].encoded_field + literal_test_case[i].encoded_field_len, dynamic_table); - - box.check(len == literal_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, - literal_test_case[i].encoded_field_len); - - int name_len; - const char *name = header.name_get(&name_len); - box.check(name_len > 0 && memcmp(name, literal_test_case[i].raw_name, name_len) == 0, "decoded header name was invalid"); - - int actual_value_len; - const char *actual_value = header.value_get(&actual_value_len); - box.check(actual_value_len > 0 && memcmp(actual_value, literal_test_case[i].raw_value, actual_value_len) == 0, - "decoded header value was invalid"); - } + forceLinkRegressionHPACK(); } -REGRESSION_TEST(HPACK_Decode)(RegressionTest *t, int, int *pstatus) -{ - TestBox box(t, pstatus); - box = REGRESSION_TEST_PASSED; - - Http2DynamicTable dynamic_table; - - for (unsigned int i = 0; i < sizeof(encoded_field_test_case) / sizeof(encoded_field_test_case[0]); i++) { - ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); - headers->create(HTTP_TYPE_REQUEST); - - http2_decode_header_blocks(headers, encoded_field_test_case[i].encoded_field, - encoded_field_test_case[i].encoded_field + encoded_field_test_case[i].encoded_field_len, - dynamic_table); - - for (unsigned int j = 0; j < sizeof(raw_field_test_case[i]) / sizeof(raw_field_test_case[i][0]); j++) { - const char *expected_name = raw_field_test_case[i][j].raw_name; - const char *expected_value = raw_field_test_case[i][j].raw_value; - if (strlen(expected_name) == 0) - break; - - MIMEField *field = headers->field_find(expected_name, strlen(expected_name)); - box.check(field != NULL, "A MIMEField that has \"%s\" as name doesn't exist", expected_name); - - if (field) { - int actual_value_len; - const char *actual_value = field->value_get(&actual_value_len); - box.check(strncmp(expected_value, actual_value, actual_value_len) == 0, - "A MIMEField that has \"%s\" as value doesn't exist", expected_value); - } - } - } -} +#include "ts/TestBox.h" /*********************************************************************************** * * @@ -1118,7 +742,6 @@ REGRESSION_TEST(HPACK_Decode)(RegressionTest *t, int, int *pstatus) * * ***********************************************************************************/ - const static struct { uint8_t ftype; uint8_t fflags; http://git-wip-us.apache.org/repos/asf/trafficserver/blob/df48fade/proxy/http2/Makefile.am ---------------------------------------------------------------------- diff --git a/proxy/http2/Makefile.am b/proxy/http2/Makefile.am index 5d526da..0db813c 100644 --- a/proxy/http2/Makefile.am +++ b/proxy/http2/Makefile.am @@ -47,6 +47,11 @@ libhttp2_a_SOURCES = \ HuffmanCodec.cc \ HuffmanCodec.h +if BUILD_TESTS + libhttp2_a_SOURCES += \ + RegressionHPACK.cc +endif + noinst_PROGRAMS = \ test_Huffmancode http://git-wip-us.apache.org/repos/asf/trafficserver/blob/df48fade/proxy/http2/RegressionHPACK.cc ---------------------------------------------------------------------- diff --git a/proxy/http2/RegressionHPACK.cc b/proxy/http2/RegressionHPACK.cc new file mode 100644 index 0000000..f4e8da8 --- /dev/null +++ b/proxy/http2/RegressionHPACK.cc @@ -0,0 +1,416 @@ +/** @file + * + * Regression Tests for HPACK + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "HTTP2.h" +#include "HPACK.h" +#include "HuffmanCodec.h" +#include "ts/TestBox.h" + +// Constants for regression test +const static int BUFSIZE_FOR_REGRESSION_TEST = 128; +const static int MAX_TEST_FIELD_NUM = 8; + +/*********************************************************************************** + * * + * Regression test for HPACK * + * * + * Some test cases are based on examples of specification. * + * - https://tools.ietf.org/html/rfc7541#appendix-C * + * * + ***********************************************************************************/ + +// [RFC 7541] C.1. Integer Representation Examples +const static struct { + uint32_t raw_integer; + uint8_t *encoded_field; + int encoded_field_len; + int prefix; +} integer_test_case[] = {{10, (uint8_t *) "\x0A", 1, 5}, {1337, (uint8_t *) "\x1F\x9A\x0A", 3, 5}, {42, (uint8_t *) "\x2A", 1, 8}}; + +// Example: custom-key: custom-header +const static struct { + char *raw_string; + uint32_t raw_string_len; + uint8_t *encoded_field; + int encoded_field_len; +} string_test_case[] = {{(char *)"custom-key", 10, (uint8_t *) "\xA" + "custom-key", + 11}, + {(char *)"custom-key", 10, (uint8_t *) "\x88" + "\x25\xa8\x49\xe9\x5b\xa9\x7d\x7f", + 9}}; + +// [RFC 7541] C.2.4. Indexed Header Field +const static struct { + int index; + char *raw_name; + char *raw_value; + uint8_t *encoded_field; + int encoded_field_len; +} indexed_test_case[] = {{2, (char *) ":method", (char *) "GET", (uint8_t *) "\x82", 1}}; + +// [RFC 7541] C.2. Header Field Representation Examples +const static struct { + char *raw_name; + char *raw_value; + int index; + HpackFieldType type; + uint8_t *encoded_field; + int encoded_field_len; +} literal_test_case[] = { + {(char *)"custom-key", (char *) "custom-header", 0, HPACK_FIELD_INDEXED_LITERAL, (uint8_t *) "\x40\x0a" + "custom-key\x0d" + "custom-header", + 26}, + {(char *)"custom-key", (char *) "custom-header", 0, HPACK_FIELD_NOINDEX_LITERAL, (uint8_t *) "\x00\x0a" + "custom-key\x0d" + "custom-header", + 26}, + {(char *)"custom-key", (char *) "custom-header", 0, HPACK_FIELD_NEVERINDEX_LITERAL, (uint8_t *) "\x10\x0a" + "custom-key\x0d" + "custom-header", + 26}, + {(char *)":path", (char *) "/sample/path", 4, HPACK_FIELD_INDEXED_LITERAL, (uint8_t *) "\x44\x0c" + "/sample/path", + 14}, + {(char *)":path", (char *) "/sample/path", 4, HPACK_FIELD_NOINDEX_LITERAL, (uint8_t *) "\x04\x0c" + "/sample/path", + 14}, + {(char *)":path", (char *) "/sample/path", 4, HPACK_FIELD_NEVERINDEX_LITERAL, (uint8_t *) "\x14\x0c" + "/sample/path", + 14}, + {(char *)"password", (char *) "secret", 0, HPACK_FIELD_INDEXED_LITERAL, (uint8_t *) "\x40\x08" + "password\x06" + "secret", + 17}, + {(char *)"password", (char *) "secret", 0, HPACK_FIELD_NOINDEX_LITERAL, (uint8_t *) "\x00\x08" + "password\x06" + "secret", + 17}, + {(char *)"password", (char *) "secret", 0, HPACK_FIELD_NEVERINDEX_LITERAL, (uint8_t *) "\x10\x08" + "password\x06" + "secret", + 17}}; + +// [RFC 7541] C.3. Request Examples without Huffman Coding - C.3.1. First Request +const static struct { + char *raw_name; + char *raw_value; +} raw_field_test_case[][MAX_TEST_FIELD_NUM] = {{ + {(char *)":method", (char *) "GET"}, + {(char *)":scheme", (char *) "http"}, + {(char *)":path", (char *) "/"}, + {(char *)":authority", (char *) "www.example.com"}, + {(char *)"", (char *) ""} // End of this test case +}}; +const static struct { + uint8_t *encoded_field; + int encoded_field_len; +} encoded_field_test_case[] = {{(uint8_t *)"\x40" + "\x7:method" + "\x3GET" + "\x40" + "\x7:scheme" + "\x4http" + "\x40" + "\x5:path" + "\x1/" + "\x40" + "\xa:authority" + "\xfwww.example.com", + 64}}; + +/*********************************************************************************** + * * + * Regression test codes * + * * + ***********************************************************************************/ + +REGRESSION_TEST(HPACK_EncodeInteger)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; + + for (unsigned int i = 0; i < sizeof(integer_test_case) / sizeof(integer_test_case[0]); i++) { + memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); + + int len = encode_integer(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, integer_test_case[i].raw_integer, integer_test_case[i].prefix); + + box.check(len == integer_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, + integer_test_case[i].encoded_field_len); + box.check(len > 0 && memcmp(buf, integer_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); + } +} + +REGRESSION_TEST(HPACK_EncodeString)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; + int len; + + // FIXME Current encoder don't support huffman conding. + for (unsigned int i = 0; i < 1; i++) { + memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); + + len = encode_string(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, string_test_case[i].raw_string, string_test_case[i].raw_string_len); + + box.check(len == string_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, + integer_test_case[i].encoded_field_len); + box.check(len > 0 && memcmp(buf, string_test_case[i].encoded_field, len) == 0, "encoded string was invalid"); + } +} + +REGRESSION_TEST(HPACK_EncodeIndexedHeaderField)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; + + for (unsigned int i = 0; i < sizeof(indexed_test_case) / sizeof(indexed_test_case[0]); i++) { + memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); + + int len = encode_indexed_header_field(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, indexed_test_case[i].index); + + box.check(len == indexed_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, + indexed_test_case[i].encoded_field_len); + box.check(len > 0 && memcmp(buf, indexed_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); + } +} + +REGRESSION_TEST(HPACK_EncodeLiteralHeaderField)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; + int len; + + for (unsigned int i = 0; i < sizeof(literal_test_case) / sizeof(literal_test_case[0]); i++) { + memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); + + ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); + headers->create(HTTP_TYPE_RESPONSE); + MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); + MIMEFieldWrapper header(field, headers->m_heap, headers->m_http->m_fields_impl); + + header.value_set(literal_test_case[i].raw_value, strlen(literal_test_case[i].raw_value)); + if (literal_test_case[i].index > 0) { + len = encode_literal_header_field(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, header, literal_test_case[i].index, + literal_test_case[i].type); + } else { + header.name_set(literal_test_case[i].raw_name, strlen(literal_test_case[i].raw_name)); + len = encode_literal_header_field(buf, buf + BUFSIZE_FOR_REGRESSION_TEST, header, literal_test_case[i].type); + } + + box.check(len == literal_test_case[i].encoded_field_len, "encoded length was %d, expecting %d", len, + literal_test_case[i].encoded_field_len); + box.check(len > 0 && memcmp(buf, literal_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); + } +} + +REGRESSION_TEST(HPACK_Encode)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + uint8_t buf[BUFSIZE_FOR_REGRESSION_TEST]; + Http2DynamicTable dynamic_table; + + // FIXME Current encoder don't support indexing. + for (unsigned int i = 0; i < sizeof(encoded_field_test_case) / sizeof(encoded_field_test_case[0]); i++) { + ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); + headers->create(HTTP_TYPE_REQUEST); + + for (unsigned int j = 0; j < sizeof(raw_field_test_case[i]) / sizeof(raw_field_test_case[i][0]); j++) { + const char *expected_name = raw_field_test_case[i][j].raw_name; + const char *expected_value = raw_field_test_case[i][j].raw_value; + if (strlen(expected_name) == 0) + break; + + MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); + mime_field_name_value_set(headers->m_heap, headers->m_http->m_fields_impl, field, -1, expected_name, strlen(expected_name), + expected_value, strlen(expected_value), 1, strlen(expected_name) + strlen(expected_value), true); + mime_hdr_field_attach(headers->m_http->m_fields_impl, field, 1, NULL); + } + + memset(buf, 0, BUFSIZE_FOR_REGRESSION_TEST); + uint64_t buf_len = BUFSIZE_FOR_REGRESSION_TEST; + int64_t len = http2_write_psuedo_headers(headers, buf, buf_len, dynamic_table); + buf_len -= len; + + MIMEFieldIter field_iter; + bool cont = false; + len += http2_write_header_fragment(headers, field_iter, buf, buf_len, dynamic_table, cont); + + box.check(len == encoded_field_test_case[i].encoded_field_len, "encoded length was %" PRId64 ", expecting %d", len, + encoded_field_test_case[i].encoded_field_len); + box.check(len > 0 && memcmp(buf, encoded_field_test_case[i].encoded_field, len) == 0, "encoded value was invalid"); + } +} + +REGRESSION_TEST(HPACK_DecodeInteger)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + uint32_t actual; + + for (unsigned int i = 0; i < sizeof(integer_test_case) / sizeof(integer_test_case[0]); i++) { + int len = + decode_integer(actual, integer_test_case[i].encoded_field, + integer_test_case[i].encoded_field + integer_test_case[i].encoded_field_len, integer_test_case[i].prefix); + + box.check(len == integer_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, + integer_test_case[i].encoded_field_len); + box.check(actual == integer_test_case[i].raw_integer, "decoded value was %d, expected %d", actual, + integer_test_case[i].raw_integer); + } +} + +REGRESSION_TEST(HPACK_DecodeString)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + Arena arena; + char *actual = NULL; + uint32_t actual_len = 0; + + hpack_huffman_init(); + + for (unsigned int i = 0; i < sizeof(string_test_case) / sizeof(string_test_case[0]); i++) { + int len = decode_string(arena, &actual, actual_len, string_test_case[i].encoded_field, + string_test_case[i].encoded_field + string_test_case[i].encoded_field_len); + + box.check(len == string_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, + string_test_case[i].encoded_field_len); + box.check(actual_len == string_test_case[i].raw_string_len, "length of decoded string was %d, expecting %d", actual_len, + string_test_case[i].raw_string_len); + box.check(memcmp(actual, string_test_case[i].raw_string, actual_len) == 0, "decoded string was invalid"); + } +} + +REGRESSION_TEST(HPACK_DecodeIndexedHeaderField)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + Http2DynamicTable dynamic_table; + + for (unsigned int i = 0; i < sizeof(indexed_test_case) / sizeof(indexed_test_case[0]); i++) { + ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); + headers->create(HTTP_TYPE_REQUEST); + MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); + MIMEFieldWrapper header(field, headers->m_heap, headers->m_http->m_fields_impl); + + int len = + decode_indexed_header_field(header, indexed_test_case[i].encoded_field, + indexed_test_case[i].encoded_field + indexed_test_case[i].encoded_field_len, dynamic_table); + + box.check(len == indexed_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, + indexed_test_case[i].encoded_field_len); + + int name_len; + const char *name = header.name_get(&name_len); + box.check(len > 0 && memcmp(name, indexed_test_case[i].raw_name, name_len) == 0, "decoded header name was invalid"); + + int actual_value_len; + const char *actual_value = header.value_get(&actual_value_len); + box.check(memcmp(actual_value, indexed_test_case[i].raw_value, actual_value_len) == 0, "decoded header value was invalid"); + } +} + +REGRESSION_TEST(HPACK_DecodeLiteralHeaderField)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + Http2DynamicTable dynamic_table; + + for (unsigned int i = 0; i < sizeof(literal_test_case) / sizeof(literal_test_case[0]); i++) { + ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); + headers->create(HTTP_TYPE_REQUEST); + MIMEField *field = mime_field_create(headers->m_heap, headers->m_http->m_fields_impl); + MIMEFieldWrapper header(field, headers->m_heap, headers->m_http->m_fields_impl); + + int len = + decode_literal_header_field(header, literal_test_case[i].encoded_field, + literal_test_case[i].encoded_field + literal_test_case[i].encoded_field_len, dynamic_table); + + box.check(len == literal_test_case[i].encoded_field_len, "decoded length was %d, expecting %d", len, + literal_test_case[i].encoded_field_len); + + int name_len; + const char *name = header.name_get(&name_len); + box.check(name_len > 0 && memcmp(name, literal_test_case[i].raw_name, name_len) == 0, "decoded header name was invalid"); + + int actual_value_len; + const char *actual_value = header.value_get(&actual_value_len); + box.check(actual_value_len > 0 && memcmp(actual_value, literal_test_case[i].raw_value, actual_value_len) == 0, + "decoded header value was invalid"); + } +} + +REGRESSION_TEST(HPACK_Decode)(RegressionTest *t, int, int *pstatus) +{ + TestBox box(t, pstatus); + box = REGRESSION_TEST_PASSED; + + Http2DynamicTable dynamic_table; + + for (unsigned int i = 0; i < sizeof(encoded_field_test_case) / sizeof(encoded_field_test_case[0]); i++) { + ats_scoped_obj<HTTPHdr> headers(new HTTPHdr); + headers->create(HTTP_TYPE_REQUEST); + + http2_decode_header_blocks(headers, encoded_field_test_case[i].encoded_field, + encoded_field_test_case[i].encoded_field + encoded_field_test_case[i].encoded_field_len, + dynamic_table); + + for (unsigned int j = 0; j < sizeof(raw_field_test_case[i]) / sizeof(raw_field_test_case[i][0]); j++) { + const char *expected_name = raw_field_test_case[i][j].raw_name; + const char *expected_value = raw_field_test_case[i][j].raw_value; + if (strlen(expected_name) == 0) + break; + + MIMEField *field = headers->field_find(expected_name, strlen(expected_name)); + box.check(field != NULL, "A MIMEField that has \"%s\" as name doesn't exist", expected_name); + + if (field) { + int actual_value_len; + const char *actual_value = field->value_get(&actual_value_len); + box.check(strncmp(expected_value, actual_value, actual_value_len) == 0, + "A MIMEField that has \"%s\" as value doesn't exist", expected_value); + } + } + } +} + +void +forceLinkRegressionHPACK() +{ + // NOTE: Do Nothing +}
