2017-07-20 08:01:46 -07:00
|
|
|
//
|
|
|
|
|
// 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/detail/endian.hpp>
|
|
|
|
|
#include <beast/websocket/detail/utf8_checker.hpp>
|
2016-05-07 14:57:15 -04:00
|
|
|
#include <beast/core/consuming_buffers.hpp>
|
|
|
|
|
#include <beast/core/static_streambuf.hpp>
|
|
|
|
|
#include <beast/core/static_string.hpp>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <boost/asio/buffer.hpp>
|
2016-09-25 12:17:32 -04:00
|
|
|
#include <boost/assert.hpp>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <boost/endian/buffers.hpp>
|
|
|
|
|
#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
|
2016-04-30 13:00:33 -04:00
|
|
|
is_valid(close_code::value code)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2016-04-30 13:00:33 -04:00
|
|
|
auto const v = code;
|
2017-07-20 08:01:46 -07:00
|
|
|
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
|
2016-08-02 13:37:41 +02:00
|
|
|
if(v <= 999)
|
2017-07-20 08:01:46 -07:00
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
2016-05-28 09:23:54 -04:00
|
|
|
// Write frame header to dynamic buffer
|
2017-07-20 08:01:46 -07:00
|
|
|
//
|
2016-05-28 09:23:54 -04:00
|
|
|
template<class DynamicBuffer>
|
2017-07-20 08:01:46 -07:00
|
|
|
void
|
2016-05-28 09:23:54 -04:00
|
|
|
write(DynamicBuffer& db, frame_header const& fh)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
|
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);
|
2016-05-06 21:51:43 -04:00
|
|
|
if(fh.rsv1)
|
|
|
|
|
b[0] |= 0x40;
|
|
|
|
|
if(fh.rsv2)
|
|
|
|
|
b[0] |= 0x20;
|
|
|
|
|
if(fh.rsv3)
|
|
|
|
|
b[0] |= 0x10;
|
2017-07-20 08:01:46 -07:00
|
|
|
b[1] = fh.mask ? 0x80 : 0x00;
|
2016-08-26 08:01:44 -04:00
|
|
|
if(fh.len <= 125)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
|
b[1] |= fh.len;
|
|
|
|
|
n = 2;
|
|
|
|
|
}
|
2016-08-26 08:01:44 -04:00
|
|
|
else if(fh.len <= 65535)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2016-04-29 06:04:40 -04:00
|
|
|
native_to_little_uint32(fh.key, &b[n]);
|
2017-07-20 08:01:46 -07:00
|
|
|
n += 4;
|
|
|
|
|
}
|
2016-05-28 09:23:54 -04:00
|
|
|
db.commit(buffer_copy(
|
|
|
|
|
db.prepare(n), buffer(b)));
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read data from buffers
|
|
|
|
|
// This is for ping and pong payloads
|
|
|
|
|
//
|
|
|
|
|
template<class Buffers>
|
|
|
|
|
void
|
2016-05-15 16:22:25 -04:00
|
|
|
read(ping_data& data, Buffers const& bs)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
|
using boost::asio::buffer_copy;
|
|
|
|
|
using boost::asio::buffer_size;
|
|
|
|
|
using boost::asio::mutable_buffers_1;
|
2016-09-25 12:17:32 -04:00
|
|
|
BOOST_ASSERT(buffer_size(bs) <= data.max_size());
|
2017-07-20 08:01:46 -07:00
|
|
|
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,
|
2016-04-30 13:00:33 -04:00
|
|
|
Buffers const& bs, close_code::value& code)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
|
using boost::asio::buffer;
|
|
|
|
|
using boost::asio::buffer_copy;
|
|
|
|
|
using boost::asio::buffer_size;
|
|
|
|
|
using namespace boost::endian;
|
|
|
|
|
auto n = buffer_size(bs);
|
2016-09-25 12:17:32 -04:00
|
|
|
BOOST_ASSERT(n <= 125);
|
2017-07-20 08:01:46 -07:00
|
|
|
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);
|
2016-04-30 13:00:33 -04:00
|
|
|
cr.code = big_uint16_to_native(&b[0]);
|
2017-07-20 08:01:46 -07:00
|
|
|
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
|