END_TEST START_TEST (indexed_many_zeroes) { /* According to the draft a large number of zero values MUST be treated as a decoding error. This test sends a considerable number of zeroes as an index header field. */ ret_t ret; chula_buffer_t raw; hpack_header_parser_t parser; hpack_header_field_t field; unsigned int consumed = 0; char data[256]; data[0] = 0xFF; memset(data+1, 0x80, sizeof(data)-1); data[sizeof(data)-1] |= 1; hpack_header_parser_init (&parser); hpack_header_field_init (&field); chula_buffer_fake_str (&raw, data); ret = hpack_header_parser_field (&parser, &raw, 0, &field, &consumed); ck_assert (ret != ret_ok); ck_assert (consumed == 0); hpack_header_parser_mrproper (&parser); }
END_TEST START_TEST (_set_max_no_table_evict_all) { ret_t ret; hpack_header_table_t *table; hpack_header_field_t field; hpack_set_t evicted; ret = hpack_header_table_new (&table); ch_assert (ret == ret_ok); hpack_header_field_init (&field); chula_buffer_add_str (&field.name, "custom-key"); chula_buffer_add_str (&field.value, "custom-header"); for (int i=0; i<10; ++i) { ret = hpack_header_table_add (table, &field, evicted); ch_assert (ret_ok == ret); ch_assert (hpack_set_is_empty(evicted)); } ret = hpack_header_table_set_max (table, 0, evicted); ch_assert (ret == ret_ok); hpack_set_is_full(evicted); ch_assert (table->max_data == 0); check_table_empty (table); hpack_header_field_mrproper (&field); hpack_header_table_free (table); }
END_TEST START_TEST (indexed_big_value) { /* This test tries to break the header field parsing by sending an index header field too big to be hold by a signed integer (works with 32bit and 64bit machines). Since it's too big, and given the algorith of the VLQ it gets transformed into a negative number if this has not been taken into account. As a negative number it would pass many of the test conditions for sizes. */ ret_t ret; chula_buffer_t raw; hpack_header_parser_t parser; hpack_header_field_t field; unsigned int consumed = 0; const char *data64 = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; const char *data32 = "\xFF\xFF\xFF\xFF\xFF\x0A"; hpack_header_parser_init (&parser); hpack_header_field_init (&field); chula_buffer_fake_str (&raw, sizeof(int)>32?data64:data32); ret = hpack_header_parser_field (&parser, &raw, 0, &field, &consumed); ck_assert (ret != ret_ok); ck_assert (consumed == 0); hpack_header_parser_mrproper (&parser); }
END_TEST START_TEST (literal_wo_index) { ret_t ret; chula_buffer_t raw; hpack_header_parser_t parser; hpack_header_field_t field; unsigned int consumed = 0; /* 44 | == Literal not indexed == | Indexed name (idx = 4) :path 0c | Literal value (len = 12) 2f73 616d 706c 652f 7061 7468 | /sample/path */ hpack_header_parser_init (&parser); hpack_header_field_init (&field); chula_buffer_fake_str (&raw, "\x44\x0c\x2f\x73\x61\x6d\x70\x6c\x65\x2f\x70\x61\x74\x68"); ret = hpack_header_parser_field (&parser, &raw, 0, &field, &consumed); ck_assert (ret == ret_ok); ck_assert (consumed == raw.len); ck_assert_str_eq (field.name.buf, ":path"); ck_assert_str_eq (field.value.buf, "/sample/path"); hpack_header_parser_mrproper (&parser); }
END_TEST START_TEST (_add_doesnt_fit) { ret_t ret; hpack_header_table_t *table; hpack_set_t evicted_set; hpack_header_field_t field; ret = hpack_header_table_new (&table); ch_assert (ret == ret_ok); hpack_header_field_init (&field); ret = chula_buffer_ensure_size (&field.name, (size_t) (table->max_data/2)); ch_assert (ret_ok == ret); ret = chula_buffer_ensure_size (&field.value, (size_t) (table->max_data/2)); ch_assert (ret_ok == ret); for (unsigned int i=0; i<field.name.len; ++i) { ret = chula_buffer_add_char (&field.name, '0' + (i%10)); ch_assert (ret_ok == ret); ret = chula_buffer_add_char (&field.value, '9' - (i%10)); ch_assert (ret_ok == ret); } ret = hpack_header_table_add (table, &field, evicted_set); ch_assert (ret_ok == ret); hpack_set_is_full (evicted_set); /* Clean up */ hpack_header_field_mrproper (&field); hpack_header_table_free (table); }
END_TEST START_TEST (literal_w_index_false_len) { /* This test tries to break the header field parsing by sending string literals with a string length field too big to be hold by a signed integer (works with 32bit and 64bit machines). Since it's too big, and given the algorith of the VLQ it gets transformed into a negative number if this has not been taken into account. As a negative number it would pass many of the test conditions for sizes. For a 32 bits machine: 00 | == Literal indexed == ffff ffff ff0a | Literal name (len = 2952790270). Since it's bigger than INT_MAX (2147483647) it results in a negative number. 6375 7374 6f6d 2d6b 6579 | custom-key 0d | Literal value (len = 13) 6375 7374 6f6d 2d68 6561 6465 72 | custom-header For a 64 bits machine: 00 | == Literal indexed == ffff ffff ffff ffff ffff | Literal name (len = 9223372036854776062). Since it's bigger than INT_MAX (9223372036854775807) it results in a negative number 6375 7374 6f6d 2d6b 6579 | custom-key 0d | Literal value (len = 13) 6375 7374 6f6d 2d68 6561 6465 72 | custom-header */ ret_t ret; chula_buffer_t raw; hpack_header_parser_t parser; hpack_header_field_t field; unsigned int consumed = 0; const char *data64 = "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\x0d\x63\x75\x73\x74\x6f\x6d\x2d\x68\x65\x61\x64\x65\x72"; const char *data32 = "\x00\xFF\xFF\xFF\xFF\xFF\x0A\x63\x75\x73\x74\x6f\x6d\x2d\x6b\x65\x79\0d\x63\x75\x73\x74\x6f\x6d\x2d\x68\x65\x61\x64\x65\x72"; hpack_header_parser_init (&parser); hpack_header_field_init (&field); chula_buffer_fake_str (&raw, sizeof(int)>32?data64:data32); ret = hpack_header_parser_field (&parser, &raw, 0, &field, &consumed); ck_assert (ret != ret_ok); ck_assert (consumed == 0); hpack_header_parser_mrproper (&parser); }
END_TEST START_TEST (indexed) { ret_t ret; chula_buffer_t raw; hpack_header_parser_t parser; hpack_header_field_t field; unsigned int consumed = 0; hpack_header_parser_init (&parser); hpack_header_field_init (&field); chula_buffer_fake_str (&raw, "\x82"); ret = hpack_header_parser_field (&parser, &raw, 0, &field, &consumed); ck_assert (ret == ret_ok); ck_assert (consumed == raw.len); ck_assert_str_eq (field.name.buf, ":method"); ck_assert_str_eq (field.value.buf, "GET"); hpack_header_parser_mrproper (&parser); }
END_TEST // //START_TEST (_add_multi_evac) { //END_TEST // START_TEST (field_get_len) { ret_t ret; uint64_t len; hpack_header_field_t field; ret = hpack_header_field_init (&field); ch_assert (ret == ret_ok); len = 1234; ret = hpack_header_field_get_size (&field, &len); ch_assert (ret == ret_ok); ch_assert (len == 0); /* (s = 55) custom-key: custom-header */ chula_buffer_add_str (&field.name, "custom-key"); chula_buffer_add_str (&field.value, "custom-header"); ret = hpack_header_field_get_size (&field, &len); ch_assert (ret == ret_ok); ch_assert (len == 55); /* (s = 42) :method: GET */ hpack_header_field_clean (&field); chula_buffer_add_str (&field.name, ":method"); chula_buffer_add_str (&field.value, "GET"); ret = hpack_header_field_get_size (&field, &len); ch_assert (ret == ret_ok); ch_assert (len == 42); ret = hpack_header_field_mrproper (&field); ch_assert (ret == ret_ok); }
END_TEST START_TEST (request1) { ret_t ret; chula_buffer_t raw; hpack_header_parser_t parser; hpack_header_field_t field; unsigned int offset = 0; unsigned int consumed = 0; /* 82 | == Indexed - Add == | idx = 2 | -> :method: GET 87 | == Indexed - Add == | idx = 7 | -> :scheme: http 86 | == Indexed - Add == | idx = 6 | -> :path: / 04 | == Literal indexed == | Indexed name (idx = 4) | :authority 0f | Literal value (len = 15) 7777 772e 6578 616d 706c 652e 636f 6d | www.example.com | -> :authority: www.example.com */ chula_buffer_fake_str (&raw, "\x82\x87\x86\x04\x0f\x77\x77\x77\x2e\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d"); hpack_header_field_init (&field); hpack_header_parser_init (&parser); chula_print_repr (chula, buffer, &raw); /* 82 - :method: GET */ ret = hpack_header_parser_field (&parser, &raw, offset, &field, &consumed); ck_assert (ret == ret_ok); ck_assert (consumed == 1); ck_assert_str_eq (field.name.buf, ":method"); ck_assert_str_eq (field.value.buf, "GET"); chula_print_repr (hpack, header_field, &field); offset += consumed; ck_assert (offset == 1); memset (&field, 0, sizeof(field)); /* 87 - :scheme: http */ ret = hpack_header_parser_field (&parser, &raw, offset, &field, &consumed); ck_assert (ret == ret_ok); ck_assert (consumed == 1); ck_assert_str_eq (field.name.buf, ":scheme"); ck_assert_str_eq (field.value.buf, "http"); chula_print_repr (hpack, header_field, &field); offset += consumed; ck_assert (offset == 2); memset (&field, 0, sizeof(field)); /* 86 - :path: / */ ret = hpack_header_parser_field (&parser, &raw, offset, &field, &consumed); ck_assert (ret == ret_ok); ck_assert (consumed == 1); ck_assert_str_eq (field.name.buf, ":path"); ck_assert_str_eq (field.value.buf, "/"); chula_print_repr (hpack, header_field, &field); offset += consumed; ck_assert (offset == 3); memset (&field, 0, sizeof(field)); /* 04 - :authority: www.example.com */ ret = hpack_header_parser_field (&parser, &raw, offset, &field, &consumed); ck_assert (ret == ret_ok); ck_assert (consumed == 17); ck_assert_str_eq (field.name.buf, ":authority"); ck_assert_str_eq (field.value.buf, "www.example.com"); chula_print_repr (hpack, header_field, &field); hpack_header_parser_mrproper (&parser); }
END_TEST START_TEST (_add_some_evacs) { ret_t ret; hpack_header_table_t *table; hpack_set_t evicted_set; hpack_header_field_t fields[6]; hpack_header_field_t field; uint64_t size; uint16_t max_size; ret = hpack_header_table_new (&table); ch_assert (ret == ret_ok); for (int i=0; i<6; i++) { hpack_header_field_init (&fields[i]); chula_buffer_add_va (&fields[i].name, "%c-%d", 'a'+i, i); chula_buffer_add_va (&fields[i].value, "foobar-%d", i); } ret = hpack_header_field_get_size (&fields[0], &size); ch_assert (ret == ret_ok); max_size = (uint16_t) size * 4.5; ret = hpack_header_table_set_max (table, max_size, evicted_set); ch_assert (ret == ret_ok); for (int i=0; i<6; i++) { ret = hpack_header_table_add (table, &fields[i], evicted_set); ch_assert (ret_ok == ret); if (i < 4) { ch_assert (hpack_set_is_empty (evicted_set)); } else { /* Since only 4 items fit, whenever we add more we lose the oldest one, that is the fifth element */ ch_assert (hpack_header_table_set_exists (table, evicted_set, 5)); } } ch_assert (table->num_headers == 4); /* Make sure relevant contents are in there */ hpack_header_field_init (&field); for (int i=1; i<5; ++i) { bool is_static; ret = hpack_header_table_get (table, i, false, &field, &is_static); ch_assert (ret == ret_ok); ch_assert_str_eq (field.name.buf, fields[6-i].name.buf); ch_assert_str_eq (field.value.buf, fields[6-i].value.buf); ch_assert (field.flags.rep == fields[6-i].flags.rep); ch_assert (field.flags.name == fields[6-i].flags.name); ch_assert (field.flags.value == fields[6-i].flags.value); hpack_header_field_clean (&field); } /* Check that table's contents are OK*/ ch_assert (table->used_data == size * 4); ch_assert (table->headers_offsets.head == 2); ch_assert (table->headers_offsets.head == table->headers_offsets.tail - 4); /* Calculate the size of each field in table->headers_data */ size -= HPACK_HEADER_ENTRY_OVERHEAD - sizeof(hpack_header_table_field_info_t); ch_assert (table->headers_data.head == size * 2); ch_assert (table->headers_data.head == table->headers_data.tail - (size * 4)); /* Clean up */ hpack_header_field_mrproper (&field); for (int i=0; i<6; ++i) { hpack_header_field_mrproper (&fields[i]); } hpack_header_table_free (table); }
END_TEST START_TEST (_add_fits) { ret_t ret; hpack_header_table_t *table; hpack_set_t evicted_set; hpack_header_field_t fields[6]; hpack_header_field_t field; uint64_t size; uint16_t real_size; ret = hpack_header_table_new (&table); ch_assert (ret == ret_ok); for (int i=0; i<6; i++) { hpack_header_field_init (&fields[i]); chula_buffer_add_va (&fields[i].name, "%c-%d", 'a'+i, i); chula_buffer_add_va (&fields[i].value, "foobar-%d", i); } ret = hpack_header_field_get_size (&fields[0], &size); ch_assert (ret == ret_ok); real_size = fields[0].name.len + fields[0].value.len + sizeof(hpack_header_table_field_info_t); for (int i=0; i<6; i++) { ret = hpack_header_table_add (table, &fields[i], evicted_set); ch_assert (ret_ok == ret); /* Confirm that there's been no evictions */ ch_assert (hpack_set_is_empty (evicted_set)); /* Confirm that the number of headers in the table is OK */ ch_assert (i+1 == table->num_headers); /* Confirm that the "used data" is correct */ ch_assert ((i+1) * size == table->used_data); /* Confirm that the offset head is in place */ ch_assert (0 == table->headers_offsets.head); /* Confirm that the offsets tail is ok */ ch_assert (i+1 == table->headers_offsets.tail); /* Confirm that the data head is in place */ ch_assert (0 == table->headers_data.head); /* Confirm that the data tail is ok */ ch_assert ((i+1) * real_size == table->headers_data.tail); /* Confirm that the offset reference is correct */ ch_assert (table->headers_offsets.buffer[i] == i * real_size); } ch_assert (table->num_headers == 6); /* Check that returned fields are OK */ hpack_header_field_init (&field); for (int i=1; i<7; ++i) { bool is_static; ret = hpack_header_table_get (table, i, false, &field, &is_static); ch_assert (ret == ret_ok); ch_assert (field.flags.rep == fields[6-i].flags.rep); ch_assert (field.flags.name == fields[6-i].flags.name); ch_assert (field.flags.value == fields[6-i].flags.value); ch_assert_str_eq (field.name.buf, fields[6-i].name.buf); ch_assert_str_eq (field.value.buf, fields[6-i].value.buf); hpack_header_field_clean (&field); } /* Check that table's contents are OK*/ ret = hpack_header_field_get_size (&fields[0], &size); ch_assert (ret == ret_ok); ch_assert (table->used_data == 6 * size); ch_assert (table->headers_offsets.head == 0); ch_assert (table->headers_offsets.head + 6 == table->headers_offsets.tail); /* Calculate the size of each field in table->headers_data */ size -= HPACK_HEADER_ENTRY_OVERHEAD - sizeof(hpack_header_table_field_info_t); ch_assert (table->headers_data.head == 0); ch_assert (table->headers_data.head == table->headers_data.tail - (size *6)); /* Clean up */ hpack_header_field_mrproper (&field); for (int i=0; i<6; ++i) { hpack_header_field_mrproper (&fields[i]); } hpack_header_table_free (table); }
END_TEST START_TEST (_set_max_no_table_evict_some) { ret_t ret; hpack_header_table_t *table; hpack_header_field_t field; hpack_set_t evicted; uint64_t size; uint16_t used_bytes; uint16_t max_size; hpack_set_iterator_t iter; ret = hpack_header_table_new (&table); ch_assert (ret == ret_ok); hpack_header_field_init (&field); chula_buffer_add_str (&field.name, "custom-key"); chula_buffer_add_str (&field.value, "custom-header"); for (int i=0; i<10; ++i) { ret = hpack_header_table_add (table, &field, evicted); ch_assert (ret_ok == ret); /* Confirm that there's been no evictions */ ch_assert (hpack_set_is_empty (evicted)); /* Confirm that the number of headers in the table is OK */ ch_assert (i+1 == table->num_headers); /* Confirm that the "used data" is correct */ ch_assert ((i+1) * (23+32) == table->used_data); /* Confirm that the offset head is in place */ ch_assert (0 == table->headers_offsets.head); /* Confirm that the offsets tail is ok */ ch_assert (i+1 == table->headers_offsets.tail); /* Confirm that the data head is in place */ ch_assert (0 == table->headers_data.head); /* Confirm that the data tail is ok */ ch_assert ((i+1) * (23+sizeof(hpack_header_table_field_info_t)) == table->headers_data.tail); /* Confirm that the offset reference is correct */ ch_assert (table->headers_offsets.buffer[i] == i * (23+sizeof(hpack_header_table_field_info_t))); } /* We'll leave only space for 3 and 1/2 elements */ ret = hpack_header_field_get_size (&field, &size); ch_assert (ret == ret_ok); used_bytes = (uint16_t) size * 3; max_size = (uint16_t) size * 3.5; ret = hpack_header_table_set_max (table, max_size, evicted); ch_assert (ret == ret_ok); hpack_header_table_iter_init (&iter, evicted); for (int i=0; i<8; ++i) { int e = hpack_header_table_iter_next (table, &iter); ch_assert (e == (i==7?-1:10-i)); } ch_assert (table->max_data == max_size); ch_assert (table->num_headers == 3); ch_assert (table->used_data == used_bytes); ch_assert (table->headers_offsets.head == 7); ch_assert (table->headers_offsets.head == table->headers_offsets.tail - 3); /* Calculate the size of each field in table->headers_data */ size -= HPACK_HEADER_ENTRY_OVERHEAD - sizeof(hpack_header_table_field_info_t); ch_assert (table->headers_data.head == size * 7); ch_assert (table->headers_data.head == table->headers_data.tail - (size * 3)); hpack_header_field_mrproper (&field); hpack_header_table_free (table); }