mirror of
https://github.com/boostorg/beast.git
synced 2026-01-29 09:50:07 +01:00
fix #123 fix #154 fix #265 This completely replaces the HTTP parser used to read and parse incoming messages. The new version is faster than the old one, and written in fewer lines. A summary of changes: * parse and async_parse renamed to read and async_read * basic_parser is optimized for linear buffers, use flat_streambuf for the best performance with these functions: - http::read - http::read_some - http::async_read - http::async_read_some * The overloads of read() and async_read() which take just a header have been removed, since they would throw away important parse metadata. * The derived class callbacks for basic_parser have been streamlined. All strings passed to callbacks are presented in their entirety, instead of being provided in pieces. These changes allow use-cases that were previously difficult or impossible, such as: - Efficient relaying - Late body type commitment - Expect: 100-continue handling
245 lines
7.1 KiB
C++
245 lines
7.1 KiB
C++
//
|
|
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
|
|
// Test that header file is self-contained.
|
|
#include <beast/http/message_parser.hpp>
|
|
|
|
#include "test_parser.hpp"
|
|
|
|
#include <beast/unit_test/suite.hpp>
|
|
#include <beast/test/string_istream.hpp>
|
|
#include <beast/test/string_ostream.hpp>
|
|
#include <beast/test/yield_to.hpp>
|
|
#include <beast/core/flat_streambuf.hpp>
|
|
#include <beast/core/streambuf.hpp>
|
|
#include <beast/http/header_parser.hpp>
|
|
#include <beast/http/read.hpp>
|
|
#include <beast/http/read.hpp>
|
|
#include <beast/http/string_body.hpp>
|
|
#include <boost/system/system_error.hpp>
|
|
|
|
namespace beast {
|
|
namespace http {
|
|
|
|
class message_parser_test
|
|
: public beast::unit_test::suite
|
|
, public beast::test::enable_yield_to
|
|
{
|
|
public:
|
|
template<bool isRequest, class Pred>
|
|
void
|
|
testMatrix(std::string const& s, Pred const& pred)
|
|
{
|
|
beast::test::string_istream ss{get_io_service(), s};
|
|
error_code ec;
|
|
#if 0
|
|
streambuf dynabuf;
|
|
#else
|
|
flat_streambuf dynabuf{1024};
|
|
#endif
|
|
message<isRequest, string_body, fields> m;
|
|
read(ss, dynabuf, m, ec);
|
|
if(! BEAST_EXPECTS(! ec, ec.message()))
|
|
return;
|
|
pred(m);
|
|
}
|
|
|
|
void
|
|
testRead()
|
|
{
|
|
testMatrix<false>(
|
|
"HTTP/1.0 200 OK\r\n"
|
|
"Server: test\r\n"
|
|
"\r\n"
|
|
"*******",
|
|
[&](message<false, string_body, fields> const& m)
|
|
{
|
|
BEAST_EXPECTS(m.body == "*******",
|
|
"body='" + m.body + "'");
|
|
}
|
|
);
|
|
testMatrix<false>(
|
|
"HTTP/1.0 200 OK\r\n"
|
|
"Server: test\r\n"
|
|
"Transfer-Encoding: chunked\r\n"
|
|
"\r\n"
|
|
"5\r\n"
|
|
"*****\r\n"
|
|
"2;a;b=1;c=\"2\"\r\n"
|
|
"--\r\n"
|
|
"0;d;e=3;f=\"4\"\r\n"
|
|
"Expires: never\r\n"
|
|
"MD5-Fingerprint: -\r\n"
|
|
"\r\n",
|
|
[&](message<false, string_body, fields> const& m)
|
|
{
|
|
BEAST_EXPECT(m.body == "*****--");
|
|
}
|
|
);
|
|
testMatrix<false>(
|
|
"HTTP/1.0 200 OK\r\n"
|
|
"Server: test\r\n"
|
|
"Content-Length: 5\r\n"
|
|
"\r\n"
|
|
"*****",
|
|
[&](message<false, string_body, fields> const& m)
|
|
{
|
|
BEAST_EXPECT(m.body == "*****");
|
|
}
|
|
);
|
|
testMatrix<true>(
|
|
"GET / HTTP/1.1\r\n"
|
|
"User-Agent: test\r\n"
|
|
"\r\n",
|
|
[&](message<true, string_body, fields> const& m)
|
|
{
|
|
}
|
|
);
|
|
testMatrix<true>(
|
|
"GET / HTTP/1.1\r\n"
|
|
"User-Agent: test\r\n"
|
|
"X: \t x \t \r\n"
|
|
"\r\n",
|
|
[&](message<true, string_body, fields> const& m)
|
|
{
|
|
BEAST_EXPECT(m.fields["X"] == "x");
|
|
}
|
|
);
|
|
}
|
|
|
|
void
|
|
testParse()
|
|
{
|
|
using boost::asio::buffer;
|
|
{
|
|
error_code ec;
|
|
beast::test::string_istream is{
|
|
get_io_service(),
|
|
"GET / HTTP/1.1\r\n"
|
|
"User-Agent: test\r\n"
|
|
"Content-Length: 1\r\n"
|
|
"\r\n"
|
|
"*"};
|
|
flat_streambuf sb{1024};
|
|
message_parser<true, string_body, fields> p;
|
|
read(is, sb, p, ec);
|
|
auto const& m = p.get();
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(p.is_complete());
|
|
BEAST_EXPECT(m.method == "GET");
|
|
BEAST_EXPECT(m.url == "/");
|
|
BEAST_EXPECT(m.version == 11);
|
|
BEAST_EXPECT(m.fields["User-Agent"] == "test");
|
|
BEAST_EXPECT(m.body == "*");
|
|
}
|
|
#if 0
|
|
{
|
|
// test partial parsing of final chunk
|
|
// parse through the chunk body
|
|
beast::test::string_istream is{
|
|
get_io_service(), ""};
|
|
streambuf sb;
|
|
sb <<
|
|
"PUT / HTTP/1.1\r\n"
|
|
"Transfer-Encoding: chunked\r\n"
|
|
"\r\n"
|
|
"1\r\n"
|
|
"*";
|
|
error_code ec;
|
|
message_parser<true, string_body, fields> p;
|
|
read(is, sb, p, ec);
|
|
BEAST_EXPECT(sb.size() == 0);
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(!p.is_complete());
|
|
BEAST_EXPECT(p.get().body == "*");
|
|
sb << "\r\n0;d;e=3;f=\"4\"\r\n"
|
|
"Expires: never\r\n"
|
|
"MD5-Fingerprint: -\r\n";
|
|
// incomplete parse, missing the final crlf
|
|
BEAST_EXPECT(p.write(sb.data(), ec) == 0);
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(!p.is_complete());
|
|
sb << "\r\n"; // final crlf to end message
|
|
BEAST_EXPECT(p.write(sb.data(), ec) == sb.size());
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(p.is_complete());
|
|
}
|
|
{
|
|
error_code ec;
|
|
message_parser<false, string_body, fields> p;
|
|
std::string const s =
|
|
"HTTP/1.1 200 OK\r\n"
|
|
"Server: test\r\n"
|
|
"Content-Length: 1\r\n"
|
|
"\r\n"
|
|
"*";
|
|
p.write(buffer(s), ec);
|
|
auto const& m = p.get();
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(p.is_complete());
|
|
BEAST_EXPECT(m.status == 200);
|
|
BEAST_EXPECT(m.reason == "OK");
|
|
BEAST_EXPECT(m.version == 11);
|
|
BEAST_EXPECT(m.fields["Server"] == "test");
|
|
BEAST_EXPECT(m.body == "*");
|
|
}
|
|
// skip body
|
|
{
|
|
error_code ec;
|
|
message_parser<false, string_body, fields> p;
|
|
std::string const s =
|
|
"HTTP/1.1 200 Connection Established\r\n"
|
|
"Proxy-Agent: Zscaler/5.1\r\n"
|
|
"\r\n";
|
|
p.skip_body();
|
|
p.write(buffer(s), ec);
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(p.is_complete());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
testExpect100Continue()
|
|
{
|
|
test::string_istream ss{ios_,
|
|
"POST / HTTP/1.1\r\n"
|
|
"Expect: 100-continue\r\n"
|
|
"Content-Length: 5\r\n"
|
|
"\r\n"
|
|
"*****"};
|
|
streambuf sb;
|
|
error_code ec;
|
|
header_parser<true, fields> p0;
|
|
auto const bytes_used =
|
|
read_some(ss, sb, p0, ec);
|
|
sb.consume(bytes_used);
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(p0.state() != parse_state::header);
|
|
BEAST_EXPECT(! p0.is_complete());
|
|
message_parser<true,
|
|
string_body, fields> p1{std::move(p0)};
|
|
read(ss, sb, p1, ec);
|
|
BEAST_EXPECTS(! ec, ec.message());
|
|
BEAST_EXPECT(p1.get().body == "*****");
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
testRead();
|
|
testParse();
|
|
testExpect100Continue();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(message_parser,http,beast);
|
|
|
|
} // http
|
|
} // beast
|
|
|