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