| // 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 |