Reorganize source files

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