summaryrefslogtreecommitdiff
path: root/test/test_fcgi_protocol.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2021-11-17 22:34:57 +0100
committerJoel Klinghed <the_jk@spawned.biz>2021-11-17 22:34:57 +0100
commit6232d13f5321b87ddf12a1aa36b4545da45f173d (patch)
tree23f3316470a14136debd9d02f9e920ca2b06f4cc /test/test_fcgi_protocol.cc
Travel3: Simple image and video display site
Reads the images and videos from filesystem and builds a site in memroy.
Diffstat (limited to 'test/test_fcgi_protocol.cc')
-rw-r--r--test/test_fcgi_protocol.cc678
1 files changed, 678 insertions, 0 deletions
diff --git a/test/test_fcgi_protocol.cc b/test/test_fcgi_protocol.cc
new file mode 100644
index 0000000..b72b6f3
--- /dev/null
+++ b/test/test_fcgi_protocol.cc
@@ -0,0 +1,678 @@
+#include "common.hh"
+
+#include "fcgi_protocol.hh"
+#include "str_buffer.hh"
+
+#include <gtest/gtest.h>
+
+namespace {
+
+constexpr const auto kSanity = std::string_view(
+ "\1" // version = 1
+ "\1" // begin_request = 1
+ "\0\1" // request_id = 1
+ "\x10\xfe" // content_length = 4350
+ "\xf0" // padding_length = 240
+ "\0", // reserved = 0
+ 8);
+
+constexpr const auto kMax = std::string_view(
+ "\1" // version = 1
+ "\xa" // get_values_result = 10
+ "\xff\xff" // request_id = 0xffff
+ "\xff\xff" // content_length = 0xffff
+ "\xff" // padding_length = 0xff
+ "\0", // reserved = 0
+ 8);
+
+constexpr const auto kBeginRequest = std::string_view(
+ "\1" // version = 1
+ "\1" // begin_request = 1
+ "\0\1" // request_id = 1
+ "\0\x8" // content_length = 8
+ "\0" // padding_length = 0
+ "\0" // reserved = 0
+ "\0\1" // role = 1
+ "\1" // flags = 1
+ "\0\0\0\0\0", // reserved
+ 16);
+
+constexpr const auto kPairShortShort = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\0" // request id = 0
+ "\0\x5" // content length = 5
+ "\x3" // padding length = 3
+ "\0" // reserved
+ "\x3" // name length = 3
+ "\0" // value length = 0
+ "foo" // name
+ "" // value
+ "\0\0\0", // padding
+ 16);
+
+#define LONG_NAME \
+ "0123456789012345678901234567890123456789012345678901234567890123456789" \
+ "0123456789012345678901234567890123456789012345678901234567"
+
+#define LONG_VALUE \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+constexpr const auto kPairShortLong = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\0" // request id = 0
+ "\1\x88" // content length = 392
+ "\0" // padding length = 0
+ "\0" // reserved
+ "\x3" // name length = 3
+ "\x80\0\1\x80" // value length = 384
+ "foo" // name
+ LONG_VALUE
+ "", // padding
+ 400);
+
+constexpr const auto kPairLongShort = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\0" // request id = 0
+ "\0\x85" // content length = 133
+ "\x3" // padding length = 3
+ "\0" // reserved
+ "\x80\0\0\x80" // name length = 128
+ "\0" // value length = 0
+ LONG_NAME
+ "" // value
+ "\0\0\0", // padding
+ 144);
+
+constexpr const auto kPairLongLong = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\0" // request id = 0
+ "\2\x8" // content length = 520
+ "\0" // padding length = 0
+ "\0" // reserved
+ "\x80\0\0\x80" // name length = 128
+ "\x80\0\1\x80" // value length = 384
+ LONG_NAME
+ LONG_VALUE
+ "", // padding
+ 528);
+
+constexpr const auto kStream1 = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\1" // request id = 1
+ "\0\x8" // content length = 8
+ "\0" // padding length = 0
+ "\0" // reserved
+ "\x3" // name length = 3
+ "\0" // value length = 0
+ "foo" // name
+ "" // value
+ "\x3" // name length = 3
+ "\x3" // value length = 3
+ "b", // name[0..1]
+ 16);
+
+constexpr const auto kStream2 = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\1" // request id = 1
+ "\0\x5" // content length = 5
+ "\x3" // padding length = 3
+ "\0" // reserved
+ "ar" // name[2..3]
+ "zum" // value
+ "\0\0\0", // padding
+ 16);
+
+constexpr const auto kStream3 = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\1" // request id = 1
+ "\0\x5" // content length = 5
+ "\x3" // padding length = 3
+ "\0" // reserved
+ "\0" // name length = 0
+ "\x3" // value length = 3
+ "" // name
+ "aaa" // value
+ "\0\0\0", // padding
+ 16);
+
+constexpr const auto kStream4 = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\1" // request id = 1
+ "\0\0" // content length = 0
+ "\0" // padding length = 0
+ "\0" // reserved
+ "", // padding
+ 8);
+
+constexpr const auto kPairInvalid = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\0" // request id = 0
+ "\0\x8" // content length = 8
+ "\0" // padding length = 0
+ "\0" // reserved
+ "\x3" // name length = 3
+ "\x10" // value length = 16
+ "foo" // name
+ "bar" // value (13 bytes to short)
+ "", // padding
+ 16);
+
+constexpr const auto kPairInvalidWithPadding = std::string_view(
+ "\1" // version = 1
+ "\x4" // Params
+ "\0\0" // request id = 0
+ "\0\x6" // content length = 6
+ "\x2" // padding length = 2
+ "\0" // reserved
+ "\x2" // name length = 2
+ "\x8" // value length = 8
+ "aa" // name
+ "bb" // value (13 bytes to short)
+ "\0\0", // padding
+ 16);
+
+} // namespace
+
+TEST(fcgi_protocol, empty) {
+ auto buf = make_strbuffer(std::string_view());
+ EXPECT_FALSE(fcgi::Record::parse(buf.get()));
+}
+
+TEST(fcgi_protocol, incomplete) {
+ for (size_t i = 1; i < kSanity.size(); ++i) {
+ auto buf = make_strbuffer(kSanity.substr(0, i));
+ EXPECT_FALSE(fcgi::Record::parse(buf.get()));
+ }
+}
+
+TEST(fcgi_protocol, bad) {
+ auto buf = make_strbuffer(std::string_view(
+ "\xff\xff\xff\xff\xff\xff\xff\xff"));
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ EXPECT_FALSE(rec->good());
+}
+
+TEST(fcgi_protocol, unknown) {
+ auto buf = make_strbuffer(std::string_view(
+ "\1\xff\xff\xff\xff\xff\xff\xff"));
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ EXPECT_TRUE(rec->good());
+ EXPECT_EQ(0xff, rec->type());
+}
+
+TEST(fcgi_protocol, sanity) {
+ auto buf = make_strbuffer(kSanity);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ EXPECT_TRUE(rec->good());
+ EXPECT_EQ(fcgi::RecordType::BeginRequest, rec->type());
+ EXPECT_EQ(1, rec->request_id());
+ EXPECT_EQ(4350, rec->content_length());
+ EXPECT_EQ(240, rec->padding_length());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, max) {
+ auto buf = make_strbuffer(kMax);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ EXPECT_TRUE(rec->good());
+ EXPECT_EQ(fcgi::RecordType::GetValuesResult, rec->type());
+ EXPECT_EQ(0xffff, rec->request_id());
+ EXPECT_EQ(0xffff, rec->content_length());
+ EXPECT_EQ(0xff, rec->padding_length());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, builder_sanity) {
+ auto str = std::make_shared<std::string>();
+ auto buf = make_strbuffer(str);
+ auto builder = fcgi::RecordBuilder::create(fcgi::RecordType::BeginRequest,
+ 1,
+ 4350,
+ 240);
+ EXPECT_TRUE(builder->build(buf.get()));
+ EXPECT_EQ(kSanity, *str);
+ EXPECT_EQ(kSanity.size() + 4350 + 240, builder->size());
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ EXPECT_TRUE(rec->good());
+ EXPECT_EQ(fcgi::RecordType::BeginRequest, rec->type());
+ EXPECT_EQ(1, rec->request_id());
+ EXPECT_EQ(4350, rec->content_length());
+ EXPECT_EQ(240, rec->padding_length());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, builder_with_body) {
+ auto str = std::make_shared<std::string>();
+ auto buf = make_strbuffer(str);
+ auto builder = fcgi::RecordBuilder::create(fcgi::RecordType::BeginRequest,
+ 1,
+ 6,
+ 2);
+ EXPECT_TRUE(builder->build(buf.get()));
+ Buffer::write(buf.get(), "foobar", 6);
+ EXPECT_TRUE(builder->padding(buf.get()));
+
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ EXPECT_TRUE(rec->good());
+ EXPECT_EQ(fcgi::RecordType::BeginRequest, rec->type());
+ EXPECT_EQ(1, rec->request_id());
+ EXPECT_EQ(6, rec->content_length());
+ EXPECT_EQ(2, rec->padding_length());
+
+ char tmp[7];
+ std::fill_n(tmp, sizeof(tmp), 0);
+ EXPECT_EQ(6, Buffer::read(buf.get(), tmp, 6));
+ EXPECT_STREQ("foobar", tmp);
+
+ std::fill_n(tmp, sizeof(tmp), 0);
+ EXPECT_EQ(2, Buffer::read(buf.get(), tmp, 2));
+
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, builder_max) {
+ auto str = std::make_shared<std::string>();
+ auto buf = make_strbuffer(str);
+ auto builder = fcgi::RecordBuilder::create(fcgi::RecordType::GetValuesResult,
+ 0xffff,
+ 0xffff,
+ 0xff);
+ EXPECT_TRUE(builder->build(buf.get()));
+ EXPECT_EQ(kMax, *str);
+ EXPECT_EQ(kMax.size() + 0xffff + 0xff, builder->size());
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ EXPECT_TRUE(rec->good());
+ EXPECT_EQ(fcgi::RecordType::GetValuesResult, rec->type());
+ EXPECT_EQ(0xffff, rec->request_id());
+ EXPECT_EQ(0xffff, rec->content_length());
+ EXPECT_EQ(0xff, rec->padding_length());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, builder_incomplete) {
+ auto buf = Buffer::fixed(7);
+ auto builder = fcgi::RecordBuilder::create(fcgi::RecordType::BeginRequest,
+ 1,
+ 0);
+ EXPECT_FALSE(builder->build(buf.get()));
+ EXPECT_EQ(8, builder->size());
+}
+
+TEST(fcgi_protocol, builder_unknown_type) {
+ auto str = std::make_shared<std::string>();
+ auto buf = make_strbuffer(str);
+ auto builder = fcgi::RecordBuilder::create_unknown_type(0xff);
+ EXPECT_TRUE(builder->build(buf.get()));
+ EXPECT_EQ(std::string_view("\1\xb\0\0\0\x8\0\0" "\xff\0\0\0\0\0\0\0", 16),
+ *str);
+ EXPECT_EQ(16, builder->size());
+}
+
+TEST(fcgi_protocol, builder_begin_request) {
+ auto str = std::make_shared<std::string>();
+ auto buf = make_strbuffer(str);
+ auto builder = fcgi::RecordBuilder::create_begin_request(
+ 1, fcgi::Role::Responder, 0);
+ EXPECT_TRUE(builder->build(buf.get()));
+ EXPECT_EQ(std::string_view("\1\1\0\1\0\x8\0\0" "\0\1\0\0\0\0\0\0", 16), *str);
+ EXPECT_EQ(16, builder->size());
+}
+
+TEST(fcgi_protocol, builder_end_request) {
+ auto str = std::make_shared<std::string>();
+ auto buf = make_strbuffer(str);
+ auto builder = fcgi::RecordBuilder::create_end_request(
+ 1, 0xbeef, fcgi::ProtocolStatus::RequestComplete);
+ EXPECT_TRUE(builder->build(buf.get()));
+ EXPECT_EQ(std::string_view("\1\3\0\1\0\x8\0\0" "\0\0\xbe\xef\0\0\0\0", 16),
+ *str);
+ EXPECT_EQ(16, builder->size());
+}
+
+TEST(fcgi_protocol, pair_short_short) {
+ auto buf = make_strbuffer(kPairShortShort);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_single(rec.get());
+ EXPECT_FALSE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("foo", pair->name());
+ EXPECT_EQ("", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_TRUE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_short_short_incomplete) {
+ for (size_t i = 8; i < kPairShortShort.size() - 1; ++i) {
+ auto buf = make_strbuffer(kPairShortShort.substr(0, i));
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_single(rec.get());
+ EXPECT_FALSE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ EXPECT_FALSE(pair);
+ }
+}
+
+TEST(fcgi_protocol, pair_short_long) {
+ auto buf = make_strbuffer(kPairShortLong);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_single(rec.get());
+ EXPECT_FALSE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("foo", pair->name());
+ EXPECT_EQ(LONG_VALUE, pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_TRUE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_long_short) {
+ auto buf = make_strbuffer(kPairLongShort);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_single(rec.get());
+ EXPECT_FALSE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ(LONG_NAME, pair->name());
+ EXPECT_EQ("", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_TRUE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_long_long) {
+ auto buf = make_strbuffer(kPairLongLong);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_single(rec.get());
+ EXPECT_FALSE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ(LONG_NAME, pair->name());
+ EXPECT_EQ(LONG_VALUE, pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_TRUE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_stream) {
+ auto str = std::make_shared<std::string>(kStream1);
+ auto buf = make_strbuffer(str);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_stream(rec.get());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("foo", pair->name());
+ EXPECT_EQ("", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+
+ str->append(kStream2);
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ ASSERT_TRUE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("bar", pair->name());
+ EXPECT_EQ("zum", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+
+ str->append(kStream3);
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ ASSERT_TRUE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("", pair->name());
+ EXPECT_EQ("aaa", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+
+ str->append(kStream4);
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_TRUE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_stream_incomplete) {
+ for (size_t i = 8; i < kStream2.size() - 1; ++i) {
+ auto str = std::make_shared<std::string>(kStream1);
+ auto buf = make_strbuffer(str);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_stream(rec.get());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+
+ str->append(kStream2.substr(0, i));
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ }
+}
+
+TEST(fcgi_protocol, pair_stream_all_avail) {
+ auto buf = make_strbuffer(std::string(kStream1) +
+ std::string(kStream2) +
+ std::string(kStream3) +
+ std::string(kStream4));
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_stream(rec.get());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("foo", pair->name());
+ EXPECT_EQ("", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ ASSERT_TRUE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("bar", pair->name());
+ EXPECT_EQ("zum", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ ASSERT_TRUE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(pair->good());
+ EXPECT_EQ("", pair->name());
+ EXPECT_EQ("aaa", pair->value());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_TRUE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_invalid) {
+ auto buf = make_strbuffer(kPairInvalid);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_single(rec.get());
+ EXPECT_FALSE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_FALSE(pair->good());
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_invalid_with_padding) {
+ auto buf = make_strbuffer(kPairInvalidWithPadding);
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_single(rec.get());
+ EXPECT_FALSE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_FALSE(pair->good());
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_FALSE(stream->end_of_stream());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, pair_stream_invalid) {
+ auto buf = make_strbuffer(std::string(kPairShortShort) +
+ std::string(kPairInvalid) +
+ std::string(kStream4));
+ auto rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ auto stream = fcgi::RecordStream::create_stream(rec.get());
+ auto pair = fcgi::Pair::start(stream.get(), buf.get());
+ ASSERT_TRUE(pair);
+ EXPECT_TRUE(pair->good());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ EXPECT_FALSE(pair->next(stream.get(), buf.get()));
+ rec = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(rec);
+ stream->add(rec.get());
+ EXPECT_TRUE(pair->next(stream.get(), buf.get()));
+ EXPECT_FALSE(pair->good());
+}
+
+TEST(fcgi_protocol, pair_builder) {
+ auto str = std::make_shared<std::string>();
+ auto buf = make_strbuffer(str);
+ auto builder = fcgi::PairBuilder::create();
+ builder->add("foo", "");
+ EXPECT_EQ(5, builder->size());
+ builder->add("foo", LONG_VALUE);
+ EXPECT_EQ(397, builder->size());
+ builder->add(LONG_NAME, "");
+ EXPECT_EQ(530, builder->size());
+ builder->add(LONG_NAME, LONG_VALUE);
+ EXPECT_EQ(1050, builder->size());
+ ASSERT_TRUE(builder->build(buf.get()));
+ EXPECT_EQ(std::string_view(
+ // ShortShort
+ "\x3" // name length = 3
+ "\0" // value length = 0
+ "foo" // name
+ "" // value
+
+ // ShortLong
+ "\x3" // name length = 3
+ "\x80\0\1\x80" // value length = 384
+ "foo" // name
+ LONG_VALUE
+
+ // LongShort
+ "\x80\0\0\x80" // name length = 128
+ "\0" // value length = 0
+ LONG_NAME
+ "" // value
+
+ // LongLong
+ "\x80\0\0\x80" // name length = 128
+ "\x80\0\1\x80" // value length = 384
+ LONG_NAME
+ LONG_VALUE,
+ 1050), *str);
+}
+
+TEST(fcgi_protocol, begin_request_body) {
+ auto buf = make_strbuffer(kBeginRequest);
+ auto req = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(req);
+ EXPECT_TRUE(req->good());
+ EXPECT_EQ(fcgi::RecordType::BeginRequest, req->type());
+ auto body = fcgi::BeginRequestBody::parse(req.get(), buf.get());
+ ASSERT_TRUE(body);
+ EXPECT_TRUE(body->good());
+ EXPECT_EQ(1, body->role());
+ EXPECT_EQ(1, body->flags());
+ EXPECT_TRUE(buf->empty());
+}
+
+TEST(fcgi_protocol, begin_request_body_incomplete) {
+ for (size_t i = 8; i < kBeginRequest.size() - 1; ++i) {
+ auto buf = make_strbuffer(kBeginRequest.substr(0, i));
+ auto req = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(req);
+ auto body = fcgi::BeginRequestBody::parse(req.get(), buf.get());
+ EXPECT_FALSE(body);
+ }
+}
+
+TEST(fcgi_protocol, empty_stream) {
+ auto buf = make_strbuffer(kStream4);
+ auto req = fcgi::Record::parse(buf.get());
+ ASSERT_TRUE(req);
+ auto stream = fcgi::RecordStream::create_stream(req.get());
+ EXPECT_TRUE(stream->end_of_record());
+ EXPECT_TRUE(stream->end_of_stream());
+}