forked from boostorg/beast
Reorganize source files
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WSPROTO_DEBUG_H_INCLUDED
|
||||
#define BEAST_WSPROTO_DEBUG_H_INCLUDED
|
||||
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
to_hex(boost::asio::const_buffer b)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
std::stringstream ss;
|
||||
auto p = buffer_cast<std::uint8_t const*>(b);
|
||||
auto n = buffer_size(b);
|
||||
while(n--)
|
||||
{
|
||||
ss <<
|
||||
std::setfill('0') <<
|
||||
std::setw(2) <<
|
||||
std::hex << int(*p++) << " ";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
std::string
|
||||
to_hex(Buffers const& bs)
|
||||
{
|
||||
std::string s;
|
||||
for(auto const& b : bs)
|
||||
s.append(to_hex(boost::asio::const_buffer(b)));
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
std::string
|
||||
buffers_to_string(Buffers const& bs)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
std::string s;
|
||||
s.reserve(buffer_size(bs));
|
||||
for(auto const& b : bs)
|
||||
s.append(buffer_cast<char const*>(b),
|
||||
buffer_size(b));
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
format(std::string s)
|
||||
{
|
||||
auto const w = 84;
|
||||
for(int n = w*(s.size()/w); n>0; n-=w)
|
||||
s.insert(n, 1, '\n');
|
||||
return s;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // wsproto
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// 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_DETAIL_DECORATOR_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
using request_type = http::request<http::empty_body>;
|
||||
|
||||
using response_type = http::response<http::string_body>;
|
||||
|
||||
struct abstract_decorator
|
||||
{
|
||||
virtual
|
||||
~abstract_decorator() = default;
|
||||
|
||||
virtual
|
||||
void
|
||||
operator()(request_type& req) = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
operator()(response_type& resp) = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class decorator : public abstract_decorator
|
||||
{
|
||||
T t_;
|
||||
|
||||
public:
|
||||
decorator() = default;
|
||||
|
||||
decorator(T&& t)
|
||||
: t_(std::move(t))
|
||||
{
|
||||
}
|
||||
|
||||
decorator(T const& t)
|
||||
: t_(t)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(request_type& req) override
|
||||
{
|
||||
t_(req);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(response_type& resp) override
|
||||
{
|
||||
t_(resp);
|
||||
}
|
||||
};
|
||||
|
||||
struct default_decorator
|
||||
{
|
||||
static
|
||||
char const*
|
||||
version()
|
||||
{
|
||||
return "Beast.WSProto/1.0";
|
||||
}
|
||||
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
operator()(http::message<true, Body, Headers>& req)
|
||||
{
|
||||
req.headers.replace("User-Agent", version());
|
||||
}
|
||||
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
operator()(http::message<false, Body, Headers>& resp)
|
||||
{
|
||||
resp.headers.replace("Server", version());
|
||||
}
|
||||
};
|
||||
|
||||
using decorator_type =
|
||||
std::unique_ptr<abstract_decorator>;
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// 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_DETAIL_ERROR_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_ERROR_HPP
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::websocket::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
class error_category : public boost::system::error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "wsproto";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
case error::closed: return "WebSocket connection closed normally";
|
||||
case error::failed: return "WebSocket connection failed due to a protocol violation";
|
||||
case error::handshake_failed: return "WebSocket Upgrade handshake failed";
|
||||
case error::keep_alive: return "WebSocket Upgrade handshake failed but connection is still open";
|
||||
|
||||
case error::response_malformed: return "malformed HTTP response";
|
||||
case error::response_failed: return "upgrade request failed";
|
||||
case error::response_denied: return "upgrade request denied";
|
||||
case error::request_malformed: return "malformed HTTP request";
|
||||
case error::request_invalid: return "upgrade request invalid";
|
||||
case error::request_denied: return "upgrade request denied";
|
||||
default:
|
||||
return "wsproto.error";
|
||||
}
|
||||
}
|
||||
|
||||
boost::system::error_condition
|
||||
default_error_condition(int ev) const noexcept override
|
||||
{
|
||||
return boost::system::error_condition(ev, *this);
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
boost::system::error_condition const& condition
|
||||
) const noexcept override
|
||||
{
|
||||
return condition.value() == ev &&
|
||||
&condition.category() == this;
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(error_code const& error, int ev) const noexcept override
|
||||
{
|
||||
return error.value() == ev &&
|
||||
&error.category() == this;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
boost::system::error_category const&
|
||||
get_error_category()
|
||||
{
|
||||
static detail::error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,363 @@
|
||||
//
|
||||
// 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_DETAIL_FRAME_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_FRAME_HPP
|
||||
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/static_string.hpp>
|
||||
#include <beast/websocket/detail/utf8_checker.hpp>
|
||||
#include <beast/consuming_buffers.hpp>
|
||||
#include <beast/static_streambuf.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Contents of a WebSocket frame header
|
||||
struct frame_header
|
||||
{
|
||||
opcode op;
|
||||
bool fin;
|
||||
bool mask;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
std::uint64_t len;
|
||||
std::uint32_t key;
|
||||
};
|
||||
|
||||
// holds the largest possible frame header
|
||||
using fh_streambuf =
|
||||
static_streambuf_n<14>;
|
||||
|
||||
// holds the largest possible control frame
|
||||
using frame_streambuf =
|
||||
static_streambuf_n< 2 + 8 + 4 + 125 >;
|
||||
|
||||
inline
|
||||
bool constexpr
|
||||
is_reserved(opcode op)
|
||||
{
|
||||
return
|
||||
(op >= opcode::rsv3 && op <= opcode::rsv7) ||
|
||||
(op >= opcode::crsvb && op <= opcode::crsvf);
|
||||
}
|
||||
|
||||
inline
|
||||
bool constexpr
|
||||
is_valid(opcode op)
|
||||
{
|
||||
return op <= opcode::crsvf;
|
||||
}
|
||||
|
||||
inline
|
||||
bool constexpr
|
||||
is_control(opcode op)
|
||||
{
|
||||
return op >= opcode::close;
|
||||
}
|
||||
|
||||
// Returns `true` if a close code is valid
|
||||
inline
|
||||
bool
|
||||
is_valid(close_code code)
|
||||
{
|
||||
auto const v = static_cast<
|
||||
std::uint16_t>(code);
|
||||
switch(v)
|
||||
{
|
||||
case 1000:
|
||||
case 1001:
|
||||
case 1002:
|
||||
case 1003:
|
||||
case 1007:
|
||||
case 1008:
|
||||
case 1009:
|
||||
case 1010:
|
||||
case 1011:
|
||||
case 1012:
|
||||
case 1013:
|
||||
return true;
|
||||
|
||||
// explicitly reserved
|
||||
case 1004:
|
||||
case 1005:
|
||||
case 1006:
|
||||
case 1014:
|
||||
case 1015:
|
||||
return false;
|
||||
}
|
||||
// reserved
|
||||
if(v >= 1016 && v <= 2999)
|
||||
return false;
|
||||
// not used
|
||||
if(v >= 0 && v <= 999)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Write frame header to streambuf
|
||||
//
|
||||
template<class Streambuf>
|
||||
void
|
||||
write(Streambuf& sb, frame_header const& fh)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using namespace boost::endian;
|
||||
std::size_t n;
|
||||
std::uint8_t b[14];
|
||||
b[0] = (fh.fin ? 0x80 : 0x00) | static_cast<std::uint8_t>(fh.op);
|
||||
b[1] = fh.mask ? 0x80 : 0x00;
|
||||
if (fh.len <= 125)
|
||||
{
|
||||
b[1] |= fh.len;
|
||||
n = 2;
|
||||
}
|
||||
else if (fh.len <= 65535)
|
||||
{
|
||||
b[1] |= 126;
|
||||
::new(&b[2]) big_uint16_buf_t{
|
||||
(std::uint16_t)fh.len};
|
||||
n = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
b[1] |= 127;
|
||||
::new(&b[2]) big_uint64_buf_t{fh.len};
|
||||
n = 10;
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
little_uint32_buf_t key(fh.key);
|
||||
std::copy(key.data(),
|
||||
key.data() + 4, &b[n]);
|
||||
n += 4;
|
||||
}
|
||||
sb.commit(buffer_copy(
|
||||
sb.prepare(n), buffer(b)));
|
||||
}
|
||||
|
||||
// Read fixed frame header
|
||||
// Requires at least 2 bytes
|
||||
//
|
||||
template<class Streambuf>
|
||||
std::size_t
|
||||
read_fh1(frame_header& fh, Streambuf& sb,
|
||||
role_type role, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
std::uint8_t b[2];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
std::size_t need;
|
||||
fh.len = b[1] & 0x7f;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126: need = 2; break;
|
||||
case 127: need = 8; break;
|
||||
default:
|
||||
need = 0;
|
||||
}
|
||||
if((fh.mask = (b[1] & 0x80) != 0))
|
||||
need += 4;
|
||||
fh.op = static_cast<opcode>(b[0] & 0x0f);
|
||||
fh.fin = (b[0] & 0x80) != 0;
|
||||
fh.rsv1 = (b[0] & 0x40) != 0;
|
||||
fh.rsv2 = (b[0] & 0x20) != 0;
|
||||
fh.rsv3 = (b[0] & 0x10) != 0;
|
||||
// invalid length for control message
|
||||
if(is_control(fh.op) && fh.len > 125)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// reserved bits not cleared
|
||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// reserved opcode
|
||||
if(is_reserved(fh.op))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// invalid opcode
|
||||
// (only in locally generated headers)
|
||||
if(! is_valid(fh.op))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// fragmented control message
|
||||
if(is_control(fh.op) && ! fh.fin)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// unmasked frame from client
|
||||
if(role == role_type::server && ! fh.mask)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// masked frame from server
|
||||
if(role == role_type::client && fh.mask)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
code = close_code::none;
|
||||
return need;
|
||||
}
|
||||
|
||||
// Decode variable frame header from stream
|
||||
//
|
||||
template<class Streambuf>
|
||||
void
|
||||
read_fh2(frame_header& fh, Streambuf& sb,
|
||||
role_type role, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using namespace boost::endian;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126:
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
fh.len = reinterpret_cast<
|
||||
big_uint16_buf_t const*>(&b[0])->value();
|
||||
// length not canonical
|
||||
if(fh.len < 126)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 127:
|
||||
{
|
||||
std::uint8_t b[8];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
fh.len = reinterpret_cast<
|
||||
big_uint64_buf_t const*>(&b[0])->value();
|
||||
// length not canonical
|
||||
if(fh.len < 65536)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
std::uint8_t b[4];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
fh.key = reinterpret_cast<
|
||||
little_uint32_buf_t const*>(&b[0])->value();
|
||||
}
|
||||
code = close_code::none;
|
||||
}
|
||||
|
||||
// Read data from buffers
|
||||
// This is for ping and pong payloads
|
||||
//
|
||||
template<class Buffers>
|
||||
void
|
||||
read(ping_payload_type& data,
|
||||
Buffers const& bs, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::mutable_buffers_1;
|
||||
assert(buffer_size(bs) <= data.max_size());
|
||||
data.resize(buffer_size(bs));
|
||||
buffer_copy(mutable_buffers_1{
|
||||
data.data(), data.size()}, bs);
|
||||
}
|
||||
|
||||
// Read close_reason, return true on success
|
||||
// This is for the close payload
|
||||
//
|
||||
template<class Buffers>
|
||||
void
|
||||
read(close_reason& cr,
|
||||
Buffers const& bs, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using namespace boost::endian;
|
||||
auto n = buffer_size(bs);
|
||||
assert(n <= 125);
|
||||
if(n == 0)
|
||||
{
|
||||
cr = close_reason{};
|
||||
code = close_code::none;
|
||||
return;
|
||||
}
|
||||
if(n == 1)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
consuming_buffers<Buffers> cb(bs);
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
buffer_copy(buffer(b), cb);
|
||||
cr.code = static_cast<close_code>(
|
||||
reinterpret_cast<
|
||||
big_uint16_buf_t const*>(&b[0])->value());
|
||||
cb.consume(2);
|
||||
n -= 2;
|
||||
if(! is_valid(cr.code))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(n > 0)
|
||||
{
|
||||
cr.reason.resize(n);
|
||||
buffer_copy(buffer(&cr.reason[0], n), cb);
|
||||
if(! detail::check_utf8(
|
||||
cr.reason.data(), cr.reason.size()))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cr.reason = "";
|
||||
}
|
||||
code = close_code::none;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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_DETAIL_HYBI13_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
|
||||
|
||||
#include <beast/detail/base64.hpp>
|
||||
#include <beast/detail/sha1.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class Gen>
|
||||
std::string
|
||||
make_sec_ws_key(Gen& g)
|
||||
{
|
||||
union U
|
||||
{
|
||||
std::array<std::uint32_t, 4> a4;
|
||||
std::array<std::uint8_t, 16> a16;
|
||||
};
|
||||
U u;
|
||||
for(int i = 0; i < 4; ++i)
|
||||
u.a4[i] = g();
|
||||
return beast::detail::base64_encode(u.a16.data(), u.a16.size());
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
make_sec_ws_accept(boost::string_ref const& key)
|
||||
{
|
||||
std::string s(key.data(), key.size());
|
||||
s += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
beast::detail::sha1_context ctx;
|
||||
beast::detail::init(ctx);
|
||||
beast::detail::update(ctx, s.data(), s.size());
|
||||
std::array<std::uint8_t,
|
||||
beast::detail::sha1_context::digest_size> digest;
|
||||
beast::detail::finish(ctx, digest.data());
|
||||
return beast::detail::base64_encode(
|
||||
digest.data(), digest.size());
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// 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_DETAIL_INVOKABLE_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_INVOKABLE_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// "Parks" a composed operation, to invoke later
|
||||
//
|
||||
class invokable
|
||||
{
|
||||
struct base
|
||||
{
|
||||
base() = default;
|
||||
base(base &&) = default;
|
||||
virtual ~base() = default;
|
||||
virtual void move(void* p) = 0;
|
||||
virtual void operator()() = 0;
|
||||
};
|
||||
|
||||
template<class F>
|
||||
struct holder : base
|
||||
{
|
||||
F f;
|
||||
|
||||
holder(holder&&) = default;
|
||||
|
||||
template<class U>
|
||||
explicit
|
||||
holder(U&& u)
|
||||
: f(std::forward<U>(u))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
move(void* p) override
|
||||
{
|
||||
::new(p) holder(std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
operator()() override
|
||||
{
|
||||
F f_(std::move(f));
|
||||
this->~holder();
|
||||
// invocation of f_() can
|
||||
// assign a new invokable.
|
||||
f_();
|
||||
}
|
||||
};
|
||||
|
||||
struct exemplar
|
||||
{
|
||||
std::shared_ptr<int> _;
|
||||
void operator()(){}
|
||||
};
|
||||
|
||||
using buf_type = std::uint8_t[
|
||||
sizeof(holder<exemplar>)];
|
||||
|
||||
bool b_ = false;
|
||||
alignas(holder<exemplar>) buf_type buf_;
|
||||
|
||||
public:
|
||||
#ifndef NDEBUG
|
||||
~invokable()
|
||||
{
|
||||
// Engaged invokables must be invoked before
|
||||
// destruction otherwise the io_service
|
||||
// invariants are broken w.r.t completions.
|
||||
assert(! b_);
|
||||
}
|
||||
#endif
|
||||
|
||||
invokable() = default;
|
||||
invokable(invokable const&) = delete;
|
||||
invokable& operator=(invokable const&) = delete;
|
||||
|
||||
invokable(invokable&& other)
|
||||
: b_(other.b_)
|
||||
{
|
||||
if(other.b_)
|
||||
{
|
||||
other.get().move(buf_);
|
||||
other.b_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
invokable&
|
||||
operator=(invokable&& other)
|
||||
{
|
||||
// Engaged invokables must be invoked before
|
||||
// assignment otherwise the io_service
|
||||
// invariants are broken w.r.t completions.
|
||||
assert(! b_);
|
||||
|
||||
if(other.b_)
|
||||
{
|
||||
b_ = true;
|
||||
other.get().move(buf_);
|
||||
other.b_ = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void
|
||||
emplace(F&& f);
|
||||
|
||||
void
|
||||
maybe_invoke()
|
||||
{
|
||||
if(b_)
|
||||
{
|
||||
b_ = false;
|
||||
get()();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
base&
|
||||
get()
|
||||
{
|
||||
return *reinterpret_cast<base*>(buf_);
|
||||
}
|
||||
};
|
||||
|
||||
template<class F>
|
||||
void
|
||||
invokable::emplace(F&& f)
|
||||
{
|
||||
static_assert(sizeof(buf_type) >= sizeof(holder<F>),
|
||||
"buffer too small");
|
||||
assert(! b_);
|
||||
::new(buf_) holder<F>(std::forward<F>(f));
|
||||
b_ = true;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,389 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WEBSOCKETDETAIL_MASKGEN_HPP
|
||||
#define BEAST_WEBSOCKETDETAIL_MASKGEN_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Pseudo-random source of mask keys
|
||||
//
|
||||
template<class = void>
|
||||
class maskgen_t
|
||||
{
|
||||
std::mt19937 g_;
|
||||
|
||||
public:
|
||||
using result_type = typename std::mt19937::result_type;
|
||||
|
||||
maskgen_t(maskgen_t const&) = delete;
|
||||
maskgen_t& operator=(maskgen_t const&) = delete;
|
||||
|
||||
maskgen_t();
|
||||
|
||||
result_type
|
||||
operator()() noexcept;
|
||||
|
||||
void
|
||||
rekey();
|
||||
};
|
||||
|
||||
template<class _>
|
||||
maskgen_t<_>::maskgen_t()
|
||||
{
|
||||
rekey();
|
||||
}
|
||||
|
||||
template<class _>
|
||||
auto
|
||||
maskgen_t<_>::operator()() noexcept ->
|
||||
result_type
|
||||
{
|
||||
for(;;)
|
||||
if(auto key = g_())
|
||||
return key;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
void
|
||||
maskgen_t<_>::rekey()
|
||||
{
|
||||
std::random_device rng;
|
||||
std::array<std::uint32_t, 32> e;
|
||||
for(auto& i : e)
|
||||
i = rng();
|
||||
std::seed_seq ss(e.begin(), e.end());
|
||||
g_.seed(ss);
|
||||
}
|
||||
|
||||
using maskgen = maskgen_t<>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//using prepared_key_type = std::size_t;
|
||||
using prepared_key_type = std::uint32_t;
|
||||
//using prepared_key_type = std::uint64_t;
|
||||
|
||||
inline
|
||||
void
|
||||
prepare_key(std::uint32_t& prepared, std::uint32_t key)
|
||||
{
|
||||
prepared = key;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
prepare_key(std::uint64_t& prepared, std::uint32_t key)
|
||||
{
|
||||
prepared =
|
||||
(static_cast<std::uint64_t>(key) << 32) | key;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
std::enable_if_t<std::is_integral<T>::value, T>
|
||||
rol(T t, unsigned n = 1)
|
||||
{
|
||||
auto constexpr bits =
|
||||
static_cast<unsigned>(
|
||||
sizeof(T) * CHAR_BIT);
|
||||
n &= bits-1;
|
||||
return static_cast<T>((t << n) |
|
||||
(static_cast<std::make_unsigned_t<T>>(t) >> (bits - n)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
std::enable_if_t<std::is_integral<T>::value, T>
|
||||
ror(T t, unsigned n = 1)
|
||||
{
|
||||
auto constexpr bits =
|
||||
static_cast<unsigned>(
|
||||
sizeof(T) * CHAR_BIT);
|
||||
n &= bits-1;
|
||||
return static_cast<T>((t << (bits - n)) |
|
||||
(static_cast<std::make_unsigned_t<T>>(t) >> n));
|
||||
}
|
||||
|
||||
// 32-bit Uuoptimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_safe(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*p ^= key ; ++p;
|
||||
*p ^= (key >> 8); ++p;
|
||||
*p ^= (key >>16); ++p;
|
||||
*p ^= (key >>24); ++p;
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-bit unoptimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_safe(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*p ^= key ; ++p;
|
||||
*p ^= (key >> 8); ++p;
|
||||
*p ^= (key >>16); ++p;
|
||||
*p ^= (key >>24); ++p;
|
||||
*p ^= (key >>32); ++p;
|
||||
*p ^= (key >>40); ++p;
|
||||
*p ^= (key >>48); ++p;
|
||||
*p ^= (key >>56); ++p;
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 7: p[6] ^= (key >>16);
|
||||
case 6: p[5] ^= (key >> 8);
|
||||
case 5: p[4] ^= key;
|
||||
case 4: p[3] ^= (key >>24);
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 32-bit optimized
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_32(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
auto m = reinterpret_cast<
|
||||
uintptr_t>(p) % sizeof(key);
|
||||
switch(m)
|
||||
{
|
||||
case 1: *p ^= key ; ++p; --n;
|
||||
case 2: *p ^= (key >> 8); ++p; --n;
|
||||
case 3: *p ^= (key >>16); ++p; --n;
|
||||
key = ror(key, m * 8);
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint32_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-bit optimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_64(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
auto m = reinterpret_cast<
|
||||
uintptr_t>(p) % sizeof(key);
|
||||
switch(m)
|
||||
{
|
||||
case 1: *p ^= key ; ++p; --n;
|
||||
case 2: *p ^= (key >> 8); ++p; --n;
|
||||
case 3: *p ^= (key >>16); ++p; --n;
|
||||
case 4: *p ^= (key >>24); ++p; --n;
|
||||
case 5: *p ^= (key >>32); ++p; --n;
|
||||
case 6: *p ^= (key >>40); ++p; --n;
|
||||
case 7: *p ^= (key >>48); ++p; --n;
|
||||
key = ror(key, m * 8);
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint64_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 32-bit x86 optimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_x86(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint32_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-bit amd64 optimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_amd(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint64_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 7: p[6] ^= (key >>16);
|
||||
case 6: p[5] ^= (key >> 8);
|
||||
case 5: p[4] ^= key;
|
||||
case 4: p[3] ^= (key >>24);
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
mask_inplace(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
mask_inplace_safe(b, key);
|
||||
//mask_inplace_32(b, key);
|
||||
//mask_inplace_x86(b, key);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
mask_inplace(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
mask_inplace_safe(b, key);
|
||||
//mask_inplace_64(b, key);
|
||||
//mask_inplace_amd(b, key);
|
||||
}
|
||||
|
||||
// Apply mask in place
|
||||
//
|
||||
template<class MutableBuffers, class KeyType>
|
||||
void
|
||||
mask_inplace(
|
||||
MutableBuffers const& bs, KeyType& key)
|
||||
{
|
||||
for(auto const& b : bs)
|
||||
mask_inplace(b, key);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// 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_DETAIL_STREAM_BASE_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_STREAM_BASE_HPP
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/detail/decorator.hpp>
|
||||
#include <beast/websocket/detail/frame.hpp>
|
||||
#include <beast/websocket/detail/invokable.hpp>
|
||||
#include <beast/websocket/detail/mask.hpp>
|
||||
#include <beast/websocket/detail/utf8_checker.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class String>
|
||||
inline
|
||||
void
|
||||
maybe_throw(error_code const& ec, String const&)
|
||||
{
|
||||
if(ec)
|
||||
throw boost::system::system_error{ec};
|
||||
}
|
||||
|
||||
template<class UInt>
|
||||
static
|
||||
std::size_t
|
||||
clamp(UInt x)
|
||||
{
|
||||
if(x >= std::numeric_limits<std::size_t>::max())
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
template<class UInt>
|
||||
static
|
||||
std::size_t
|
||||
clamp(UInt x, std::size_t limit)
|
||||
{
|
||||
if(x >= limit)
|
||||
return limit;
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct stream_base
|
||||
{
|
||||
protected:
|
||||
struct op {};
|
||||
|
||||
detail::maskgen maskgen_; // source of mask keys
|
||||
decorator_type d_; // adorns http messages
|
||||
bool keep_alive_ = false; // close on failed upgrade
|
||||
role_type role_; // server or client
|
||||
bool error_ = false; // non-zero ec was delivered
|
||||
|
||||
std::size_t rd_msg_max_ =
|
||||
16 * 1024 * 1024; // max message size
|
||||
detail::frame_header rd_fh_; // current frame header
|
||||
detail::prepared_key_type rd_key_; // prepared masking key
|
||||
detail::utf8_checker rd_utf8_check_;// for current text msg
|
||||
std::uint64_t rd_size_; // size of the current message so far
|
||||
std::uint64_t rd_need_ = 0; // bytes left in msg frame payload
|
||||
opcode rd_opcode_; // opcode of current msg
|
||||
bool rd_cont_ = false; // expecting a continuation frame
|
||||
bool rd_close_ = false; // got close frame
|
||||
op* rd_block_ = nullptr; // op currently reading
|
||||
|
||||
std::size_t
|
||||
wr_frag_size_ = 16 * 1024; // size of auto-fragments
|
||||
std::size_t wr_buf_size_ = 4096; // write buffer size
|
||||
opcode wr_opcode_ = opcode::text; // outgoing message type
|
||||
bool wr_close_ = false; // sent close frame
|
||||
bool wr_cont_ = false; // next write is continuation frame
|
||||
op* wr_block_ = nullptr; // op currenly writing
|
||||
|
||||
invokable rd_op_; // invoked after write completes
|
||||
invokable wr_op_; // invoked after read completes
|
||||
close_reason cr_; // set from received close frame
|
||||
|
||||
stream_base()
|
||||
: d_(std::make_unique<
|
||||
decorator<default_decorator>>())
|
||||
{
|
||||
}
|
||||
|
||||
stream_base(stream_base&&) = default;
|
||||
stream_base(stream_base const&) = delete;
|
||||
stream_base& operator=(stream_base&&) = default;
|
||||
stream_base& operator=(stream_base const&) = delete;
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
prepare_fh(close_code& code);
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_close(Streambuf& sb,
|
||||
close_reason const& rc);
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_ping(Streambuf& sb, opcode op,
|
||||
ping_payload_type const& data);
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// 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_DETAIL_UTF8_CHECKER_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <string> // DEPRECATED
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Code adapted from
|
||||
// http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
/*
|
||||
Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
*/
|
||||
template<class = void>
|
||||
class utf8_checker_t
|
||||
{
|
||||
// Table for the UTF8 decode state machine
|
||||
using lut_type = std::uint8_t[400];
|
||||
static
|
||||
lut_type const&
|
||||
lut()
|
||||
{
|
||||
// 400 elements
|
||||
static std::uint8_t constexpr tab[] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
|
||||
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
|
||||
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
|
||||
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
|
||||
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
|
||||
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
|
||||
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // s7..s8
|
||||
};
|
||||
return tab;
|
||||
}
|
||||
|
||||
std::uint32_t state_ = 0;
|
||||
std::uint32_t codepoint_ = 0;
|
||||
|
||||
public:
|
||||
utf8_checker_t() = default;
|
||||
utf8_checker_t(utf8_checker_t&&) = default;
|
||||
utf8_checker_t(utf8_checker_t const&) = default;
|
||||
utf8_checker_t& operator=(utf8_checker_t&&) = default;
|
||||
utf8_checker_t& operator=(utf8_checker_t const&) = default;
|
||||
|
||||
void
|
||||
reset();
|
||||
|
||||
// Returns `true` on success
|
||||
bool
|
||||
write(void const* buffer, std::size_t size);
|
||||
|
||||
// Returns `true` on success
|
||||
template<class BufferSequence>
|
||||
bool
|
||||
write(BufferSequence const& bs);
|
||||
|
||||
// Returns `true` on success
|
||||
bool
|
||||
finish();
|
||||
};
|
||||
|
||||
template<class _>
|
||||
void
|
||||
utf8_checker_t<_>::reset()
|
||||
{
|
||||
state_ = 0;
|
||||
codepoint_ = 0;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
bool
|
||||
utf8_checker_t<_>::write(void const* buffer, std::size_t size)
|
||||
{
|
||||
auto p = static_cast<std::uint8_t const*>(buffer);
|
||||
auto plut = &lut()[0];
|
||||
while(size)
|
||||
{
|
||||
auto const byte = *p;
|
||||
auto const type = plut[byte];
|
||||
if(state_)
|
||||
codepoint_ = (byte & 0x3fu) | (codepoint_ << 6);
|
||||
else
|
||||
codepoint_ = (0xff >> type) & byte;
|
||||
state_ = plut[256 + state_ * 16 + type];
|
||||
if(state_ == 1)
|
||||
{
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
++p;
|
||||
--size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
template<class BufferSequence>
|
||||
bool
|
||||
utf8_checker_t<_>::write(BufferSequence const& bs)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for (auto const& b : bs)
|
||||
if(! write(buffer_cast<void const*>(b),
|
||||
buffer_size(b)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
bool
|
||||
utf8_checker_t<_>::finish()
|
||||
{
|
||||
auto const success = state_ == 0;
|
||||
reset();
|
||||
return success;
|
||||
}
|
||||
|
||||
using utf8_checker = utf8_checker_t<>;
|
||||
|
||||
template<class = void>
|
||||
bool
|
||||
check_utf8(char const* p, std::size_t n)
|
||||
{
|
||||
utf8_checker c;
|
||||
if(! c.write(p, n))
|
||||
return false;
|
||||
return c.finish();
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// 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_ERROR_HPP
|
||||
#define BEAST_WEBSOCKET_ERROR_HPP
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
using error_code = boost::system::error_code;
|
||||
|
||||
/// Error values
|
||||
enum class error
|
||||
{
|
||||
/// Both sides performed a WebSocket close
|
||||
closed = 1,
|
||||
|
||||
/// WebSocket connection failed, protocol violation
|
||||
failed,
|
||||
|
||||
/// Upgrade request failed, connection is closed
|
||||
handshake_failed,
|
||||
|
||||
/// Upgrade request failed, but connection is still open
|
||||
keep_alive,
|
||||
|
||||
/// HTTP response is malformed
|
||||
response_malformed,
|
||||
|
||||
/// HTTP response failed the upgrade
|
||||
response_failed,
|
||||
|
||||
/// Upgrade request denied for invalid fields.
|
||||
response_denied,
|
||||
|
||||
/// Upgrade request is malformed
|
||||
request_malformed,
|
||||
|
||||
/// Upgrade request fields incorrect
|
||||
request_invalid,
|
||||
|
||||
/// Upgrade request denied
|
||||
request_denied
|
||||
};
|
||||
|
||||
error_code
|
||||
make_error_code(error e);
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#include <beast/websocket/impl/error.ipp>
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,293 @@
|
||||
//
|
||||
// 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_OPTION_HPP
|
||||
#define BEAST_WEBSOCKET_OPTION_HPP
|
||||
|
||||
#include <beast/websocket/detail/stream_base.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/** Automatic fragmentation size option.
|
||||
|
||||
Sets the maximum size of fragments generated when sending
|
||||
messages on a WebSocket socket.
|
||||
|
||||
When the automatic fragmentation size is non-zero, messages
|
||||
exceeding the size will be split into multiple frames no
|
||||
larger than the size. This setting does not affect frames
|
||||
send explicitly using `write_frame` or `async_write_frame`.
|
||||
|
||||
The default setting is to fragment messages into 16KB frames.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the automatic fragmentation size option:
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> stream(ios);
|
||||
stream.set_option(auto_fragment_size{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using auto_fragment_size = implementation_defined;
|
||||
#else
|
||||
struct auto_fragment_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
auto_fragment_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** HTTP decorator option.
|
||||
|
||||
The decorator transforms the HTTP requests and responses used
|
||||
when requesting or responding to the WebSocket Upgrade. This may
|
||||
be used to set or change header fields. For example to set the
|
||||
Server or User-Agent fields. The default setting applies no
|
||||
transformation to the HTTP message.
|
||||
|
||||
For synchronous operations, the implementation will call the
|
||||
decorator before the function call to perform the operation
|
||||
returns.
|
||||
|
||||
For asynchronous operations, the implementation guarantees that
|
||||
calls to the decorator will be made from the same implicit or
|
||||
explicit strand used to call the asynchronous initiation
|
||||
function.
|
||||
|
||||
The default setting is no decorator.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the decorator.
|
||||
@code
|
||||
struct identity
|
||||
{
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
operator()(http::message<isRequest, Body, Headers>& m)
|
||||
{
|
||||
if(isRequest)
|
||||
m.headers.replace("User-Agent", "MyClient");
|
||||
else
|
||||
m.headers.replace("Server", "MyServer");
|
||||
}
|
||||
};
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(decorate(identity{}));
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using decorate = implementation_defined;
|
||||
#else
|
||||
template<class Decorator>
|
||||
inline
|
||||
auto
|
||||
decorate(Decorator&& d)
|
||||
{
|
||||
return std::make_unique<detail::decorator<
|
||||
std::decay_t<Decorator>>>(
|
||||
std::forward<Decorator>(d));
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Keep-alive option.
|
||||
|
||||
Determines if the connection is closed after a failed upgrade
|
||||
request.
|
||||
|
||||
This setting only affects the behavior of HTTP requests that
|
||||
implicitly or explicitly ask for a keepalive. For HTTP requests
|
||||
that indicate the connection should be closed, the connection is
|
||||
closed as per rfc2616.
|
||||
|
||||
The default setting is to close connections after a failed
|
||||
upgrade request.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the keep alive option.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(keep_alive{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using keep_alive = implementation_defined;
|
||||
#else
|
||||
struct keep_alive
|
||||
{
|
||||
bool value;
|
||||
|
||||
keep_alive(bool v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Message type option.
|
||||
|
||||
This controls the opcode set for outgoing messages. Valid
|
||||
choices are opcode::binary or opcode::text. The setting is
|
||||
only applied at the start when a caller begins a new message.
|
||||
Changing the opcode after a message is started will only
|
||||
take effect after the current message being sent is complete.
|
||||
|
||||
The default setting is opcode::text.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the message type to binary.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(message_type{opcode::binary});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using message_type = implementation_defined;
|
||||
#else
|
||||
struct message_type
|
||||
{
|
||||
opcode value;
|
||||
|
||||
explicit
|
||||
message_type(opcode op)
|
||||
{
|
||||
if(op != opcode::binary && op != opcode::text)
|
||||
throw std::domain_error("bad opcode");
|
||||
value = op;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Read buffer size option.
|
||||
|
||||
Sets the number of bytes allocated to the socket's read buffer.
|
||||
If this is zero, then reads are not buffered. Setting this
|
||||
higher can improve performance when expecting to receive
|
||||
many small frames.
|
||||
|
||||
The default is no buffering.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the read buffer size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(read_buffer_size{16 * 1024});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using read_buffer_size = implementation_defined;
|
||||
#else
|
||||
struct read_buffer_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
read_buffer_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Maximum incoming message size option.
|
||||
|
||||
Sets the largest permissible incoming message size. Message
|
||||
frame headers indicating a size that would bring the total
|
||||
message size over this limit will cause a protocol failure.
|
||||
|
||||
The default setting is 16 megabytes.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the maximum read message size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(read_message_max{65536});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using read_message_max = implementation_defined;
|
||||
#else
|
||||
struct read_message_max
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
read_message_max(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Write buffer size option.
|
||||
|
||||
Sets the number of bytes allocated to the socket's write buffer.
|
||||
This buffer is used to hold masked frame payload data. Lowering
|
||||
the size of the buffer can decrease the memory requirements for
|
||||
each connection, at the cost of an increased number of calls to
|
||||
perform socket writes.
|
||||
|
||||
This setting does not affect connections operating in the server
|
||||
role, since servers do not apply a masking key to frame payloads.
|
||||
|
||||
The default setting is 4096. The minimum value is 1024.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the write buffer size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(write_buffer_size{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using write_buffer_size = implementation_defined;
|
||||
#else
|
||||
struct write_buffer_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
write_buffer_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// 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_RFC6455_HPP
|
||||
#define BEAST_WEBSOCKET_RFC6455_HPP
|
||||
|
||||
#include <beast/websocket/static_string.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/** WebSocket frame header opcodes. */
|
||||
enum class opcode : std::uint8_t
|
||||
{
|
||||
cont = 0,
|
||||
text = 1,
|
||||
binary = 2,
|
||||
rsv3 = 3,
|
||||
rsv4 = 4,
|
||||
rsv5 = 5,
|
||||
rsv6 = 6,
|
||||
rsv7 = 7,
|
||||
close = 8,
|
||||
ping = 9,
|
||||
pong = 10,
|
||||
crsvb = 11,
|
||||
crsvc = 12,
|
||||
crsvd = 13,
|
||||
crsve = 14,
|
||||
crsvf = 15
|
||||
};
|
||||
|
||||
/** Close status codes.
|
||||
|
||||
These codes accompany close frames.
|
||||
|
||||
@see RFC 6455 7.4.1 Defined Status Codes
|
||||
https://tools.ietf.org/html/rfc6455#section-7.4.1
|
||||
*/
|
||||
enum class close_code : std::uint16_t
|
||||
{
|
||||
// used internally to mean "no error"
|
||||
none = 0,
|
||||
|
||||
normal = 1000,
|
||||
going_away = 1001,
|
||||
protocol_error = 1002,
|
||||
unknown_data = 1003,
|
||||
bad_payload = 1007,
|
||||
policy_error = 1008,
|
||||
too_big = 1009,
|
||||
needs_extension = 1010,
|
||||
internal_error = 1011,
|
||||
|
||||
service_restart = 1012,
|
||||
try_again_later = 1013,
|
||||
|
||||
reserved1 = 1004,
|
||||
no_status = 1005, // illegal on wire
|
||||
abnormal = 1006, // illegal on wire
|
||||
reserved2 = 1015,
|
||||
|
||||
last = 5000 // satisfy warnings
|
||||
};
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
|
||||
using reason_string_type =
|
||||
static_string<123, char>;
|
||||
|
||||
/// Payload type for pings and pongs
|
||||
using ping_payload_type =
|
||||
static_string<125, char>;
|
||||
|
||||
#endif
|
||||
|
||||
/** Description of the close reason.
|
||||
|
||||
This object stores the close code (if any) and the optional
|
||||
utf-8 encoded implementation defined reason string.
|
||||
*/
|
||||
struct close_reason
|
||||
{
|
||||
/// The close code.
|
||||
close_code code = close_code::none;
|
||||
|
||||
/// The optional utf8-encoded reason string.
|
||||
reason_string_type reason;
|
||||
|
||||
/** Default constructor.
|
||||
|
||||
The code will be none. Default constructed objects
|
||||
will explicitly convert to bool as `false`.
|
||||
*/
|
||||
close_reason() = default;
|
||||
|
||||
/// Construct from a code.
|
||||
close_reason(close_code code_)
|
||||
: code(code_)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct from a reason. code is close_code::normal.
|
||||
template<class CharT>
|
||||
close_reason(CharT const* reason_)
|
||||
: code(close_code::normal)
|
||||
, reason(reason_)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct from a code and reason.
|
||||
template<class CharT>
|
||||
close_reason(close_code code_,
|
||||
CharT const* reason_)
|
||||
: code(code_)
|
||||
, reason(reason_)
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns `true` if a code was specified
|
||||
operator bool() const
|
||||
{
|
||||
return code != close_code::none;
|
||||
}
|
||||
};
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
|
||||
/// Identifies the role of a WebSockets stream.
|
||||
enum class role_type
|
||||
{
|
||||
/// Stream is operating as a client.
|
||||
client,
|
||||
|
||||
/// Stream is operating as a server.
|
||||
server
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// 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_SSL_HPP
|
||||
#define BEAST_WEBSOCKET_SSL_HPP
|
||||
|
||||
#include <beast/websocket/teardown.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/** Tear down a `boost::asio::ssl::stream`.
|
||||
|
||||
This tears down a connection. The implementation will call
|
||||
the overload of this function based on the `Stream` parameter
|
||||
used to consruct the socket. When `Stream` is a user defined
|
||||
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||
`boost::asio::ssl::stream`, callers are responsible for
|
||||
providing a suitable overload of this function.
|
||||
|
||||
@param socket The stream to tear down.
|
||||
|
||||
@param ec Set to the error if any occurred.
|
||||
*/
|
||||
template<class AsyncStream>
|
||||
void
|
||||
teardown(
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
error_code& ec);
|
||||
|
||||
/** Start tearing down a `boost::asio::ssl::stream`.
|
||||
|
||||
This begins tearing down a connection asynchronously.
|
||||
The implementation will call the overload of this function
|
||||
based on the `Stream` parameter used to consruct the socket.
|
||||
When `Stream` is a user defined type, and not a
|
||||
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||
callers are responsible for providing a suitable overload
|
||||
of this function.
|
||||
|
||||
@param stream The stream to tear down.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
|
||||
*/
|
||||
template<class AsyncStream, class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
async_teardown(
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
TeardownHandler&& handler);
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#include <beast/websocket/impl/ssl.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,337 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP
|
||||
#define BEAST_WEBSOCKET_STATIC_STRING_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/** A string with a fixed-size storage area.
|
||||
|
||||
`static_string` objects behave like `std::string` except that
|
||||
the storage is not dynamically allocated but rather fixed in
|
||||
size.
|
||||
|
||||
These strings offer performance advantages when a protocol
|
||||
imposes a natural small upper limit on the size of a value.
|
||||
*/
|
||||
template<std::size_t N, class CharT,
|
||||
class Traits = std::char_traits<CharT>>
|
||||
class static_string
|
||||
{
|
||||
std::size_t n_;
|
||||
std::array<CharT, N+1> s_;
|
||||
|
||||
public:
|
||||
using traits_type = Traits;
|
||||
using value_type = typename Traits::char_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
using const_pointer = value_type const*;
|
||||
using const_reference = value_type const&;
|
||||
using iterator = value_type*;
|
||||
using const_iterator = value_type const*;
|
||||
using reverse_iterator =
|
||||
std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator =
|
||||
std::reverse_iterator<const_iterator>;
|
||||
|
||||
static_string()
|
||||
{
|
||||
resize(0);
|
||||
}
|
||||
|
||||
static_string(static_string const& s)
|
||||
: n_(s.n_)
|
||||
{
|
||||
std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]);
|
||||
}
|
||||
|
||||
static_string&
|
||||
operator=(static_string const& s)
|
||||
{
|
||||
n_ = s.n_;
|
||||
std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static_string(CharT const* s)
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
static_string& operator=(CharT const* s)
|
||||
{
|
||||
assign(s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference
|
||||
at(size_type pos)
|
||||
{
|
||||
if(pos >= n_)
|
||||
throw std::out_of_range("static_string::at");
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
const_reference
|
||||
at(size_type pos) const
|
||||
{
|
||||
if(pos >= n_)
|
||||
throw std::out_of_range("static_string::at");
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
reference
|
||||
operator[](size_type pos);
|
||||
|
||||
const_reference
|
||||
operator[](size_type pos) const;
|
||||
|
||||
CharT&
|
||||
front()
|
||||
{
|
||||
return s_[0];
|
||||
}
|
||||
|
||||
CharT const&
|
||||
front() const
|
||||
{
|
||||
return s_[0];
|
||||
}
|
||||
|
||||
CharT&
|
||||
back()
|
||||
{
|
||||
return s_[n_-1];
|
||||
}
|
||||
|
||||
CharT const&
|
||||
back() const
|
||||
{
|
||||
return s_[n_-1];
|
||||
}
|
||||
|
||||
CharT*
|
||||
data()
|
||||
{
|
||||
return &s_[0];
|
||||
}
|
||||
|
||||
CharT const*
|
||||
data() const
|
||||
{
|
||||
return &s_[0];
|
||||
}
|
||||
|
||||
CharT const*
|
||||
c_str() const
|
||||
{
|
||||
s_[n_] = 0;
|
||||
return &s_[0];
|
||||
}
|
||||
|
||||
iterator
|
||||
begin()
|
||||
{
|
||||
return &s_[0];
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return &s_[0];
|
||||
}
|
||||
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return &s_[0];
|
||||
}
|
||||
|
||||
iterator
|
||||
end()
|
||||
{
|
||||
return &s_[n_];
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return &s_[n_];
|
||||
}
|
||||
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return &s_[n_];
|
||||
}
|
||||
|
||||
reverse_iterator
|
||||
rbegin()
|
||||
{
|
||||
return reverse_iterator{end()};
|
||||
}
|
||||
|
||||
const_reverse_iterator
|
||||
rbegin() const
|
||||
{
|
||||
return reverse_iterator{end()};
|
||||
}
|
||||
|
||||
const_reverse_iterator
|
||||
crbegin() const
|
||||
{
|
||||
return reverse_iterator{cend()};
|
||||
}
|
||||
|
||||
reverse_iterator
|
||||
rend()
|
||||
{
|
||||
return reverse_iterator{begin()};
|
||||
}
|
||||
|
||||
const_reverse_iterator
|
||||
rend() const
|
||||
{
|
||||
return reverse_iterator{begin()};
|
||||
}
|
||||
|
||||
const_reverse_iterator
|
||||
crend() const
|
||||
{
|
||||
return reverse_iterator{cbegin()};
|
||||
}
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return n_ == 0;
|
||||
}
|
||||
|
||||
size_type
|
||||
size() const
|
||||
{
|
||||
return n_;
|
||||
}
|
||||
|
||||
size_type constexpr
|
||||
max_size() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
size_type
|
||||
capacity() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
void
|
||||
shrink_to_fit()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void
|
||||
clear()
|
||||
{
|
||||
resize(0);
|
||||
}
|
||||
|
||||
// Does not perform value-initialization
|
||||
void
|
||||
resize(std::size_t n);
|
||||
|
||||
std::basic_string<CharT, Traits>
|
||||
to_string() const
|
||||
{
|
||||
return std::basic_string<
|
||||
CharT, Traits>{&s_[0], n_};
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
assign(CharT const* s);
|
||||
};
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
operator[](size_type pos) ->
|
||||
reference
|
||||
{
|
||||
static CharT null{0};
|
||||
if(pos == n_)
|
||||
return null;
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
operator[](size_type pos) const ->
|
||||
const_reference
|
||||
{
|
||||
static CharT constexpr null{0};
|
||||
if(pos == n_)
|
||||
return null;
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
resize(std::size_t n)
|
||||
{
|
||||
if(n > N)
|
||||
throw std::length_error(
|
||||
"static_string overflow");
|
||||
n_ = n;
|
||||
s_[n_] = 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(CharT const* s)
|
||||
{
|
||||
size_type n = 0;
|
||||
for(auto p = s; *p; ++p)
|
||||
++n;
|
||||
if(n > N)
|
||||
throw std::out_of_range(
|
||||
"too large");
|
||||
std::copy(s, s+n, s_.begin());
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,161 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_TEARDOWN_HPP
|
||||
#define BEAST_WEBSOCKET_TEARDOWN_HPP
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/** Tear down a connection.
|
||||
|
||||
This tears down a connection. The implementation will call
|
||||
the overload of this function based on the `Stream` parameter
|
||||
used to consruct the socket. When `Stream` is a user defined
|
||||
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||
`boost::asio::ssl::stream`, callers are responsible for
|
||||
providing a suitable overload of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param ec Set to the error if any occurred.
|
||||
*/
|
||||
template<class Socket>
|
||||
void
|
||||
teardown(Socket& socket, error_code& ec) = delete;
|
||||
|
||||
/** Start tearing down a connection.
|
||||
|
||||
This begins tearing down a connection asynchronously.
|
||||
The implementation will call the overload of this function
|
||||
based on the `Stream` parameter used to consruct the socket.
|
||||
When `Stream` is a user defined type, and not a
|
||||
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||
callers are responsible for providing a suitable overload
|
||||
of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
|
||||
*/
|
||||
template<class AsyncSocket, class TeardownHandler>
|
||||
void
|
||||
async_teardown(AsyncSocket& socket, TeardownHandler&& handler) = delete;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Tear down a `boost::asio::ip::tcp::socket`.
|
||||
|
||||
This tears down a connection. The implementation will call
|
||||
the overload of this function based on the `Stream` parameter
|
||||
used to consruct the socket. When `Stream` is a user defined
|
||||
type, and not a `boost::asio::ip::tcp::socket` or any
|
||||
`boost::asio::ssl::stream`, callers are responsible for
|
||||
providing a suitable overload of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param ec Set to the error if any occurred.
|
||||
*/
|
||||
void
|
||||
teardown(
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
error_code& ec);
|
||||
|
||||
/** Start tearing down a `boost::asio::ip::tcp::socket`.
|
||||
|
||||
This begins tearing down a connection asynchronously.
|
||||
The implementation will call the overload of this function
|
||||
based on the `Stream` parameter used to consruct the socket.
|
||||
When `Stream` is a user defined type, and not a
|
||||
`boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
|
||||
callers are responsible for providing a suitable overload
|
||||
of this function.
|
||||
|
||||
@param socket The socket to tear down.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
|
||||
*/
|
||||
template<class TeardownHandler>
|
||||
void
|
||||
async_teardown(
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
TeardownHandler&& handler);
|
||||
|
||||
} // websocket
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace wsproto_helpers {
|
||||
|
||||
// Calls to teardown and async_teardown must be made from
|
||||
// a namespace that does not contain any overloads of these
|
||||
// functions. The wsproto_helpers namespace is defined here
|
||||
// for that purpose.
|
||||
|
||||
template<class Socket>
|
||||
inline
|
||||
void
|
||||
call_teardown(Socket& socket, websocket::error_code& ec)
|
||||
{
|
||||
using websocket::teardown;
|
||||
teardown(socket, ec);
|
||||
}
|
||||
|
||||
template<class Socket, class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
call_async_teardown(Socket& socket, TeardownHandler&& handler)
|
||||
{
|
||||
using websocket::async_teardown;
|
||||
async_teardown(socket,
|
||||
std::forward<TeardownHandler>(handler));
|
||||
}
|
||||
|
||||
} // websocket_helpers
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/websocket/impl/teardown.ipp>
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user