#include "gtest/gtest.h" #include "nsString.h" #include #include #include "nsTArray.h" struct ParsedHeader { nsCString name; nsTArray value; }; extern "C" { using HeaderCallback = void (*)(void* user_data, const uint8_t* name_ptr, size_t name_len, const uint8_t* value_ptr, size_t value_len); bool neqo_glue_test_parse_headers(const nsACString* headers_input, HeaderCallback callback, void* user_data); // C callback that collects headers into a vector void collect_header_callback(void* user_data, const uint8_t* name_ptr, size_t name_len, const uint8_t* value_ptr, size_t value_len) { auto* headers = static_cast*>(user_data); ParsedHeader header; header.name.Assign(reinterpret_cast(name_ptr), name_len); header.value.AppendElements(value_ptr, value_len); headers->push_back(std::move(header)); } } TEST(TestParseHeaders, ParseHeadersBasic) { nsAutoCString headers; headers.AssignLiteral( "\r\nContent-Type: text/html\r\nContent-Length: 1234\r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_TRUE(success); ASSERT_EQ(parsed_headers.size(), 2U); EXPECT_TRUE(parsed_headers[0].name.EqualsLiteral("content-type")); EXPECT_EQ(parsed_headers[0].value.Length(), 9U); EXPECT_EQ(0, memcmp(parsed_headers[0].value.Elements(), "text/html", 9)); EXPECT_TRUE(parsed_headers[1].name.EqualsLiteral("content-length")); EXPECT_EQ(parsed_headers[1].value.Length(), 4U); EXPECT_EQ(0, memcmp(parsed_headers[1].value.Elements(), "1234", 4)); } TEST(TestParseHeaders, ParseHeadersWithWhitespace) { nsAutoCString headers; headers.AssignLiteral( "\r\n X-Custom : test-value \r\nUser-Agent: Mozilla/5.0 \r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_TRUE(success); ASSERT_EQ(parsed_headers.size(), 2U); EXPECT_TRUE(parsed_headers[0].name.EqualsLiteral("x-custom")); EXPECT_EQ(parsed_headers[0].value.Length(), 10U); EXPECT_EQ(0, memcmp(parsed_headers[0].value.Elements(), "test-value", 10)); EXPECT_TRUE(parsed_headers[1].name.EqualsLiteral("user-agent")); EXPECT_EQ(parsed_headers[1].value.Length(), 11U); EXPECT_EQ(0, memcmp(parsed_headers[1].value.Elements(), "Mozilla/5.0", 11)); } TEST(TestParseHeaders, ParseHeadersWithColonInValue) { nsAutoCString headers; headers.AssignLiteral("\r\nLocation: http://example.com:8080/path\r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_TRUE(success); ASSERT_EQ(parsed_headers.size(), 1U); EXPECT_TRUE(parsed_headers[0].name.EqualsLiteral("location")); EXPECT_EQ(parsed_headers[0].value.Length(), 28U); EXPECT_EQ(0, memcmp(parsed_headers[0].value.Elements(), "http://example.com:8080/path", 28)); } TEST(TestParseHeaders, ParseHeadersNonUtf8Value) { // Create headers with non-UTF8 bytes in the value nsAutoCString headers; headers.AssignLiteral("\r\nX-Custom: "); headers.Append(char(0xFF)); headers.Append(char(0xFE)); headers.Append(char(0xFD)); headers.AppendLiteral("\r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_TRUE(success); ASSERT_EQ(parsed_headers.size(), 1U); EXPECT_TRUE(parsed_headers[0].name.EqualsLiteral("x-custom")); EXPECT_EQ(parsed_headers[0].value.Length(), 3U); EXPECT_EQ(parsed_headers[0].value[0], 0xFF); EXPECT_EQ(parsed_headers[0].value[1], 0xFE); EXPECT_EQ(parsed_headers[0].value[2], 0xFD); } TEST(TestParseHeaders, ParseHeadersExcludesColonHeaders) { nsAutoCString headers; headers.AssignLiteral("\r\n:method: GET\r\nContent-Type: text/html\r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_TRUE(success); ASSERT_EQ(parsed_headers.size(), 1U); // :method should be excluded EXPECT_TRUE(parsed_headers[0].name.EqualsLiteral("content-type")); } TEST(TestParseHeaders, ParseHeadersExcludesForbiddenHeaders) { nsAutoCString headers; headers.AssignLiteral( "\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nHost: " "example.com\r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_TRUE(success); ASSERT_EQ(parsed_headers.size(), 1U); // Connection and Host should be excluded EXPECT_TRUE(parsed_headers[0].name.EqualsLiteral("content-type")); } TEST(TestParseHeaders, ParseHeadersEmptyValue) { nsAutoCString headers; headers.AssignLiteral("\r\nX-Empty:\r\nX-Spaces: \r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_TRUE(success); ASSERT_EQ(parsed_headers.size(), 2U); EXPECT_TRUE(parsed_headers[0].name.EqualsLiteral("x-empty")); EXPECT_EQ(parsed_headers[0].value.Length(), 0U); EXPECT_TRUE(parsed_headers[1].name.EqualsLiteral("x-spaces")); EXPECT_EQ(parsed_headers[1].value.Length(), 0U); } TEST(TestParseHeaders, ParseHeadersInvalidUtf8Name) { // Create headers with non-UTF8 bytes in the name nsAutoCString headers; headers.AssignLiteral("\r\n"); headers.Append(char(0xFF)); headers.Append(char(0xFE)); headers.AppendLiteral(": value\r\n"); std::vector parsed_headers; bool success = neqo_glue_test_parse_headers(&headers, collect_header_callback, &parsed_headers); EXPECT_FALSE(success); // Should fail with invalid UTF-8 in name }