// 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) {
  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++
                                    StrEq("[0x0, 0]")));  // libc++ on Mac
  }
  {
    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
