Parser concept, fixes:

A new concept Parser is introduced with routines to read from a stream
into the parser. This solves a problem with the old read interface where
messages must be default constructible and move assignable.

Parser fixes:

* Fix detect invalid reason-phrase octets
* Fix write_eof to set the 'complete' state on success
* Fix consider parse complete if eof received on empty body

WebSocket:

* Increase coverage
This commit is contained in:
Vinnie Falco committed 2016-05-12 19:20:57 -04:00
1 parent 8921da91b8
commit 2a8de0fd6b
28 files changed
+1536 -701

No files matched your search

+1 -1
View File
@@ -14,9 +14,9 @@ add_executable (websocket-tests
rfc6455.cpp
stream.cpp
teardown.cpp
utf8_checker.cpp
detail/frame.cpp
detail/mask.cpp
detail/utf8_checker.cpp
)
if (NOT WIN32)
+4 -2
View File
@@ -32,7 +32,7 @@ operator==(frame_header const& lhs, frame_header const& rhs)
class frame_test : public beast::unit_test::suite
{
public:
void testValidOpcode()
void testCloseCodes()
{
expect(! is_valid(0));
expect(! is_valid(1));
@@ -43,6 +43,8 @@ public:
expect(! is_valid(1016));
expect(! is_valid(2000));
expect(! is_valid(2999));
expect(is_valid(1000));
expect(is_valid(1002));
expect(is_valid(3000));
expect(is_valid(4000));
expect(is_valid(5000));
@@ -220,7 +222,7 @@ public:
void run() override
{
testValidOpcode();
testCloseCodes();
testFrameHeader();
testBadFrameHeaders();
pass();
@@ -8,12 +8,14 @@
// Test that header file is self-contained.
#include <beast/websocket/detail/utf8_checker.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/unit_test/suite.hpp>
#include <array>
namespace beast {
namespace websocket {
namespace detail {
class utf8_checker_test : public beast::unit_test::suite
{
@@ -21,7 +23,7 @@ public:
void
testOneByteSequence()
{
detail::utf8_checker utf8;
utf8_checker utf8;
std::array<std::uint8_t, 256> const buf =
([]()
{
@@ -50,7 +52,7 @@ public:
void
testTwoByteSequence()
{
detail::utf8_checker utf8;
utf8_checker utf8;
std::uint8_t buf[2];
for(auto i = 194; i <= 223; ++i)
{
@@ -84,7 +86,7 @@ public:
void
testThreeByteSequence()
{
detail::utf8_checker utf8;
utf8_checker utf8;
std::uint8_t buf[3];
for (auto i = 224; i <= 239; ++i)
{
@@ -140,7 +142,8 @@ public:
void
testFourByteSequence()
{
detail::utf8_checker utf8;
using boost::asio::const_buffers_1;
utf8_checker utf8;
std::uint8_t buf[4];
for (auto i = 240; i <= 244; ++i)
{
@@ -163,7 +166,7 @@ public:
{
// Fourth byte valid range 128-191
buf[3] = static_cast<std::uint8_t>(n);
expect(utf8.write(buf, 4));
expect(utf8.write(const_buffers_1{buf, 4}));
expect(utf8.finish());
}
@@ -171,7 +174,7 @@ public:
{
// Fourth byte invalid range 0-127
buf[3] = static_cast<std::uint8_t>(n);
expect(! utf8.write(buf, 4));
expect(! utf8.write(const_buffers_1{buf, 4}));
}
for (auto n = 192; n <= 255; ++n)
@@ -217,34 +220,47 @@ public:
testWithStreamBuffer()
{
using namespace boost::asio;
// Valid UTF8 encoded text
std::vector<std::vector<std::uint8_t>> const data{
{0x48,0x65,0x69,0x7A,0xC3,0xB6,0x6C,0x72,0xC3,0xBC,0x63,0x6B,
0x73,0x74,0x6F,0xC3,0x9F,0x61,0x62,0x64,0xC3,0xA4,0x6D,0x70,
0x66,0x75,0x6E,0x67},
{0xCE,0x93,0xCE,0xB1,0xCE,0xB6,0xCE,0xAD,0xCE,0xB5,0xCF,0x82,
0x20,0xCE,0xBA,0xCE,0xB1,0xE1,0xBD,0xB6,0x20,0xCE,0xBC,0xCF,
0x85,0xCF,0x81,0xCF,0x84,0xCE,0xB9,0xE1,0xBD,0xB2,0xCF,0x82,
0x20,0xCE,0xB4,0xE1,0xBD,0xB2,0xCE,0xBD,0x20,0xCE,0xB8,0xE1,
0xBD,0xB0,0x20,0xCE,0xB2,0xCF,0x81,0xE1,0xBF,0xB6,0x20,0xCF,
0x80,0xCE,0xB9,0xE1,0xBD,0xB0,0x20,0xCF,0x83,0xCF,0x84,0xE1,
0xBD,0xB8,0x20,0xCF,0x87,0xCF,0x81,0xCF,0x85,0xCF,0x83,0xCE,
0xB1,0xCF,0x86,0xE1,0xBD,0xB6,0x20,0xCE,0xBE,0xCE,0xAD,0xCF,
0x86,0xCF,0x89,0xCF,0x84,0xCE,0xBF},
{0xC3,0x81,0x72,0x76,0xC3,0xAD,0x7A,0x74,0xC5,0xB1,0x72,0xC5,
0x91,0x20,0x74,0xC3,0xBC,0x6B,0xC3,0xB6,0x72,0x66,0xC3,0xBA,
0x72,0xC3,0xB3,0x67,0xC3,0xA9,0x70}
};
detail::utf8_checker utf8;
for(auto const& s : data)
{
beast::streambuf sb(
s.size() / 4); // Force split across blocks
sb.commit(buffer_copy(
sb.prepare(s.size()),
const_buffer(s.data(), s.size())));
expect(utf8.write(sb.data()));
expect(utf8.finish());
// Valid UTF8 encoded text
std::vector<std::vector<std::uint8_t>> const data{{
0x48,0x65,0x69,0x7A,0xC3,0xB6,0x6C,0x72,0xC3,0xBC,0x63,0x6B,
0x73,0x74,0x6F,0xC3,0x9F,0x61,0x62,0x64,0xC3,0xA4,0x6D,0x70,
0x66,0x75,0x6E,0x67
}, {
0xCE,0x93,0xCE,0xB1,0xCE,0xB6,0xCE,0xAD,0xCE,0xB5,0xCF,0x82,
0x20,0xCE,0xBA,0xCE,0xB1,0xE1,0xBD,0xB6,0x20,0xCE,0xBC,0xCF,
0x85,0xCF,0x81,0xCF,0x84,0xCE,0xB9,0xE1,0xBD,0xB2,0xCF,0x82,
0x20,0xCE,0xB4,0xE1,0xBD,0xB2,0xCE,0xBD,0x20,0xCE,0xB8,0xE1,
0xBD,0xB0,0x20,0xCE,0xB2,0xCF,0x81,0xE1,0xBF,0xB6,0x20,0xCF,
0x80,0xCE,0xB9,0xE1,0xBD,0xB0,0x20,0xCF,0x83,0xCF,0x84,0xE1,
0xBD,0xB8,0x20,0xCF,0x87,0xCF,0x81,0xCF,0x85,0xCF,0x83,0xCE,
0xB1,0xCF,0x86,0xE1,0xBD,0xB6,0x20,0xCE,0xBE,0xCE,0xAD,0xCF,
0x86,0xCF,0x89,0xCF,0x84,0xCE,0xBF
}, {
0xC3,0x81,0x72,0x76,0xC3,0xAD,0x7A,0x74,0xC5,0xB1,0x72,0xC5,
0x91,0x20,0x74,0xC3,0xBC,0x6B,0xC3,0xB6,0x72,0x66,0xC3,0xBA,
0x72,0xC3,0xB3,0x67,0xC3,0xA9,0x70
}
};
utf8_checker utf8;
for(auto const& s : data)
{
static std::size_t constexpr size = 8;
std::size_t n = s.size();
auto cb = consumed_buffers(
boost::asio::const_buffers_1(
s.data(), n), 0);
streambuf sb(size);
while(n)
{
auto const amount = std::min(n, size);
sb.commit(buffer_copy(sb.prepare(amount), cb));
cb.consume(amount);
n -= amount;
}
expect(utf8.write(sb.data()));
expect(utf8.finish());
}
}
}
@@ -260,5 +276,6 @@ public:
BEAST_DEFINE_TESTSUITE(utf8_checker,websocket,beast);
} // detail
} // websocket
} // beast
+88
View File
@@ -477,6 +477,88 @@ public:
}
}
struct con
{
stream<socket_type> ws;
explicit
con(endpoint_type const& ep, boost::asio::io_service& ios)
: ws(ios)
{
ws.next_layer().connect(ep);
ws.handshake("localhost", "/");
}
};
template<std::size_t N>
class cbuf_helper
{
std::array<std::uint8_t, N> v_;
boost::asio::const_buffer cb_;
public:
using value_type = decltype(cb_);
using const_iterator = value_type const*;
template<class... Vn>
explicit
cbuf_helper(Vn... vn)
: v_({{ static_cast<std::uint8_t>(vn)... }})
, cb_(v_.data(), v_.size())
{
}
const_iterator
begin() const
{
return &cb_;
}
const_iterator
end() const
{
return begin()+1;
}
};
template<class... Vn>
cbuf_helper<sizeof...(Vn)>
cbuf(Vn... vn)
{
return cbuf_helper<sizeof...(Vn)>(vn...);
}
void testClose(endpoint_type const& ep, yield_context do_yield)
{
using boost::asio::buffer;
{
// payload length 1
con c(ep, ios_);
boost::asio::write(c.ws.next_layer(),
cbuf(0x88, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00));
}
{
// invalid close code 1005
con c(ep, ios_);
boost::asio::write(c.ws.next_layer(),
cbuf(0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x12));
}
{
// invalid utf8
con c(ep, ios_);
boost::asio::write(c.ws.next_layer(),
cbuf(0x88, 0x86, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x15,
0x0f, 0xd7, 0x73, 0x43));
}
{
// good utf8
con c(ep, ios_);
boost::asio::write(c.ws.next_layer(),
cbuf(0x88, 0x86, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x15,
'u', 't', 'f', '8'));
}
}
void testWriteFrame(endpoint_type const& ep)
{
for(;;)
@@ -528,6 +610,9 @@ public:
yield_to(std::bind(&stream_test::testMask,
this, ep, std::placeholders::_1));
yield_to(std::bind(&stream_test::testClose,
this, ep, std::placeholders::_1));
testWriteFrame(ep);
}
{
@@ -542,6 +627,9 @@ public:
yield_to(std::bind(&stream_test::testMask,
this, ep, std::placeholders::_1));
yield_to(std::bind(&stream_test::testClose,
this, ep, std::placeholders::_1));
}
pass();