#include "common.hh" #include "fcgi_protocol.hh" #include "str_buffer.hh" #include 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(); 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(); 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(); 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(); 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(); 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(); 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(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(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(); 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()); }