Beast.WebSocket:

Beast.WebSocket provides developers with a robust WebSocket
implementation built on Boost.Asio with a consistent asynchronous
model using a modern C++ approach.
This commit is contained in:
Vinnie Falco
2016-02-25 16:17:19 -05:00
parent 5e8d028da2
commit 2cb3834bbb
106 changed files with 14671 additions and 772 deletions

View File

@@ -7,14 +7,14 @@
import os ;
path-constant main : ../../beast/unit_test/src/main.cpp ;
path-constant test_main : ../beast/unit_test/src/main.cpp ;
unit-test all :
append_buffers.cpp
asio.cpp
async_completion.cpp
basic_streambuf.cpp
../basic_headers.cpp
basic_headers.cpp
bind_handler.cpp
buffers_adapter.cpp
buffers_debug.cpp
@@ -26,5 +26,5 @@ unit-test all :
streambuf.cpp
streambuf_readstream.cpp
type_check.cpp
$(main)
$(test_main)
;

View File

@@ -0,0 +1,358 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/wsproto/src/test/async_echo_peer.h>
#include <beast/wsproto/src/test/sync_echo_peer.h>
#include <beast/unit_test/suite.h>
#include <beast/unit_test/thread.h>
#include <beast/http.h>
#include <boost/asio/spawn.hpp>
namespace beast {
namespace wsproto {
class ws_test : public unit_test::suite
{
public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using yield_context = boost::asio::yield_context;
endpoint_type ep_;
//--------------------------------------------------------------------------
// opcodes for creating the test plans
// concurrent read and write
struct case_1{};
// write a bad frame and shut down
struct case_2{};
//--------------------------------------------------------------------------
class coro_peer
{
error_code ec_;
boost::asio::io_service ios_;
boost::asio::ip::tcp::acceptor acceptor_;
socket_type sock_;
socket<socket_type&> ws_;
opcode::value op_;
beast::streambuf rb_;
beast::streambuf wb_;
yield_context* yield_;
int state_ = 0;
//unit_test::suite& test_;
public:
coro_peer(coro_peer&&) = default;
coro_peer(coro_peer const&) = delete;
coro_peer& operator=(coro_peer&&) = delete;
coro_peer& operator=(coro_peer const&) = delete;
template<class... Ops>
coro_peer(bool server, endpoint_type ep,
unit_test::suite& test, Ops const&... ops)
: acceptor_(ios_)
, sock_(ios_)
, ws_(sock_)
//, test_(test)
{
if(server)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
boost::asio::spawn(ios_,
[=](auto yield)
{
yield_ = &yield;
state_ = 10;
acceptor_.async_accept(sock_, (*yield_)[ec_]);
if(ec_)
return this->fail("accept");
state_ = 20;
ws_.async_accept((*yield_)[ec_]);
if(ec_)
return this->fail("ws.accept");
this->invoke(ops...);
state_ = -1;
});
}
else
{
boost::asio::spawn(ios_,
[=](auto yield)
{
yield_ = &yield;
state_ = 30;
sock_.async_connect(ep, (*yield_)[ec_]);
if(ec_)
return this->fail("connect");
state_ = 40;
ws_.async_handshake(ep.address().to_string() +
std::to_string(ep.port()), "/", (*yield_)[ec_]);
if(ec_)
return this->fail("handshake");
this->invoke(ops...);
state_ = -1;
});
}
}
~coro_peer()
{
}
int
state() const
{
return state_;
}
void
run_one()
{
ios_.run_one();
}
void
step_to(int to = 0)
{
while(state_ != to)
ios_.run_one();
}
private:
template<class String>
void fail(String const& s)
{
}
void invoke_1(case_1)
{
async_read(ws_, op_, rb_,
[&](auto ec)
{
if(ec)
return this->fail(ec);
rb_.consume(rb_.size());
});
state_ = 100;
async_write(ws_, opcode::text,
boost::asio::null_buffers{}, (*yield_)[ec_]);
if(ec_)
return fail("write");
}
void invoke_1(case_2)
{
detail::frame_header fh;
fh.op = opcode::rsv5; // bad opcode
fh.fin = true;
fh.mask = true;
fh.rsv1 = false;
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = 0;
fh.key = 0;
detail::write(wb_, fh);
state_ = 200;
boost::asio::async_write(
ws_.next_layer(), wb_.data(),
(*yield_)[ec_]);
if(ec_)
return fail("write");
ws_.next_layer().shutdown(
socket_type::shutdown_both, ec_);
if(ec_)
return fail("shutdown");
}
inline
void
invoke()
{
}
template<class Op, class... Ops>
inline
void
invoke(Op op, Ops const&... ops)
{
invoke_1(op);
invoke(ops...);
}
};
void
testInvokable()
{
endpoint_type const ep{
address_type::from_string(
"127.0.0.1"), 6000};
coro_peer server(true, ep, *this, case_1{});
coro_peer client(false, ep, *this, case_2{});
server.step_to(10); // async_accept
client.step_to(30); // async_connect
server.step_to(20); // async_accept(ws)
client.step_to(40); // async_handshake
server.step_to(100); // case_1
client.step_to(200); // case_2
client.step_to(-1);
server.step_to(-1);
}
//--------------------------------------------------------------------------
void
maybe_fail(error_code const& ec, std::string const& what)
{
expect(! ec, what + ": " + ec.message());
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
maybe_fail(ec, what);
throw ec;
}
}
template<class Buffers>
static
std::string
buffers_to_string(Buffers const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
for(auto i = s.size(); i-- > 0;)
if(s[i] == '\r')
s.replace(i, 1, "\\r");
else if(s[i] == '\n')
s.replace(i, 1, "\\n\n");
return s;
}
int
makeRequest(endpoint_type ep, std::string const& s)
{
using boost::asio::buffer;
boost::asio::io_service ios;
boost::asio::ip::tcp::socket sock(ios);
sock.connect(ep);
write(sock, append_buffers(
buffer(s), buffer("\r\n")));
using namespace http;
parsed_response<string_body> m;
streambuf sb;
read(sock, sb, m);
return m.status;
}
void
expectStatus(endpoint_type ep,
int status, std::string const& s)
{
expect(makeRequest(ep, s) == status);
}
void
testHandshake(endpoint_type ep)
{
expectStatus(ep, 400, "GET / HTTP/1.0\r\n");
}
void
syncEchoClient(endpoint_type ep)
{
using boost::asio::buffer;
error_code ec;
boost::asio::io_service ios;
wsproto::socket<socket_type> ws(ios);
ws.next_layer().connect(ep, ec);
maybe_fail(ec, "connect");
ws.handshake(ep.address().to_string(), "/", ec);
maybe_fail(ec, "upgrade");
std::string const s = "Hello, world!";
ws.write(wsproto::opcode::text, true, buffer(s), ec);
maybe_fail(ec, "write");
boost::asio::streambuf sb;
wsproto::opcode::value op;
read(ws, op, sb, ec);
maybe_fail(ec, "read");
if(! ec)
expect(op == wsproto::opcode::text);
expect(buffers_to_string(sb.data()) == s);
sb.consume(sb.size());
ws.close({}, ec);
maybe_fail(ec, "close");
while(! ec)
{
read(ws, op, sb, ec);
if(! ec)
sb.consume(sb.size());
}
if(ec != error::closed)
maybe_fail(ec, "teardown");
}
void
run() override
{
//testInvokable();
#if 0
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6000};
testcase("Echo Server");
test::sync_echo_peer s(true, ep, *this);
testHandshake(ep);
syncEchoClient(ep);
}
#endif
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6001};
testcase("Async Echo Server");
test::async_echo_peer s(true, ep, *this);
//testHandshake(ep);
syncEchoClient(ep);
}
}
};
BEAST_DEFINE_TESTSUITE(ws,asio,beast);
} // wsproto
} // beast

View File

@@ -0,0 +1,43 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <beast/asio/ssl_error.h>
#include <beast/unit_test/suite.h>
#include <string>
namespace beast {
class ssl_error_test : public unit_test::suite
{
public:
void run()
{
{
boost::system::error_code ec =
boost::system::error_code (335544539,
boost::asio::error::get_ssl_category ());
std::string const s = beast::error_message_with_ssl(ec);
expect(s == " (20,0,219) error:140000DB:SSL routines:SSL routines:short read");
}
}
};
BEAST_DEFINE_TESTSUITE(ssl_error,asio,beast);
} // beast

View File

@@ -67,7 +67,7 @@ public:
std::integral_constant<bool, Move>;
using propagate_on_container_swap =
std::integral_constant<bool, Swap>;
template<class U>
struct rebind
{
@@ -122,7 +122,7 @@ public:
::operator delete(p);
}
std::size_t
std::size_t
id() const
{
return id_;
@@ -274,11 +274,6 @@ public:
sb_type sb3(sb, alloc_type{});
//expect(sb3.get_allocator().id() == 3);
}
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
}
}
void run() override