//
// Copyright (c) 2016-2019 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)
//
// Official repository: https://github.com/boostorg/beast
//
// Test that header file is self-contained.
#include ("GET /x HTTP/1.0\r\n\r\n");
parsegrind ("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz / HTTP/1.0\r\n\r\n");
parsegrind ("GET / HTTP/1.0\r\n\r\n", expect_version{*this, 10});
parsegrind ("G / HTTP/1.1\r\n\r\n", expect_version{*this, 11});
// VFALCO TODO various forms of good request-target (uri)
failgrind ("\tGET / HTTP/1.0\r\n" "\r\n", error::bad_method);
failgrind ("GET\x01 / HTTP/1.0\r\n" "\r\n", error::bad_method);
failgrind ("GET / HTTP/1.0\r\n" "\r\n", error::bad_target);
failgrind ("GET \x01 HTTP/1.0\r\n" "\r\n", error::bad_target);
failgrind ("GET /\x01 HTTP/1.0\r\n" "\r\n", error::bad_target);
// VFALCO TODO various forms of bad request-target (uri)
failgrind ("GET / HTTP/1.0\r\n" "\r\n", error::bad_version);
failgrind ("GET / _TTP/1.0\r\n" "\r\n", error::bad_version);
failgrind ("GET / H_TP/1.0\r\n" "\r\n", error::bad_version);
failgrind ("GET / HT_P/1.0\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTT_/1.0\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP_1.0\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/01.2\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/3.45\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/67.89\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/x.0\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/1.x\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/1.0 \r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/1_0\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/1.0\n\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/1.0\n\r\r\n" "\r\n", error::bad_version);
failgrind ("GET / HTTP/1.0\r\r\n" "\r\n", error::bad_version);
}
void
testStatusLine()
{
using P = test_parser ("HTTP/1.0 000 OK\r\n" "\r\n", expect_status{*this, 0});
parsegrind ("HTTP/1.1 012 OK\r\n" "\r\n", expect_status{*this, 12});
parsegrind ("HTTP/1.0 345 OK\r\n" "\r\n", expect_status{*this, 345});
parsegrind ("HTTP/1.0 678 OK\r\n" "\r\n", expect_status{*this, 678});
parsegrind ("HTTP/1.0 999 OK\r\n" "\r\n", expect_status{*this, 999});
parsegrind ("HTTP/1.0 200 \tX\r\n" "\r\n", expect_version{*this, 10});
parsegrind ("HTTP/1.1 200 X\r\n" "\r\n", expect_version{*this, 11});
parsegrind ("HTTP/1.0 200 \r\n" "\r\n");
parsegrind ("HTTP/1.1 200 X \r\n" "\r\n");
parsegrind ("HTTP/1.1 200 X\t\r\n" "\r\n");
parsegrind ("HTTP/1.1 200 \x80\x81...\xfe\xff\r\n\r\n");
parsegrind ("HTTP/1.1 200 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n\r\n");
failgrind ("\rHTTP/1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("\nHTTP/1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind (" HTTP/1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("_TTP/1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("H_TP/1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HT_P/1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTT_/1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP_1.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP/01.2 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP/3.45 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP/67.89 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP/x.0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP/1.x 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP/1_0 200 OK\r\n" "\r\n", error::bad_version);
failgrind ("HTTP/1.0 200 OK\r\n" "\r\n", error::bad_status);
failgrind ("HTTP/1.0 0 OK\r\n" "\r\n", error::bad_status);
failgrind ("HTTP/1.0 12 OK\r\n" "\r\n", error::bad_status);
failgrind ("HTTP/1.0 3456 OK\r\n" "\r\n", error::bad_status);
failgrind ("HTTP/1.0 200\r\n" "\r\n", error::bad_status);
failgrind ("HTTP/1.0 200 \n\r\n" "\r\n", error::bad_reason);
failgrind ("HTTP/1.0 200 \x01\r\n" "\r\n", error::bad_reason);
failgrind ("HTTP/1.0 200 \x7f\r\n" "\r\n", error::bad_reason);
failgrind ("HTTP/1.0 200 OK\n\r\n" "\r\n", error::bad_reason);
failgrind ("HTTP/1.0 200 OK\r\r\n" "\r\n", error::bad_line_ending);
}
void
testFields()
{
auto const m =
[](std::string const& s)
{
return "GET / HTTP/1.1\r\n" + s + "\r\n";
};
using P = test_parser (m("f:\r\n"));
parsegrind (m("f: \r\n"));
parsegrind (m("f:\t\r\n"));
parsegrind (m("f: \t\r\n"));
parsegrind (m("f: v\r\n"));
parsegrind (m("f:\tv\r\n"));
parsegrind (m("f:\tv \r\n"));
parsegrind (m("f:\tv\t\r\n"));
parsegrind (m("f:\tv\t \r\n"));
parsegrind (m("f:\r\n \r\n"));
parsegrind (m("f:v\r\n"));
parsegrind (m("f: v\r\n u\r\n"));
parsegrind (m("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz: v\r\n"));
parsegrind (m("f: !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x80\x81...\xfe\xff\r\n"));
failgrind (m(" f: v\r\n"), error::bad_field);
failgrind (m("\tf: v\r\n"), error::bad_field);
failgrind (m("f : v\r\n"), error::bad_field);
failgrind (m("f\t: v\r\n"), error::bad_field);
failgrind (m("f: \n\r\n"), error::bad_value);
failgrind (m("f: v\r \r\n"), error::bad_line_ending);
failgrind (m("f: \r v\r\n"), error::bad_line_ending);
failgrind (
"GET / HTTP/1.1\r\n"
"\r \n\r\n"
"\r\n", error::bad_line_ending);
}
void
testConnectionField()
{
auto const m = [](std::string const& s)
{ return "GET / HTTP/1.1\r\n" + s + "\r\n"; };
auto const cn = [](std::string const& s)
{ return "GET / HTTP/1.1\r\nConnection: " + s + "\r\n"; };
#if 0
auto const keepalive = [&](bool v)
{ //return keepalive_f{*this, v}; return true; };
#endif
using P = test_parser (cn("close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn(",close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn(" close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("\tclose\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("close,\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("close\t\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn(" ,\t,,close,, ,\t,,\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("\r\n close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("close\r\n \r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("any,close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("close,any\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("any\r\n ,close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("close\r\n ,any\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (cn("close,close\r\n"), expect_flags{*this, parse_flag::connection_close}); // weird but allowed
parsegrind (cn("keep-alive\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (cn("keep-alive \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (cn("keep-alive\t \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (cn("keep-alive\t ,x\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (cn("\r\n keep-alive \t\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (cn("keep-alive \r\n \t \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (cn("keep-alive\r\n \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (cn("upgrade\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
parsegrind (cn("upgrade \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
parsegrind (cn("upgrade\t \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
parsegrind (cn("upgrade\t ,x\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
parsegrind (cn("\r\n upgrade \t\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
parsegrind (cn("upgrade \r\n \t \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
parsegrind (cn("upgrade\r\n \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
// VFALCO What's up with these?
//parsegrind (cn("close,keep-alive\r\n"), expect_flags{*this, parse_flag::connection_close | parse_flag::connection_keep_alive});
parsegrind (cn("upgrade,keep-alive\r\n"), expect_flags{*this, parse_flag::connection_upgrade | parse_flag::connection_keep_alive});
parsegrind (cn("upgrade,\r\n keep-alive\r\n"), expect_flags{*this, parse_flag::connection_upgrade | parse_flag::connection_keep_alive});
//parsegrind (cn("close,keep-alive,upgrade\r\n"), expect_flags{*this, parse_flag::connection_close | parse_flag::connection_keep_alive | parse_flag::connection_upgrade});
parsegrind ("GET / HTTP/1.1\r\n\r\n", expect_keepalive(*this, true));
parsegrind ("GET / HTTP/1.0\r\n\r\n", expect_keepalive(*this, false));
parsegrind ("GET / HTTP/1.0\r\n"
"Connection: keep-alive\r\n\r\n", expect_keepalive(*this, true));
parsegrind ("GET / HTTP/1.1\r\n"
"Connection: close\r\n\r\n", expect_keepalive(*this, false));
parsegrind (cn("x\r\n"), expect_flags{*this, 0});
parsegrind (cn("x,y\r\n"), expect_flags{*this, 0});
parsegrind (cn("x ,y\r\n"), expect_flags{*this, 0});
parsegrind (cn("x\t,y\r\n"), expect_flags{*this, 0});
parsegrind (cn("keep\r\n"), expect_flags{*this, 0});
parsegrind (cn(",keep\r\n"), expect_flags{*this, 0});
parsegrind (cn(" keep\r\n"), expect_flags{*this, 0});
parsegrind (cn("\tnone\r\n"), expect_flags{*this, 0});
parsegrind (cn("keep,\r\n"), expect_flags{*this, 0});
parsegrind (cn("keep\t\r\n"), expect_flags{*this, 0});
parsegrind (cn("keep\r\n"), expect_flags{*this, 0});
parsegrind (cn(" ,\t,,keep,, ,\t,,\r\n"), expect_flags{*this, 0});
parsegrind (cn("\r\n keep\r\n"), expect_flags{*this, 0});
parsegrind (cn("keep\r\n \r\n"), expect_flags{*this, 0});
parsegrind (cn("closet\r\n"), expect_flags{*this, 0});
parsegrind (cn(",closet\r\n"), expect_flags{*this, 0});
parsegrind (cn(" closet\r\n"), expect_flags{*this, 0});
parsegrind (cn("\tcloset\r\n"), expect_flags{*this, 0});
parsegrind (cn("closet,\r\n"), expect_flags{*this, 0});
parsegrind (cn("closet\t\r\n"), expect_flags{*this, 0});
parsegrind (cn("closet\r\n"), expect_flags{*this, 0});
parsegrind (cn(" ,\t,,closet,, ,\t,,\r\n"), expect_flags{*this, 0});
parsegrind (cn("\r\n closet\r\n"), expect_flags{*this, 0});
parsegrind (cn("closet\r\n \r\n"), expect_flags{*this, 0});
parsegrind (cn("clog\r\n"), expect_flags{*this, 0});
parsegrind (cn("key\r\n"), expect_flags{*this, 0});
parsegrind (cn("uptown\r\n"), expect_flags{*this, 0});
parsegrind (cn("keeper\r\n \r\n"), expect_flags{*this, 0});
parsegrind (cn("keep-alively\r\n \r\n"), expect_flags{*this, 0});
parsegrind (cn("up\r\n \r\n"), expect_flags{*this, 0});
parsegrind (cn("upgrader\r\n \r\n"), expect_flags{*this, 0});
parsegrind (cn("none\r\n"), expect_flags{*this, 0});
parsegrind (cn("\r\n none\r\n"), expect_flags{*this, 0});
parsegrind (m("ConnectioX: close\r\n"), expect_flags{*this, 0});
parsegrind (m("Condor: close\r\n"), expect_flags{*this, 0});
parsegrind (m("Connect: close\r\n"), expect_flags{*this, 0});
parsegrind (m("Connections: close\r\n"), expect_flags{*this, 0});
parsegrind (m("Proxy-Connection: close\r\n"), expect_flags{*this, parse_flag::connection_close});
parsegrind (m("Proxy-Connection: keep-alive\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
parsegrind (m("Proxy-Connection: upgrade\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
parsegrind (m("Proxy-ConnectioX: none\r\n"), expect_flags{*this, 0});
parsegrind (m("Proxy-Connections: 1\r\n"), expect_flags{*this, 0});
parsegrind (m("Proxy-Connotes: see-also\r\n"), expect_flags{*this, 0});
failgrind (cn("[\r\n"), error::bad_value);
failgrind (cn("close[\r\n"), error::bad_value);
failgrind (cn("close [\r\n"), error::bad_value);
failgrind (cn("close, upgrade [\r\n"), error::bad_value);
failgrind (cn("upgrade[]\r\n"), error::bad_value);
failgrind (cn("keep\r\n -alive\r\n"), error::bad_value);
failgrind (cn("keep-alive[\r\n"), error::bad_value);
failgrind (cn("keep-alive []\r\n"), error::bad_value);
failgrind (cn("no[ne]\r\n"), error::bad_value);
}
void
testContentLengthField()
{
using P = test_parser (c(s),
[&](P const& p)
{
BEAST_EXPECT(p.content_length());
BEAST_EXPECT(p.content_length() && *p.content_length() == v);
}, true);
};
check("0\r\n", 0);
check("00\r\n", 0);
check("1\r\n", 1);
check("01\r\n", 1);
check("9\r\n", 9);
check("42 \r\n", 42);
check("42\t\r\n", 42);
check("42 \t \r\n", 42);
check("42\r\n \t \r\n", 42);
parsegrind (m("Content-LengtX: 0\r\n"), expect_flags{*this, 0});
parsegrind (m("Content-Lengths: many\r\n"), expect_flags{*this, 0});
parsegrind (m("Content: full\r\n"), expect_flags{*this, 0});
failgrind (c("\r\n"), error::bad_content_length);
failgrind (c("18446744073709551616\r\n"), error::bad_content_length);
failgrind (c("0 0\r\n"), error::bad_content_length);
failgrind (c("0 1\r\n"), error::bad_content_length);
failgrind (c(",\r\n"), error::bad_content_length);
failgrind (c("0,\r\n"), error::bad_content_length);
failgrind (m(
"Content-Length: 0\r\nContent-Length: 0\r\n"), error::bad_content_length);
}
void
testTransferEncodingField()
{
auto const m = [](std::string const& s)
{ return "GET / HTTP/1.1\r\n" + s + "\r\n"; };
auto const ce = [](std::string const& s)
{ return "GET / HTTP/1.1\r\nTransfer-Encoding: " + s + "\r\n0\r\n\r\n"; };
auto const te = [](std::string const& s)
{ return "GET / HTTP/1.1\r\nTransfer-Encoding: " + s + "\r\n"; };
using P = test_parser (ce("chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("chunked \r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("chunked\t\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("chunked \t\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce(" chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("\tchunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("chunked,\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("chunked ,\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("chunked, \r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce(",chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce(", chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce(" ,chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("chunked\r\n \r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("\r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce(",\r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("\r\n ,chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce(",\r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("gzip, chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("gzip, chunked \r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (ce("gzip, \r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
// Technically invalid but beyond the parser's scope to detect
// VFALCO Look into this
//parsegrind (ce("custom;key=\",chunked\r\n"), expect_flags{*this, parse_flag::chunked});
parsegrind (te("gzip\r\n"), expect_flags{*this, 0});
parsegrind (te("chunked, gzip\r\n"), expect_flags{*this, 0});
parsegrind (te("chunked\r\n , gzip\r\n"), expect_flags{*this, 0});
parsegrind (te("chunked,\r\n gzip\r\n"), expect_flags{*this, 0});
parsegrind (te("chunked,\r\n ,gzip\r\n"), expect_flags{*this, 0});
parsegrind (te("bigchunked\r\n"), expect_flags{*this, 0});
parsegrind (te("chunk\r\n ked\r\n"), expect_flags{*this, 0});
parsegrind (te("bar\r\n ley chunked\r\n"), expect_flags{*this, 0});
parsegrind (te("barley\r\n chunked\r\n"), expect_flags{*this, 0});
parsegrind (m("Transfer-EncodinX: none\r\n"), expect_flags{*this, 0});
parsegrind (m("Transfer-Encodings: 2\r\n"), expect_flags{*this, 0});
parsegrind (m("Transfer-Encoded: false\r\n"), expect_flags{*this, 0});
failgrind (m("Upgrade:\r\n"), expect_flags{*this, parse_flag::upgrade});
parsegrind (m("Upgrade: \r\n"), expect_flags{*this, parse_flag::upgrade});
parsegrind (m("Upgrade: yes\r\n"), expect_flags{*this, parse_flag::upgrade});
parsegrind (m("Up: yes\r\n"), expect_flags{*this, 0});
parsegrind (m("UpgradX: none\r\n"), expect_flags{*this, 0});
parsegrind (m("Upgrades: 2\r\n"), expect_flags{*this, 0});
parsegrind (m("Upsample: 4x\r\n"), expect_flags{*this, 0});
parsegrind (
"GET / HTTP/1.1\r\n"
"Connection: upgrade\r\n"
"Upgrade: WebSocket\r\n"
"\r\n",
[&](P const& p)
{
BEAST_EXPECT(p.upgrade());
});
}
void
testPartial()
{
// Make sure the slow-loris defense works and that
// we don't get duplicate or missing fields on a split.
parsegrind (
"HTTP/1.1 200 OK\r\n"
"Transfer-Encoding: chunked\r\n"
"Content-Type: application/octet-stream\r\n"
"\r\n"
"0004\r\nabcd\r\n"
"0\r\n\r\n"
,[&](P const& p)
{
BEAST_EXPECT(p.body == "abcd");
});
}
// https://github.com/boostorg/beast/issues/692
void
testIssue692()
{
error_code ec;
test_parser