| // Copyright 2022 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Note that several of the BalsaHeaders functions are | |
| // tested in the balsa_frame_test as the BalsaFrame and | |
| // BalsaHeaders classes are fairly related. | |
| #include "quiche/balsa/balsa_headers.h" | |
| #include <cstring> | |
| #include <limits> | |
| #include <memory> | |
| #include <sstream> | |
| #include <string> | |
| #include <tuple> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/base/macros.h" | |
| #include "absl/memory/memory.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/balsa/balsa_enums.h" | |
| #include "quiche/balsa/balsa_frame.h" | |
| #include "quiche/balsa/simple_buffer.h" | |
| #include "quiche/common/platform/api/quiche_expect_bug.h" | |
| #include "quiche/common/platform/api/quiche_logging.h" | |
| #include "quiche/common/platform/api/quiche_test.h" | |
| using absl::make_unique; | |
| using testing::AnyOf; | |
| using testing::Combine; | |
| using testing::ElementsAre; | |
| using testing::Eq; | |
| using testing::StrEq; | |
| using testing::ValuesIn; | |
| namespace quiche { | |
| namespace test { | |
| class BalsaHeadersTestPeer { | |
| public: | |
| static void WriteFromFramer(BalsaHeaders* headers, const char* ptr, | |
| size_t size) { | |
| headers->WriteFromFramer(ptr, size); | |
| } | |
| }; | |
| namespace { | |
| class BalsaBufferTest : public QuicheTest { | |
| public: | |
| void CreateBuffer(size_t blocksize) { | |
| buffer_ = std::make_unique<BalsaBuffer>(blocksize); | |
| } | |
| void CreateBuffer() { buffer_ = std::make_unique<BalsaBuffer>(); } | |
| static std::unique_ptr<BalsaBuffer> CreateUnmanagedBuffer(size_t blocksize) { | |
| return std::make_unique<BalsaBuffer>(blocksize); | |
| } | |
| absl::string_view Write(absl::string_view sp, size_t* block_buffer_idx) { | |
| if (sp.empty()) { | |
| return sp; | |
| } | |
| char* storage = buffer_->Reserve(sp.size(), block_buffer_idx); | |
| memcpy(storage, sp.data(), sp.size()); | |
| return absl::string_view(storage, sp.size()); | |
| } | |
| protected: | |
| std::unique_ptr<BalsaBuffer> buffer_; | |
| }; | |
| using BufferBlock = BalsaBuffer::BufferBlock; | |
| BufferBlock MakeBufferBlock(const std::string& s) { | |
| // Make the buffer twice the size needed to verify that CopyFrom copies our | |
| // buffer_size (as opposed to shrinking to fit or reusing an old buffer). | |
| BufferBlock block{make_unique<char[]>(s.size()), s.size() * 2, s.size()}; | |
| std::memcpy(block.buffer.get(), s.data(), s.size()); | |
| return block; | |
| } | |
| BalsaHeaders CreateHTTPHeaders(bool request, absl::string_view s) { | |
| BalsaHeaders headers; | |
| BalsaFrame framer; | |
| framer.set_is_request(request); | |
| framer.set_balsa_headers(&headers); | |
| QUICHE_CHECK_EQ(s.size(), framer.ProcessInput(s.data(), s.size())); | |
| QUICHE_CHECK(framer.MessageFullyRead()); | |
| return headers; | |
| } | |
| class BufferBlockTest | |
| : public QuicheTestWithParam<std::tuple<const char*, const char*>> {}; | |
| TEST_P(BufferBlockTest, CopyFrom) { | |
| const std::string s1 = std::get<0>(GetParam()); | |
| const std::string s2 = std::get<1>(GetParam()); | |
| BufferBlock block; | |
| block.CopyFrom(MakeBufferBlock(s1)); | |
| EXPECT_EQ(s1.size(), block.bytes_free); | |
| ASSERT_EQ(2 * s1.size(), block.buffer_size); | |
| EXPECT_EQ(0, memcmp(s1.data(), block.buffer.get(), s1.size())); | |
| block.CopyFrom(MakeBufferBlock(s2)); | |
| EXPECT_EQ(s2.size(), block.bytes_free); | |
| ASSERT_EQ(2 * s2.size(), block.buffer_size); | |
| EXPECT_EQ(0, memcmp(s2.data(), block.buffer.get(), s2.size())); | |
| } | |
| const char* block_strings[] = {"short string", "longer than the other string"}; | |
| INSTANTIATE_TEST_SUITE_P(VariousSizes, BufferBlockTest, | |
| Combine(ValuesIn(block_strings), | |
| ValuesIn(block_strings))); | |
| TEST_F(BalsaBufferTest, BlocksizeSet) { | |
| CreateBuffer(); | |
| EXPECT_EQ(BalsaBuffer::kDefaultBlocksize, buffer_->blocksize()); | |
| CreateBuffer(1024); | |
| EXPECT_EQ(1024u, buffer_->blocksize()); | |
| } | |
| TEST_F(BalsaBufferTest, GetMemorySize) { | |
| CreateBuffer(10); | |
| EXPECT_EQ(0u, buffer_->GetTotalBytesUsed()); | |
| EXPECT_EQ(0u, buffer_->GetTotalBufferBlockSize()); | |
| BalsaBuffer::Blocks::size_type index; | |
| buffer_->Reserve(1024, &index); | |
| EXPECT_EQ(10u + 1024u, buffer_->GetTotalBufferBlockSize()); | |
| EXPECT_EQ(1024u, buffer_->GetTotalBytesUsed()); | |
| } | |
| TEST_F(BalsaBufferTest, ManyWritesToContiguousBuffer) { | |
| CreateBuffer(0); | |
| // The test is that the process completes. If it needs to do a resize on | |
| // every write, it will timeout or run out of memory. | |
| // ( 10 + 20 + 30 + ... + 1.2e6 bytes => ~1e11 bytes ) | |
| std::string data = "0123456789"; | |
| for (int i = 0; i < 120 * 1000; ++i) { | |
| buffer_->WriteToContiguousBuffer(data); | |
| } | |
| } | |
| TEST_F(BalsaBufferTest, CopyFrom) { | |
| CreateBuffer(10); | |
| std::unique_ptr<BalsaBuffer> ptr = CreateUnmanagedBuffer(1024); | |
| ASSERT_EQ(1024u, ptr->blocksize()); | |
| EXPECT_EQ(0u, ptr->num_blocks()); | |
| std::string data1 = "foobarbaz01"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| buffer_->NoMoreWriteToContiguousBuffer(); | |
| std::string data2 = "12345"; | |
| Write(data2, nullptr); | |
| std::string data3 = "6789"; | |
| Write(data3, nullptr); | |
| std::string data4 = "123456789012345"; | |
| Write(data3, nullptr); | |
| ptr->CopyFrom(*buffer_); | |
| EXPECT_EQ(ptr->can_write_to_contiguous_buffer(), | |
| buffer_->can_write_to_contiguous_buffer()); | |
| ASSERT_EQ(ptr->num_blocks(), buffer_->num_blocks()); | |
| for (size_t i = 0; i < buffer_->num_blocks(); ++i) { | |
| ASSERT_EQ(ptr->bytes_used(i), buffer_->bytes_used(i)); | |
| ASSERT_EQ(ptr->buffer_size(i), buffer_->buffer_size(i)); | |
| EXPECT_EQ(0, | |
| memcmp(ptr->GetPtr(i), buffer_->GetPtr(i), ptr->bytes_used(i))); | |
| } | |
| } | |
| TEST_F(BalsaBufferTest, ClearWorks) { | |
| CreateBuffer(10); | |
| std::string data1 = "foobarbaz01"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| buffer_->NoMoreWriteToContiguousBuffer(); | |
| std::string data2 = "12345"; | |
| Write(data2, nullptr); | |
| std::string data3 = "6789"; | |
| Write(data3, nullptr); | |
| std::string data4 = "123456789012345"; | |
| Write(data3, nullptr); | |
| buffer_->Clear(); | |
| EXPECT_TRUE(buffer_->can_write_to_contiguous_buffer()); | |
| EXPECT_EQ(10u, buffer_->blocksize()); | |
| EXPECT_EQ(0u, buffer_->num_blocks()); | |
| } | |
| TEST_F(BalsaBufferTest, ClearWorksWhenLargerThanBlocksize) { | |
| CreateBuffer(10); | |
| std::string data1 = "foobarbaz01lkjasdlkjasdlkjasd"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| buffer_->NoMoreWriteToContiguousBuffer(); | |
| std::string data2 = "12345"; | |
| Write(data2, nullptr); | |
| std::string data3 = "6789"; | |
| Write(data3, nullptr); | |
| std::string data4 = "123456789012345"; | |
| Write(data3, nullptr); | |
| buffer_->Clear(); | |
| EXPECT_TRUE(buffer_->can_write_to_contiguous_buffer()); | |
| EXPECT_EQ(10u, buffer_->blocksize()); | |
| EXPECT_EQ(0u, buffer_->num_blocks()); | |
| } | |
| TEST_F(BalsaBufferTest, ContiguousWriteSmallerThanBlocksize) { | |
| CreateBuffer(1024); | |
| std::string data1 = "foo"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| std::string composite = data1; | |
| const char* buf_ptr = buffer_->GetPtr(0); | |
| ASSERT_LE(composite.size(), buffer_->buffer_size(0)); | |
| EXPECT_EQ(0, memcmp(composite.data(), buf_ptr, composite.size())); | |
| std::string data2 = "barbaz"; | |
| buffer_->WriteToContiguousBuffer(data2); | |
| composite += data2; | |
| buf_ptr = buffer_->GetPtr(0); | |
| ASSERT_LE(composite.size(), buffer_->buffer_size(0)); | |
| EXPECT_EQ(0, memcmp(composite.data(), buf_ptr, composite.size())); | |
| } | |
| TEST_F(BalsaBufferTest, SingleContiguousWriteLargerThanBlocksize) { | |
| CreateBuffer(10); | |
| std::string data1 = "abracadabrawords"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| std::string composite = data1; | |
| const char* buf_ptr = buffer_->GetPtr(0); | |
| ASSERT_LE(data1.size(), buffer_->buffer_size(0)); | |
| EXPECT_EQ(0, memcmp(composite.data(), buf_ptr, composite.size())) | |
| << composite << "\n" | |
| << absl::string_view(buf_ptr, buffer_->bytes_used(0)); | |
| } | |
| TEST_F(BalsaBufferTest, ContiguousWriteLargerThanBlocksize) { | |
| CreateBuffer(10); | |
| std::string data1 = "123456789"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| std::string composite = data1; | |
| ASSERT_LE(10u, buffer_->buffer_size(0)); | |
| std::string data2 = "0123456789"; | |
| buffer_->WriteToContiguousBuffer(data2); | |
| composite += data2; | |
| const char* buf_ptr = buffer_->GetPtr(0); | |
| ASSERT_LE(composite.size(), buffer_->buffer_size(0)); | |
| EXPECT_EQ(0, memcmp(composite.data(), buf_ptr, composite.size())) | |
| << "composite: " << composite << "\n" | |
| << " actual: " << absl::string_view(buf_ptr, buffer_->bytes_used(0)); | |
| } | |
| TEST_F(BalsaBufferTest, TwoContiguousWritesLargerThanBlocksize) { | |
| CreateBuffer(5); | |
| std::string data1 = "123456"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| std::string composite = data1; | |
| ASSERT_LE(composite.size(), buffer_->buffer_size(0)); | |
| std::string data2 = "7890123"; | |
| buffer_->WriteToContiguousBuffer(data2); | |
| composite += data2; | |
| const char* buf_ptr = buffer_->GetPtr(0); | |
| ASSERT_LE(composite.size(), buffer_->buffer_size(0)); | |
| EXPECT_EQ(0, memcmp(composite.data(), buf_ptr, composite.size())) | |
| << "composite: " << composite << "\n" | |
| << " actual: " << absl::string_view(buf_ptr, buffer_->bytes_used(0)); | |
| } | |
| TEST_F(BalsaBufferTest, WriteSmallerThanBlocksize) { | |
| CreateBuffer(5); | |
| std::string data1 = "1234"; | |
| size_t block_idx = 0; | |
| absl::string_view write_result = Write(data1, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data1)); | |
| CreateBuffer(5); | |
| data1 = "1234"; | |
| block_idx = 0; | |
| write_result = Write(data1, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data1)); | |
| } | |
| TEST_F(BalsaBufferTest, TwoWritesSmallerThanBlocksizeThenAnotherWrite) { | |
| CreateBuffer(10); | |
| std::string data1 = "12345"; | |
| size_t block_idx = 0; | |
| absl::string_view write_result = Write(data1, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data1)); | |
| std::string data2 = "data2"; | |
| block_idx = 0; | |
| write_result = Write(data2, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data2)); | |
| std::string data3 = "data3"; | |
| block_idx = 0; | |
| write_result = Write(data3, &block_idx); | |
| ASSERT_EQ(2u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data3)); | |
| CreateBuffer(10); | |
| buffer_->NoMoreWriteToContiguousBuffer(); | |
| data1 = "12345"; | |
| block_idx = 0; | |
| write_result = Write(data1, &block_idx); | |
| ASSERT_EQ(0u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data1)); | |
| data2 = "data2"; | |
| block_idx = 0; | |
| write_result = Write(data2, &block_idx); | |
| ASSERT_EQ(0u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data2)); | |
| data3 = "data3"; | |
| block_idx = 0; | |
| write_result = Write(data3, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data3)); | |
| } | |
| TEST_F(BalsaBufferTest, WriteLargerThanBlocksize) { | |
| CreateBuffer(5); | |
| std::string data1 = "123456789"; | |
| size_t block_idx = 0; | |
| absl::string_view write_result = Write(data1, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data1)); | |
| CreateBuffer(5); | |
| buffer_->NoMoreWriteToContiguousBuffer(); | |
| data1 = "123456789"; | |
| block_idx = 0; | |
| write_result = Write(data1, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| EXPECT_THAT(write_result, StrEq(data1)); | |
| } | |
| TEST_F(BalsaBufferTest, ContiguousThenTwoSmallerThanBlocksize) { | |
| CreateBuffer(5); | |
| std::string data1 = "1234567890"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| size_t block_idx = 0; | |
| std::string data2 = "1234"; | |
| absl::string_view write_result = Write(data2, &block_idx); | |
| ASSERT_EQ(1u, block_idx); | |
| std::string data3 = "1234"; | |
| write_result = Write(data3, &block_idx); | |
| ASSERT_EQ(2u, block_idx); | |
| } | |
| TEST_F(BalsaBufferTest, AccessFirstBlockUninitialized) { | |
| CreateBuffer(5); | |
| EXPECT_EQ(0u, buffer_->GetReadableBytesOfFirstBlock()); | |
| EXPECT_QUICHE_BUG(buffer_->StartOfFirstBlock(), | |
| "First block not allocated yet!"); | |
| EXPECT_QUICHE_BUG(buffer_->EndOfFirstBlock(), | |
| "First block not allocated yet!"); | |
| } | |
| TEST_F(BalsaBufferTest, AccessFirstBlockInitialized) { | |
| CreateBuffer(5); | |
| std::string data1 = "1234567890"; | |
| buffer_->WriteToContiguousBuffer(data1); | |
| const char* start = buffer_->StartOfFirstBlock(); | |
| EXPECT_TRUE(start != nullptr); | |
| const char* end = buffer_->EndOfFirstBlock(); | |
| EXPECT_TRUE(end != nullptr); | |
| EXPECT_EQ(data1.length(), static_cast<size_t>(end - start)); | |
| EXPECT_EQ(data1.length(), buffer_->GetReadableBytesOfFirstBlock()); | |
| } | |
| TEST(BalsaHeaders, CanAssignBeginToIterator) { | |
| { | |
| BalsaHeaders header; | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| static_cast<void>(chli); | |
| } | |
| { | |
| const BalsaHeaders header; | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| static_cast<void>(chli); | |
| } | |
| } | |
| TEST(BalsaHeaders, CanAssignEndToIterator) { | |
| { | |
| BalsaHeaders header; | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().end(); | |
| static_cast<void>(chli); | |
| } | |
| { | |
| const BalsaHeaders header; | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().end(); | |
| static_cast<void>(chli); | |
| } | |
| } | |
| TEST(BalsaHeaders, ReplaceOrAppendHeaderTestAppending) { | |
| BalsaHeaders header; | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| header.ReplaceOrAppendHeader(key_1, value_1); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ASSERT_EQ(absl::string_view("key_1"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_1"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, ReplaceOrAppendHeaderTestReplacing) { | |
| BalsaHeaders header; | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| std::string key_2 = "key_2"; | |
| header.ReplaceOrAppendHeader(key_1, value_1); | |
| header.ReplaceOrAppendHeader(key_2, value_1); | |
| std::string value_2 = "value_2_string"; | |
| header.ReplaceOrAppendHeader(key_1, value_2); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ASSERT_EQ(key_1, chli->first); | |
| ASSERT_EQ(value_2, chli->second); | |
| ++chli; | |
| ASSERT_EQ(key_2, chli->first); | |
| ASSERT_EQ(value_1, chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, ReplaceOrAppendHeaderTestReplacingMultiple) { | |
| BalsaHeaders header; | |
| std::string key_1 = "key_1"; | |
| std::string key_2 = "key_2"; | |
| std::string value_1 = "val_1"; | |
| std::string value_2 = "val_2"; | |
| std::string value_3 = | |
| "value_3_is_longer_than_value_1_and_value_2_and_their_keys"; | |
| // Set up header keys 1, 1, 2. We will replace the value of key 1 with a long | |
| // enough string that it should be moved to the end. This regression tests | |
| // that replacement works if we move the header to the end. | |
| header.AppendHeader(key_1, value_1); | |
| header.AppendHeader(key_1, value_2); | |
| header.AppendHeader(key_2, value_1); | |
| header.ReplaceOrAppendHeader(key_1, value_3); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ASSERT_EQ(key_1, chli->first); | |
| ASSERT_EQ(value_3, chli->second); | |
| ++chli; | |
| ASSERT_EQ(key_2, chli->first); | |
| ASSERT_EQ(value_1, chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| // Now test that replacement works with a shorter value, so that if we ever do | |
| // in-place replacement it's tested. | |
| header.ReplaceOrAppendHeader(key_1, value_1); | |
| chli = header.lines().begin(); | |
| ASSERT_EQ(key_1, chli->first); | |
| ASSERT_EQ(value_1, chli->second); | |
| ++chli; | |
| ASSERT_EQ(key_2, chli->first); | |
| ASSERT_EQ(value_1, chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, AppendHeaderAndIteratorTest1) { | |
| BalsaHeaders header; | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| { | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| header.AppendHeader(key_1, value_1); | |
| key_1 = "garbage"; | |
| value_1 = "garbage"; | |
| } | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ASSERT_EQ(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_1"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_1"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, AppendHeaderAndIteratorTest2) { | |
| BalsaHeaders header; | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| { | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| header.AppendHeader(key_1, value_1); | |
| key_1 = "garbage"; | |
| value_1 = "garbage"; | |
| } | |
| { | |
| std::string key_2 = "key_2"; | |
| std::string value_2 = "value_2"; | |
| header.AppendHeader(key_2, value_2); | |
| key_2 = "garbage"; | |
| value_2 = "garbage"; | |
| } | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ASSERT_EQ(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_1"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_1"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_2"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_2"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, AppendHeaderAndIteratorTest3) { | |
| BalsaHeaders header; | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| { | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| header.AppendHeader(key_1, value_1); | |
| key_1 = "garbage"; | |
| value_1 = "garbage"; | |
| } | |
| { | |
| std::string key_2 = "key_2"; | |
| std::string value_2 = "value_2"; | |
| header.AppendHeader(key_2, value_2); | |
| key_2 = "garbage"; | |
| value_2 = "garbage"; | |
| } | |
| { | |
| std::string key_3 = "key_3"; | |
| std::string value_3 = "value_3"; | |
| header.AppendHeader(key_3, value_3); | |
| key_3 = "garbage"; | |
| value_3 = "garbage"; | |
| } | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ASSERT_EQ(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_1"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_1"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_2"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_2"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_3"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_3"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, AppendHeaderAndTestEraseWithIterator) { | |
| BalsaHeaders header; | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| { | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| header.AppendHeader(key_1, value_1); | |
| key_1 = "garbage"; | |
| value_1 = "garbage"; | |
| } | |
| { | |
| std::string key_2 = "key_2"; | |
| std::string value_2 = "value_2"; | |
| header.AppendHeader(key_2, value_2); | |
| key_2 = "garbage"; | |
| value_2 = "garbage"; | |
| } | |
| { | |
| std::string key_3 = "key_3"; | |
| std::string value_3 = "value_3"; | |
| header.AppendHeader(key_3, value_3); | |
| key_3 = "garbage"; | |
| value_3 = "garbage"; | |
| } | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ++chli; // should now point to key_2. | |
| ASSERT_EQ(absl::string_view("key_2"), chli->first); | |
| header.erase(chli); | |
| chli = header.lines().begin(); | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| ASSERT_EQ(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_1"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_1"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_NE(header.lines().end(), chli); | |
| ASSERT_EQ(absl::string_view("key_3"), chli->first); | |
| ASSERT_EQ(absl::string_view("value_3"), chli->second); | |
| ++chli; | |
| ASSERT_NE(header.lines().begin(), chli); | |
| ASSERT_EQ(header.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, TestSetFirstlineInAdditionalBuffer) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET / HTTP/1.0")); | |
| } | |
| TEST(BalsaHeaders, TestSetFirstlineInOriginalBufferAndIsShorterThanOriginal) { | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "GET /foobar HTTP/1.0\r\n" | |
| "\r\n"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET /foobar HTTP/1.0")); | |
| // Note that this SetRequestFirstlineFromStringPieces should replace the | |
| // original one in the -non- 'additional' buffer. | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET / HTTP/1.0")); | |
| } | |
| TEST(BalsaHeaders, TestSetFirstlineInOriginalBufferAndIsLongerThanOriginal) { | |
| // Similar to above, but this time the new firstline is larger than | |
| // the original, yet it should still fit into the original -non- | |
| // 'additional' buffer as the first header-line has been erased. | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "some_key: some_value\r\n" | |
| "another_key: another_value\r\n" | |
| "\r\n"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET / HTTP/1.0")); | |
| headers.erase(headers.lines().begin()); | |
| // Note that this SetRequestFirstlineFromStringPieces should replace the | |
| // original one in the -non- 'additional' buffer. | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/foobar", "HTTP/1.0"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET /foobar HTTP/1.0")); | |
| } | |
| TEST(BalsaHeaders, TestSetFirstlineInAdditionalDataAndIsShorterThanOriginal) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/foobar", "HTTP/1.0"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET /foobar HTTP/1.0")); | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET / HTTP/1.0")); | |
| } | |
| TEST(BalsaHeaders, TestSetFirstlineInAdditionalDataAndIsLongerThanOriginal) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET / HTTP/1.0")); | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/foobar", "HTTP/1.0"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET /foobar HTTP/1.0")); | |
| } | |
| TEST(BalsaHeaders, TestDeletingSubstring) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key2", "value2"); | |
| headers.AppendHeader("key", "value"); | |
| headers.AppendHeader("unrelated", "value"); | |
| // RemoveAllOfHeader should not delete key1 or key2 given a substring. | |
| headers.RemoveAllOfHeader("key"); | |
| EXPECT_TRUE(headers.HasHeader("key1")); | |
| EXPECT_TRUE(headers.HasHeader("key2")); | |
| EXPECT_TRUE(headers.HasHeader("unrelated")); | |
| EXPECT_FALSE(headers.HasHeader("key")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("key")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("KeY")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("UNREL")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("key3")); | |
| EXPECT_FALSE(headers.GetHeader("key1").empty()); | |
| EXPECT_FALSE(headers.GetHeader("KEY1").empty()); | |
| EXPECT_FALSE(headers.GetHeader("key2").empty()); | |
| EXPECT_FALSE(headers.GetHeader("unrelated").empty()); | |
| EXPECT_TRUE(headers.GetHeader("key").empty()); | |
| // Add key back in. | |
| headers.AppendHeader("key", ""); | |
| EXPECT_TRUE(headers.HasHeader("key")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("key")); | |
| EXPECT_TRUE(headers.GetHeader("key").empty()); | |
| // RemoveAllHeadersWithPrefix should delete everything starting with key. | |
| headers.RemoveAllHeadersWithPrefix("key"); | |
| EXPECT_FALSE(headers.HasHeader("key1")); | |
| EXPECT_FALSE(headers.HasHeader("key2")); | |
| EXPECT_TRUE(headers.HasHeader("unrelated")); | |
| EXPECT_FALSE(headers.HasHeader("key")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("key")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("key1")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("key2")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("kEy")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("unrelated")); | |
| EXPECT_TRUE(headers.GetHeader("key1").empty()); | |
| EXPECT_TRUE(headers.GetHeader("key2").empty()); | |
| EXPECT_FALSE(headers.GetHeader("unrelated").empty()); | |
| EXPECT_TRUE(headers.GetHeader("key").empty()); | |
| } | |
| TEST(BalsaHeaders, TestRemovingValues) { | |
| // Remove entire line from headers, twice. Ensures working line-skipping. | |
| // Skip consideration of a line whose key is larger than our search key. | |
| // Skip consideration of a line whose key is smaller than our search key. | |
| // Skip consideration of a line that is already marked for skipping. | |
| // Skip consideration of a line whose value is too small. | |
| // Skip consideration of a line whose key is correct in length but doesn't | |
| // match. | |
| { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("hi", "hello"); | |
| headers.AppendHeader("key1", "val1"); | |
| headers.AppendHeader("key1", "value2"); | |
| headers.AppendHeader("key1", "value3"); | |
| headers.AppendHeader("key2", "value4"); | |
| headers.AppendHeader("unrelated", "value"); | |
| EXPECT_EQ(0u, headers.RemoveValue("key1", "")); | |
| EXPECT_EQ(1u, headers.RemoveValue("key1", "value2")); | |
| std::string key1_vals = headers.GetAllOfHeaderAsString("key1"); | |
| EXPECT_THAT(key1_vals, StrEq("val1,value3")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "val1")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "value3")); | |
| EXPECT_EQ("value4", headers.GetHeader("key2")); | |
| EXPECT_EQ("hello", headers.GetHeader("hi")); | |
| EXPECT_EQ("value", headers.GetHeader("unrelated")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key1", "value2")); | |
| EXPECT_EQ(1u, headers.RemoveValue("key1", "value3")); | |
| key1_vals = headers.GetAllOfHeaderAsString("key1"); | |
| EXPECT_THAT(key1_vals, StrEq("val1")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "val1")); | |
| EXPECT_EQ("value4", headers.GetHeader("key2")); | |
| EXPECT_EQ("hello", headers.GetHeader("hi")); | |
| EXPECT_EQ("value", headers.GetHeader("unrelated")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key1", "value3")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key1", "value2")); | |
| } | |
| // Remove/keep values with surrounding spaces. | |
| // Remove values from in between others in multi-value line. | |
| // Remove entire multi-value line. | |
| // Keep value in between removed values in multi-value line. | |
| // Keep trailing value that is too small to be matched after removing a match. | |
| // Keep value containing matched value (partial but not complete match). | |
| // Keep an empty header. | |
| { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key1", "value2, value3,value2"); | |
| headers.AppendHeader("key1", "value4 ,value2,value5,val6"); | |
| headers.AppendHeader("key1", "value2, value2 , value2"); | |
| headers.AppendHeader("key1", " value2 , value2 "); | |
| headers.AppendHeader("key1", " value2 a"); | |
| headers.AppendHeader("key1", ""); | |
| headers.AppendHeader("key1", ", ,,"); | |
| headers.AppendHeader("unrelated", "value"); | |
| EXPECT_EQ(8u, headers.RemoveValue("key1", "value2")); | |
| std::string key1_vals = headers.GetAllOfHeaderAsString("key1"); | |
| EXPECT_THAT(key1_vals, | |
| StrEq("value1,value3,value4 ,value5,val6,value2 a,,, ,,")); | |
| EXPECT_EQ("value", headers.GetHeader("unrelated")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "value1")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "value3")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "value4")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "value5")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key1", "val6")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key1", "value2")); | |
| } | |
| { | |
| const absl::string_view key("key"); | |
| const absl::string_view value1("foo\0bar", 7); | |
| const absl::string_view value2("value2"); | |
| const std::string value = absl::StrCat(value1, ",", value2); | |
| { | |
| BalsaHeaders headers; | |
| headers.AppendHeader(key, value); | |
| EXPECT_TRUE(headers.HeaderHasValue(key, value1)); | |
| EXPECT_TRUE(headers.HeaderHasValue(key, value2)); | |
| EXPECT_EQ(value, headers.GetAllOfHeaderAsString(key)); | |
| EXPECT_EQ(1u, headers.RemoveValue(key, value2)); | |
| EXPECT_TRUE(headers.HeaderHasValue(key, value1)); | |
| EXPECT_FALSE(headers.HeaderHasValue(key, value2)); | |
| EXPECT_EQ(value1, headers.GetAllOfHeaderAsString(key)); | |
| } | |
| { | |
| BalsaHeaders headers; | |
| headers.AppendHeader(key, value1); | |
| headers.AppendHeader(key, value2); | |
| EXPECT_TRUE(headers.HeaderHasValue(key, value1)); | |
| EXPECT_TRUE(headers.HeaderHasValue(key, value2)); | |
| EXPECT_EQ(value, headers.GetAllOfHeaderAsString(key)); | |
| EXPECT_EQ(1u, headers.RemoveValue(key, value2)); | |
| EXPECT_TRUE(headers.HeaderHasValue(key, value1)); | |
| EXPECT_FALSE(headers.HeaderHasValue(key, value2)); | |
| EXPECT_EQ(value1, headers.GetAllOfHeaderAsString(key)); | |
| } | |
| } | |
| } | |
| TEST(BalsaHeaders, ZeroAppendToHeaderWithCommaAndSpace) { | |
| // Create an initial header with zero 'X-Forwarded-For' headers. | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "\r\n"); | |
| // Use AppendToHeaderWithCommaAndSpace to add 4 new 'X-Forwarded-For' headers. | |
| // Appending these headers should preserve the order in which they are added. | |
| // i.e. 1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4 | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "1.1.1.1"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "2.2.2.2"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "3.3.3.3"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "4.4.4.4"); | |
| // Fetch the 'X-Forwarded-For' headers and compare them to the expected order. | |
| EXPECT_THAT(headers.GetAllOfHeader("X-Forwarded-For"), | |
| ElementsAre("1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4")); | |
| } | |
| TEST(BalsaHeaders, SingleAppendToHeaderWithCommaAndSpace) { | |
| // Create an initial header with one 'X-Forwarded-For' header. | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "X-Forwarded-For: 1.1.1.1\r\n" | |
| "\r\n"); | |
| // Use AppendToHeaderWithCommaAndSpace to add 4 new 'X-Forwarded-For' headers. | |
| // Appending these headers should preserve the order in which they are added. | |
| // i.e. 1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4, 5.5.5.5 | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "2.2.2.2"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "3.3.3.3"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "4.4.4.4"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "5.5.5.5"); | |
| // Fetch the 'X-Forwarded-For' headers and compare them to the expected order. | |
| EXPECT_THAT(headers.GetAllOfHeader("X-Forwarded-For"), | |
| ElementsAre("1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4, 5.5.5.5")); | |
| } | |
| TEST(BalsaHeaders, MultipleAppendToHeaderWithCommaAndSpace) { | |
| // Create an initial header with two 'X-Forwarded-For' headers. | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "X-Forwarded-For: 1.1.1.1\r\n" | |
| "X-Forwarded-For: 2.2.2.2\r\n" | |
| "\r\n"); | |
| // Use AppendToHeaderWithCommaAndSpace to add 4 new 'X-Forwarded-For' headers. | |
| // Appending these headers should preserve the order in which they are added. | |
| // i.e. 1.1.1.1, 2.2.2.2, 3.3.3.3, 4.4.4.4, 5.5.5.5, 6.6.6.6 | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "3.3.3.3"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "4.4.4.4"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "5.5.5.5"); | |
| headers.AppendToHeaderWithCommaAndSpace("X-Forwarded-For", "6.6.6.6"); | |
| // Fetch the 'X-Forwarded-For' headers and compare them to the expected order. | |
| EXPECT_THAT( | |
| headers.GetAllOfHeader("X-Forwarded-For"), | |
| ElementsAre("1.1.1.1", "2.2.2.2, 3.3.3.3, 4.4.4.4, 5.5.5.5, 6.6.6.6")); | |
| } | |
| TEST(BalsaHeaders, HeaderHasValues) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| // Make sure we find values at the beginning, middle, and end, and we handle | |
| // multiple .find() calls correctly. | |
| headers.AppendHeader("key", "val1,val2val2,val2,val3"); | |
| // Make sure we don't mess up comma/boundary checks for beginning, middle and | |
| // end. | |
| headers.AppendHeader("key", "val4val5val6"); | |
| headers.AppendHeader("key", "val11 val12"); | |
| headers.AppendHeader("key", "v val13"); | |
| // Make sure we catch the line header | |
| headers.AppendHeader("key", "val7"); | |
| // Make sure there's no out-of-bounds indexing on an empty line. | |
| headers.AppendHeader("key", ""); | |
| // Make sure it works when there's spaces before or after a comma. | |
| headers.AppendHeader("key", "val8 , val9 , val10"); | |
| // Make sure it works when val is surrounded by spaces. | |
| headers.AppendHeader("key", " val14 "); | |
| // Make sure other keys aren't used. | |
| headers.AppendHeader("key2", "val15"); | |
| // Mixed case. | |
| headers.AppendHeader("key", "Val16"); | |
| headers.AppendHeader("key", "foo, Val17, bar"); | |
| // All case-sensitive. | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val1")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val2")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val3")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val7")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val8")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val9")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val10")); | |
| EXPECT_TRUE(headers.HeaderHasValue("key", "val14")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val4")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val5")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val6")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val11")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val12")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val13")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val15")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val16")); | |
| EXPECT_FALSE(headers.HeaderHasValue("key", "val17")); | |
| // All case-insensitive, only change is for val16 and val17. | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val1")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val2")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val3")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val7")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val8")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val9")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val10")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val14")); | |
| EXPECT_FALSE(headers.HeaderHasValueIgnoreCase("key", "val4")); | |
| EXPECT_FALSE(headers.HeaderHasValueIgnoreCase("key", "val5")); | |
| EXPECT_FALSE(headers.HeaderHasValueIgnoreCase("key", "val6")); | |
| EXPECT_FALSE(headers.HeaderHasValueIgnoreCase("key", "val11")); | |
| EXPECT_FALSE(headers.HeaderHasValueIgnoreCase("key", "val12")); | |
| EXPECT_FALSE(headers.HeaderHasValueIgnoreCase("key", "val13")); | |
| EXPECT_FALSE(headers.HeaderHasValueIgnoreCase("key", "val15")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val16")); | |
| EXPECT_TRUE(headers.HeaderHasValueIgnoreCase("key", "val17")); | |
| } | |
| // Because we're dealing with one giant buffer, make sure we don't go beyond | |
| // the bounds of the key when doing compares! | |
| TEST(BalsaHeaders, TestNotDeletingBeyondString) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.RemoveAllHeadersWithPrefix("key1: value1"); | |
| EXPECT_NE(headers.lines().begin(), headers.lines().end()); | |
| } | |
| TEST(BalsaHeaders, TestIteratingOverErasedHeaders) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key2", "value2"); | |
| headers.AppendHeader("key3", "value3"); | |
| headers.AppendHeader("key4", "value4"); | |
| headers.AppendHeader("key5", "value5"); | |
| headers.AppendHeader("key6", "value6"); | |
| headers.RemoveAllOfHeader("key6"); | |
| headers.RemoveAllOfHeader("key5"); | |
| headers.RemoveAllOfHeader("key4"); | |
| BalsaHeaders::const_header_lines_iterator chli = headers.lines().begin(); | |
| EXPECT_NE(headers.lines().end(), chli); | |
| EXPECT_EQ(headers.lines().begin(), chli); | |
| EXPECT_THAT(chli->first, StrEq("key1")); | |
| EXPECT_THAT(chli->second, StrEq("value1")); | |
| ++chli; | |
| EXPECT_NE(headers.lines().end(), chli); | |
| EXPECT_NE(headers.lines().begin(), chli); | |
| EXPECT_THAT(chli->first, StrEq("key2")); | |
| EXPECT_THAT(chli->second, StrEq("value2")); | |
| ++chli; | |
| EXPECT_NE(headers.lines().end(), chli); | |
| EXPECT_NE(headers.lines().begin(), chli); | |
| EXPECT_THAT(chli->first, StrEq("key3")); | |
| EXPECT_THAT(chli->second, StrEq("value3")); | |
| ++chli; | |
| EXPECT_EQ(headers.lines().end(), chli); | |
| EXPECT_NE(headers.lines().begin(), chli); | |
| headers.RemoveAllOfHeader("key1"); | |
| headers.RemoveAllOfHeader("key2"); | |
| chli = headers.lines().begin(); | |
| EXPECT_THAT(chli->first, StrEq("key3")); | |
| EXPECT_THAT(chli->second, StrEq("value3")); | |
| EXPECT_NE(headers.lines().end(), chli); | |
| EXPECT_EQ(headers.lines().begin(), chli); | |
| ++chli; | |
| EXPECT_EQ(headers.lines().end(), chli); | |
| EXPECT_NE(headers.lines().begin(), chli); | |
| } | |
| TEST(BalsaHeaders, CanCompareIterators) { | |
| BalsaHeaders header; | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| { | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| header.AppendHeader(key_1, value_1); | |
| key_1 = "garbage"; | |
| value_1 = "garbage"; | |
| } | |
| { | |
| std::string key_2 = "key_2"; | |
| std::string value_2 = "value_2"; | |
| header.AppendHeader(key_2, value_2); | |
| key_2 = "garbage"; | |
| value_2 = "garbage"; | |
| } | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| BalsaHeaders::const_header_lines_iterator chlj = header.lines().begin(); | |
| EXPECT_EQ(chli, chlj); | |
| ++chlj; | |
| EXPECT_NE(chli, chlj); | |
| EXPECT_LT(chli, chlj); | |
| EXPECT_LE(chli, chlj); | |
| EXPECT_LE(chli, chli); | |
| EXPECT_GT(chlj, chli); | |
| EXPECT_GE(chlj, chli); | |
| EXPECT_GE(chlj, chlj); | |
| } | |
| TEST(BalsaHeaders, AppendHeaderAndTestThatYouCanEraseEverything) { | |
| BalsaHeaders header; | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| { | |
| std::string key_1 = "key_1"; | |
| std::string value_1 = "value_1"; | |
| header.AppendHeader(key_1, value_1); | |
| key_1 = "garbage"; | |
| value_1 = "garbage"; | |
| } | |
| { | |
| std::string key_2 = "key_2"; | |
| std::string value_2 = "value_2"; | |
| header.AppendHeader(key_2, value_2); | |
| key_2 = "garbage"; | |
| value_2 = "garbage"; | |
| } | |
| { | |
| std::string key_3 = "key_3"; | |
| std::string value_3 = "value_3"; | |
| header.AppendHeader(key_3, value_3); | |
| key_3 = "garbage"; | |
| value_3 = "garbage"; | |
| } | |
| EXPECT_NE(header.lines().begin(), header.lines().end()); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| while (chli != header.lines().end()) { | |
| header.erase(chli); | |
| chli = header.lines().begin(); | |
| } | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| } | |
| TEST(BalsaHeaders, GetHeaderPositionWorksAsExpectedWithNoHeaderLines) { | |
| BalsaHeaders header; | |
| BalsaHeaders::const_header_lines_iterator i = header.GetHeaderPosition("foo"); | |
| EXPECT_EQ(i, header.lines().end()); | |
| } | |
| TEST(BalsaHeaders, GetHeaderPositionWorksAsExpectedWithBalsaFrameProcessInput) { | |
| BalsaHeaders headers = CreateHTTPHeaders( | |
| true, | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value_1\r\n" | |
| "key1: value_foo\r\n" // this one cannot be fetched via GetHeader | |
| "key2: value_2\r\n" | |
| "key3: value_3\r\n" | |
| "a: value_a\r\n" | |
| "b: value_b\r\n" | |
| "\r\n"); | |
| BalsaHeaders::const_header_lines_iterator header_position_b = | |
| headers.GetHeaderPosition("b"); | |
| ASSERT_NE(header_position_b, headers.lines().end()); | |
| absl::string_view header_key_b_value = header_position_b->second; | |
| ASSERT_FALSE(header_key_b_value.empty()); | |
| EXPECT_EQ(std::string("value_b"), header_key_b_value); | |
| BalsaHeaders::const_header_lines_iterator header_position_1 = | |
| headers.GetHeaderPosition("key1"); | |
| ASSERT_NE(header_position_1, headers.lines().end()); | |
| absl::string_view header_key_1_value = header_position_1->second; | |
| ASSERT_FALSE(header_key_1_value.empty()); | |
| EXPECT_EQ(std::string("value_1"), header_key_1_value); | |
| BalsaHeaders::const_header_lines_iterator header_position_3 = | |
| headers.GetHeaderPosition("key3"); | |
| ASSERT_NE(header_position_3, headers.lines().end()); | |
| absl::string_view header_key_3_value = header_position_3->second; | |
| ASSERT_FALSE(header_key_3_value.empty()); | |
| EXPECT_EQ(std::string("value_3"), header_key_3_value); | |
| BalsaHeaders::const_header_lines_iterator header_position_2 = | |
| headers.GetHeaderPosition("key2"); | |
| ASSERT_NE(header_position_2, headers.lines().end()); | |
| absl::string_view header_key_2_value = header_position_2->second; | |
| ASSERT_FALSE(header_key_2_value.empty()); | |
| EXPECT_EQ(std::string("value_2"), header_key_2_value); | |
| BalsaHeaders::const_header_lines_iterator header_position_a = | |
| headers.GetHeaderPosition("a"); | |
| ASSERT_NE(header_position_a, headers.lines().end()); | |
| absl::string_view header_key_a_value = header_position_a->second; | |
| ASSERT_FALSE(header_key_a_value.empty()); | |
| EXPECT_EQ(std::string("value_a"), header_key_a_value); | |
| } | |
| TEST(BalsaHeaders, GetHeaderWorksAsExpectedWithNoHeaderLines) { | |
| BalsaHeaders header; | |
| absl::string_view value = header.GetHeader("foo"); | |
| EXPECT_TRUE(value.empty()); | |
| value = header.GetHeader(""); | |
| EXPECT_TRUE(value.empty()); | |
| } | |
| TEST(BalsaHeaders, HasHeaderWorksAsExpectedWithNoHeaderLines) { | |
| BalsaHeaders header; | |
| EXPECT_FALSE(header.HasHeader("foo")); | |
| EXPECT_FALSE(header.HasHeader("")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("foo")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("")); | |
| } | |
| TEST(BalsaHeaders, HasHeaderWorksAsExpectedWithBalsaFrameProcessInput) { | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value_1\r\n" | |
| "key1: value_foo\r\n" | |
| "key2:\r\n" | |
| "\r\n"); | |
| EXPECT_FALSE(headers.HasHeader("foo")); | |
| EXPECT_TRUE(headers.HasHeader("key1")); | |
| EXPECT_TRUE(headers.HasHeader("key2")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("foo")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("key")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("KEY")); | |
| } | |
| TEST(BalsaHeaders, GetHeaderWorksAsExpectedWithBalsaFrameProcessInput) { | |
| BalsaHeaders headers = CreateHTTPHeaders( | |
| true, | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value_1\r\n" | |
| "key1: value_foo\r\n" // this one cannot be fetched via GetHeader | |
| "key2: value_2\r\n" | |
| "key3: value_3\r\n" | |
| "key4:\r\n" | |
| "a: value_a\r\n" | |
| "b: value_b\r\n" | |
| "\r\n"); | |
| absl::string_view header_key_b_value = headers.GetHeader("b"); | |
| ASSERT_FALSE(header_key_b_value.empty()); | |
| EXPECT_EQ(std::string("value_b"), header_key_b_value); | |
| absl::string_view header_key_1_value = headers.GetHeader("key1"); | |
| ASSERT_FALSE(header_key_1_value.empty()); | |
| EXPECT_EQ(std::string("value_1"), header_key_1_value); | |
| absl::string_view header_key_3_value = headers.GetHeader("key3"); | |
| ASSERT_FALSE(header_key_3_value.empty()); | |
| EXPECT_EQ(std::string("value_3"), header_key_3_value); | |
| absl::string_view header_key_2_value = headers.GetHeader("key2"); | |
| ASSERT_FALSE(header_key_2_value.empty()); | |
| EXPECT_EQ(std::string("value_2"), header_key_2_value); | |
| absl::string_view header_key_a_value = headers.GetHeader("a"); | |
| ASSERT_FALSE(header_key_a_value.empty()); | |
| EXPECT_EQ(std::string("value_a"), header_key_a_value); | |
| EXPECT_TRUE(headers.GetHeader("key4").empty()); | |
| } | |
| TEST(BalsaHeaders, GetHeaderWorksAsExpectedWithAppendHeader) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key1", "value_1"); | |
| // note that this (following) one cannot be found using GetHeader. | |
| header.AppendHeader("key1", "value_2"); | |
| header.AppendHeader("key2", "value_2"); | |
| header.AppendHeader("key3", "value_3"); | |
| header.AppendHeader("a", "value_a"); | |
| header.AppendHeader("b", "value_b"); | |
| absl::string_view header_key_b_value = header.GetHeader("b"); | |
| absl::string_view header_key_1_value = header.GetHeader("key1"); | |
| absl::string_view header_key_3_value = header.GetHeader("key3"); | |
| absl::string_view header_key_2_value = header.GetHeader("key2"); | |
| absl::string_view header_key_a_value = header.GetHeader("a"); | |
| ASSERT_FALSE(header_key_1_value.empty()); | |
| ASSERT_FALSE(header_key_2_value.empty()); | |
| ASSERT_FALSE(header_key_3_value.empty()); | |
| ASSERT_FALSE(header_key_a_value.empty()); | |
| ASSERT_FALSE(header_key_b_value.empty()); | |
| EXPECT_TRUE(header.HasHeader("key1")); | |
| EXPECT_TRUE(header.HasHeader("key2")); | |
| EXPECT_TRUE(header.HasHeader("key3")); | |
| EXPECT_TRUE(header.HasHeader("a")); | |
| EXPECT_TRUE(header.HasHeader("b")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key1")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key2")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key3")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("a")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("b")); | |
| EXPECT_EQ(std::string("value_1"), header_key_1_value); | |
| EXPECT_EQ(std::string("value_2"), header_key_2_value); | |
| EXPECT_EQ(std::string("value_3"), header_key_3_value); | |
| EXPECT_EQ(std::string("value_a"), header_key_a_value); | |
| EXPECT_EQ(std::string("value_b"), header_key_b_value); | |
| } | |
| TEST(BalsaHeaders, HasHeaderWorksAsExpectedWithAppendHeader) { | |
| BalsaHeaders header; | |
| ASSERT_FALSE(header.HasHeader("key1")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("K")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("ke")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key1")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key2")); | |
| header.AppendHeader("key1", "value_1"); | |
| EXPECT_TRUE(header.HasHeader("key1")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("K")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("ke")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key1")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key2")); | |
| header.AppendHeader("key1", "value_2"); | |
| EXPECT_TRUE(header.HasHeader("key1")); | |
| EXPECT_FALSE(header.HasHeader("key2")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("k")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("ke")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key1")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key2")); | |
| } | |
| TEST(BalsaHeaders, GetHeaderWorksAsExpectedWithHeadersErased) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key1", "value_1"); | |
| header.AppendHeader("key1", "value_2"); | |
| header.AppendHeader("key2", "value_2"); | |
| header.AppendHeader("key3", "value_3"); | |
| header.AppendHeader("a", "value_a"); | |
| header.AppendHeader("b", "value_b"); | |
| header.erase(header.GetHeaderPosition("key2")); | |
| absl::string_view header_key_b_value = header.GetHeader("b"); | |
| absl::string_view header_key_1_value = header.GetHeader("key1"); | |
| absl::string_view header_key_3_value = header.GetHeader("key3"); | |
| absl::string_view header_key_2_value = header.GetHeader("key2"); | |
| absl::string_view header_key_a_value = header.GetHeader("a"); | |
| ASSERT_FALSE(header_key_1_value.empty()); | |
| ASSERT_TRUE(header_key_2_value.empty()); | |
| ASSERT_FALSE(header_key_3_value.empty()); | |
| ASSERT_FALSE(header_key_a_value.empty()); | |
| ASSERT_FALSE(header_key_b_value.empty()); | |
| EXPECT_EQ(std::string("value_1"), header_key_1_value); | |
| EXPECT_EQ(std::string("value_3"), header_key_3_value); | |
| EXPECT_EQ(std::string("value_a"), header_key_a_value); | |
| EXPECT_EQ(std::string("value_b"), header_key_b_value); | |
| // Erasing one makes the next one visible: | |
| header.erase(header.GetHeaderPosition("key1")); | |
| header_key_1_value = header.GetHeader("key1"); | |
| ASSERT_FALSE(header_key_1_value.empty()); | |
| EXPECT_EQ(std::string("value_2"), header_key_1_value); | |
| // Erase both: | |
| header.erase(header.GetHeaderPosition("key1")); | |
| ASSERT_TRUE(header.GetHeader("key1").empty()); | |
| } | |
| TEST(BalsaHeaders, HasHeaderWorksAsExpectedWithHeadersErased) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key1", "value_1"); | |
| header.AppendHeader("key2", "value_2a"); | |
| header.AppendHeader("key2", "value_2b"); | |
| ASSERT_TRUE(header.HasHeader("key1")); | |
| ASSERT_TRUE(header.HasHeadersWithPrefix("key1")); | |
| ASSERT_TRUE(header.HasHeadersWithPrefix("key2")); | |
| ASSERT_TRUE(header.HasHeadersWithPrefix("kEY")); | |
| header.erase(header.GetHeaderPosition("key1")); | |
| EXPECT_FALSE(header.HasHeader("key1")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key1")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key2")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("kEY")); | |
| ASSERT_TRUE(header.HasHeader("key2")); | |
| header.erase(header.GetHeaderPosition("key2")); | |
| ASSERT_TRUE(header.HasHeader("key2")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key1")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("key2")); | |
| EXPECT_TRUE(header.HasHeadersWithPrefix("kEY")); | |
| header.erase(header.GetHeaderPosition("key2")); | |
| EXPECT_FALSE(header.HasHeader("key2")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key1")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("key2")); | |
| EXPECT_FALSE(header.HasHeadersWithPrefix("kEY")); | |
| } | |
| TEST(BalsaHeaders, HasNonEmptyHeaderWorksAsExpectedWithNoHeaderLines) { | |
| BalsaHeaders header; | |
| EXPECT_FALSE(header.HasNonEmptyHeader("foo")); | |
| EXPECT_FALSE(header.HasNonEmptyHeader("")); | |
| } | |
| TEST(BalsaHeaders, HasNonEmptyHeaderWorksAsExpectedWithAppendHeader) { | |
| BalsaHeaders header; | |
| EXPECT_FALSE(header.HasNonEmptyHeader("key1")); | |
| header.AppendHeader("key1", ""); | |
| EXPECT_FALSE(header.HasNonEmptyHeader("key1")); | |
| header.AppendHeader("key1", "value_2"); | |
| EXPECT_TRUE(header.HasNonEmptyHeader("key1")); | |
| EXPECT_FALSE(header.HasNonEmptyHeader("key2")); | |
| } | |
| TEST(BalsaHeaders, HasNonEmptyHeaderWorksAsExpectedWithHeadersErased) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key1", "value_1"); | |
| header.AppendHeader("key2", "value_2a"); | |
| header.AppendHeader("key2", ""); | |
| EXPECT_TRUE(header.HasNonEmptyHeader("key1")); | |
| header.erase(header.GetHeaderPosition("key1")); | |
| EXPECT_FALSE(header.HasNonEmptyHeader("key1")); | |
| EXPECT_TRUE(header.HasNonEmptyHeader("key2")); | |
| header.erase(header.GetHeaderPosition("key2")); | |
| EXPECT_FALSE(header.HasNonEmptyHeader("key2")); | |
| header.erase(header.GetHeaderPosition("key2")); | |
| EXPECT_FALSE(header.HasNonEmptyHeader("key2")); | |
| } | |
| TEST(BalsaHeaders, HasNonEmptyHeaderWorksAsExpectedWithBalsaFrameProcessInput) { | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value_1\r\n" | |
| "key2:\r\n" | |
| "key3:\r\n" | |
| "key3: value_3\r\n" | |
| "key4:\r\n" | |
| "key4:\r\n" | |
| "key5: value_5\r\n" | |
| "key5:\r\n" | |
| "\r\n"); | |
| EXPECT_FALSE(headers.HasNonEmptyHeader("foo")); | |
| EXPECT_TRUE(headers.HasNonEmptyHeader("key1")); | |
| EXPECT_FALSE(headers.HasNonEmptyHeader("key2")); | |
| EXPECT_TRUE(headers.HasNonEmptyHeader("key3")); | |
| EXPECT_FALSE(headers.HasNonEmptyHeader("key4")); | |
| EXPECT_TRUE(headers.HasNonEmptyHeader("key5")); | |
| headers.erase(headers.GetHeaderPosition("key5")); | |
| EXPECT_FALSE(headers.HasNonEmptyHeader("key5")); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeader) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("Key", "value_2,value_3"); | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("KEY", "value_4"); | |
| std::vector<absl::string_view> result; | |
| header.GetAllOfHeader("key", &result); | |
| ASSERT_EQ(4u, result.size()); | |
| EXPECT_EQ("value_1", result[0]); | |
| EXPECT_EQ("value_2,value_3", result[1]); | |
| EXPECT_EQ("", result[2]); | |
| EXPECT_EQ("value_4", result[3]); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), result); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderDoesWhatItSays) { | |
| BalsaHeaders header; | |
| // Multiple values for a given header. | |
| // Some values appear multiple times | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("key", "value_1"); | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(4u, out.size()); | |
| EXPECT_EQ("value_1", out[0]); | |
| EXPECT_EQ("value_2", out[1]); | |
| EXPECT_EQ("", out[2]); | |
| EXPECT_EQ("value_1", out[3]); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithPrefix) { | |
| BalsaHeaders header; | |
| header.AppendHeader("foo-Foo", "value_1"); | |
| header.AppendHeader("Foo-bar", "value_2,value_3"); | |
| header.AppendHeader("foo-Foo", ""); | |
| header.AppendHeader("bar", "value_not"); | |
| header.AppendHeader("fOO-fOO", "value_4"); | |
| std::vector<std::pair<absl::string_view, absl::string_view>> result; | |
| header.GetAllOfHeaderWithPrefix("abc", &result); | |
| ASSERT_EQ(0u, result.size()); | |
| header.GetAllOfHeaderWithPrefix("foo", &result); | |
| ASSERT_EQ(4u, result.size()); | |
| EXPECT_EQ("foo-Foo", result[0].first); | |
| EXPECT_EQ("value_1", result[0].second); | |
| EXPECT_EQ("Foo-bar", result[1].first); | |
| EXPECT_EQ("value_2,value_3", result[1].second); | |
| EXPECT_EQ("", result[2].second); | |
| EXPECT_EQ("value_4", result[3].second); | |
| std::vector<std::pair<absl::string_view, absl::string_view>> result2; | |
| header.GetAllOfHeaderWithPrefix("FoO", &result2); | |
| ASSERT_EQ(4u, result2.size()); | |
| } | |
| TEST(BalsaHeaders, GetAllHeadersWithLimit) { | |
| BalsaHeaders header; | |
| header.AppendHeader("foo-Foo", "value_1"); | |
| header.AppendHeader("Foo-bar", "value_2,value_3"); | |
| header.AppendHeader("foo-Foo", ""); | |
| header.AppendHeader("bar", "value_4"); | |
| header.AppendHeader("fOO-fOO", "value_5"); | |
| std::vector<std::pair<absl::string_view, absl::string_view>> result; | |
| header.GetAllHeadersWithLimit(&result, 4); | |
| ASSERT_EQ(4u, result.size()); | |
| EXPECT_EQ("foo-Foo", result[0].first); | |
| EXPECT_EQ("value_1", result[0].second); | |
| EXPECT_EQ("Foo-bar", result[1].first); | |
| EXPECT_EQ("value_2,value_3", result[1].second); | |
| EXPECT_EQ("", result[2].second); | |
| EXPECT_EQ("value_4", result[3].second); | |
| std::vector<std::pair<absl::string_view, absl::string_view>> result2; | |
| header.GetAllHeadersWithLimit(&result2, -1); | |
| ASSERT_EQ(5u, result2.size()); | |
| } | |
| TEST(BalsaHeaders, RangeFor) { | |
| BalsaHeaders header; | |
| // Multiple values for a given header. | |
| // Some values appear multiple times | |
| header.AppendHeader("key1", "value_1a"); | |
| header.AppendHeader("key1", "value_1b"); | |
| header.AppendHeader("key2", ""); | |
| header.AppendHeader("key3", "value_3"); | |
| std::vector<std::pair<absl::string_view, absl::string_view>> out; | |
| for (const auto& line : header.lines()) { | |
| out.push_back(line); | |
| } | |
| const std::vector<std::pair<absl::string_view, absl::string_view>> expected = | |
| {{"key1", "value_1a"}, | |
| {"key1", "value_1b"}, | |
| {"key2", ""}, | |
| {"key3", "value_3"}}; | |
| EXPECT_EQ(expected, out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithNonExistentKey) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key_non_existent", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("key_non_existent"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderEmptyValVariation1) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("key", "v1"); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(3u, out.size()); | |
| EXPECT_EQ("", out[0]); | |
| EXPECT_EQ("", out[1]); | |
| EXPECT_EQ("v1", out[2]); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderEmptyValVariation2) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("key", "v1"); | |
| header.AppendHeader("key", ""); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(3u, out.size()); | |
| EXPECT_EQ("", out[0]); | |
| EXPECT_EQ("v1", out[1]); | |
| EXPECT_EQ("", out[2]); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderEmptyValVariation3) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("key", "v1"); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(2u, out.size()); | |
| EXPECT_EQ("", out[0]); | |
| EXPECT_EQ("v1", out[1]); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderEmptyValVariation4) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "v1"); | |
| header.AppendHeader("key", ""); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(2u, out.size()); | |
| EXPECT_EQ("v1", out[0]); | |
| EXPECT_EQ("", out[1]); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithAppendHeaders) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key_new", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("key_new"), out); | |
| // Add key_new to the header | |
| header.AppendHeader("key_new", "value_3"); | |
| header.GetAllOfHeader("key_new", &out); | |
| ASSERT_EQ(1u, out.size()); | |
| EXPECT_EQ("value_3", out[0]); | |
| EXPECT_EQ(header.GetAllOfHeader("key_new"), out); | |
| // Get the keys that are not modified | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(3u, out.size()); | |
| EXPECT_EQ("value_1", out[1]); | |
| EXPECT_EQ("value_2", out[2]); | |
| EXPECT_THAT(header.GetAllOfHeader("key"), ElementsAre("value_1", "value_2")); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithRemoveHeaders) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| header.AppendHeader("a", "va"); | |
| header.RemoveAllOfHeader("key"); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| header.GetAllOfHeader("a", &out); | |
| ASSERT_EQ(1u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("a"), out); | |
| out.clear(); | |
| header.RemoveAllOfHeader("a"); | |
| header.GetAllOfHeader("a", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("a"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithRemoveNonExistentHeaders) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("Accept-Encoding", "deflate,compress"); | |
| EXPECT_EQ(0u, headers.RemoveValue("Accept-Encoding", "gzip(gfe)")); | |
| std::string accept_encoding_vals = | |
| headers.GetAllOfHeaderAsString("Accept-Encoding"); | |
| EXPECT_EQ("deflate,compress", accept_encoding_vals); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithEraseHeaders) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| header.AppendHeader("a", "va"); | |
| std::vector<absl::string_view> out; | |
| header.erase(header.GetHeaderPosition("key")); | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(1u, out.size()); | |
| EXPECT_EQ("value_2", out[0]); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| out.clear(); | |
| header.erase(header.GetHeaderPosition("key")); | |
| header.GetAllOfHeader("key", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| out.clear(); | |
| header.GetAllOfHeader("a", &out); | |
| ASSERT_EQ(1u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("a"), out); | |
| out.clear(); | |
| header.erase(header.GetHeaderPosition("a")); | |
| header.GetAllOfHeader("a", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithNoHeaderLines) { | |
| BalsaHeaders header; | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key", &out); | |
| EXPECT_EQ(0u, out.size()); | |
| EXPECT_EQ(header.GetAllOfHeader("key"), out); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderDoesWhatItSaysForVariousKeys) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key1", "value_11"); | |
| header.AppendHeader("key2", "value_21"); | |
| header.AppendHeader("key1", "value_12"); | |
| header.AppendHeader("key2", "value_22"); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeader("key1", &out); | |
| EXPECT_EQ("value_11", out[0]); | |
| EXPECT_EQ("value_12", out[1]); | |
| EXPECT_EQ(header.GetAllOfHeader("key1"), out); | |
| header.GetAllOfHeader("key2", &out); | |
| EXPECT_EQ("value_21", out[2]); | |
| EXPECT_EQ("value_22", out[3]); | |
| EXPECT_THAT(header.GetAllOfHeader("key2"), | |
| ElementsAre("value_21", "value_22")); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderWithBalsaFrameProcessInput) { | |
| BalsaHeaders header = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value_1\r\n" | |
| "key1: value_foo\r\n" | |
| "key2: value_2\r\n" | |
| "a: value_a\r\n" | |
| "key2: \r\n" | |
| "b: value_b\r\n" | |
| "\r\n"); | |
| std::vector<absl::string_view> out; | |
| int index = 0; | |
| header.GetAllOfHeader("key1", &out); | |
| EXPECT_EQ("value_1", out[index++]); | |
| EXPECT_EQ("value_foo", out[index++]); | |
| EXPECT_EQ(header.GetAllOfHeader("key1"), out); | |
| header.GetAllOfHeader("key2", &out); | |
| EXPECT_EQ("value_2", out[index++]); | |
| EXPECT_EQ("", out[index++]); | |
| EXPECT_THAT(header.GetAllOfHeader("key2"), ElementsAre("value_2", "")); | |
| header.GetAllOfHeader("a", &out); | |
| EXPECT_EQ("value_a", out[index++]); | |
| EXPECT_THAT(header.GetAllOfHeader("a"), ElementsAre("value_a")); | |
| header.GetAllOfHeader("b", &out); | |
| EXPECT_EQ("value_b", out[index++]); | |
| EXPECT_THAT(header.GetAllOfHeader("b"), ElementsAre("value_b")); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderIncludeRemovedDoesWhatItSays) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key1", "value_11"); | |
| header.AppendHeader("key2", "value_21"); | |
| header.AppendHeader("key1", "value_12"); | |
| header.AppendHeader("key2", "value_22"); | |
| header.AppendHeader("key1", ""); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeaderIncludeRemoved("key1", &out); | |
| ASSERT_EQ(3u, out.size()); | |
| EXPECT_EQ("value_11", out[0]); | |
| EXPECT_EQ("value_12", out[1]); | |
| EXPECT_EQ("", out[2]); | |
| header.GetAllOfHeaderIncludeRemoved("key2", &out); | |
| ASSERT_EQ(5u, out.size()); | |
| EXPECT_EQ("value_21", out[3]); | |
| EXPECT_EQ("value_22", out[4]); | |
| header.erase(header.GetHeaderPosition("key1")); | |
| out.clear(); | |
| header.GetAllOfHeaderIncludeRemoved("key1", &out); | |
| ASSERT_EQ(3u, out.size()); | |
| EXPECT_EQ("value_12", out[0]); | |
| EXPECT_EQ("", out[1]); | |
| EXPECT_EQ("value_11", out[2]); | |
| header.GetAllOfHeaderIncludeRemoved("key2", &out); | |
| ASSERT_EQ(5u, out.size()); | |
| EXPECT_EQ("value_21", out[3]); | |
| EXPECT_EQ("value_22", out[4]); | |
| header.RemoveAllOfHeader("key1"); | |
| out.clear(); | |
| header.GetAllOfHeaderIncludeRemoved("key1", &out); | |
| ASSERT_EQ(3u, out.size()); | |
| EXPECT_EQ("value_11", out[0]); | |
| EXPECT_EQ("value_12", out[1]); | |
| EXPECT_EQ("", out[2]); | |
| header.GetAllOfHeaderIncludeRemoved("key2", &out); | |
| ASSERT_EQ(5u, out.size()); | |
| EXPECT_EQ("value_21", out[3]); | |
| EXPECT_EQ("value_22", out[4]); | |
| header.Clear(); | |
| out.clear(); | |
| header.GetAllOfHeaderIncludeRemoved("key1", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| header.GetAllOfHeaderIncludeRemoved("key2", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderIncludeRemovedWithNonExistentKey) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| std::vector<absl::string_view> out; | |
| header.GetAllOfHeaderIncludeRemoved("key_non_existent", &out); | |
| ASSERT_EQ(0u, out.size()); | |
| } | |
| TEST(BalsaHeaders, GetIteratorForKeyDoesWhatItSays) { | |
| BalsaHeaders header; | |
| // Multiple values for a given header. | |
| // Some values appear multiple times | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("Key", "value_2"); | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("KEY", "value_1"); | |
| BalsaHeaders::const_header_lines_key_iterator key_it = | |
| header.GetIteratorForKey("key"); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key", key_it->first); | |
| EXPECT_EQ("value_1", key_it->second); | |
| ++key_it; | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("Key", key_it->first); | |
| EXPECT_EQ("value_2", key_it->second); | |
| ++key_it; | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key", key_it->first); | |
| EXPECT_EQ("", key_it->second); | |
| ++key_it; | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("KEY", key_it->first); | |
| EXPECT_EQ("value_1", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| } | |
| TEST(BalsaHeaders, GetIteratorForKeyWithNonExistentKey) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| BalsaHeaders::const_header_lines_key_iterator key_it = | |
| header.GetIteratorForKey("key_non_existent"); | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| const auto lines = header.lines("key_non_existent"); | |
| EXPECT_EQ(lines.begin(), header.lines().end()); | |
| EXPECT_EQ(lines.end(), header.header_lines_key_end()); | |
| } | |
| TEST(BalsaHeaders, GetIteratorForKeyWithAppendHeaders) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| BalsaHeaders::const_header_lines_key_iterator key_it = | |
| header.GetIteratorForKey("key_new"); | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| // Add key_new to the header | |
| header.AppendHeader("key_new", "value_3"); | |
| key_it = header.GetIteratorForKey("key_new"); | |
| const auto lines1 = header.lines("key_new"); | |
| EXPECT_EQ(lines1.begin(), key_it); | |
| EXPECT_EQ(lines1.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key_new", key_it->first); | |
| EXPECT_EQ("value_3", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| // Get the keys that are not modified | |
| key_it = header.GetIteratorForKey("key"); | |
| const auto lines2 = header.lines("key"); | |
| EXPECT_EQ(lines2.begin(), key_it); | |
| EXPECT_EQ(lines2.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key", key_it->first); | |
| EXPECT_EQ("value_1", key_it->second); | |
| ++key_it; | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key", key_it->first); | |
| EXPECT_EQ("value_2", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| } | |
| TEST(BalsaHeaders, GetIteratorForKeyWithRemoveHeaders) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| header.AppendHeader("a", "va"); | |
| header.RemoveAllOfHeader("a"); | |
| BalsaHeaders::const_header_lines_key_iterator key_it = | |
| header.GetIteratorForKey("key"); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| const auto lines1 = header.lines("key"); | |
| EXPECT_EQ(lines1.begin(), key_it); | |
| EXPECT_EQ(lines1.end(), header.header_lines_key_end()); | |
| EXPECT_EQ("value_1", key_it->second); | |
| ++key_it; | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key", key_it->first); | |
| EXPECT_EQ("value_2", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| // Check that a typical loop works properly. | |
| for (BalsaHeaders::const_header_lines_key_iterator it = | |
| header.GetIteratorForKey("key"); | |
| it != header.lines().end(); ++it) { | |
| EXPECT_EQ("key", it->first); | |
| } | |
| } | |
| TEST(BalsaHeaders, GetIteratorForKeyWithEraseHeaders) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| header.AppendHeader("a", "va"); | |
| header.erase(header.GetHeaderPosition("key")); | |
| BalsaHeaders::const_header_lines_key_iterator key_it = | |
| header.GetIteratorForKey("key"); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| const auto lines1 = header.lines("key"); | |
| EXPECT_EQ(lines1.begin(), key_it); | |
| EXPECT_EQ(lines1.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key", key_it->first); | |
| EXPECT_EQ("value_2", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| header.erase(header.GetHeaderPosition("key")); | |
| key_it = header.GetIteratorForKey("key"); | |
| const auto lines2 = header.lines("key"); | |
| EXPECT_EQ(lines2.begin(), key_it); | |
| EXPECT_EQ(lines2.end(), header.header_lines_key_end()); | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| key_it = header.GetIteratorForKey("a"); | |
| const auto lines3 = header.lines("a"); | |
| EXPECT_EQ(lines3.begin(), key_it); | |
| EXPECT_EQ(lines3.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("a", key_it->first); | |
| EXPECT_EQ("va", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| header.erase(header.GetHeaderPosition("a")); | |
| key_it = header.GetIteratorForKey("a"); | |
| const auto lines4 = header.lines("a"); | |
| EXPECT_EQ(lines4.begin(), key_it); | |
| EXPECT_EQ(lines4.end(), header.header_lines_key_end()); | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| } | |
| TEST(BalsaHeaders, GetIteratorForKeyWithNoHeaderLines) { | |
| BalsaHeaders header; | |
| BalsaHeaders::const_header_lines_key_iterator key_it = | |
| header.GetIteratorForKey("key"); | |
| const auto lines = header.lines("key"); | |
| EXPECT_EQ(lines.begin(), key_it); | |
| EXPECT_EQ(lines.end(), header.header_lines_key_end()); | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| } | |
| TEST(BalsaHeaders, GetIteratorForKeyWithBalsaFrameProcessInput) { | |
| BalsaHeaders header = CreateHTTPHeaders(true, | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value_1\r\n" | |
| "Key1: value_foo\r\n" | |
| "key2: value_2\r\n" | |
| "a: value_a\r\n" | |
| "key2: \r\n" | |
| "b: value_b\r\n" | |
| "\r\n"); | |
| BalsaHeaders::const_header_lines_key_iterator key_it = | |
| header.GetIteratorForKey("Key1"); | |
| const auto lines1 = header.lines("Key1"); | |
| EXPECT_EQ(lines1.begin(), key_it); | |
| EXPECT_EQ(lines1.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key1", key_it->first); | |
| EXPECT_EQ("value_1", key_it->second); | |
| ++key_it; | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("Key1", key_it->first); | |
| EXPECT_EQ("value_foo", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| key_it = header.GetIteratorForKey("key2"); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| const auto lines2 = header.lines("key2"); | |
| EXPECT_EQ(lines2.begin(), key_it); | |
| EXPECT_EQ(lines2.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key2", key_it->first); | |
| EXPECT_EQ("value_2", key_it->second); | |
| ++key_it; | |
| EXPECT_NE(header.lines().end(), key_it); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("key2", key_it->first); | |
| EXPECT_EQ("", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| key_it = header.GetIteratorForKey("a"); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| const auto lines3 = header.lines("a"); | |
| EXPECT_EQ(lines3.begin(), key_it); | |
| EXPECT_EQ(lines3.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("a", key_it->first); | |
| EXPECT_EQ("value_a", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| key_it = header.GetIteratorForKey("b"); | |
| EXPECT_NE(header.lines().end(), key_it); | |
| const auto lines4 = header.lines("b"); | |
| EXPECT_EQ(lines4.begin(), key_it); | |
| EXPECT_EQ(lines4.end(), header.header_lines_key_end()); | |
| EXPECT_NE(header.header_lines_key_end(), key_it); | |
| EXPECT_EQ("b", key_it->first); | |
| EXPECT_EQ("value_b", key_it->second); | |
| ++key_it; | |
| EXPECT_EQ(header.lines().end(), key_it); | |
| EXPECT_EQ(header.header_lines_key_end(), key_it); | |
| } | |
| TEST(BalsaHeaders, GetAllOfHeaderAsStringDoesWhatItSays) { | |
| BalsaHeaders header; | |
| // Multiple values for a given header. | |
| // Some values appear multiple times | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("Key", "value_2"); | |
| header.AppendHeader("key", ""); | |
| header.AppendHeader("KEY", "value_1"); | |
| std::string result = header.GetAllOfHeaderAsString("key"); | |
| EXPECT_EQ("value_1,value_2,,value_1", result); | |
| } | |
| TEST(BalsaHeaders, RemoveAllOfHeaderDoesWhatItSays) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| header.RemoveAllOfHeader("key"); | |
| ASSERT_EQ(header.lines().begin(), header.lines().end()); | |
| } | |
| TEST(BalsaHeaders, | |
| RemoveAllOfHeaderDoesWhatItSaysEvenWhenThingsHaveBeenErased) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key1", "value_1"); | |
| header.AppendHeader("key1", "value_2"); | |
| header.AppendHeader("key2", "value_3"); | |
| header.AppendHeader("key1", "value_4"); | |
| header.AppendHeader("key2", "value_5"); | |
| header.AppendHeader("key1", "value_6"); | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| BalsaHeaders::const_header_lines_iterator chli = header.lines().begin(); | |
| ++chli; | |
| ++chli; | |
| ++chli; | |
| header.erase(chli); | |
| chli = header.lines().begin(); | |
| ++chli; | |
| header.erase(chli); | |
| header.RemoveAllOfHeader("key1"); | |
| for (const auto& line : header.lines()) { | |
| EXPECT_NE(std::string("key1"), line.first); | |
| } | |
| } | |
| TEST(BalsaHeaders, RemoveAllOfHeaderDoesNothingWhenNoKeyOfThatNameExists) { | |
| BalsaHeaders header; | |
| header.AppendHeader("key", "value_1"); | |
| header.AppendHeader("key", "value_2"); | |
| ASSERT_NE(header.lines().begin(), header.lines().end()); | |
| header.RemoveAllOfHeader("foo"); | |
| int num_found = 0; | |
| for (const auto& line : header.lines()) { | |
| ++num_found; | |
| EXPECT_EQ(absl::string_view("key"), line.first); | |
| } | |
| EXPECT_EQ(2, num_found); | |
| EXPECT_NE(header.lines().begin(), header.lines().end()); | |
| } | |
| TEST(BalsaHeaders, WriteHeaderEndingToBuffer) { | |
| BalsaHeaders header; | |
| SimpleBuffer simple_buffer; | |
| header.WriteHeaderEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq("\r\n")); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferDoesntCrashWithUninitializedHeader) { | |
| BalsaHeaders header; | |
| SimpleBuffer simple_buffer; | |
| header.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWorksWithBalsaHeadersParsedByFramer) { | |
| std::string input = | |
| "GET / HTTP/1.0\r\n" | |
| "key_with_value: value\r\n" | |
| "key_with_continuation_value: \r\n" | |
| " with continuation\r\n" | |
| "key_with_two_continuation_value: \r\n" | |
| " continuation 1\r\n" | |
| " continuation 2\r\n" | |
| "a: foo \r\n" | |
| "b-s:\n" | |
| " bar\t\n" | |
| "foo: \r\n" | |
| "bazzzzzzzleriffic!: snaps\n" | |
| "\n"; | |
| std::string expected = | |
| "GET / HTTP/1.0\r\n" | |
| "key_with_value: value\r\n" | |
| "key_with_continuation_value: with continuation\r\n" | |
| "key_with_two_continuation_value: continuation 1\r\n" | |
| " continuation 2\r\n" | |
| "a: foo\r\n" | |
| "b-s: bar\r\n" | |
| "foo: \r\n" | |
| "bazzzzzzzleriffic!: snaps\r\n" | |
| "\r\n"; | |
| BalsaHeaders headers = CreateHTTPHeaders(true, input); | |
| SimpleBuffer simple_buffer; | |
| size_t expected_write_buffer_size = headers.GetSizeForWriteBuffer(); | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected)); | |
| EXPECT_EQ(expected_write_buffer_size, | |
| static_cast<size_t>(simple_buffer.ReadableBytes())); | |
| } | |
| TEST(BalsaHeaders, | |
| WriteToBufferWorksWithBalsaHeadersParsedByFramerTabContinuations) { | |
| std::string input = | |
| "GET / HTTP/1.0\r\n" | |
| "key_with_value: value\r\n" | |
| "key_with_continuation_value: \r\n" | |
| "\twith continuation\r\n" | |
| "key_with_two_continuation_value: \r\n" | |
| "\tcontinuation 1\r\n" | |
| "\tcontinuation 2\r\n" | |
| "a: foo \r\n" | |
| "b-s:\n" | |
| "\tbar\t\n" | |
| "foo: \r\n" | |
| "bazzzzzzzleriffic!: snaps\n" | |
| "\n"; | |
| std::string expected = | |
| "GET / HTTP/1.0\r\n" | |
| "key_with_value: value\r\n" | |
| "key_with_continuation_value: with continuation\r\n" | |
| "key_with_two_continuation_value: continuation 1\r\n" | |
| "\tcontinuation 2\r\n" | |
| "a: foo\r\n" | |
| "b-s: bar\r\n" | |
| "foo: \r\n" | |
| "bazzzzzzzleriffic!: snaps\r\n" | |
| "\r\n"; | |
| BalsaHeaders headers = CreateHTTPHeaders(true, input); | |
| SimpleBuffer simple_buffer; | |
| size_t expected_write_buffer_size = headers.GetSizeForWriteBuffer(); | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected)); | |
| EXPECT_EQ(expected_write_buffer_size, | |
| static_cast<size_t>(simple_buffer.ReadableBytes())); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWorksWhenFirstlineSetThroughHeaders) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| std::string expected = | |
| "GET / HTTP/1.0\r\n" | |
| "\r\n"; | |
| SimpleBuffer simple_buffer; | |
| size_t expected_write_buffer_size = headers.GetSizeForWriteBuffer(); | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected)); | |
| EXPECT_EQ(expected_write_buffer_size, | |
| static_cast<size_t>(simple_buffer.ReadableBytes())); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWorksWhenSetThroughHeaders) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key 2", "value\n 2"); | |
| headers.AppendHeader("key\n 3", "value3"); | |
| std::string expected = | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value1\r\n" | |
| "key 2: value\n" | |
| " 2\r\n" | |
| "key\n" | |
| " 3: value3\r\n" | |
| "\r\n"; | |
| SimpleBuffer simple_buffer; | |
| size_t expected_write_buffer_size = headers.GetSizeForWriteBuffer(); | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected)); | |
| EXPECT_EQ(expected_write_buffer_size, | |
| static_cast<size_t>(simple_buffer.ReadableBytes())); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWorkWhensOnlyLinesSetThroughHeaders) { | |
| BalsaHeaders headers; | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key 2", "value\n 2"); | |
| headers.AppendHeader("key\n 3", "value3"); | |
| std::string expected = | |
| "\r\n" | |
| "key1: value1\r\n" | |
| "key 2: value\n" | |
| " 2\r\n" | |
| "key\n" | |
| " 3: value3\r\n" | |
| "\r\n"; | |
| SimpleBuffer simple_buffer; | |
| size_t expected_write_buffer_size = headers.GetSizeForWriteBuffer(); | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected)); | |
| EXPECT_EQ(expected_write_buffer_size, | |
| static_cast<size_t>(simple_buffer.ReadableBytes())); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWorksWhenSetThroughHeadersWithElementsErased) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key 2", "value\n 2"); | |
| headers.AppendHeader("key\n 3", "value3"); | |
| headers.RemoveAllOfHeader("key1"); | |
| headers.RemoveAllOfHeader("key\n 3"); | |
| std::string expected = | |
| "GET / HTTP/1.0\r\n" | |
| "key 2: value\n" | |
| " 2\r\n" | |
| "\r\n"; | |
| SimpleBuffer simple_buffer; | |
| size_t expected_write_buffer_size = headers.GetSizeForWriteBuffer(); | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected)); | |
| EXPECT_EQ(expected_write_buffer_size, | |
| static_cast<size_t>(simple_buffer.ReadableBytes())); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWithManuallyAppendedHeaderLine) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key 2", "value\n 2"); | |
| std::string expected = | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value1\r\n" | |
| "key 2: value\n" | |
| " 2\r\n" | |
| "key 3: value 3\r\n" | |
| "\r\n"; | |
| SimpleBuffer simple_buffer; | |
| size_t expected_write_buffer_size = headers.GetSizeForWriteBuffer(); | |
| headers.WriteToBuffer(&simple_buffer); | |
| headers.WriteHeaderLineToBuffer(&simple_buffer, "key 3", "value 3", | |
| BalsaHeaders::CaseOption::kNoModification); | |
| headers.WriteHeaderEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected)); | |
| EXPECT_EQ(expected_write_buffer_size + 16, | |
| static_cast<size_t>(simple_buffer.ReadableBytes())); | |
| } | |
| TEST(BalsaHeaders, DumpToStringEmptyHeaders) { | |
| BalsaHeaders headers; | |
| std::string headers_str; | |
| headers.DumpToString(&headers_str); | |
| EXPECT_EQ("\n <empty header>\n", headers_str); | |
| } | |
| TEST(BalsaHeaders, DumpToStringParsedHeaders) { | |
| std::string input = | |
| "GET / HTTP/1.0\r\n" | |
| "Header1: value\r\n" | |
| "Header2: value\r\n" | |
| "\r\n"; | |
| std::string output = | |
| "\n" | |
| " GET / HTTP/1.0\n" | |
| " Header1: value\n" | |
| " Header2: value\n"; | |
| BalsaHeaders headers = CreateHTTPHeaders(true, input); | |
| std::string headers_str; | |
| headers.DumpToString(&headers_str); | |
| EXPECT_EQ(output, headers_str); | |
| EXPECT_TRUE(headers.FramerIsDoneWriting()); | |
| } | |
| TEST(BalsaHeaders, DumpToStringPartialHeaders) { | |
| BalsaHeaders headers; | |
| BalsaFrame balsa_frame; | |
| balsa_frame.set_is_request(true); | |
| balsa_frame.set_balsa_headers(&headers); | |
| std::string input = | |
| "GET / HTTP/1.0\r\n" | |
| "Header1: value\r\n" | |
| "Header2: value\r\n"; | |
| std::string output = absl::StrFormat("\n <incomplete header len: %d>\n ", | |
| static_cast<int>(input.size())); | |
| output += input; | |
| output += '\n'; | |
| ASSERT_EQ(input.size(), balsa_frame.ProcessInput(input.data(), input.size())); | |
| ASSERT_FALSE(balsa_frame.MessageFullyRead()); | |
| std::string headers_str; | |
| headers.DumpToString(&headers_str); | |
| EXPECT_EQ(output, headers_str); | |
| EXPECT_FALSE(headers.FramerIsDoneWriting()); | |
| } | |
| TEST(BalsaHeaders, DumpToStringParsingNonHeadersData) { | |
| BalsaHeaders headers; | |
| BalsaFrame balsa_frame; | |
| balsa_frame.set_is_request(true); | |
| balsa_frame.set_balsa_headers(&headers); | |
| std::string input = | |
| "This is not a header. " | |
| "Just some random data to simulate mismatch."; | |
| std::string output = absl::StrFormat("\n <incomplete header len: %d>\n ", | |
| static_cast<int>(input.size())); | |
| output += input; | |
| output += '\n'; | |
| ASSERT_EQ(input.size(), balsa_frame.ProcessInput(input.data(), input.size())); | |
| ASSERT_FALSE(balsa_frame.MessageFullyRead()); | |
| std::string headers_str; | |
| headers.DumpToString(&headers_str); | |
| EXPECT_EQ(output, headers_str); | |
| } | |
| TEST(BalsaHeaders, Clear) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key 2", "value\n 2"); | |
| headers.AppendHeader("key\n 3", "value3"); | |
| headers.RemoveAllOfHeader("key1"); | |
| headers.RemoveAllOfHeader("key\n 3"); | |
| headers.Clear(); | |
| EXPECT_TRUE(headers.first_line().empty()); | |
| EXPECT_EQ(headers.lines().begin(), headers.lines().end()); | |
| EXPECT_TRUE(headers.IsEmpty()); | |
| } | |
| TEST(BalsaHeaders, | |
| TestSetFromStringPiecesWithInitialFirstlineInHeaderStreamAndNewToo) { | |
| BalsaHeaders headers = CreateHTTPHeaders(false, | |
| "HTTP/1.1 200 reason phrase\r\n" | |
| "content-length: 0\r\n" | |
| "\r\n"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.1")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("reason phrase")); | |
| headers.SetResponseFirstline("HTTP/1.0", 404, "a reason"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.0")); | |
| EXPECT_THAT(headers.response_code(), StrEq("404")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(404)); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("a reason")); | |
| EXPECT_THAT(headers.first_line(), StrEq("HTTP/1.0 404 a reason")); | |
| } | |
| TEST(BalsaHeaders, | |
| TestSetFromStringPiecesWithInitialFirstlineInHeaderStreamButNotNew) { | |
| BalsaHeaders headers = CreateHTTPHeaders(false, | |
| "HTTP/1.1 200 reason phrase\r\n" | |
| "content-length: 0\r\n" | |
| "\r\n"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.1")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("reason phrase")); | |
| headers.SetResponseFirstline("HTTP/1.000", 404000, | |
| "supercalifragilisticexpealidocious"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.000")); | |
| EXPECT_THAT(headers.response_code(), StrEq("404000")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(404000)); | |
| EXPECT_THAT(headers.response_reason_phrase(), | |
| StrEq("supercalifragilisticexpealidocious")); | |
| EXPECT_THAT(headers.first_line(), | |
| StrEq("HTTP/1.000 404000 supercalifragilisticexpealidocious")); | |
| } | |
| TEST(BalsaHeaders, | |
| TestSetFromStringPiecesWithFirstFirstlineInHeaderStreamButNotNew2) { | |
| SCOPED_TRACE( | |
| "This test tests the codepath where the new firstline is" | |
| " too large to fit within the space used by the original" | |
| " firstline, but large enuogh to space in the free space" | |
| " available in both firstline plus the space made available" | |
| " with deleted header lines (specifically, the first one"); | |
| BalsaHeaders headers = CreateHTTPHeaders( | |
| false, | |
| "HTTP/1.1 200 reason phrase\r\n" | |
| "a: 0987123409871234078130948710938471093827401983740198327401982374\r\n" | |
| "content-length: 0\r\n" | |
| "\r\n"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.1")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("reason phrase")); | |
| headers.erase(headers.lines().begin()); | |
| headers.SetResponseFirstline("HTTP/1.000", 404000, | |
| "supercalifragilisticexpealidocious"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.000")); | |
| EXPECT_THAT(headers.response_code(), StrEq("404000")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(404000)); | |
| EXPECT_THAT(headers.response_reason_phrase(), | |
| StrEq("supercalifragilisticexpealidocious")); | |
| EXPECT_THAT(headers.first_line(), | |
| StrEq("HTTP/1.000 404000 supercalifragilisticexpealidocious")); | |
| } | |
| TEST(BalsaHeaders, TestSetFirstlineFromStringPiecesWithNoInitialFirstline) { | |
| BalsaHeaders headers; | |
| headers.SetResponseFirstline("HTTP/1.1", 200, "don't need a reason"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.1")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(200)); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("don't need a reason")); | |
| EXPECT_THAT(headers.first_line(), StrEq("HTTP/1.1 200 don't need a reason")); | |
| } | |
| TEST(BalsaHeaders, TestSettingFirstlineElementsWithOtherElementsMissing) { | |
| { | |
| BalsaHeaders headers; | |
| headers.SetRequestMethod("GET"); | |
| headers.SetRequestUri("/"); | |
| EXPECT_THAT(headers.first_line(), StrEq("GET / ")); | |
| } | |
| { | |
| BalsaHeaders headers; | |
| headers.SetRequestMethod("GET"); | |
| headers.SetRequestVersion("HTTP/1.1"); | |
| EXPECT_THAT(headers.first_line(), StrEq("GET HTTP/1.1")); | |
| } | |
| { | |
| BalsaHeaders headers; | |
| headers.SetRequestUri("/"); | |
| headers.SetRequestVersion("HTTP/1.1"); | |
| EXPECT_THAT(headers.first_line(), StrEq(" / HTTP/1.1")); | |
| } | |
| } | |
| TEST(BalsaHeaders, TestSettingMissingFirstlineElementsAfterBalsaHeadersParsed) { | |
| { | |
| BalsaHeaders headers = CreateHTTPHeaders(true, "GET /foo\r\n"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET /foo")); | |
| headers.SetRequestVersion("HTTP/1.1"); | |
| EXPECT_THAT(headers.first_line(), StrEq("GET /foo HTTP/1.1")); | |
| } | |
| { | |
| BalsaHeaders headers = CreateHTTPHeaders(true, "GET\r\n"); | |
| ASSERT_THAT(headers.first_line(), StrEq("GET")); | |
| headers.SetRequestUri("/foo"); | |
| EXPECT_THAT(headers.first_line(), StrEq("GET /foo ")); | |
| } | |
| } | |
| // Here we exersize the codepaths involved in setting a new firstine when the | |
| // previously set firstline is stored in the 'additional_data_stream_' | |
| // variable, and the new firstline is larger than the previously set firstline. | |
| TEST(BalsaHeaders, | |
| SetFirstlineFromStringPiecesFirstInAdditionalDataAndNewLarger) { | |
| BalsaHeaders headers; | |
| // This one will end up being put into the additional data stream | |
| headers.SetResponseFirstline("HTTP/1.1", 200, "don't need a reason"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.1")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(200)); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("don't need a reason")); | |
| EXPECT_THAT(headers.first_line(), StrEq("HTTP/1.1 200 don't need a reason")); | |
| // Now, we set it again, this time we're extending what exists | |
| // here. | |
| headers.SetResponseFirstline("HTTP/1.10", 2000, "REALLY don't need a reason"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.10")); | |
| EXPECT_THAT(headers.response_code(), StrEq("2000")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(2000)); | |
| EXPECT_THAT(headers.response_reason_phrase(), | |
| StrEq("REALLY don't need a reason")); | |
| EXPECT_THAT(headers.first_line(), | |
| StrEq("HTTP/1.10 2000 REALLY don't need a reason")); | |
| } | |
| // Here we exersize the codepaths involved in setting a new firstine when the | |
| // previously set firstline is stored in the 'additional_data_stream_' | |
| // variable, and the new firstline is smaller than the previously set firstline. | |
| TEST(BalsaHeaders, | |
| TestSetFirstlineFromStringPiecesWithPreviousInAdditionalDataNewSmaller) { | |
| BalsaHeaders headers; | |
| // This one will end up being put into the additional data stream | |
| // | |
| headers.SetResponseFirstline("HTTP/1.10", 2000, "REALLY don't need a reason"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.10")); | |
| EXPECT_THAT(headers.response_code(), StrEq("2000")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(2000)); | |
| EXPECT_THAT(headers.response_reason_phrase(), | |
| StrEq("REALLY don't need a reason")); | |
| EXPECT_THAT(headers.first_line(), | |
| StrEq("HTTP/1.10 2000 REALLY don't need a reason")); | |
| // Now, we set it again, this time we're extending what exists | |
| // here. | |
| headers.SetResponseFirstline("HTTP/1.0", 200, "a reason"); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.0")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.parsed_response_code(), Eq(200)); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("a reason")); | |
| EXPECT_THAT(headers.first_line(), StrEq("HTTP/1.0 200 a reason")); | |
| } | |
| TEST(BalsaHeaders, CopyFrom) { | |
| // TODO(fenix): test -all- member variables. | |
| // (remaining: transfer_encoding_is_chunked_ | |
| // content_length_ | |
| // content_length_status_ | |
| // parsed_response_code_ | |
| // connection_close_token_found_ | |
| // connection_keep_alive_token_found_ | |
| // content_encoding_gzip_token_found_) | |
| BalsaHeaders headers1, headers2; | |
| absl::string_view method("GET"); | |
| absl::string_view uri("/foo"); | |
| absl::string_view version("HTTP/1.0"); | |
| headers1.SetRequestFirstlineFromStringPieces(method, uri, version); | |
| headers1.AppendHeader("key1", "value1"); | |
| headers1.AppendHeader("key 2", "value\n 2"); | |
| headers1.AppendHeader("key\n 3", "value3"); | |
| // "GET /foo HTTP/1.0" // 17 | |
| // "key1: value1\r\n" // 14 | |
| // "key 2: value\n 2\r\n" // 17 | |
| // "key\n 3: value3\r\n" // 16 | |
| headers2.CopyFrom(headers1); | |
| EXPECT_THAT(headers1.first_line(), StrEq("GET /foo HTTP/1.0")); | |
| BalsaHeaders::const_header_lines_iterator chli = headers1.lines().begin(); | |
| EXPECT_THAT(chli->first, StrEq("key1")); | |
| EXPECT_THAT(chli->second, StrEq("value1")); | |
| ++chli; | |
| EXPECT_THAT(chli->first, StrEq("key 2")); | |
| EXPECT_THAT(chli->second, StrEq("value\n 2")); | |
| ++chli; | |
| EXPECT_THAT(chli->first, StrEq("key\n 3")); | |
| EXPECT_THAT(chli->second, StrEq("value3")); | |
| ++chli; | |
| EXPECT_EQ(headers1.lines().end(), chli); | |
| EXPECT_THAT(headers1.request_method(), | |
| StrEq((std::string(headers2.request_method())))); | |
| EXPECT_THAT(headers1.request_uri(), | |
| StrEq((std::string(headers2.request_uri())))); | |
| EXPECT_THAT(headers1.request_version(), | |
| StrEq((std::string(headers2.request_version())))); | |
| EXPECT_THAT(headers2.first_line(), StrEq("GET /foo HTTP/1.0")); | |
| chli = headers2.lines().begin(); | |
| EXPECT_THAT(chli->first, StrEq("key1")); | |
| EXPECT_THAT(chli->second, StrEq("value1")); | |
| ++chli; | |
| EXPECT_THAT(chli->first, StrEq("key 2")); | |
| EXPECT_THAT(chli->second, StrEq("value\n 2")); | |
| ++chli; | |
| EXPECT_THAT(chli->first, StrEq("key\n 3")); | |
| EXPECT_THAT(chli->second, StrEq("value3")); | |
| ++chli; | |
| EXPECT_EQ(headers2.lines().end(), chli); | |
| version = absl::string_view("HTTP/1.1"); | |
| int code = 200; | |
| absl::string_view reason_phrase("reason phrase asdf"); | |
| headers1.RemoveAllOfHeader("key1"); | |
| headers1.AppendHeader("key4", "value4"); | |
| headers1.SetResponseFirstline(version, code, reason_phrase); | |
| headers2.CopyFrom(headers1); | |
| // "GET /foo HTTP/1.0" // 17 | |
| // "XXXXXXXXXXXXXX" // 14 | |
| // "key 2: value\n 2\r\n" // 17 | |
| // "key\n 3: value3\r\n" // 16 | |
| // "key4: value4\r\n" // 14 | |
| // | |
| // -> | |
| // | |
| // "HTTP/1.1 200 reason phrase asdf" // 31 = (17 + 14) | |
| // "key 2: value\n 2\r\n" // 17 | |
| // "key\n 3: value3\r\n" // 16 | |
| // "key4: value4\r\n" // 14 | |
| EXPECT_THAT(headers1.request_method(), | |
| StrEq((std::string(headers2.request_method())))); | |
| EXPECT_THAT(headers1.request_uri(), | |
| StrEq((std::string(headers2.request_uri())))); | |
| EXPECT_THAT(headers1.request_version(), | |
| StrEq((std::string(headers2.request_version())))); | |
| EXPECT_THAT(headers2.first_line(), StrEq("HTTP/1.1 200 reason phrase asdf")); | |
| chli = headers2.lines().begin(); | |
| EXPECT_THAT(chli->first, StrEq("key 2")); | |
| EXPECT_THAT(chli->second, StrEq("value\n 2")); | |
| ++chli; | |
| EXPECT_THAT(chli->first, StrEq("key\n 3")); | |
| EXPECT_THAT(chli->second, StrEq("value3")); | |
| ++chli; | |
| EXPECT_THAT(chli->first, StrEq("key4")); | |
| EXPECT_THAT(chli->second, StrEq("value4")); | |
| ++chli; | |
| EXPECT_EQ(headers2.lines().end(), chli); | |
| } | |
| // Test BalsaHeaders move constructor and move assignment operator. | |
| TEST(BalsaHeaders, Move) { | |
| BalsaHeaders headers1, headers3; | |
| absl::string_view method("GET"); | |
| absl::string_view uri("/foo"); | |
| absl::string_view version("HTTP/1.0"); | |
| headers1.SetRequestFirstlineFromStringPieces(method, uri, version); | |
| headers1.AppendHeader("key1", "value1"); | |
| headers1.AppendHeader("key 2", "value\n 2"); | |
| headers1.AppendHeader("key\n 3", "value3"); | |
| // "GET /foo HTTP/1.0" // 17 | |
| // "key1: value1\r\n" // 14 | |
| // "key 2: value\n 2\r\n" // 17 | |
| // "key\n 3: value3\r\n" // 16 | |
| BalsaHeaders headers2 = std::move(headers1); | |
| EXPECT_EQ("GET /foo HTTP/1.0", headers2.first_line()); | |
| BalsaHeaders::const_header_lines_iterator chli = headers2.lines().begin(); | |
| EXPECT_EQ("key1", chli->first); | |
| EXPECT_EQ("value1", chli->second); | |
| ++chli; | |
| EXPECT_EQ("key 2", chli->first); | |
| EXPECT_EQ("value\n 2", chli->second); | |
| ++chli; | |
| EXPECT_EQ("key\n 3", chli->first); | |
| EXPECT_EQ("value3", chli->second); | |
| ++chli; | |
| EXPECT_EQ(headers2.lines().end(), chli); | |
| EXPECT_EQ("GET", headers2.request_method()); | |
| EXPECT_EQ("/foo", headers2.request_uri()); | |
| EXPECT_EQ("HTTP/1.0", headers2.request_version()); | |
| headers3 = std::move(headers2); | |
| version = absl::string_view("HTTP/1.1"); | |
| int code = 200; | |
| absl::string_view reason_phrase("reason phrase asdf"); | |
| headers3.RemoveAllOfHeader("key1"); | |
| headers3.AppendHeader("key4", "value4"); | |
| headers3.SetResponseFirstline(version, code, reason_phrase); | |
| BalsaHeaders headers4 = std::move(headers3); | |
| // "GET /foo HTTP/1.0" // 17 | |
| // "XXXXXXXXXXXXXX" // 14 | |
| // "key 2: value\n 2\r\n" // 17 | |
| // "key\n 3: value3\r\n" // 16 | |
| // "key4: value4\r\n" // 14 | |
| // | |
| // -> | |
| // | |
| // "HTTP/1.1 200 reason phrase asdf" // 31 = (17 + 14) | |
| // "key 2: value\n 2\r\n" // 17 | |
| // "key\n 3: value3\r\n" // 16 | |
| // "key4: value4\r\n" // 14 | |
| EXPECT_EQ("200", headers4.response_code()); | |
| EXPECT_EQ("reason phrase asdf", headers4.response_reason_phrase()); | |
| EXPECT_EQ("HTTP/1.1", headers4.response_version()); | |
| EXPECT_EQ("HTTP/1.1 200 reason phrase asdf", headers4.first_line()); | |
| chli = headers4.lines().begin(); | |
| EXPECT_EQ("key 2", chli->first); | |
| EXPECT_EQ("value\n 2", chli->second); | |
| ++chli; | |
| EXPECT_EQ("key\n 3", chli->first); | |
| EXPECT_EQ("value3", chli->second); | |
| ++chli; | |
| EXPECT_EQ("key4", chli->first); | |
| EXPECT_EQ("value4", chli->second); | |
| ++chli; | |
| EXPECT_EQ(headers4.lines().end(), chli); | |
| } | |
| TEST(BalsaHeaders, IteratorWorksWithOStreamAsExpected) { | |
| { | |
| std::stringstream actual; | |
| BalsaHeaders::const_header_lines_iterator chli; | |
| actual << chli; | |
| // Note that the output depends on the flavor of standard library in use. | |
| EXPECT_THAT(actual.str(), AnyOf(StrEq("[0, 0]"), // libstdc++ | |
| StrEq("[(nil), 0]"))); // libc++ | |
| } | |
| { | |
| BalsaHeaders headers; | |
| std::stringstream actual; | |
| BalsaHeaders::const_header_lines_iterator chli = headers.lines().begin(); | |
| actual << chli; | |
| std::stringstream expected; | |
| expected << "[" << &headers << ", 0]"; | |
| EXPECT_THAT(expected.str(), StrEq(actual.str())); | |
| } | |
| } | |
| TEST(BalsaHeaders, TestSetResponseReasonPhraseWithNoInitialFirstline) { | |
| BalsaHeaders balsa_headers; | |
| balsa_headers.SetResponseReasonPhrase("don't need a reason"); | |
| EXPECT_THAT(balsa_headers.first_line(), StrEq(" don't need a reason")); | |
| EXPECT_TRUE(balsa_headers.response_version().empty()); | |
| EXPECT_TRUE(balsa_headers.response_code().empty()); | |
| EXPECT_THAT(balsa_headers.response_reason_phrase(), | |
| StrEq("don't need a reason")); | |
| } | |
| // Testing each of 9 combinations separately was taking up way too much of this | |
| // file (not to mention the inordinate amount of stupid code duplication), thus | |
| // this test tests all 9 combinations of smaller, equal, and larger in one | |
| // place. | |
| TEST(BalsaHeaders, TestSetResponseReasonPhrase) { | |
| const char* response_reason_phrases[] = { | |
| "qwerty asdfgh", | |
| "qwerty", | |
| "qwerty asdfghjkl", | |
| }; | |
| size_t arraysize_squared = (ABSL_ARRAYSIZE(response_reason_phrases) * | |
| ABSL_ARRAYSIZE(response_reason_phrases)); | |
| // We go through the 9 different permutations of (response_reason_phrases | |
| // choose 2) in the loop below. For each permutation, we mutate the firstline | |
| // twice-- once from the original, and once from the previous. | |
| for (size_t iteration = 0; iteration < arraysize_squared; ++iteration) { | |
| SCOPED_TRACE("Original firstline: \"HTTP/1.0 200 reason phrase\""); | |
| BalsaHeaders headers = CreateHTTPHeaders(true, | |
| "HTTP/1.0 200 reason phrase\r\n" | |
| "content-length: 0\r\n" | |
| "\r\n"); | |
| ASSERT_THAT(headers.first_line(), StrEq("HTTP/1.0 200 reason phrase")); | |
| { | |
| int first = iteration / ABSL_ARRAYSIZE(response_reason_phrases); | |
| const char* response_reason_phrase_first = response_reason_phrases[first]; | |
| std::string expected_new_firstline = | |
| absl::StrFormat("HTTP/1.0 200 %s", response_reason_phrase_first); | |
| SCOPED_TRACE(absl::StrFormat("Then set response_reason_phrase(\"%s\")", | |
| response_reason_phrase_first)); | |
| headers.SetResponseReasonPhrase(response_reason_phrase_first); | |
| EXPECT_THAT(headers.first_line(), | |
| StrEq(absl::StrFormat("HTTP/1.0 200 %s", | |
| response_reason_phrase_first))); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.0")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.response_reason_phrase(), | |
| StrEq(response_reason_phrase_first)); | |
| } | |
| // Note that each iteration of the outer loop causes the headers to be left | |
| // in a different state. Nothing wrong with that, but we should use each of | |
| // these states, and try each of our scenarios again. This inner loop does | |
| // that. | |
| { | |
| int second = iteration % ABSL_ARRAYSIZE(response_reason_phrases); | |
| const char* response_reason_phrase_second = | |
| response_reason_phrases[second]; | |
| std::string expected_new_firstline = | |
| absl::StrFormat("HTTP/1.0 200 %s", response_reason_phrase_second); | |
| SCOPED_TRACE(absl::StrFormat("Then set response_reason_phrase(\"%s\")", | |
| response_reason_phrase_second)); | |
| headers.SetResponseReasonPhrase(response_reason_phrase_second); | |
| EXPECT_THAT(headers.first_line(), | |
| StrEq(absl::StrFormat("HTTP/1.0 200 %s", | |
| response_reason_phrase_second))); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.0")); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.response_reason_phrase(), | |
| StrEq(response_reason_phrase_second)); | |
| } | |
| } | |
| } | |
| TEST(BalsaHeaders, TestSetResponseVersionWithNoInitialFirstline) { | |
| BalsaHeaders balsa_headers; | |
| balsa_headers.SetResponseVersion("HTTP/1.1"); | |
| EXPECT_THAT(balsa_headers.first_line(), StrEq("HTTP/1.1 ")); | |
| EXPECT_THAT(balsa_headers.response_version(), StrEq("HTTP/1.1")); | |
| EXPECT_TRUE(balsa_headers.response_code().empty()); | |
| EXPECT_TRUE(balsa_headers.response_reason_phrase().empty()); | |
| } | |
| // Testing each of 9 combinations separately was taking up way too much of this | |
| // file (not to mention the inordinate amount of stupid code duplication), thus | |
| // this test tests all 9 combinations of smaller, equal, and larger in one | |
| // place. | |
| TEST(BalsaHeaders, TestSetResponseVersion) { | |
| const char* response_versions[] = { | |
| "ABCD/123", | |
| "ABCD", | |
| "ABCD/123456", | |
| }; | |
| size_t arraysize_squared = | |
| (ABSL_ARRAYSIZE(response_versions) * ABSL_ARRAYSIZE(response_versions)); | |
| // We go through the 9 different permutations of (response_versions choose 2) | |
| // in the loop below. For each permutation, we mutate the firstline twice-- | |
| // once from the original, and once from the previous. | |
| for (size_t iteration = 0; iteration < arraysize_squared; ++iteration) { | |
| SCOPED_TRACE("Original firstline: \"HTTP/1.0 200 reason phrase\""); | |
| BalsaHeaders headers = CreateHTTPHeaders(false, | |
| "HTTP/1.0 200 reason phrase\r\n" | |
| "content-length: 0\r\n" | |
| "\r\n"); | |
| ASSERT_THAT(headers.first_line(), StrEq("HTTP/1.0 200 reason phrase")); | |
| // This structure guarantees that we'll visit all of the possible | |
| // variations of setting. | |
| { | |
| int first = iteration / ABSL_ARRAYSIZE(response_versions); | |
| const char* response_version_first = response_versions[first]; | |
| std::string expected_new_firstline = | |
| absl::StrFormat("%s 200 reason phrase", response_version_first); | |
| SCOPED_TRACE(absl::StrFormat("Then set response_version(\"%s\")", | |
| response_version_first)); | |
| headers.SetResponseVersion(response_version_first); | |
| EXPECT_THAT(headers.first_line(), StrEq(expected_new_firstline)); | |
| EXPECT_THAT(headers.response_version(), StrEq(response_version_first)); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("reason phrase")); | |
| } | |
| { | |
| int second = iteration % ABSL_ARRAYSIZE(response_versions); | |
| const char* response_version_second = response_versions[second]; | |
| std::string expected_new_firstline = | |
| absl::StrFormat("%s 200 reason phrase", response_version_second); | |
| SCOPED_TRACE(absl::StrFormat("Then set response_version(\"%s\")", | |
| response_version_second)); | |
| headers.SetResponseVersion(response_version_second); | |
| EXPECT_THAT(headers.first_line(), StrEq(expected_new_firstline)); | |
| EXPECT_THAT(headers.response_version(), StrEq(response_version_second)); | |
| EXPECT_THAT(headers.response_code(), StrEq("200")); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("reason phrase")); | |
| } | |
| } | |
| } | |
| TEST(BalsaHeaders, TestSetResponseReasonAndVersionWithNoInitialFirstline) { | |
| BalsaHeaders headers; | |
| headers.SetResponseVersion("HTTP/1.1"); | |
| headers.SetResponseReasonPhrase("don't need a reason"); | |
| EXPECT_THAT(headers.first_line(), StrEq("HTTP/1.1 don't need a reason")); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.1")); | |
| EXPECT_TRUE(headers.response_code().empty()); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("don't need a reason")); | |
| } | |
| TEST(BalsaHeaders, TestSetResponseCodeWithNoInitialFirstline) { | |
| BalsaHeaders balsa_headers; | |
| balsa_headers.SetParsedResponseCodeAndUpdateFirstline(2002); | |
| EXPECT_THAT(balsa_headers.first_line(), StrEq(" 2002 ")); | |
| EXPECT_TRUE(balsa_headers.response_version().empty()); | |
| EXPECT_THAT(balsa_headers.response_code(), StrEq("2002")); | |
| EXPECT_TRUE(balsa_headers.response_reason_phrase().empty()); | |
| EXPECT_THAT(balsa_headers.parsed_response_code(), Eq(2002)); | |
| } | |
| TEST(BalsaHeaders, TestSetParsedResponseCode) { | |
| BalsaHeaders balsa_headers; | |
| balsa_headers.set_parsed_response_code(std::numeric_limits<int>::max()); | |
| EXPECT_THAT(balsa_headers.parsed_response_code(), | |
| Eq(std::numeric_limits<int>::max())); | |
| } | |
| TEST(BalsaHeaders, TestSetResponseCode) { | |
| const char* response_codes[] = { | |
| "200" | |
| "23", | |
| "200200", | |
| }; | |
| size_t arraysize_squared = | |
| (ABSL_ARRAYSIZE(response_codes) * ABSL_ARRAYSIZE(response_codes)); | |
| // We go through the 9 different permutations of (response_codes choose 2) | |
| // in the loop below. For each permutation, we mutate the firstline twice-- | |
| // once from the original, and once from the previous. | |
| for (size_t iteration = 0; iteration < arraysize_squared; ++iteration) { | |
| SCOPED_TRACE("Original firstline: \"HTTP/1.0 200 reason phrase\""); | |
| BalsaHeaders headers = CreateHTTPHeaders(false, | |
| "HTTP/1.0 200 reason phrase\r\n" | |
| "content-length: 0\r\n" | |
| "\r\n"); | |
| ASSERT_THAT(headers.first_line(), StrEq("HTTP/1.0 200 reason phrase")); | |
| // This structure guarantees that we'll visit all of the possible | |
| // variations of setting. | |
| { | |
| int first = iteration / ABSL_ARRAYSIZE(response_codes); | |
| const char* response_code_first = response_codes[first]; | |
| std::string expected_new_firstline = | |
| absl::StrFormat("HTTP/1.0 %s reason phrase", response_code_first); | |
| SCOPED_TRACE(absl::StrFormat("Then set response_code(\"%s\")", | |
| response_code_first)); | |
| headers.SetResponseCode(response_code_first); | |
| EXPECT_THAT(headers.first_line(), StrEq(expected_new_firstline)); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.0")); | |
| EXPECT_THAT(headers.response_code(), StrEq(response_code_first)); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("reason phrase")); | |
| } | |
| { | |
| int second = iteration % ABSL_ARRAYSIZE(response_codes); | |
| const char* response_code_second = response_codes[second]; | |
| std::string expected_new_secondline = | |
| absl::StrFormat("HTTP/1.0 %s reason phrase", response_code_second); | |
| SCOPED_TRACE(absl::StrFormat("Then set response_code(\"%s\")", | |
| response_code_second)); | |
| headers.SetResponseCode(response_code_second); | |
| EXPECT_THAT(headers.first_line(), StrEq(expected_new_secondline)); | |
| EXPECT_THAT(headers.response_version(), StrEq("HTTP/1.0")); | |
| EXPECT_THAT(headers.response_code(), StrEq(response_code_second)); | |
| EXPECT_THAT(headers.response_reason_phrase(), StrEq("reason phrase")); | |
| } | |
| } | |
| } | |
| TEST(BalsaHeaders, TestAppendToHeader) { | |
| // Test the basic case of appending to a header. | |
| BalsaHeaders headers; | |
| headers.AppendHeader("foo", "foo_value"); | |
| headers.AppendHeader("bar", "bar_value"); | |
| headers.AppendToHeader("foo", "foo_value2"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value,foo_value2")); | |
| EXPECT_THAT(headers.GetHeader("bar"), StrEq("bar_value")); | |
| } | |
| TEST(BalsaHeaders, TestInitialAppend) { | |
| // Test that AppendToHeader works properly when the header did not already | |
| // exist. | |
| BalsaHeaders headers; | |
| headers.AppendToHeader("foo", "foo_value"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value")); | |
| headers.AppendToHeader("foo", "foo_value2"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value,foo_value2")); | |
| } | |
| TEST(BalsaHeaders, TestAppendAndRemove) { | |
| // Test that AppendToHeader works properly with removing. | |
| BalsaHeaders headers; | |
| headers.AppendToHeader("foo", "foo_value"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value")); | |
| headers.AppendToHeader("foo", "foo_value2"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value,foo_value2")); | |
| headers.RemoveAllOfHeader("foo"); | |
| headers.AppendToHeader("foo", "foo_value3"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value3")); | |
| headers.AppendToHeader("foo", "foo_value4"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value3,foo_value4")); | |
| } | |
| TEST(BalsaHeaders, TestAppendToHeaderWithCommaAndSpace) { | |
| // Test the basic case of appending to a header with comma and space. | |
| BalsaHeaders headers; | |
| headers.AppendHeader("foo", "foo_value"); | |
| headers.AppendHeader("bar", "bar_value"); | |
| headers.AppendToHeaderWithCommaAndSpace("foo", "foo_value2"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value, foo_value2")); | |
| EXPECT_THAT(headers.GetHeader("bar"), StrEq("bar_value")); | |
| } | |
| TEST(BalsaHeaders, TestInitialAppendWithCommaAndSpace) { | |
| // Test that AppendToHeadeWithCommaAndSpace works properly when the | |
| // header did not already exist. | |
| BalsaHeaders headers; | |
| headers.AppendToHeaderWithCommaAndSpace("foo", "foo_value"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value")); | |
| headers.AppendToHeaderWithCommaAndSpace("foo", "foo_value2"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value, foo_value2")); | |
| } | |
| TEST(BalsaHeaders, TestAppendWithCommaAndSpaceAndRemove) { | |
| // Test that AppendToHeadeWithCommaAndSpace works properly with removing. | |
| BalsaHeaders headers; | |
| headers.AppendToHeaderWithCommaAndSpace("foo", "foo_value"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value")); | |
| headers.AppendToHeaderWithCommaAndSpace("foo", "foo_value2"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value, foo_value2")); | |
| headers.RemoveAllOfHeader("foo"); | |
| headers.AppendToHeaderWithCommaAndSpace("foo", "foo_value3"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value3")); | |
| headers.AppendToHeaderWithCommaAndSpace("foo", "foo_value4"); | |
| EXPECT_THAT(headers.GetHeader("foo"), StrEq("foo_value3, foo_value4")); | |
| } | |
| TEST(BalsaHeaders, SetContentLength) { | |
| // Test that SetContentLength correctly sets the content-length header and | |
| // sets the content length status. | |
| BalsaHeaders headers; | |
| headers.SetContentLength(10); | |
| EXPECT_THAT(headers.GetHeader("Content-length"), StrEq("10")); | |
| EXPECT_EQ(BalsaHeadersEnums::VALID_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_TRUE(headers.content_length_valid()); | |
| // Test overwriting the content-length. | |
| headers.SetContentLength(0); | |
| EXPECT_THAT(headers.GetHeader("Content-length"), StrEq("0")); | |
| EXPECT_EQ(BalsaHeadersEnums::VALID_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_TRUE(headers.content_length_valid()); | |
| // Make sure there is only one header line after the overwrite. | |
| BalsaHeaders::const_header_lines_iterator iter = | |
| headers.GetHeaderPosition("Content-length"); | |
| EXPECT_EQ(headers.lines().begin(), iter); | |
| EXPECT_EQ(headers.lines().end(), ++iter); | |
| // Test setting the same content-length again, this should be no-op. | |
| headers.SetContentLength(0); | |
| EXPECT_THAT(headers.GetHeader("Content-length"), StrEq("0")); | |
| EXPECT_EQ(BalsaHeadersEnums::VALID_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_TRUE(headers.content_length_valid()); | |
| // Make sure the number of header lines didn't change. | |
| iter = headers.GetHeaderPosition("Content-length"); | |
| EXPECT_EQ(headers.lines().begin(), iter); | |
| EXPECT_EQ(headers.lines().end(), ++iter); | |
| } | |
| TEST(BalsaHeaders, ToggleChunkedEncoding) { | |
| // Test that SetTransferEncodingToChunkedAndClearContentLength correctly adds | |
| // chunk-encoding header and sets the transfer_encoding_is_chunked_ | |
| // flag. | |
| BalsaHeaders headers; | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| EXPECT_EQ("chunked", headers.GetAllOfHeaderAsString("Transfer-Encoding")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("Transfer-Encoding")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("transfer-encoding")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("transfer")); | |
| EXPECT_TRUE(headers.transfer_encoding_is_chunked()); | |
| // Set it to the same value, nothing should change. | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| EXPECT_EQ("chunked", headers.GetAllOfHeaderAsString("Transfer-Encoding")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("Transfer-Encoding")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("transfer-encoding")); | |
| EXPECT_TRUE(headers.HasHeadersWithPrefix("transfer")); | |
| EXPECT_TRUE(headers.transfer_encoding_is_chunked()); | |
| BalsaHeaders::const_header_lines_iterator iter = | |
| headers.GetHeaderPosition("Transfer-Encoding"); | |
| EXPECT_EQ(headers.lines().begin(), iter); | |
| EXPECT_EQ(headers.lines().end(), ++iter); | |
| // Removes the chunked encoding, and there should be no transfer-encoding | |
| // headers left. | |
| headers.SetNoTransferEncoding(); | |
| EXPECT_FALSE(headers.HasHeader("Transfer-Encoding")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("Transfer-Encoding")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("transfer-encoding")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("transfer")); | |
| EXPECT_FALSE(headers.transfer_encoding_is_chunked()); | |
| EXPECT_EQ(headers.lines().end(), headers.lines().begin()); | |
| // Clear chunked again, this should be a no-op and the header should not | |
| // change. | |
| headers.SetNoTransferEncoding(); | |
| EXPECT_FALSE(headers.HasHeader("Transfer-Encoding")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("Transfer-Encoding")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("transfer-encoding")); | |
| EXPECT_FALSE(headers.HasHeadersWithPrefix("transfer")); | |
| EXPECT_FALSE(headers.transfer_encoding_is_chunked()); | |
| EXPECT_EQ(headers.lines().end(), headers.lines().begin()); | |
| } | |
| TEST(BalsaHeaders, SetNoTransferEncodingByRemoveHeader) { | |
| // Tests that calling Remove() methods to clear the Transfer-Encoding | |
| // header correctly resets transfer_encoding_is_chunked_ internal state. | |
| BalsaHeaders headers; | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| headers.RemoveAllOfHeader("Transfer-Encoding"); | |
| EXPECT_FALSE(headers.transfer_encoding_is_chunked()); | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| std::vector<absl::string_view> headers_to_remove; | |
| headers_to_remove.emplace_back("Transfer-Encoding"); | |
| headers.RemoveAllOfHeaderInList(headers_to_remove); | |
| EXPECT_FALSE(headers.transfer_encoding_is_chunked()); | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| headers.RemoveAllHeadersWithPrefix("Transfer"); | |
| EXPECT_FALSE(headers.transfer_encoding_is_chunked()); | |
| } | |
| TEST(BalsaHeaders, ClearContentLength) { | |
| // Test that ClearContentLength() removes the content-length header and | |
| // resets content_length_status(). | |
| BalsaHeaders headers; | |
| headers.SetContentLength(10); | |
| headers.ClearContentLength(); | |
| EXPECT_FALSE(headers.HasHeader("Content-length")); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| // Clear it again; nothing should change. | |
| headers.ClearContentLength(); | |
| EXPECT_FALSE(headers.HasHeader("Content-length")); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| // Set chunked encoding and test that ClearContentLength() has no effect. | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| headers.ClearContentLength(); | |
| EXPECT_EQ("chunked", headers.GetAllOfHeaderAsString("Transfer-Encoding")); | |
| EXPECT_TRUE(headers.transfer_encoding_is_chunked()); | |
| BalsaHeaders::const_header_lines_iterator iter = | |
| headers.GetHeaderPosition("Transfer-Encoding"); | |
| EXPECT_EQ(headers.lines().begin(), iter); | |
| EXPECT_EQ(headers.lines().end(), ++iter); | |
| // Remove chunked encoding, and verify that the state is the same as after | |
| // ClearContentLength(). | |
| headers.SetNoTransferEncoding(); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| } | |
| TEST(BalsaHeaders, ClearContentLengthByRemoveHeader) { | |
| // Test that calling Remove() methods to clear the content-length header | |
| // correctly resets internal content length fields. | |
| BalsaHeaders headers; | |
| headers.SetContentLength(10); | |
| headers.RemoveAllOfHeader("Content-Length"); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_EQ(0u, headers.content_length()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| headers.SetContentLength(11); | |
| std::vector<absl::string_view> headers_to_remove; | |
| headers_to_remove.emplace_back("Content-Length"); | |
| headers.RemoveAllOfHeaderInList(headers_to_remove); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_EQ(0u, headers.content_length()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| headers.SetContentLength(12); | |
| headers.RemoveAllHeadersWithPrefix("Content"); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_EQ(0u, headers.content_length()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| } | |
| // Chunk-encoding an identity-coded BalsaHeaders removes the identity-coding. | |
| TEST(BalsaHeaders, IdentityCodingToChunked) { | |
| std::string message = | |
| "HTTP/1.1 200 OK\r\n" | |
| "Transfer-Encoding: identity\r\n\r\n"; | |
| BalsaHeaders headers; | |
| BalsaFrame balsa_frame; | |
| balsa_frame.set_is_request(false); | |
| balsa_frame.set_balsa_headers(&headers); | |
| EXPECT_EQ(message.size(), | |
| balsa_frame.ProcessInput(message.data(), message.size())); | |
| EXPECT_TRUE(headers.is_framed_by_connection_close()); | |
| EXPECT_FALSE(headers.transfer_encoding_is_chunked()); | |
| EXPECT_THAT(headers.GetAllOfHeader("Transfer-Encoding"), | |
| ElementsAre("identity")); | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| EXPECT_FALSE(headers.is_framed_by_connection_close()); | |
| EXPECT_TRUE(headers.transfer_encoding_is_chunked()); | |
| EXPECT_THAT(headers.GetAllOfHeader("Transfer-Encoding"), | |
| ElementsAre("chunked")); | |
| } | |
| TEST(BalsaHeaders, SwitchContentLengthToChunk) { | |
| // Test that a header originally with content length header is correctly | |
| // switched to using chunk encoding. | |
| BalsaHeaders headers; | |
| headers.SetContentLength(10); | |
| EXPECT_THAT(headers.GetHeader("Content-length"), StrEq("10")); | |
| EXPECT_EQ(BalsaHeadersEnums::VALID_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_TRUE(headers.content_length_valid()); | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| EXPECT_EQ("chunked", headers.GetAllOfHeaderAsString("Transfer-Encoding")); | |
| EXPECT_TRUE(headers.transfer_encoding_is_chunked()); | |
| EXPECT_FALSE(headers.HasHeader("Content-length")); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| } | |
| TEST(BalsaHeaders, SwitchChunkedToContentLength) { | |
| // Test that a header originally with chunk encoding is correctly | |
| // switched to using content length. | |
| BalsaHeaders headers; | |
| headers.SetTransferEncodingToChunkedAndClearContentLength(); | |
| EXPECT_EQ("chunked", headers.GetAllOfHeaderAsString("Transfer-Encoding")); | |
| EXPECT_TRUE(headers.transfer_encoding_is_chunked()); | |
| EXPECT_FALSE(headers.HasHeader("Content-length")); | |
| EXPECT_EQ(BalsaHeadersEnums::NO_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_FALSE(headers.content_length_valid()); | |
| headers.SetContentLength(10); | |
| EXPECT_THAT(headers.GetHeader("Content-length"), StrEq("10")); | |
| EXPECT_EQ(BalsaHeadersEnums::VALID_CONTENT_LENGTH, | |
| headers.content_length_status()); | |
| EXPECT_TRUE(headers.content_length_valid()); | |
| EXPECT_FALSE(headers.HasHeader("Transfer-Encoding")); | |
| EXPECT_FALSE(headers.transfer_encoding_is_chunked()); | |
| } | |
| TEST(BalsaHeaders, OneHundredResponseMessagesNoFramedByClose) { | |
| BalsaHeaders headers; | |
| headers.SetResponseFirstline("HTTP/1.1", 100, "Continue"); | |
| EXPECT_FALSE(headers.is_framed_by_connection_close()); | |
| } | |
| TEST(BalsaHeaders, TwoOhFourResponseMessagesNoFramedByClose) { | |
| BalsaHeaders headers; | |
| headers.SetResponseFirstline("HTTP/1.1", 204, "Continue"); | |
| EXPECT_FALSE(headers.is_framed_by_connection_close()); | |
| } | |
| TEST(BalsaHeaders, ThreeOhFourResponseMessagesNoFramedByClose) { | |
| BalsaHeaders headers; | |
| headers.SetResponseFirstline("HTTP/1.1", 304, "Continue"); | |
| EXPECT_FALSE(headers.is_framed_by_connection_close()); | |
| } | |
| TEST(BalsaHeaders, InvalidCharInHeaderValue) { | |
| std::string message = | |
| "GET http://www.256.com/foo HTTP/1.1\r\n" | |
| "Host: \x01\x01www.265.com\r\n" | |
| "\r\n"; | |
| BalsaHeaders headers = CreateHTTPHeaders(true, message); | |
| EXPECT_EQ("www.265.com", headers.GetHeader("Host")); | |
| SimpleBuffer buffer; | |
| headers.WriteHeaderAndEndingToBuffer(&buffer); | |
| message.replace(message.find_first_of(0x1), 2, ""); | |
| EXPECT_EQ(message, buffer.GetReadableRegion()); | |
| } | |
| TEST(BalsaHeaders, CarriageReturnAtStartOfLine) { | |
| std::string message = | |
| "GET /foo HTTP/1.1\r\n" | |
| "Host: www.265.com\r\n" | |
| "Foo: bar\r\n" | |
| "\rX-User-Ip: 1.2.3.4\r\n" | |
| "\r\n"; | |
| BalsaHeaders headers; | |
| BalsaFrame balsa_frame; | |
| balsa_frame.set_is_request(true); | |
| balsa_frame.set_balsa_headers(&headers); | |
| EXPECT_EQ(message.size(), | |
| balsa_frame.ProcessInput(message.data(), message.size())); | |
| EXPECT_EQ(BalsaFrameEnums::INVALID_HEADER_FORMAT, balsa_frame.ErrorCode()); | |
| EXPECT_TRUE(balsa_frame.Error()); | |
| } | |
| TEST(BalsaHeaders, CheckEmpty) { | |
| BalsaHeaders headers; | |
| EXPECT_TRUE(headers.IsEmpty()); | |
| } | |
| TEST(BalsaHeaders, CheckNonEmpty) { | |
| BalsaHeaders headers; | |
| BalsaHeadersTestPeer::WriteFromFramer(&headers, "a b c", 5); | |
| EXPECT_FALSE(headers.IsEmpty()); | |
| } | |
| TEST(BalsaHeaders, ForEachHeader) { | |
| BalsaHeaders headers; | |
| headers.AppendHeader(":host", "SomeHost"); | |
| headers.AppendHeader("key", "val1,val2val2,val2,val3"); | |
| headers.AppendHeader("key", "val4val5val6"); | |
| headers.AppendHeader("key", "val11 val12"); | |
| headers.AppendHeader("key", "v val13"); | |
| headers.AppendHeader("key", "val7"); | |
| headers.AppendHeader("key", ""); | |
| headers.AppendHeader("key", "val8 , val9 ,, val10"); | |
| headers.AppendHeader("key", " val14 "); | |
| headers.AppendHeader("key2", "val15"); | |
| headers.AppendHeader("key", "Val16"); | |
| headers.AppendHeader("key", "foo, Val17, bar"); | |
| headers.AppendHeader("date", "2 Jan 1970"); | |
| headers.AppendHeader("AcceptEncoding", "MyFavoriteEncoding"); | |
| { | |
| std::string result; | |
| EXPECT_TRUE(headers.ForEachHeader( | |
| [&result](const absl::string_view key, absl::string_view value) { | |
| result.append("<") | |
| .append(key.data(), key.size()) | |
| .append("> = <") | |
| .append(value.data(), value.size()) | |
| .append(">\n"); | |
| return true; | |
| })); | |
| EXPECT_EQ(result, | |
| "<:host> = <SomeHost>\n" | |
| "<key> = <val1,val2val2,val2,val3>\n" | |
| "<key> = <val4val5val6>\n" | |
| "<key> = <val11 val12>\n" | |
| "<key> = <v val13>\n" | |
| "<key> = <val7>\n" | |
| "<key> = <>\n" | |
| "<key> = <val8 , val9 ,, val10>\n" | |
| "<key> = < val14 >\n" | |
| "<key2> = <val15>\n" | |
| "<key> = <Val16>\n" | |
| "<key> = <foo, Val17, bar>\n" | |
| "<date> = <2 Jan 1970>\n" | |
| "<AcceptEncoding> = <MyFavoriteEncoding>\n"); | |
| } | |
| { | |
| std::string result; | |
| EXPECT_FALSE(headers.ForEachHeader( | |
| [&result](const absl::string_view key, absl::string_view value) { | |
| result.append("<") | |
| .append(key.data(), key.size()) | |
| .append("> = <") | |
| .append(value.data(), value.size()) | |
| .append(">\n"); | |
| return !value.empty(); | |
| })); | |
| EXPECT_EQ(result, | |
| "<:host> = <SomeHost>\n" | |
| "<key> = <val1,val2val2,val2,val3>\n" | |
| "<key> = <val4val5val6>\n" | |
| "<key> = <val11 val12>\n" | |
| "<key> = <v val13>\n" | |
| "<key> = <val7>\n" | |
| "<key> = <>\n"); | |
| } | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWithLowerCasedHeaderKey) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("Key1", "value1"); | |
| headers.AppendHeader("Key2", "value2"); | |
| std::string expected_lower_case = | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value1\r\n" | |
| "key2: value2\r\n"; | |
| std::string expected_lower_case_with_end = | |
| "GET / HTTP/1.0\r\n" | |
| "key1: value1\r\n" | |
| "key2: value2\r\n\r\n"; | |
| std::string expected_upper_case = | |
| "GET / HTTP/1.0\r\n" | |
| "Key1: value1\r\n" | |
| "Key2: value2\r\n"; | |
| std::string expected_upper_case_with_end = | |
| "GET / HTTP/1.0\r\n" | |
| "Key1: value1\r\n" | |
| "Key2: value2\r\n\r\n"; | |
| SimpleBuffer simple_buffer; | |
| headers.WriteToBuffer(&simple_buffer, BalsaHeaders::CaseOption::kLowercase, | |
| BalsaHeaders::CoalesceOption::kNoCoalesce); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected_lower_case)); | |
| simple_buffer.Clear(); | |
| headers.WriteToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), StrEq(expected_upper_case)); | |
| simple_buffer.Clear(); | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), | |
| StrEq(expected_upper_case_with_end)); | |
| simple_buffer.Clear(); | |
| headers.WriteHeaderAndEndingToBuffer( | |
| &simple_buffer, BalsaHeaders::CaseOption::kLowercase, | |
| BalsaHeaders::CoalesceOption::kNoCoalesce); | |
| EXPECT_THAT(simple_buffer.GetReadableRegion(), | |
| StrEq(expected_lower_case_with_end)); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferWithProperCasedHeaderKey) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("Te", "value1"); | |
| headers.AppendHeader("my-Test-header", "value2"); | |
| std::string expected_proper_case = | |
| "GET / HTTP/1.0\r\n" | |
| "TE: value1\r\n" | |
| "My-Test-Header: value2\r\n"; | |
| std::string expected_proper_case_with_end = | |
| "GET / HTTP/1.0\r\n" | |
| "TE: value1\r\n" | |
| "My-Test-Header: value2\r\n\r\n"; | |
| std::string expected_unmodified = | |
| "GET / HTTP/1.0\r\n" | |
| "Te: value1\r\n" | |
| "my-Test-header: value2\r\n"; | |
| std::string expected_unmodified_with_end = | |
| "GET / HTTP/1.0\r\n" | |
| "Te: value1\r\n" | |
| "my-Test-header: value2\r\n\r\n"; | |
| SimpleBuffer simple_buffer; | |
| headers.WriteToBuffer(&simple_buffer, BalsaHeaders::CaseOption::kPropercase, | |
| BalsaHeaders::CoalesceOption::kNoCoalesce); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_proper_case); | |
| simple_buffer.Clear(); | |
| headers.WriteToBuffer(&simple_buffer, | |
| BalsaHeaders::CaseOption::kNoModification, | |
| BalsaHeaders::CoalesceOption::kNoCoalesce); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_unmodified); | |
| simple_buffer.Clear(); | |
| headers.WriteHeaderAndEndingToBuffer( | |
| &simple_buffer, BalsaHeaders::CaseOption::kNoModification, | |
| BalsaHeaders::CoalesceOption::kNoCoalesce); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_unmodified_with_end); | |
| simple_buffer.Clear(); | |
| headers.WriteHeaderAndEndingToBuffer( | |
| &simple_buffer, BalsaHeaders::CaseOption::kPropercase, | |
| BalsaHeaders::CoalesceOption::kNoCoalesce); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_proper_case_with_end); | |
| } | |
| TEST(BalsaHeadersTest, ToPropercaseTest) { | |
| EXPECT_EQ(BalsaHeaders::ToPropercase(""), ""); | |
| EXPECT_EQ(BalsaHeaders::ToPropercase("Foo"), "Foo"); | |
| EXPECT_EQ(BalsaHeaders::ToPropercase("foO"), "Foo"); | |
| EXPECT_EQ(BalsaHeaders::ToPropercase("my-test-header"), "My-Test-Header"); | |
| EXPECT_EQ(BalsaHeaders::ToPropercase("my--test-header"), "My--Test-Header"); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferCoalescingMultivaluedHeaders) { | |
| BalsaHeaders::MultivaluedHeadersSet multivalued_headers; | |
| multivalued_headers.insert("KeY1"); | |
| multivalued_headers.insert("another_KEY"); | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("Key1", "value1"); | |
| headers.AppendHeader("Key2", "value2"); | |
| headers.AppendHeader("Key1", "value11"); | |
| headers.AppendHeader("Key2", "value21"); | |
| headers.AppendHeader("Key1", "multiples, values, already"); | |
| std::string expected_non_coalesced = | |
| "GET / HTTP/1.0\r\n" | |
| "Key1: value1\r\n" | |
| "Key2: value2\r\n" | |
| "Key1: value11\r\n" | |
| "Key2: value21\r\n" | |
| "Key1: multiples, values, already\r\n"; | |
| std::string expected_coalesced = | |
| "Key1: value1,value11,multiples, values, already\r\n" | |
| "Key2: value2\r\n" | |
| "Key2: value21\r\n"; | |
| SimpleBuffer simple_buffer; | |
| headers.WriteToBuffer(&simple_buffer); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_non_coalesced); | |
| simple_buffer.Clear(); | |
| headers.WriteToBufferCoalescingMultivaluedHeaders( | |
| &simple_buffer, multivalued_headers, | |
| BalsaHeaders::CaseOption::kNoModification); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_coalesced); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferCoalescingMultivaluedHeadersMultiLine) { | |
| BalsaHeaders::MultivaluedHeadersSet multivalued_headers; | |
| multivalued_headers.insert("Key 2"); | |
| multivalued_headers.insert("key\n 3"); | |
| BalsaHeaders headers; | |
| headers.AppendHeader("key1", "value1"); | |
| headers.AppendHeader("key 2", "value\n 2"); | |
| headers.AppendHeader("key\n 3", "value3"); | |
| headers.AppendHeader("key 2", "value 21"); | |
| headers.AppendHeader("key 3", "value 33"); | |
| std::string expected_non_coalesced = | |
| "\r\n" | |
| "key1: value1\r\n" | |
| "key 2: value\n" | |
| " 2\r\n" | |
| "key\n" | |
| " 3: value3\r\n" | |
| "key 2: value 21\r\n" | |
| "key 3: value 33\r\n"; | |
| SimpleBuffer simple_buffer; | |
| headers.WriteToBuffer(&simple_buffer); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_non_coalesced); | |
| std::string expected_coalesced = | |
| "key1: value1\r\n" | |
| "key 2: value\n" | |
| " 2,value 21\r\n" | |
| "key\n" | |
| " 3: value3\r\n" | |
| "key 3: value 33\r\n"; | |
| simple_buffer.Clear(); | |
| headers.WriteToBufferCoalescingMultivaluedHeaders( | |
| &simple_buffer, multivalued_headers, | |
| BalsaHeaders::CaseOption::kNoModification); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_coalesced); | |
| } | |
| TEST(BalsaHeaders, WriteToBufferCoalescingEnvoyHeaders) { | |
| BalsaHeaders headers; | |
| headers.SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); | |
| headers.AppendHeader("User-Agent", "UserAgent1"); | |
| headers.AppendHeader("Key2", "value2"); | |
| headers.AppendHeader("USER-AGENT", "UA2"); | |
| headers.AppendHeader("Set-Cookie", "Cookie1=aaa"); | |
| headers.AppendHeader("user-agent", "agent3"); | |
| headers.AppendHeader("Set-Cookie", "Cookie2=bbb"); | |
| std::string expected_non_coalesced = | |
| "GET / HTTP/1.0\r\n" | |
| "User-Agent: UserAgent1\r\n" | |
| "Key2: value2\r\n" | |
| "USER-AGENT: UA2\r\n" | |
| "Set-Cookie: Cookie1=aaa\r\n" | |
| "user-agent: agent3\r\n" | |
| "Set-Cookie: Cookie2=bbb\r\n" | |
| "\r\n"; | |
| std::string expected_coalesced = | |
| "GET / HTTP/1.0\r\n" | |
| "User-Agent: UserAgent1,UA2,agent3\r\n" | |
| "Key2: value2\r\n" | |
| "Set-Cookie: Cookie1=aaa\r\n" | |
| "Set-Cookie: Cookie2=bbb\r\n" | |
| "\r\n"; | |
| SimpleBuffer simple_buffer; | |
| headers.WriteHeaderAndEndingToBuffer(&simple_buffer); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_non_coalesced); | |
| simple_buffer.Clear(); | |
| headers.WriteHeaderAndEndingToBuffer( | |
| &simple_buffer, BalsaHeaders::CaseOption::kNoModification, | |
| BalsaHeaders::CoalesceOption::kCoalesce); | |
| EXPECT_EQ(simple_buffer.GetReadableRegion(), expected_coalesced); | |
| } | |
| TEST(BalsaHeadersTest, RemoveLastTokenFromOneLineHeader) { | |
| BalsaHeaders headers = | |
| CreateHTTPHeaders(true, | |
| "GET /foo HTTP/1.1\r\n" | |
| "Content-Length: 0\r\n" | |
| "Content-Encoding: gzip, 3des, tar, prc\r\n\r\n"); | |
| BalsaHeaders::const_header_lines_key_iterator it = | |
| headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip, 3des, tar, prc", it->second); | |
| EXPECT_EQ(headers.header_lines_key_end(), ++it); | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| it = headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip, 3des, tar", it->second); | |
| EXPECT_EQ(headers.header_lines_key_end(), ++it); | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| it = headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip, 3des", it->second); | |
| EXPECT_EQ(headers.header_lines_key_end(), ++it); | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| it = headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip", it->second); | |
| EXPECT_EQ(headers.header_lines_key_end(), ++it); | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| EXPECT_FALSE(headers.HasHeader("Content-Encoding")); | |
| } | |
| TEST(BalsaHeadersTest, RemoveLastTokenFromMultiLineHeader) { | |
| BalsaHeaders headers = | |
| CreateHTTPHeaders(true, | |
| "GET /foo HTTP/1.1\r\n" | |
| "Content-Length: 0\r\n" | |
| "Content-Encoding: gzip, 3des\r\n" | |
| "Content-Encoding: tar, prc\r\n\r\n"); | |
| BalsaHeaders::const_header_lines_key_iterator it = | |
| headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip, 3des", it->second); | |
| ASSERT_EQ("tar, prc", (++it)->second); | |
| ASSERT_EQ(headers.header_lines_key_end(), ++it); | |
| // First, we should start removing tokens from the second line. | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| it = headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip, 3des", it->second); | |
| ASSERT_EQ("tar", (++it)->second); | |
| ASSERT_EQ(headers.header_lines_key_end(), ++it); | |
| // Second line should be entirely removed after all its tokens are gone. | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| it = headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip, 3des", it->second); | |
| ASSERT_EQ(headers.header_lines_key_end(), ++it); | |
| // Now we should be removing the tokens from the first line. | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| it = headers.GetIteratorForKey("Content-Encoding"); | |
| ASSERT_EQ("gzip", it->second); | |
| ASSERT_EQ(headers.header_lines_key_end(), ++it); | |
| headers.RemoveLastTokenFromHeaderValue("Content-Encoding"); | |
| EXPECT_FALSE(headers.HasHeader("Content-Encoding")); | |
| } | |
| TEST(BalsaHeadersTest, ResponseCanHaveBody) { | |
| // 1xx, 204 no content and 304 not modified responses can't have bodies. | |
| EXPECT_FALSE(BalsaHeaders::ResponseCanHaveBody(100)); | |
| EXPECT_FALSE(BalsaHeaders::ResponseCanHaveBody(101)); | |
| EXPECT_FALSE(BalsaHeaders::ResponseCanHaveBody(102)); | |
| EXPECT_FALSE(BalsaHeaders::ResponseCanHaveBody(204)); | |
| EXPECT_FALSE(BalsaHeaders::ResponseCanHaveBody(304)); | |
| // Other responses can have body. | |
| EXPECT_TRUE(BalsaHeaders::ResponseCanHaveBody(200)); | |
| EXPECT_TRUE(BalsaHeaders::ResponseCanHaveBody(302)); | |
| EXPECT_TRUE(BalsaHeaders::ResponseCanHaveBody(404)); | |
| EXPECT_TRUE(BalsaHeaders::ResponseCanHaveBody(502)); | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quiche |