diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/http/fetch/httpparser_ut.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/http/fetch/httpparser_ut.cpp')
-rw-r--r-- | library/cpp/http/fetch/httpparser_ut.cpp | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/library/cpp/http/fetch/httpparser_ut.cpp b/library/cpp/http/fetch/httpparser_ut.cpp new file mode 100644 index 0000000000..3b3b938e7a --- /dev/null +++ b/library/cpp/http/fetch/httpparser_ut.cpp @@ -0,0 +1,231 @@ +#include "httpparser.h" + +#include <library/cpp/testing/unittest/registar.h> + +#define ENUM_OUT(arg) \ + case type ::arg: { \ + out << #arg; \ + return; \ + } + +template <> +void Out<THttpParserBase::States>(IOutputStream& out, THttpParserBase::States st) { + using type = THttpParserBase::States; + switch (st) { + ENUM_OUT(hp_error) + ENUM_OUT(hp_eof) + ENUM_OUT(hp_in_header) + ENUM_OUT(hp_read_alive) + ENUM_OUT(hp_read_closed) + ENUM_OUT(hp_begin_chunk_header) + ENUM_OUT(hp_chunk_header) + ENUM_OUT(hp_read_chunk) + } +} + +namespace { + class TSomethingLikeFakeCheck; + + using TTestHttpParser = THttpParser<TSomethingLikeFakeCheck>; + + class TSomethingLikeFakeCheck { + TString Body_; + + public: + const TString& Body() const { + return Body_; + } + + // other functions are not really called by THttpParser + void CheckDocPart(const void* buf, size_t len, THttpHeader* /* header */) { + TString s(static_cast<const char*>(buf), len); + Cout << "State = " << static_cast<TTestHttpParser*>(this)->GetState() << ", CheckDocPart(" << s.Quote() << ")\n"; + Body_ += s; + } + }; + +} + +Y_UNIT_TEST_SUITE(TestHttpParser) { + Y_UNIT_TEST(TestTrivialRequest) { + const TString blob{ + "GET /search?q=hi HTTP/1.1\r\n" + "Host: www.google.ru:8080 \r\n" + "\r\n"}; + THttpHeader hdr; + THttpParser<> parser; + parser.Init(&hdr); + parser.Parse((void*)blob.data(), blob.size()); + UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_error); // can't parse request as response + } + + // XXX: `entity_size` is i32 and `content_length` is i64! + Y_UNIT_TEST(TestTrivialResponse) { + const TString blob{ + "HTTP/1.1 200 Ok\r\n" + "Content-Length: 2\r\n" + "\r\n" + "OK"}; + THttpHeader hdr; + TTestHttpParser parser; + parser.Init(&hdr); + parser.Parse((void*)blob.data(), blob.size()); + UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof); + UNIT_ASSERT_EQUAL(parser.Body(), "OK"); + UNIT_ASSERT_EQUAL(hdr.header_size, strlen( + "HTTP/1.1 200 Ok\r\n" + "Content-Length: 2\r\n" + "\r\n")); + UNIT_ASSERT_EQUAL(hdr.entity_size, strlen("OK")); + } + + // XXX: `entity_size` is off by one in TE:chunked case. + Y_UNIT_TEST(TestChunkedResponse) { + const TString blob{ + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "Ok\r\n" + "8\r\n" + "AllRight\r\n" + "0\r\n" + "\r\n"}; + THttpHeader hdr; + TTestHttpParser parser; + parser.Init(&hdr); + parser.Parse((void*)blob.data(), blob.size()); + UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof); + UNIT_ASSERT_EQUAL(parser.Body(), "OkAllRight"); + UNIT_ASSERT_EQUAL(hdr.header_size, strlen( + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n")); + const int off_by_one_err = -1; // XXX: it really looks so + UNIT_ASSERT_EQUAL(hdr.entity_size + off_by_one_err, strlen( + "2\r\n" + "Ok\r\n" + "8\r\n" + "AllRight\r\n" + "0\r\n" + "\r\n")); + } + + static const TString PipelineClenBlob_{ + "HTTP/1.1 200 Ok\r\n" + "Content-Length: 4\r\n" + "\r\n" + "OK\r\n" + "HTTP/1.1 200 Zz\r\n" + "Content-Length: 4\r\n" + "\r\n" + "ZZ\r\n"}; + + void AssertPipelineClen(TTestHttpParser & parser, const THttpHeader& hdr) { + UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof); + UNIT_ASSERT_EQUAL(4, hdr.content_length); + UNIT_ASSERT_EQUAL(hdr.header_size, strlen( + "HTTP/1.1 200 Ok\r\n" + "Content-Length: 4\r\n" + "\r\n")); + } + + Y_UNIT_TEST(TestPipelineClenByteByByte) { + const TString& blob = PipelineClenBlob_; + THttpHeader hdr; + TTestHttpParser parser; + parser.Init(&hdr); + for (size_t i = 0; i < blob.size(); ++i) { + const TStringBuf d{blob, i, 1}; + parser.Parse((void*)d.data(), d.size()); + Cout << TString(d).Quote() << " -> " << parser.GetState() << Endl; + } + AssertPipelineClen(parser, hdr); + UNIT_ASSERT_EQUAL(parser.Body(), "OK\r\n"); + UNIT_ASSERT_EQUAL(hdr.entity_size, hdr.content_length); + } + + // XXX: Content-Length is ignored, Body() looks unexpected! + Y_UNIT_TEST(TestPipelineClenOneChunk) { + const TString& blob = PipelineClenBlob_; + THttpHeader hdr; + TTestHttpParser parser; + parser.Init(&hdr); + parser.Parse((void*)blob.data(), blob.size()); + AssertPipelineClen(parser, hdr); + UNIT_ASSERT_EQUAL(parser.Body(), + "OK\r\n" + "HTTP/1.1 200 Zz\r\n" + "Content-Length: 4\r\n" + "\r\n" + "ZZ\r\n"); + UNIT_ASSERT_EQUAL(hdr.entity_size, strlen( + "OK\r\n" + "HTTP/1.1 200 Zz\r\n" + "Content-Length: 4\r\n" + "\r\n" + "ZZ\r\n")); + } + + static const TString PipelineChunkedBlob_{ + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "Ok\r\n" + "8\r\n" + "AllRight\r\n" + "0\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "2\r\n" + "Yo\r\n" + "8\r\n" + "uWin!Iam\r\n" + "0\r\n" + "\r\n"}; + + void AssertPipelineChunked(TTestHttpParser & parser, const THttpHeader& hdr) { + UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof); + UNIT_ASSERT_EQUAL(parser.Body(), "OkAllRight"); + UNIT_ASSERT_EQUAL(-1, hdr.content_length); + UNIT_ASSERT_EQUAL(hdr.header_size, strlen( + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n")); + const int off_by_one_err = -1; + UNIT_ASSERT_EQUAL(hdr.entity_size + off_by_one_err, strlen( + "2\r\n" + "Ok\r\n" + "8\r\n" + "AllRight\r\n" + "0\r\n" + "\r\n")); + } + + Y_UNIT_TEST(TestPipelineChunkedByteByByte) { + const TString& blob = PipelineChunkedBlob_; + THttpHeader hdr; + TTestHttpParser parser; + parser.Init(&hdr); + for (size_t i = 0; i < blob.size(); ++i) { + const TStringBuf d{blob, i, 1}; + parser.Parse((void*)d.data(), d.size()); + Cout << TString(d).Quote() << " -> " << parser.GetState() << Endl; + if (blob.size() / 2 - 1 <= i) // last \n sets EOF + UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof); + } + AssertPipelineChunked(parser, hdr); + } + + Y_UNIT_TEST(TestPipelineChunkedOneChunk) { + const TString& blob = PipelineChunkedBlob_; + THttpHeader hdr; + TTestHttpParser parser; + parser.Init(&hdr); + parser.Parse((void*)blob.data(), blob.size()); + AssertPipelineChunked(parser, hdr); + } +} |