Reorganize source files

This commit is contained in:
Vinnie Falco
2016-04-20 12:01:25 -04:00
parent 54f6f0ceba
commit f07cd8ceb4
239 changed files with 2692 additions and 19127 deletions
+146
View File
@@ -0,0 +1,146 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP
#define BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP
#include <beast/websocket/impl/response_op.ipp>
#include <beast/handler_alloc.hpp>
#include <beast/prepare_buffers.hpp>
#include <beast/http/parser.hpp>
#include <beast/http/read.hpp>
#include <cassert>
#include <memory>
#include <type_traits>
namespace beast {
namespace websocket {
// read and respond to an upgrade request
//
template<class NextLayer>
template<class Handler>
class stream<NextLayer>::accept_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& ws;
http::request<http::empty_body> req;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler, class Buffers>
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
Buffers const& buffers)
: ws(ws_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
ws.stream_.buffer().commit(buffer_copy(
ws.stream_.buffer().prepare(
buffer_size(buffers)), buffers));
}
};
std::shared_ptr<data> d_;
public:
accept_op(accept_op&&) = default;
accept_op(accept_op const&) = default;
template<class DeducedHandler, class... Args>
accept_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0, false);
}
void operator()(error_code const& ec)
{
(*this)(ec, 0);
}
void operator()(error_code const& ec,
std::size_t bytes_transferred, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, accept_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, accept_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(accept_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, accept_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Handler>
void
stream<NextLayer>::accept_op<Handler>::
operator()(error_code const& ec,
std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
// read message
d.state = 1;
http::async_read(d.ws.next_layer_,
d.ws.stream_.buffer(), d.req,
std::move(*this));
return;
// got message
case 1:
// respond to request
response_op<Handler>{
std::move(d.h), d.ws, d.req, true};
return;
}
}
d.h(ec);
}
} // websocket
} // beast
#endif
+186
View File
@@ -0,0 +1,186 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP
#define BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP
#include <beast/handler_alloc.hpp>
#include <beast/static_streambuf.hpp>
#include <memory>
namespace beast {
namespace websocket {
// send the close message and wait for the response
//
template<class NextLayer>
template<class Handler>
class stream<NextLayer>::close_op
{
using alloc_type =
handler_alloc<char, Handler>;
using fb_type =
detail::frame_streambuf;
using fmb_type =
typename fb_type::mutable_buffers_type;
struct data : op
{
stream<NextLayer>& ws;
close_reason cr;
Handler h;
fb_type fb;
fmb_type fmb;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
close_reason const& cr_)
: ws(ws_)
, cr(cr_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
ws.template write_close<
static_streambuf>(fb, cr);
}
};
std::shared_ptr<data> d_;
public:
close_op(close_op&&) = default;
close_op(close_op const&) = default;
template<class DeducedHandler, class... Args>
close_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0, false);
}
void operator()()
{
auto& d = *d_;
d.cont = false;
(*this)(error_code{}, 0, false);
}
void operator()(error_code const& ec)
{
(*this)(ec, 0);
}
void
operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, close_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, close_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(close_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, close_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Handler>
void
stream<NextLayer>::close_op<Handler>::operator()(
error_code ec, std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
if(d.ws.wr_block_)
{
// suspend
d.state = 1;
d.ws.rd_op_.template emplace<
close_op>(std::move(*this));
return;
}
if(d.ws.error_)
{
// call handler
d.state = 99;
d.ws.get_io_service().post(
bind_handler(std::move(*this),
boost::asio::error::operation_aborted, 0));
return;
}
d.state = 2;
break;
// resume
case 1:
if(d.ws.error_)
{
// call handler
d.state = 99;
ec = boost::asio::error::operation_aborted;
break;
}
d.state = 2;
break;
case 2:
// send close
d.state = 99;
assert(! d.ws.wr_close_);
d.ws.wr_close_ = true;
assert(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_,
d.fb.data(), std::move(*this));
return;
}
}
if(ec)
d.ws.error_ = true;
if(d.ws.wr_block_ == &d)
d.ws.wr_block_ = nullptr;
d.h(ec);
d.ws.rd_op_.maybe_invoke();
}
} // websocket
} // beast
#endif
+27
View File
@@ -0,0 +1,27 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_ERROR_IPP_HPP
#define BEAST_WEBSOCKET_IMPL_ERROR_IPP_HPP
#include <beast/websocket/detail/error.hpp>
namespace beast {
namespace websocket {
inline
error_code
make_error_code(error e)
{
return error_code(
static_cast<int>(e), detail::get_error_category());
}
} // websocket
} // beast
#endif
@@ -0,0 +1,158 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP
#include <beast/handler_alloc.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp>
#include <beast/http/read.hpp>
#include <beast/http/write.hpp>
#include <cassert>
#include <memory>
namespace beast {
namespace websocket {
// send the upgrade request and process the response
//
template<class NextLayer>
template<class Handler>
class stream<NextLayer>::handshake_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& ws;
Handler h;
std::string key;
http::request<http::empty_body> req;
http::response<http::string_body> resp;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
boost::string_ref const& host,
boost::string_ref const& resource)
: ws(ws_)
, h(std::forward<DeducedHandler>(h_))
, req(ws.build_request(host, resource, key))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
handshake_op(handshake_op&&) = default;
handshake_op(handshake_op const&) = default;
template<class DeducedHandler, class... Args>
handshake_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0, false);
}
void operator()(error_code const& ec)
{
(*this)(ec, 0);
}
void operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, handshake_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, handshake_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(handshake_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, handshake_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Handler>
void
stream<NextLayer>::handshake_op<
Handler>::operator()(error_code ec,
std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
{
// send http upgrade
d.state = 1;
// VFALCO Do we need the ability to move
// a message on the async_write?
http::async_write(d.ws.stream_,
d.req, std::move(*this));
return;
}
// sent upgrade
case 1:
// read http response
d.state = 2;
http::async_read(d.ws.next_layer_,
d.ws.stream_.buffer(), d.resp,
std::move(*this));
return;
// got response
case 2:
{
d.ws.do_response(d.resp, d.key, ec);
// call handler
d.state = 99;
break;
}
}
}
d.h(ec);
}
} // websocket
} // beast
#endif
@@ -0,0 +1,507 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP
#define BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP
#include <beast/websocket/teardown.hpp>
#include <beast/handler_alloc.hpp>
#include <beast/prepare_buffers.hpp>
#include <beast/static_streambuf.hpp>
#include <cassert>
#include <memory>
namespace beast {
namespace websocket {
// Reads a single message frame,
// processes any received control frames.
//
template<class NextLayer>
template<class Streambuf, class Handler>
class stream<NextLayer>::read_frame_op
{
using alloc_type =
handler_alloc<char, Handler>;
using fb_type =
detail::frame_streambuf;
using fmb_type =
typename fb_type::mutable_buffers_type;
using smb_type =
typename Streambuf::mutable_buffers_type;
struct data : op
{
stream<NextLayer>& ws;
frame_info& fi;
Streambuf& sb;
smb_type smb;
Handler h;
fb_type fb;
fmb_type fmb;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
frame_info& fi_, Streambuf& sb_)
: ws(ws_)
, fi(fi_)
, sb(sb_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
read_frame_op(read_frame_op&&) = default;
read_frame_op(read_frame_op const&) = default;
template<class DeducedHandler, class... Args>
read_frame_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0, false);
}
void operator()()
{
auto& d = *d_;
d.cont = false;
(*this)(error_code{}, 0, false);
}
void operator()(error_code const& ec)
{
(*this)(ec, 0);
}
void operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, read_frame_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, read_frame_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(read_frame_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, read_frame_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Buffers, class Handler>
void
stream<NextLayer>::read_frame_op<Buffers, Handler>::
operator()(error_code ec,std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
close_code code;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
if(d.ws.error_)
{
// call handler
d.state = 99;
d.ws.get_io_service().post(
bind_handler(std::move(*this),
boost::asio::error::operation_aborted, 0));
return;
}
if(d.ws.rd_need_ > 0)
{
d.state = 1;
break;
}
d.state = 2;
break;
case 1:
// read payload
d.state = 3;
d.smb = d.sb.prepare(
detail::clamp(d.ws.rd_need_));
d.ws.stream_.async_read_some(
d.smb, std::move(*this));
return;
case 2:
// read fixed header
d.state = 5;
boost::asio::async_read(d.ws.stream_,
d.fb.prepare(2), std::move(*this));
return;
// got payload
case 3:
{
d.ws.rd_need_ -= bytes_transferred;
auto const pb = prepare_buffers(
bytes_transferred, d.smb);
if(d.ws.rd_fh_.mask)
detail::mask_inplace(pb, d.ws.rd_key_);
if(d.ws.rd_opcode_ == opcode::text)
{
if(! d.ws.rd_utf8_check_.write(pb) ||
(d.ws.rd_need_ == 0 && d.ws.rd_fh_.fin &&
! d.ws.rd_utf8_check_.finish()))
{
// invalid utf8
d.state = 16;
code = close_code::bad_payload;
break;
}
}
d.sb.commit(bytes_transferred);
d.state = 4;
break;
}
// call handler
case 4:
d.state = 99;
d.fi.op = d.ws.rd_opcode_;
d.fi.fin = d.ws.rd_fh_.fin &&
d.ws.rd_need_ == 0;
break;
// got fixed header
case 5:
{
d.fb.commit(bytes_transferred);
code = close_code::none;
auto const n = detail::read_fh1(
d.ws.rd_fh_, d.fb, d.ws.role_, code);
if(code != close_code::none)
{
// protocol error
d.state = 16;
break;
}
d.state = 6;
if (n == 0)
{
bytes_transferred = 0;
break;
}
// read variable header
boost::asio::async_read(d.ws.stream_,
d.fb.prepare(n), std::move(*this));
return;
}
// got variable header
case 6:
d.fb.commit(bytes_transferred);
code = close_code::none;
detail::read_fh2(d.ws.rd_fh_,
d.fb, d.ws.role_, code);
if(code == close_code::none)
d.ws.prepare_fh(code);
if(code != close_code::none)
{
// protocol error
d.state = 16;
break;
}
if(detail::is_control(d.ws.rd_fh_.op))
{
if(d.ws.rd_fh_.len > 0)
{
// read control payload
d.state = 7;
d.fmb = d.fb.prepare(static_cast<
std::size_t>(d.ws.rd_fh_.len));
boost::asio::async_read(d.ws.stream_,
d.fmb, std::move(*this));
return;
}
d.state = 8;
break;
}
if(d.ws.rd_need_ > 0)
{
d.state = 1;
break;
}
if(! d.ws.rd_fh_.fin)
{
d.state = 2;
break;
}
// empty frame with fin
d.state = 4;
break;
// got control payload
case 7:
if(d.ws.rd_fh_.mask)
detail::mask_inplace(
d.fmb, d.ws.rd_key_);
d.fb.commit(bytes_transferred);
d.state = 8;
break;
// do control
case 8:
if(d.ws.rd_fh_.op == opcode::ping)
{
code = close_code::none;
ping_payload_type data;
detail::read(data, d.fb.data(), code);
if(code != close_code::none)
{
// protocol error
d.state = 16;
break;
}
d.fb.reset();
if(d.ws.wr_close_)
{
d.state = 2;
break;
}
d.ws.template write_ping<static_streambuf>(
d.fb, opcode::pong, data);
if(d.ws.wr_block_)
{
assert(d.ws.wr_block_ != &d);
// suspend
d.state = 13;
d.ws.rd_op_.template emplace<
read_frame_op>(std::move(*this));
return;
}
d.state = 14;
break;
}
else if(d.ws.rd_fh_.op == opcode::pong)
{
code = close_code::none;
ping_payload_type data;
detail::read(data, d.fb.data(), code);
if(code != close_code::none)
{
// protocol error
d.state = 16;
break;
}
d.fb.reset();
// VFALCO TODO maybe_invoke an async pong handler
// For now just ignore the pong.
d.state = 2;
break;
}
assert(d.ws.rd_fh_.op == opcode::close);
{
detail::read(d.ws.cr_, d.fb.data(), code);
if(code != close_code::none)
{
d.state = 16;
break;
}
if(! d.ws.wr_close_)
{
auto cr = d.ws.cr_;
if(cr.code == close_code::none)
cr.code = close_code::normal;
cr.reason = "";
d.fb.reset();
d.ws.template write_close<
static_streambuf>(d.fb, cr);
if(d.ws.wr_block_)
{
// suspend
d.state = 9;
d.ws.rd_op_.template emplace<
read_frame_op>(std::move(*this));
return;
}
d.state = 10;
break;
}
// call handler;
d.state = 99;
ec = error::closed;
break;
}
// resume
case 9:
if(d.ws.error_)
{
// call handler
d.state = 99;
ec = boost::asio::error::operation_aborted;
break;
}
if(d.ws.wr_close_)
{
// call handler
d.state = 99;
ec = error::closed;
break;
}
d.state = 10;
break;
// send close
case 10:
d.state = 11;
assert(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_,
d.fb.data(), std::move(*this));
return;;
// teardown
case 11:
d.state = 12;
wsproto_helpers::call_async_teardown(
d.ws.next_layer_, std::move(*this));
return;
case 12:
// call handler
d.state = 99;
ec = error::closed;
break;
// resume
case 13:
if(d.ws.error_)
{
// call handler
d.state = 99;
ec = boost::asio::error::operation_aborted;
break;
}
if(d.ws.wr_close_)
{
d.fb.reset();
d.state = 2;
break;
}
d.state = 14;
break;
case 14:
// write ping/pong
d.state = 15;
assert(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_,
d.fb.data(), std::move(*this));
return;
// sent ping/pong
case 15:
d.fb.reset();
d.state = 2;
d.ws.wr_block_ = nullptr;
break;
// fail the connection
case 16:
if(! d.ws.wr_close_)
{
d.fb.reset();
d.ws.template write_close<
static_streambuf>(d.fb, code);
if(d.ws.wr_block_)
{
// suspend
d.state = 17;
d.ws.rd_op_.template emplace<
read_frame_op>(std::move(*this));
return;
}
d.state = 18;
break;
}
// resume
case 17:
if(d.ws.wr_close_)
{
d.state = 19;
break;
}
d.state = 18;
break;
case 18:
// send close
d.state = 19;
d.ws.wr_close_ = true;
assert(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_,
d.fb.data(), std::move(*this));
return;
// teardown
case 19:
d.state = 20;
wsproto_helpers::call_async_teardown(
d.ws.next_layer_, std::move(*this));
return;
case 20:
// call handler
d.state = 99;
ec = error::failed;
break;
}
}
if(ec)
d.ws.error_ = true;
if(d.ws.wr_block_ == &d)
d.ws.wr_block_ = nullptr;
d.h(ec);
d.ws.wr_op_.maybe_invoke();
}
} // websocket
} // beast
#endif
+132
View File
@@ -0,0 +1,132 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_READ_OP_HPP
#define BEAST_WEBSOCKET_IMPL_READ_OP_HPP
#include <beast/handler_alloc.hpp>
#include <memory>
namespace beast {
namespace websocket {
// read an entire message
//
template<class NextLayer>
template<class Streambuf, class Handler>
class stream<NextLayer>::read_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& ws;
opcode& op;
Streambuf& sb;
Handler h;
frame_info fi;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
stream<NextLayer>& ws_, opcode& op_,
Streambuf& sb_)
: ws(ws_)
, op(op_)
, sb(sb_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = default;
template<class DeducedHandler, class... Args>
read_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, false);
}
void operator()(
error_code const& ec, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(read_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, read_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Streambuf, class Handler>
void
stream<NextLayer>::read_op<Streambuf, Handler>::
operator()(error_code const& ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
// read payload
d.state = 1;
d.ws.async_read_frame(
d.fi, d.sb, std::move(*this));
return;
// got payload
case 1:
d.op = d.fi.op;
d.state = d.fi.fin ? 99 : 0;
break;
}
}
d.h(ec);
}
} // websocket
} // beast
#endif
@@ -0,0 +1,135 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP
#define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP
#include <beast/handler_alloc.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/write.hpp>
#include <memory>
namespace beast {
namespace websocket {
// Respond to an upgrade HTTP request
template<class NextLayer>
template<class Handler>
class stream<NextLayer>::response_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& ws;
http::response<http::string_body> resp;
Handler h;
error_code final_ec;
bool cont;
int state = 0;
template<class DeducedHandler,
class Body, class Headers>
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
http::message<true, Body, Headers> const& req,
bool cont_)
: ws(ws_)
, resp(ws_.build_response(req))
, h(std::forward<DeducedHandler>(h_))
, cont(cont_)
{
if(resp.status != 101)
final_ec = error::handshake_failed;
}
};
std::shared_ptr<data> d_;
public:
response_op(response_op&&) = default;
response_op(response_op const&) = default;
template<class DeducedHandler, class... Args>
response_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, false);
}
void operator()(
error_code ec, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, response_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, response_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(response_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, response_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Handler>
void
stream<NextLayer>::response_op<Handler>::
operator()(error_code ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
// send response
d.state = 1;
http::async_write(d.ws.next_layer_,
d.resp, std::move(*this));
return;
// sent response
case 1:
d.state = 99;
ec = d.final_ec;
if(! ec)
d.ws.role_ = role_type::server;
break;
}
}
d.h(ec);
}
} // websocket
} // beast
#endif
+803
View File
@@ -0,0 +1,803 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_SOCKET_IPP
#define BEAST_WEBSOCKET_IMPL_SOCKET_IPP
#include <beast/websocket/teardown.hpp>
#include <beast/websocket/detail/hybi13.hpp>
#include <beast/websocket/impl/accept_op.ipp>
#include <beast/websocket/impl/close_op.ipp>
#include <beast/websocket/impl/handshake_op.ipp>
#include <beast/websocket/impl/read_op.ipp>
#include <beast/websocket/impl/read_frame_op.ipp>
#include <beast/websocket/impl/response_op.ipp>
#include <beast/websocket/impl/write_op.ipp>
#include <beast/websocket/impl/write_frame_op.ipp>
#include <beast/buffer_cat.hpp>
#include <beast/async_completion.hpp>
#include <beast/consuming_buffers.hpp>
#include <beast/prepare_buffers.hpp>
#include <beast/static_streambuf.hpp>
#include <beast/streambuf.hpp>
#include <beast/type_check.hpp>
#include <beast/http/read.hpp>
#include <beast/http/write.hpp>
#include <beast/http/reason.hpp>
#include <beast/http/rfc2616.hpp>
#include <boost/endian/buffers.hpp>
#include <algorithm>
#include <cassert>
#include <memory>
#include <utility>
namespace beast {
namespace websocket {
namespace detail {
template<class _>
void
stream_base::prepare_fh(close_code& code)
{
// continuation without an active message
if(! rd_cont_ && rd_fh_.op == opcode::cont)
{
code = close_code::protocol_error;
return;
}
// new data frame when continuation expected
if(rd_cont_ && ! is_control(rd_fh_.op) &&
rd_fh_.op != opcode::cont)
{
code = close_code::protocol_error;
return;
}
if(rd_fh_.mask)
prepare_key(rd_key_, rd_fh_.key);
if(! is_control(rd_fh_.op))
{
if(rd_fh_.op != opcode::cont)
{
rd_size_ = rd_fh_.len;
rd_opcode_ = rd_fh_.op;
}
else
{
if(rd_size_ > std::numeric_limits<
std::uint64_t>::max() - rd_fh_.len)
{
code = close_code::too_big;
return;
}
rd_size_ += rd_fh_.len;
}
if(rd_size_ > rd_msg_max_)
{
code = close_code::too_big;
return;
}
rd_need_ = rd_fh_.len;
rd_cont_ = ! rd_fh_.fin;
}
}
template<class Streambuf>
void
stream_base::write_close(
Streambuf& sb, close_reason const& cr)
{
using namespace boost::endian;
frame_header fh;
fh.op = opcode::close;
fh.fin = true;
fh.rsv1 = false;
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = cr.code == close_code::none ?
0 : 2 + cr.reason.size();
if((fh.mask = (role_ == role_type::client)))
fh.key = maskgen_();
detail::write(sb, fh);
if(cr.code != close_code::none)
{
detail::prepared_key_type key;
if(fh.mask)
detail::prepare_key(key, fh.key);
{
std::uint8_t b[2];
::new(&b[0]) big_uint16_buf_t{
(std::uint16_t)cr.code};
auto d = sb.prepare(2);
boost::asio::buffer_copy(d,
boost::asio::buffer(b));
if(fh.mask)
detail::mask_inplace(d, key);
sb.commit(2);
}
if(! cr.reason.empty())
{
auto d = sb.prepare(cr.reason.size());
boost::asio::buffer_copy(d,
boost::asio::const_buffer(
cr.reason.data(), cr.reason.size()));
if(fh.mask)
detail::mask_inplace(d, key);
sb.commit(cr.reason.size());
}
}
}
template<class Streambuf>
void
stream_base::write_ping(Streambuf& sb,
opcode op, ping_payload_type const& data)
{
frame_header fh;
fh.op = op;
fh.fin = true;
fh.rsv1 = false;
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = data.size();
if((fh.mask = (role_ == role_type::client)))
fh.key = maskgen_();
detail::write(sb, fh);
if(data.empty())
return;
detail::prepared_key_type key;
if(fh.mask)
detail::prepare_key(key, fh.key);
auto d = sb.prepare(data.size());
boost::asio::buffer_copy(d,
boost::asio::const_buffers_1(
data.data(), data.size()));
if(fh.mask)
detail::mask_inplace(d, key);
sb.commit(data.size());
}
} // detail
//------------------------------------------------------------------------------
template<class NextLayer>
template<class... Args>
stream<NextLayer>::stream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
, stream_(next_layer_)
{
static_assert(is_Stream<next_layer_type>::value,
"Stream requirements not met");
}
template<class NextLayer>
void
stream<NextLayer>::accept(error_code& ec)
{
accept(boost::asio::null_buffers{}, ec);
}
template<class NextLayer>
template<class AcceptHandler>
auto
stream<NextLayer>::async_accept(AcceptHandler&& handler)
{
return async_accept(boost::asio::null_buffers{},
std::forward<AcceptHandler>(handler));
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::accept(
ConstBufferSequence const& buffers)
{
static_assert(is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
error_code ec;
accept(buffers, ec);
detail::maybe_throw(ec, "accept");
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::accept(
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
http::request<http::empty_body> m;
http::read(next_layer_, stream_.buffer(), m, ec);
if(ec)
return;
accept(m, ec);
}
template<class NextLayer>
template<class ConstBufferSequence, class AcceptHandler>
auto
stream<NextLayer>::async_accept(
ConstBufferSequence const& bs, AcceptHandler&& handler)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
beast::async_completion<
AcceptHandler, void(error_code)
> completion(handler);
accept_op<decltype(completion.handler)>{
completion.handler, *this, bs};
return completion.result.get();
}
template<class NextLayer>
template<class Body, class Headers>
void
stream<NextLayer>::accept(
http::message<true, Body, Headers> const& request)
{
error_code ec;
accept(request, ec);
detail::maybe_throw(ec, "accept");
}
template<class NextLayer>
template<class Body, class Headers>
void
stream<NextLayer>::accept(
http::message<true, Body, Headers> const& req,
error_code& ec)
{
auto resp = build_response(req);
http::write(stream_, resp, ec);
if(resp.status != 101)
{
ec = error::handshake_failed;
// VFALCO TODO Respect keep alive setting, perform
// teardown if Connection: close.
return;
}
role_ = role_type::server;
}
template<class NextLayer>
template<class Body, class Headers, class AcceptHandler>
auto
stream<NextLayer>::async_accept(
http::message<true, Body, Headers> const& req,
AcceptHandler&& handler)
{
beast::async_completion<
AcceptHandler, void(error_code)
> completion(handler);
response_op<decltype(completion.handler)>{
completion.handler, *this, req,
boost_asio_handler_cont_helpers::
is_continuation(completion.handler)};
return completion.result.get();
}
template<class NextLayer>
void
stream<NextLayer>::handshake(boost::string_ref const& host,
boost::string_ref const& resource, error_code& ec)
{
std::string key;
http::write(stream_,
build_request(host, resource, key), ec);
if(ec)
return;
http::response<http::string_body> resp;
http::read(next_layer_, stream_.buffer(), resp, ec);
if(ec)
return;
do_response(resp, key, ec);
}
template<class NextLayer>
template<class HandshakeHandler>
auto
stream<NextLayer>::async_handshake(boost::string_ref const& host,
boost::string_ref const& resource, HandshakeHandler&& handler)
{
beast::async_completion<
HandshakeHandler, void(error_code)
> completion(handler);
handshake_op<decltype(completion.handler)>{
completion.handler, *this, host, resource};
return completion.result.get();
}
template<class NextLayer>
void
stream<NextLayer>::close(
close_reason const& cr, error_code& ec)
{
assert(! wr_close_);
wr_close_ = true;
detail::frame_streambuf fb;
write_close<static_streambuf>(fb, cr);
boost::asio::write(stream_, fb.data(), ec);
error_ = ec != 0;
}
template<class NextLayer>
template<class CloseHandler>
auto
stream<NextLayer>::async_close(
close_reason const& cr, CloseHandler&& handler)
{
beast::async_completion<
CloseHandler, void(error_code)
> completion(handler);
close_op<decltype(completion.handler)>{
completion.handler, *this, cr};
return completion.result.get();
}
template<class NextLayer>
template<class Streambuf>
void
stream<NextLayer>::
read(opcode& op, Streambuf& streambuf, error_code& ec)
{
frame_info fi;
for(;;)
{
read_frame(fi, streambuf, ec);
if(ec)
break;
op = fi.op;
if(fi.fin)
break;
}
}
template<class NextLayer>
template<class Streambuf, class ReadHandler>
auto
stream<NextLayer>::
async_read(opcode& op,
Streambuf& streambuf, ReadHandler&& handler)
{
static_assert(beast::is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
beast::async_completion<
ReadHandler, void(error_code)
> completion(handler);
read_op<Streambuf, decltype(completion.handler)>{
completion.handler, *this, op, streambuf};
return completion.result.get();
}
template<class NextLayer>
template<class Streambuf>
void
stream<NextLayer>::read_frame(frame_info& fi,
Streambuf& streambuf, error_code& ec)
{
close_code code{};
for(;;)
{
if(rd_need_ == 0)
{
// read header
detail::frame_streambuf fb;
do_read_fh(fb, code, ec);
if((error_ = ec != 0))
return;
if(code != close_code::none)
break;
if(detail::is_control(rd_fh_.op))
{
// read control payload
if(rd_fh_.len > 0)
{
auto const mb = fb.prepare(
static_cast<std::size_t>(rd_fh_.len));
fb.commit(boost::asio::read(stream_, mb, ec));
if((error_ = ec != 0))
return;
if(rd_fh_.mask)
detail::mask_inplace(mb, rd_key_);
fb.commit(static_cast<std::size_t>(rd_fh_.len));
}
if(rd_fh_.op == opcode::ping)
{
ping_payload_type data;
detail::read(data, fb.data(), code);
if(code != close_code::none)
break;
fb.reset();
write_ping<static_streambuf>(
fb, opcode::pong, data);
boost::asio::write(stream_, fb.data(), ec);
if((error_ = ec != 0))
return;
continue;
}
else if(rd_fh_.op == opcode::pong)
{
ping_payload_type data;
detail::read(data, fb.data(), code);
if((error_ = ec != 0))
break;
// VFALCO How to notify callers using
// the synchronous interface?
continue;
}
assert(rd_fh_.op == opcode::close);
{
detail::read(cr_, fb.data(), code);
if(code != close_code::none)
break;
if(! wr_close_)
{
auto cr = cr_;
if(cr.code == close_code::none)
cr.code = close_code::normal;
cr.reason = "";
fb.reset();
wr_close_ = true;
write_close<static_streambuf>(fb, cr);
boost::asio::write(stream_, fb.data(), ec);
if((error_ = ec != 0))
return;
}
break;
}
}
if(rd_need_ == 0 && ! rd_fh_.fin)
{
// empty frame
continue;
}
}
// read payload
auto smb = streambuf.prepare(
detail::clamp(rd_need_));
auto const bytes_transferred =
stream_.read_some(smb, ec);
if((error_ = ec != 0))
return;
rd_need_ -= bytes_transferred;
auto const pb = prepare_buffers(
bytes_transferred, smb);
if(rd_fh_.mask)
detail::mask_inplace(pb, rd_key_);
if(rd_opcode_ == opcode::text)
{
if(! rd_utf8_check_.write(pb) ||
(rd_need_ == 0 && rd_fh_.fin &&
! rd_utf8_check_.finish()))
{
code = close_code::bad_payload;
break;
}
}
streambuf.commit(bytes_transferred);
fi.op = rd_opcode_;
fi.fin = rd_fh_.fin && rd_need_ == 0;
return;
}
if(code != close_code::none)
{
// Fail the connection (per rfc6455)
if(! wr_close_)
{
wr_close_ = true;
detail::frame_streambuf fb;
write_close<static_streambuf>(fb, code);
boost::asio::write(stream_, fb.data(), ec);
if((error_ = ec != 0))
return;
}
wsproto_helpers::call_teardown(next_layer_, ec);
if((error_ = ec != 0))
return;
ec = error::failed;
error_ = true;
return;
}
if(! ec)
wsproto_helpers::call_teardown(next_layer_, ec);
if(! ec)
ec = error::closed;
error_ = ec != 0;
}
template<class NextLayer>
template<class Streambuf, class ReadHandler>
auto
stream<NextLayer>::async_read_frame(frame_info& fi,
Streambuf& streambuf, ReadHandler&& handler)
{
static_assert(beast::is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
beast::async_completion<
ReadHandler, void(error_code)> completion(handler);
read_frame_op<Streambuf, decltype(completion.handler)>{
completion.handler, *this, fi, streambuf};
return completion.result.get();
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::write(
ConstBufferSequence const& bs, error_code& ec)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_size;
consuming_buffers<ConstBufferSequence> cb(bs);
auto remain = buffer_size(cb);
for(;;)
{
auto const n =
detail::clamp(remain, wr_frag_size_);
remain -= n;
auto const fin = remain <= 0;
write_frame(fin, prepare_buffers(n, cb), ec);
cb.consume(n);
if(ec)
return;
if(fin)
break;
}
}
template<class NextLayer>
template<class ConstBufferSequence, class WriteHandler>
auto
stream<NextLayer>::async_write(
ConstBufferSequence const& bs, WriteHandler&& handler)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
beast::async_completion<
WriteHandler, void(error_code)> completion(handler);
write_op<ConstBufferSequence, decltype(completion.handler)>{
completion.handler, *this, bs};
return completion.result.get();
}
template<class NextLayer>
template<class ConstBufferSequence>
void
stream<NextLayer>::write_frame(bool fin,
ConstBufferSequence const& bs, error_code& ec)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
using boost::asio::mutable_buffers_1;
detail::frame_header fh;
fh.op = wr_cont_ ? opcode::cont : wr_opcode_;
wr_cont_ = ! fin;
fh.fin = fin;
fh.rsv1 = false;
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = buffer_size(bs);
if((fh.mask = (role_ == role_type::client)))
fh.key = maskgen_();
detail::fh_streambuf fh_buf;
detail::write<static_streambuf>(fh_buf, fh);
if(! fh.mask)
{
// send header and payload
boost::asio::write(stream_,
buffer_cat(fh_buf.data(), bs), ec);
error_ = ec != 0;
return;
}
detail::prepared_key_type key;
detail::prepare_key(key, fh.key);
auto const tmp_size = detail::clamp(
fh.len, wr_buf_size_);
std::unique_ptr<std::uint8_t[]> up(
new std::uint8_t[tmp_size]);
auto const tmp = up.get();
std::uint64_t remain = fh.len;
consuming_buffers<ConstBufferSequence> cb(bs);
{
auto const n =
detail::clamp(remain, tmp_size);
mutable_buffers_1 mb{tmp, n};
buffer_copy(mb, cb);
cb.consume(n);
remain -= n;
detail::mask_inplace(mb, key);
// send header and payload
boost::asio::write(stream_,
buffer_cat(fh_buf.data(), mb), ec);
if(ec)
{
error_ = ec != 0;
return;
}
}
while(remain > 0)
{
auto const n =
detail::clamp(remain, tmp_size);
mutable_buffers_1 mb{tmp, n};
buffer_copy(mb, cb);
cb.consume(n);
remain -= n;
detail::mask_inplace(mb, key);
// send payload
boost::asio::write(stream_, mb, ec);
if(ec)
{
error_ = ec != 0;
return;
}
}
}
template<class NextLayer>
template<class ConstBufferSequence, class WriteHandler>
auto
stream<NextLayer>::async_write_frame(bool fin,
ConstBufferSequence const& bs, WriteHandler&& handler)
{
static_assert(beast::is_ConstBufferSequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
beast::async_completion<
WriteHandler, void(error_code)
> completion(handler);
write_frame_op<ConstBufferSequence, decltype(
completion.handler)>{completion.handler,
*this, fin, bs};
return completion.result.get();
}
//------------------------------------------------------------------------------
template<class NextLayer>
http::request<http::empty_body>
stream<NextLayer>::build_request(boost::string_ref const& host,
boost::string_ref const& resource, std::string& key)
{
http::request<http::empty_body> req;
req.url = "/";
req.version = 11;
req.method = http::method_t::http_get;
req.headers.insert("Host", host);
req.headers.insert("Connection", "upgrade");
req.headers.insert("Upgrade", "websocket");
key = detail::make_sec_ws_key(maskgen_);
req.headers.insert("Sec-WebSocket-Key", key);
req.headers.insert("Sec-WebSocket-Version", "13");
(*d_)(req);
return req;
}
template<class NextLayer>
template<class Body, class Headers>
http::response<http::string_body>
stream<NextLayer>::build_response(
http::message<true, Body, Headers> const& req)
{
auto err =
[&](auto const& text)
{
http::response<http::string_body> resp(
{400, http::reason_string(400), req.version});
resp.body = text;
// VFALCO TODO respect keep-alive here
return resp;
};
if(req.version < 11)
return err("HTTP version 1.1 required");
if(req.method != http::method_t::http_get)
return err("Wrong method");
if(! is_upgrade(req))
return err("Expected Upgrade request");
if(! req.headers.exists("Host"))
return err("Missing Host");
if(! req.headers.exists("Sec-WebSocket-Key"))
return err("Missing Sec-WebSocket-Key");
{
auto const version =
req.headers["Sec-WebSocket-Version"];
if(version.empty())
return err("Missing Sec-WebSocket-Version");
if(version != "13")
return err("Unsupported Sec-WebSocket-Version");
}
if(! rfc2616::token_in_list(
req.headers["Upgrade"], "websocket"))
return err("Missing websocket Upgrade token");
http::response<http::string_body> resp(
{101, http::reason_string(101), req.version});
resp.headers.insert("Upgrade", "websocket");
resp.headers.insert("Connection", "upgrade");
{
auto const key =
req.headers["Sec-WebSocket-Key"];
resp.headers.insert("Sec-WebSocket-Key", key);
resp.headers.insert("Sec-WebSocket-Accept",
detail::make_sec_ws_accept(key));
}
resp.headers.replace("Server", "Beast.WSProto");
(*d_)(resp);
return resp;
}
template<class NextLayer>
template<class Body, class Headers>
void
stream<NextLayer>::do_response(
http::message<false, Body, Headers> const& resp,
boost::string_ref const& key, error_code& ec)
{
// VFALCO Review these error codes
auto fail = [&]{ ec = error::response_failed; };
if(resp.status != 101)
return fail();
if(! is_upgrade(resp))
return fail();
if(! rfc2616::ci_equal(
resp.headers["Upgrade"], "websocket"))
return fail();
if(! resp.headers.exists("Sec-WebSocket-Accept"))
return fail();
if(resp.headers["Sec-WebSocket-Accept"] !=
detail::make_sec_ws_accept(key))
return fail();
role_ = role_type::client;
}
template<class NextLayer>
void
stream<NextLayer>::do_read_fh(
detail::frame_streambuf& fb,
close_code& code, error_code& ec)
{
fb.commit(boost::asio::read(
stream_, fb.prepare(2), ec));
if(ec)
return;
auto const n = detail::read_fh1(
rd_fh_, fb, role_, code);
if(code != close_code::none)
return;
if(n > 0)
{
fb.commit(boost::asio::read(
stream_, fb.prepare(n), ec));
if(ec)
return;
}
detail::read_fh2(
rd_fh_, fb, role_, code);
if(code != close_code::none)
return;
prepare_fh(code);
}
} // websocket
} // beast
#endif
+158
View File
@@ -0,0 +1,158 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
#define BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
#include <beast/async_completion.hpp>
#include <beast/type_check.hpp>
namespace beast {
namespace websocket {
namespace detail {
/*
See
http://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket/32054476#32054476
Behavior of ssl::stream regarding close_
If the remote host calls async_shutdown then the
local host's async_read will complete with eof.
If both hosts call async_shutdown then the calls
to async_shutdown will complete with eof.
*/
template<class AsyncStream, class Handler>
class teardown_ssl_op
{
using stream_type =
boost::asio::ssl::stream<AsyncStream>;
struct data
{
stream_type& stream;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
stream_type& stream_)
: stream(stream_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
template<class DeducedHandler>
explicit
teardown_ssl_op(
DeducedHandler&& h,
stream_type& stream)
: d_(std::make_shared<data>(
std::forward<DeducedHandler>(h),
stream))
{
(*this)(error_code{}, false);
}
void
operator()(error_code ec, bool again = true);
friend
auto asio_handler_allocate(std::size_t size,
teardown_ssl_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(void* p,
std::size_t size, teardown_ssl_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(
teardown_ssl_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f,
teardown_ssl_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class AsyncStream, class Handler>
void
teardown_ssl_op<AsyncStream, Handler>::
operator()(error_code ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(!ec && d.state != 99)
{
switch(d.state)
{
case 0:
d.state = 99;
d.stream.async_shutdown(*this);
return;
}
}
d.h(ec);
}
} // detail
//------------------------------------------------------------------------------
template<class AsyncStream>
void
teardown(
boost::asio::ssl::stream<AsyncStream>& stream,
error_code& ec)
{
stream.shutdown(ec);
}
template<class AsyncStream, class TeardownHandler>
void
async_teardown(
boost::asio::ssl::stream<AsyncStream>& stream,
TeardownHandler&& handler)
{
static_assert(beast::is_Handler<
TeardownHandler, void(error_code)>::value,
"TeardownHandler requirements not met");
detail::teardown_ssl_op<AsyncStream, std::decay_t<
TeardownHandler>>{std::forward<TeardownHandler>(
handler), stream};
}
} // websocket
} // beast
#endif
+171
View File
@@ -0,0 +1,171 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
#define BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
#include <beast/async_completion.hpp>
#include <beast/type_check.hpp>
#include <memory>
namespace beast {
namespace websocket {
namespace detail {
template<class Handler>
class teardown_tcp_op
{
using socket_type =
boost::asio::ip::tcp::socket;
struct data
{
socket_type& socket;
Handler h;
char buf[8192];
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
socket_type& socket_)
: socket(socket_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
template<class DeducedHandler>
teardown_tcp_op(
DeducedHandler&& h,
socket_type& socket)
: d_(std::make_shared<data>(
std::forward<DeducedHandler>(h),
socket))
{
(*this)(error_code{}, 0, false);
}
void
operator()(
error_code ec, std::size_t, bool again = true);
friend
auto asio_handler_allocate(std::size_t size,
teardown_tcp_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(void* p,
std::size_t size, teardown_tcp_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(teardown_tcp_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f,
teardown_tcp_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Handler>
void
teardown_tcp_op<Handler>::
operator()(error_code ec, std::size_t, bool again)
{
using boost::asio::buffer;
auto& d = *d_;
d.cont = d.cont || again;
while(! ec)
{
switch(d.state)
{
case 0:
d.state = 1;
d.socket.shutdown(
boost::asio::ip::tcp::socket::shutdown_send, ec);
break;
case 1:
d.socket.async_read_some(
buffer(d.buf), std::move(*this));
return;
}
}
if(ec == boost::asio::error::eof)
{
d.socket.close(ec);
ec = error_code{};
}
d.h(ec);
}
} // detail
//------------------------------------------------------------------------------
inline
void
teardown(
boost::asio::ip::tcp::socket& socket,
error_code& ec)
{
using boost::asio::buffer;
socket.shutdown(
boost::asio::ip::tcp::socket::shutdown_send, ec);
while(! ec)
{
char buf[8192];
auto const n = socket.read_some(
buffer(buf), ec);
if(! n)
break;
}
if(ec == boost::asio::error::eof)
ec = error_code{};
socket.close(ec);
}
template<class TeardownHandler>
inline
void
async_teardown(
boost::asio::ip::tcp::socket& socket,
TeardownHandler&& handler)
{
static_assert(beast::is_Handler<
TeardownHandler, void(error_code)>::value,
"TeardownHandler requirements not met");
detail::teardown_tcp_op<std::decay_t<
TeardownHandler>>{std::forward<
TeardownHandler>(handler), socket};
}
} // websocket
} // beast
#endif
@@ -0,0 +1,265 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP
#define BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP
#include <beast/buffer_cat.hpp>
#include <beast/bind_handler.hpp>
#include <beast/consuming_buffers.hpp>
#include <beast/handler_alloc.hpp>
#include <beast/static_streambuf.hpp>
#include <beast/websocket/detail/frame.hpp>
#include <algorithm>
#include <cassert>
#include <memory>
namespace beast {
namespace websocket {
// write a frame
//
template<class NextLayer>
template<class Buffers, class Handler>
class stream<NextLayer>::write_frame_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data : op
{
stream<NextLayer>& ws;
consuming_buffers<Buffers> cb;
Handler h;
detail::frame_header fh;
detail::fh_streambuf fh_buf;
detail::prepared_key_type key;
void* tmp;
std::size_t tmp_size;
std::uint64_t remain;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
bool fin, Buffers const& bs)
: ws(ws_)
, cb(bs)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
fh.op = ws.wr_cont_ ?
opcode::cont : ws.wr_opcode_;
ws.wr_cont_ = ! fin;
fh.fin = fin;
fh.rsv1 = 0;
fh.rsv2 = 0;
fh.rsv3 = 0;
fh.len = boost::asio::buffer_size(cb);
if((fh.mask = (ws.role_ == role_type::client)))
{
fh.key = ws.maskgen_();
detail::prepare_key(key, fh.key);
tmp_size = detail::clamp(
fh.len, ws.wr_buf_size_);
tmp = boost_asio_handler_alloc_helpers::
allocate(tmp_size, h);
remain = fh.len;
}
else
{
tmp = nullptr;
}
detail::write<static_streambuf>(fh_buf, fh);
}
~data()
{
if(tmp)
boost_asio_handler_alloc_helpers::
deallocate(tmp, tmp_size, h);
}
};
std::shared_ptr<data> d_;
public:
write_frame_op(write_frame_op&&) = default;
write_frame_op(write_frame_op const&) = default;
template<class DeducedHandler, class... Args>
write_frame_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::make_shared<data>(
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0, false);
}
void operator()()
{
auto& d = *d_;
d.cont = false;
(*this)(error_code{}, 0, false);
}
void operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, write_frame_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, write_frame_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(write_frame_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, write_frame_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Buffers, class Handler>
void
stream<NextLayer>::
write_frame_op<Buffers, Handler>::
operator()(
error_code ec, std::size_t bytes_transferred, bool again)
{
using boost::asio::buffer_copy;
using boost::asio::mutable_buffers_1;
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
if(d.ws.wr_block_)
{
// suspend
d.state = 1;
d.ws.wr_op_.template emplace<
write_frame_op>(std::move(*this));
return;
}
if(d.ws.error_)
{
// call handler
d.state = 99;
d.ws.get_io_service().post(
bind_handler(std::move(*this),
boost::asio::error::operation_aborted, 0));
return;
}
assert(! d.ws.wr_close_);
d.state = 2;
break;
// resume
case 1:
if(d.ws.error_)
{
// call handler
d.state = 99;
ec = boost::asio::error::operation_aborted;
break;
}
d.state = 2;
break;
case 2:
{
if(! d.fh.mask)
{
// send header and payload
d.state = 99;
assert(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_,
buffer_cat(d.fh_buf.data(), d.cb),
std::move(*this));
return;
}
auto const n =
detail::clamp(d.remain, d.tmp_size);
mutable_buffers_1 mb{d.tmp, n};
buffer_copy(mb, d.cb);
d.cb.consume(n);
d.remain -= n;
detail::mask_inplace(mb, d.key);
// send header and payload
d.state = d.remain > 0 ? 3 : 99;
assert(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(d.ws.stream_,
buffer_cat(d.fh_buf.data(),
mb), std::move(*this));
return;
}
// sent masked payload
case 3:
{
auto const n =
detail::clamp(d.remain, d.tmp_size);
mutable_buffers_1 mb{d.tmp,
static_cast<std::size_t>(n)};
buffer_copy(mb, d.cb);
d.cb.consume(n);
d.remain -= n;
detail::mask_inplace(mb, d.key);
// send payload
if(d.remain == 0)
d.state = 99;
assert(! d.ws.wr_block_);
d.ws.wr_block_ = &d;
boost::asio::async_write(
d.ws.stream_, mb, std::move(*this));
return;
}
}
}
if(ec)
d.ws.error_ = true;
if(d.ws.wr_block_ == &d)
d.ws.wr_block_ = nullptr;
if(d.tmp)
{
boost_asio_handler_alloc_helpers::
deallocate(d.tmp, d.tmp_size, d.h);
d.tmp = nullptr;
}
d.h(ec);
d.ws.rd_op_.maybe_invoke();
}
} // websocket
} // beast
#endif
+137
View File
@@ -0,0 +1,137 @@
//
// Copyright (c) 2013-2016 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)
//
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP
#define BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP
#include <beast/consuming_buffers.hpp>
#include <beast/prepare_buffers.hpp>
#include <beast/handler_alloc.hpp>
#include <beast/websocket/detail/frame.hpp>
#include <algorithm>
#include <cassert>
#include <memory>
namespace beast {
namespace websocket {
// write a message
//
template<class NextLayer>
template<class Buffers, class Handler>
class stream<NextLayer>::write_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data : op
{
stream<NextLayer>& ws;
consuming_buffers<Buffers> cb;
Handler h;
std::size_t remain;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
stream<NextLayer>& ws_, Buffers const& bs)
: ws(ws_)
, cb(bs)
, h(std::forward<DeducedHandler>(h_))
, remain(boost::asio::buffer_size(cb))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
write_op(write_op&&) = default;
write_op(write_op const&) = default;
template<class DeducedHandler, class... Args>
explicit
write_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), ws,
std::forward<Args>(args)...))
{
(*this)(error_code{}, false);
}
void operator()(error_code ec, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(write_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, write_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer>
template<class Buffers, class Handler>
void
stream<NextLayer>::
write_op<Buffers, Handler>::
operator()(error_code ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
{
auto const n = std::min(
d.remain, d.ws.wr_frag_size_);
d.remain -= n;
auto const fin = d.remain <= 0;
if(fin)
d.state = 99;
d.ws.async_write_frame(fin,
prepare_buffers(n, d.cb), std::move(*this));
d.cb.consume(n);
return;
}
}
}
d.h(ec);
}
} // websocket
} // beast
#endif