mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 14:24:31 +02:00
Enable more split compilation in websocket and http
Signed-off-by: Damian Jarek <damian.jarek93@gmail.com>
This commit is contained in:
committed by
Vinnie Falco
parent
d2041c0322
commit
dc52df351a
@@ -86,12 +86,12 @@ private:
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
std::string
|
||||
unquote(string_view sr);
|
||||
|
||||
template<class = void>
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
increment();
|
||||
};
|
||||
@@ -132,46 +132,6 @@ cend() const ->
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class>
|
||||
std::string
|
||||
param_list::const_iterator::
|
||||
unquote(string_view sr)
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(sr.size());
|
||||
auto it = sr.begin() + 1;
|
||||
auto end = sr.end() - 1;
|
||||
while(it != end)
|
||||
{
|
||||
if(*it == '\\')
|
||||
++it;
|
||||
s.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
param_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
s_.clear();
|
||||
pi_.increment();
|
||||
if(pi_.empty())
|
||||
{
|
||||
pi_.it = pi_.last;
|
||||
pi_.first = pi_.last;
|
||||
}
|
||||
else if(! pi_.v.second.empty() &&
|
||||
pi_.v.second.front() == '"')
|
||||
{
|
||||
s_ = unquote(pi_.v.second);
|
||||
pi_.v.second = string_view{
|
||||
s_.data(), s_.size()};
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ext_list::const_iterator
|
||||
@@ -243,7 +203,7 @@ private:
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
increment();
|
||||
};
|
||||
@@ -305,74 +265,6 @@ exists(T const& s)
|
||||
return find(s) != end();
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
ext_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS "=" OWS ( token / quoted-string )
|
||||
|
||||
chunked;a=b;i=j;gzip;windowBits=12
|
||||
x,y
|
||||
,,,,,chameleon
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_.first = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_.first = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
if (it_ == last_)
|
||||
return;
|
||||
detail::param_iter pi;
|
||||
pi.it = it_;
|
||||
pi.first = it_;
|
||||
pi.last = last_;
|
||||
for(;;)
|
||||
{
|
||||
pi.increment();
|
||||
if(pi.empty())
|
||||
break;
|
||||
}
|
||||
v_.second = param_list{string_view{&*it_,
|
||||
static_cast<std::size_t>(pi.it - it_)}};
|
||||
it_ = pi.it;
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -445,7 +337,7 @@ private:
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
increment();
|
||||
};
|
||||
@@ -486,53 +378,6 @@ cend() const ->
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
token_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_ = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_ = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool
|
||||
token_list::
|
||||
@@ -570,5 +415,9 @@ validate_list(detail::basic_parsed_list<
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/rfc7230.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
176
include/boost/beast/http/impl/rfc7230.ipp
Normal file
176
include/boost/beast/http/impl/rfc7230.ipp
Normal file
@@ -0,0 +1,176 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
|
||||
std::string
|
||||
param_list::const_iterator::
|
||||
unquote(string_view sr)
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(sr.size());
|
||||
auto it = sr.begin() + 1;
|
||||
auto end = sr.end() - 1;
|
||||
while(it != end)
|
||||
{
|
||||
if(*it == '\\')
|
||||
++it;
|
||||
s.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
param_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
s_.clear();
|
||||
pi_.increment();
|
||||
if(pi_.empty())
|
||||
{
|
||||
pi_.it = pi_.last;
|
||||
pi_.first = pi_.last;
|
||||
}
|
||||
else if(! pi_.v.second.empty() &&
|
||||
pi_.v.second.front() == '"')
|
||||
{
|
||||
s_ = unquote(pi_.v.second);
|
||||
pi_.v.second = string_view{
|
||||
s_.data(), s_.size()};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ext_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS "=" OWS ( token / quoted-string )
|
||||
|
||||
chunked;a=b;i=j;gzip;windowBits=12
|
||||
x,y
|
||||
,,,,,chameleon
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_.first = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_.first = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
if (it_ == last_)
|
||||
return;
|
||||
detail::param_iter pi;
|
||||
pi.it = it_;
|
||||
pi.first = it_;
|
||||
pi.last = last_;
|
||||
for(;;)
|
||||
{
|
||||
pi.increment();
|
||||
if(pi.empty())
|
||||
break;
|
||||
}
|
||||
v_.second = param_list{string_view{&*it_,
|
||||
static_cast<std::size_t>(pi.it - it_)}};
|
||||
it_ = pi.it;
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
token_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_ = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_ = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
@@ -43,11 +43,15 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined.
|
||||
#include <boost/beast/http/impl/basic_parser.ipp>
|
||||
#include <boost/beast/http/impl/error.ipp>
|
||||
#include <boost/beast/http/impl/field.ipp>
|
||||
#include <boost/beast/http/impl/rfc7230.ipp>
|
||||
#include <boost/beast/http/impl/status.ipp>
|
||||
#include <boost/beast/http/impl/verb.ipp>
|
||||
|
||||
#include <boost/beast/websocket/detail/hybi13.ipp>
|
||||
#include <boost/beast/websocket/detail/pmd_extension.ipp>
|
||||
#include <boost/beast/websocket/detail/prng.ipp>
|
||||
#include <boost/beast/websocket/detail/service.ipp>
|
||||
#include <boost/beast/websocket/detail/utf8_checker.ipp>
|
||||
#include <boost/beast/websocket/impl/error.ipp>
|
||||
|
||||
#include <boost/beast/zlib/detail/deflate_stream.ipp>
|
||||
|
@@ -13,13 +13,7 @@
|
||||
#include <boost/beast/core/static_string.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/base64.hpp>
|
||||
#include <boost/beast/core/detail/sha1.hpp>
|
||||
#include <boost/beast/websocket/detail/prng.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
@@ -32,47 +26,23 @@ using sec_ws_key_type = static_string<
|
||||
using sec_ws_accept_type = static_string<
|
||||
beast::detail::base64::encoded_size(20)>;
|
||||
|
||||
inline
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
make_sec_ws_key(sec_ws_key_type& key)
|
||||
{
|
||||
auto g = make_prng(true);
|
||||
char a[16];
|
||||
for(int i = 0; i < 16; i += 4)
|
||||
{
|
||||
auto const v = g();
|
||||
a[i ] = v & 0xff;
|
||||
a[i+1] = (v >> 8) & 0xff;
|
||||
a[i+2] = (v >> 16) & 0xff;
|
||||
a[i+3] = (v >> 24) & 0xff;
|
||||
}
|
||||
key.resize(key.max_size());
|
||||
key.resize(beast::detail::base64::encode(
|
||||
key.data(), &a[0], 16));
|
||||
}
|
||||
make_sec_ws_key(sec_ws_key_type& key);
|
||||
|
||||
template<class = void>
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
make_sec_ws_accept(
|
||||
sec_ws_accept_type& accept,
|
||||
string_view key)
|
||||
{
|
||||
BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
|
||||
static_string<sec_ws_key_type::max_size_n + 36> m(key);
|
||||
m.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||
beast::detail::sha1_context ctx;
|
||||
beast::detail::init(ctx);
|
||||
beast::detail::update(ctx, m.data(), m.size());
|
||||
char digest[beast::detail::sha1_context::digest_size];
|
||||
beast::detail::finish(ctx, &digest[0]);
|
||||
accept.resize(accept.max_size());
|
||||
accept.resize(beast::detail::base64::encode(
|
||||
accept.data(), &digest[0], sizeof(digest)));
|
||||
}
|
||||
string_view key);
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#if BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/websocket/detail/hybi13.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
67
include/boost/beast/websocket/detail/hybi13.ipp
Normal file
67
include/boost/beast/websocket/detail/hybi13.ipp
Normal file
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP
|
||||
|
||||
#include <boost/beast/websocket/detail/hybi13.hpp>
|
||||
#include <boost/beast/core/detail/sha1.hpp>
|
||||
#include <boost/beast/websocket/detail/prng.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
void
|
||||
make_sec_ws_key(sec_ws_key_type& key)
|
||||
{
|
||||
auto g = make_prng(true);
|
||||
char a[16];
|
||||
for(int i = 0; i < 16; i += 4)
|
||||
{
|
||||
auto const v = g();
|
||||
a[i ] = v & 0xff;
|
||||
a[i+1] = (v >> 8) & 0xff;
|
||||
a[i+2] = (v >> 16) & 0xff;
|
||||
a[i+3] = (v >> 24) & 0xff;
|
||||
}
|
||||
key.resize(key.max_size());
|
||||
key.resize(beast::detail::base64::encode(
|
||||
key.data(), &a[0], 16));
|
||||
}
|
||||
|
||||
void
|
||||
make_sec_ws_accept(
|
||||
sec_ws_accept_type& accept,
|
||||
string_view key)
|
||||
{
|
||||
BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
|
||||
static_string<sec_ws_key_type::max_size_n + 36> m(key);
|
||||
m.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||
beast::detail::sha1_context ctx;
|
||||
beast::detail::init(ctx);
|
||||
beast::detail::update(ctx, m.data(), m.size());
|
||||
char digest[beast::detail::sha1_context::digest_size];
|
||||
beast::detail::finish(ctx, &digest[0]);
|
||||
accept.resize(accept.max_size());
|
||||
accept.resize(beast::detail::base64::encode(
|
||||
accept.data(), &digest[0], sizeof(digest)));
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_IPP
|
@@ -11,13 +11,8 @@
|
||||
#define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/buffers_suffix.hpp>
|
||||
#include <boost/beast/core/read_size.hpp>
|
||||
#include <boost/beast/zlib/deflate_stream.hpp>
|
||||
#include <boost/beast/zlib/inflate_stream.hpp>
|
||||
#include <boost/beast/websocket/option.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -48,28 +43,24 @@ struct pmd_offer
|
||||
bool client_no_context_takeover;
|
||||
};
|
||||
|
||||
template<class = void>
|
||||
BOOST_BEAST_DECL
|
||||
int
|
||||
parse_bits(string_view s)
|
||||
{
|
||||
if(s.size() == 0)
|
||||
return -1;
|
||||
if(s.size() > 2)
|
||||
return -1;
|
||||
if(s[0] < '1' || s[0] > '9')
|
||||
return -1;
|
||||
unsigned i = 0;
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c < '0' || c > '9')
|
||||
return -1;
|
||||
auto const i0 = i;
|
||||
i = 10 * i + (c - '0');
|
||||
if(i < i0)
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
parse_bits(string_view s);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
pmd_read_impl(pmd_offer& offer, http::ext_list const& list);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static_string<512>
|
||||
pmd_write_impl(pmd_offer const& offer);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static_string<512>
|
||||
pmd_negotiate_impl(
|
||||
pmd_offer& config,
|
||||
pmd_offer const& offer,
|
||||
permessage_deflate const& o);
|
||||
|
||||
// Parse permessage-deflate request fields
|
||||
//
|
||||
@@ -78,126 +69,9 @@ void
|
||||
pmd_read(pmd_offer& offer,
|
||||
http::basic_fields<Allocator> const& fields)
|
||||
{
|
||||
offer.accept = false;
|
||||
offer.server_max_window_bits= 0;
|
||||
offer.client_max_window_bits = 0;
|
||||
offer.server_no_context_takeover = false;
|
||||
offer.client_no_context_takeover = false;
|
||||
|
||||
http::ext_list list{
|
||||
fields["Sec-WebSocket-Extensions"]};
|
||||
for(auto const& ext : list)
|
||||
{
|
||||
if(iequals(ext.first, "permessage-deflate"))
|
||||
{
|
||||
for(auto const& param : ext.second)
|
||||
{
|
||||
if(iequals(param.first,
|
||||
"server_max_window_bits"))
|
||||
{
|
||||
if(offer.server_max_window_bits != 0)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(param.second.empty())
|
||||
{
|
||||
// The negotiation offer extension
|
||||
// parameter is missing the value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
offer.server_max_window_bits =
|
||||
parse_bits(param.second);
|
||||
if( offer.server_max_window_bits < 8 ||
|
||||
offer.server_max_window_bits > 15)
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
}
|
||||
else if(iequals(param.first,
|
||||
"client_max_window_bits"))
|
||||
{
|
||||
if(offer.client_max_window_bits != 0)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(! param.second.empty())
|
||||
{
|
||||
offer.client_max_window_bits =
|
||||
parse_bits(param.second);
|
||||
if( offer.client_max_window_bits < 8 ||
|
||||
offer.client_max_window_bits > 15)
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offer.client_max_window_bits = -1;
|
||||
}
|
||||
}
|
||||
else if(iequals(param.first,
|
||||
"server_no_context_takeover"))
|
||||
{
|
||||
if(offer.server_no_context_takeover)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(! param.second.empty())
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
offer.server_no_context_takeover = true;
|
||||
}
|
||||
else if(iequals(param.first,
|
||||
"client_no_context_takeover"))
|
||||
{
|
||||
if(offer.client_no_context_takeover)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(! param.second.empty())
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
offer.client_no_context_takeover = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The negotiation offer contains an extension
|
||||
// parameter not defined for use in an offer.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
}
|
||||
offer.accept = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
detail::pmd_read_impl(offer, list);
|
||||
}
|
||||
|
||||
// Set permessage-deflate fields for a client offer
|
||||
@@ -207,42 +81,7 @@ void
|
||||
pmd_write(http::basic_fields<Allocator>& fields,
|
||||
pmd_offer const& offer)
|
||||
{
|
||||
static_string<512> s;
|
||||
s = "permessage-deflate";
|
||||
if(offer.server_max_window_bits != 0)
|
||||
{
|
||||
if(offer.server_max_window_bits != -1)
|
||||
{
|
||||
s += "; server_max_window_bits=";
|
||||
s += to_static_string(
|
||||
offer.server_max_window_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
s += "; server_max_window_bits";
|
||||
}
|
||||
}
|
||||
if(offer.client_max_window_bits != 0)
|
||||
{
|
||||
if(offer.client_max_window_bits != -1)
|
||||
{
|
||||
s += "; client_max_window_bits=";
|
||||
s += to_static_string(
|
||||
offer.client_max_window_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
s += "; client_max_window_bits";
|
||||
}
|
||||
}
|
||||
if(offer.server_no_context_takeover)
|
||||
{
|
||||
s += "; server_no_context_takeover";
|
||||
}
|
||||
if(offer.client_no_context_takeover)
|
||||
{
|
||||
s += "; client_no_context_takeover";
|
||||
}
|
||||
auto s = detail::pmd_write_impl(offer);
|
||||
fields.set(http::field::sec_websocket_extensions, s);
|
||||
}
|
||||
|
||||
@@ -263,102 +102,24 @@ pmd_negotiate(
|
||||
}
|
||||
config.accept = true;
|
||||
|
||||
static_string<512> s = "permessage-deflate";
|
||||
|
||||
config.server_no_context_takeover =
|
||||
offer.server_no_context_takeover ||
|
||||
o.server_no_context_takeover;
|
||||
if(config.server_no_context_takeover)
|
||||
s += "; server_no_context_takeover";
|
||||
|
||||
config.client_no_context_takeover =
|
||||
o.client_no_context_takeover ||
|
||||
offer.client_no_context_takeover;
|
||||
if(config.client_no_context_takeover)
|
||||
s += "; client_no_context_takeover";
|
||||
|
||||
if(offer.server_max_window_bits != 0)
|
||||
config.server_max_window_bits = (std::min)(
|
||||
offer.server_max_window_bits,
|
||||
o.server_max_window_bits);
|
||||
else
|
||||
config.server_max_window_bits =
|
||||
o.server_max_window_bits;
|
||||
if(config.server_max_window_bits < 15)
|
||||
{
|
||||
// ZLib's deflateInit silently treats 8 as
|
||||
// 9 due to a bug, so prevent 8 from being used.
|
||||
//
|
||||
if(config.server_max_window_bits < 9)
|
||||
config.server_max_window_bits = 9;
|
||||
|
||||
s += "; server_max_window_bits=";
|
||||
s += to_static_string(
|
||||
config.server_max_window_bits);
|
||||
}
|
||||
|
||||
switch(offer.client_max_window_bits)
|
||||
{
|
||||
case -1:
|
||||
// extension parameter is present with no value
|
||||
config.client_max_window_bits =
|
||||
o.client_max_window_bits;
|
||||
if(config.client_max_window_bits < 15)
|
||||
{
|
||||
s += "; client_max_window_bits=";
|
||||
s += to_static_string(
|
||||
config.client_max_window_bits);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* extension parameter is absent.
|
||||
|
||||
If a received extension negotiation offer doesn't have the
|
||||
"client_max_window_bits" extension parameter, the corresponding
|
||||
extension negotiation response to the offer MUST NOT include the
|
||||
"client_max_window_bits" extension parameter.
|
||||
*/
|
||||
if(o.client_max_window_bits == 15)
|
||||
config.client_max_window_bits = 15;
|
||||
else
|
||||
config.accept = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
// extension parameter has value in [8..15]
|
||||
config.client_max_window_bits = (std::min)(
|
||||
o.client_max_window_bits,
|
||||
offer.client_max_window_bits);
|
||||
s += "; client_max_window_bits=";
|
||||
s += to_static_string(
|
||||
config.client_max_window_bits);
|
||||
break;
|
||||
}
|
||||
auto s = detail::pmd_negotiate_impl(config, offer, o);
|
||||
if(config.accept)
|
||||
fields.set(http::field::sec_websocket_extensions, s);
|
||||
}
|
||||
|
||||
// Normalize the server's response
|
||||
//
|
||||
inline
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
pmd_normalize(pmd_offer& offer)
|
||||
{
|
||||
if(offer.accept)
|
||||
{
|
||||
if( offer.server_max_window_bits == 0)
|
||||
offer.server_max_window_bits = 15;
|
||||
|
||||
if( offer.client_max_window_bits == 0 ||
|
||||
offer.client_max_window_bits == -1)
|
||||
offer.client_max_window_bits = 15;
|
||||
}
|
||||
}
|
||||
pmd_normalize(pmd_offer& offer);
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#if BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/websocket/detail/pmd_extension.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
310
include/boost/beast/websocket/detail/pmd_extension.ipp
Normal file
310
include/boost/beast/websocket/detail/pmd_extension.ipp
Normal file
@@ -0,0 +1,310 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_IPP
|
||||
|
||||
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
int
|
||||
parse_bits(string_view s)
|
||||
{
|
||||
if(s.size() == 0)
|
||||
return -1;
|
||||
if(s.size() > 2)
|
||||
return -1;
|
||||
if(s[0] < '1' || s[0] > '9')
|
||||
return -1;
|
||||
unsigned i = 0;
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c < '0' || c > '9')
|
||||
return -1;
|
||||
auto const i0 = i;
|
||||
i = 10 * i + (c - '0');
|
||||
if(i < i0)
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
// Parse permessage-deflate request fields
|
||||
//
|
||||
void
|
||||
pmd_read_impl(pmd_offer& offer, http::ext_list const& list)
|
||||
{
|
||||
offer.accept = false;
|
||||
offer.server_max_window_bits= 0;
|
||||
offer.client_max_window_bits = 0;
|
||||
offer.server_no_context_takeover = false;
|
||||
offer.client_no_context_takeover = false;
|
||||
|
||||
for(auto const& ext : list)
|
||||
{
|
||||
if(iequals(ext.first, "permessage-deflate"))
|
||||
{
|
||||
for(auto const& param : ext.second)
|
||||
{
|
||||
if(iequals(param.first,
|
||||
"server_max_window_bits"))
|
||||
{
|
||||
if(offer.server_max_window_bits != 0)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(param.second.empty())
|
||||
{
|
||||
// The negotiation offer extension
|
||||
// parameter is missing the value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
offer.server_max_window_bits =
|
||||
parse_bits(param.second);
|
||||
if( offer.server_max_window_bits < 8 ||
|
||||
offer.server_max_window_bits > 15)
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
}
|
||||
else if(iequals(param.first,
|
||||
"client_max_window_bits"))
|
||||
{
|
||||
if(offer.client_max_window_bits != 0)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(! param.second.empty())
|
||||
{
|
||||
offer.client_max_window_bits =
|
||||
parse_bits(param.second);
|
||||
if( offer.client_max_window_bits < 8 ||
|
||||
offer.client_max_window_bits > 15)
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offer.client_max_window_bits = -1;
|
||||
}
|
||||
}
|
||||
else if(iequals(param.first,
|
||||
"server_no_context_takeover"))
|
||||
{
|
||||
if(offer.server_no_context_takeover)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(! param.second.empty())
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
offer.server_no_context_takeover = true;
|
||||
}
|
||||
else if(iequals(param.first,
|
||||
"client_no_context_takeover"))
|
||||
{
|
||||
if(offer.client_no_context_takeover)
|
||||
{
|
||||
// The negotiation offer contains multiple
|
||||
// extension parameters with the same name.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
if(! param.second.empty())
|
||||
{
|
||||
// The negotiation offer contains an
|
||||
// extension parameter with an invalid value.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
offer.client_no_context_takeover = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The negotiation offer contains an extension
|
||||
// parameter not defined for use in an offer.
|
||||
//
|
||||
return; // MUST decline
|
||||
}
|
||||
}
|
||||
offer.accept = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static_string<512>
|
||||
pmd_write_impl(pmd_offer const& offer)
|
||||
{
|
||||
static_string<512> s = "permessage-deflate";
|
||||
if(offer.server_max_window_bits != 0)
|
||||
{
|
||||
if(offer.server_max_window_bits != -1)
|
||||
{
|
||||
s += "; server_max_window_bits=";
|
||||
s += to_static_string(
|
||||
offer.server_max_window_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
s += "; server_max_window_bits";
|
||||
}
|
||||
}
|
||||
if(offer.client_max_window_bits != 0)
|
||||
{
|
||||
if(offer.client_max_window_bits != -1)
|
||||
{
|
||||
s += "; client_max_window_bits=";
|
||||
s += to_static_string(
|
||||
offer.client_max_window_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
s += "; client_max_window_bits";
|
||||
}
|
||||
}
|
||||
if(offer.server_no_context_takeover)
|
||||
{
|
||||
s += "; server_no_context_takeover";
|
||||
}
|
||||
if(offer.client_no_context_takeover)
|
||||
{
|
||||
s += "; client_no_context_takeover";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static_string<512>
|
||||
pmd_negotiate_impl(
|
||||
pmd_offer& config,
|
||||
pmd_offer const& offer,
|
||||
permessage_deflate const& o)
|
||||
{
|
||||
static_string<512> s = "permessage-deflate";
|
||||
|
||||
config.server_no_context_takeover =
|
||||
offer.server_no_context_takeover ||
|
||||
o.server_no_context_takeover;
|
||||
if(config.server_no_context_takeover)
|
||||
s += "; server_no_context_takeover";
|
||||
|
||||
config.client_no_context_takeover =
|
||||
o.client_no_context_takeover ||
|
||||
offer.client_no_context_takeover;
|
||||
if(config.client_no_context_takeover)
|
||||
s += "; client_no_context_takeover";
|
||||
|
||||
if(offer.server_max_window_bits != 0)
|
||||
config.server_max_window_bits = (std::min)(
|
||||
offer.server_max_window_bits,
|
||||
o.server_max_window_bits);
|
||||
else
|
||||
config.server_max_window_bits =
|
||||
o.server_max_window_bits;
|
||||
if(config.server_max_window_bits < 15)
|
||||
{
|
||||
// ZLib's deflateInit silently treats 8 as
|
||||
// 9 due to a bug, so prevent 8 from being used.
|
||||
//
|
||||
if(config.server_max_window_bits < 9)
|
||||
config.server_max_window_bits = 9;
|
||||
|
||||
s += "; server_max_window_bits=";
|
||||
s += to_static_string(
|
||||
config.server_max_window_bits);
|
||||
}
|
||||
|
||||
switch(offer.client_max_window_bits)
|
||||
{
|
||||
case -1:
|
||||
// extension parameter is present with no value
|
||||
config.client_max_window_bits =
|
||||
o.client_max_window_bits;
|
||||
if(config.client_max_window_bits < 15)
|
||||
{
|
||||
s += "; client_max_window_bits=";
|
||||
s += to_static_string(
|
||||
config.client_max_window_bits);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* extension parameter is absent.
|
||||
|
||||
If a received extension negotiation offer doesn't have the
|
||||
"client_max_window_bits" extension parameter, the corresponding
|
||||
extension negotiation response to the offer MUST NOT include the
|
||||
"client_max_window_bits" extension parameter.
|
||||
*/
|
||||
if(o.client_max_window_bits == 15)
|
||||
config.client_max_window_bits = 15;
|
||||
else
|
||||
config.accept = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
// extension parameter has value in [8..15]
|
||||
config.client_max_window_bits = (std::min)(
|
||||
o.client_max_window_bits,
|
||||
offer.client_max_window_bits);
|
||||
s += "; client_max_window_bits=";
|
||||
s += to_static_string(
|
||||
config.client_max_window_bits);
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
pmd_normalize(pmd_offer& offer)
|
||||
{
|
||||
if(offer.accept)
|
||||
{
|
||||
if( offer.server_max_window_bits == 0)
|
||||
offer.server_max_window_bits = 15;
|
||||
|
||||
if( offer.client_max_window_bits == 0 ||
|
||||
offer.client_max_window_bits == -1)
|
||||
offer.client_max_window_bits = 15;
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_IPP
|
@@ -12,8 +12,7 @@
|
||||
|
||||
#include <boost/beast/core/buffers_range.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
@@ -27,8 +26,7 @@ namespace detail {
|
||||
valid. The write function may be called incrementally with segmented UTF8
|
||||
sequences. The finish function determines if all processed text is valid.
|
||||
*/
|
||||
template<class = void>
|
||||
class utf8_checker_t
|
||||
class utf8_checker
|
||||
{
|
||||
std::size_t need_ = 0; // chars we need to finish the code point
|
||||
std::uint8_t* p_ = cp_; // current position in temp buffer
|
||||
@@ -37,11 +35,13 @@ class utf8_checker_t
|
||||
public:
|
||||
/** Prepare to process text as valid utf8
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
reset();
|
||||
|
||||
/** Check that all processed text is valid utf8
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
bool
|
||||
finish();
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
|
||||
@return `true` if the text is valid utf8 or false otherwise.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
bool
|
||||
write(std::uint8_t const* in, std::size_t size);
|
||||
|
||||
@@ -61,29 +62,10 @@ public:
|
||||
write(ConstBufferSequence const& bs);
|
||||
};
|
||||
|
||||
template<class _>
|
||||
void
|
||||
utf8_checker_t<_>::
|
||||
reset()
|
||||
{
|
||||
need_ = 0;
|
||||
p_ = cp_;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
bool
|
||||
utf8_checker_t<_>::
|
||||
finish()
|
||||
{
|
||||
auto const success = need_ == 0;
|
||||
reset();
|
||||
return success;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
template<class ConstBufferSequence>
|
||||
bool
|
||||
utf8_checker_t<_>::
|
||||
utf8_checker::
|
||||
write(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(
|
||||
@@ -97,300 +79,18 @@ write(ConstBufferSequence const& buffers)
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
bool
|
||||
utf8_checker_t<_>::
|
||||
write(std::uint8_t const* in, std::size_t size)
|
||||
{
|
||||
auto const valid =
|
||||
[](std::uint8_t const*& p)
|
||||
{
|
||||
if(p[0] < 128)
|
||||
{
|
||||
++p;
|
||||
return true;
|
||||
}
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
if( (p[1] & 0xc0) != 0x80 ||
|
||||
(p[0] & 0x1e) == 0) // overlong
|
||||
return false;
|
||||
p += 2;
|
||||
return true;
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
if( (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
|
||||
|| (p[0] == 0xed && (p[1] & 0x20) == 0x20) // surrogate
|
||||
//|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
|
||||
)
|
||||
return false;
|
||||
p += 3;
|
||||
return true;
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
if( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
|
||||
|| (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[3] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
|
||||
|| (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4 // > U+10FFFF
|
||||
)
|
||||
return false;
|
||||
p += 4;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto const fail_fast =
|
||||
[&]()
|
||||
{
|
||||
if(cp_[0] < 128)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& p = cp_; // alias, only to keep this code similar to valid() above
|
||||
const auto known_only = p_ - cp_;
|
||||
if (known_only == 1)
|
||||
{
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
return ((p[0] & 0x1e) == 0); // overlong
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
return ((p[0] & 0x07) >= 0x05); // invalid F5...FF characters
|
||||
}
|
||||
}
|
||||
else if (known_only == 2)
|
||||
{
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
return ((p[1] & 0xc0) != 0x80 ||
|
||||
(p[0] & 0x1e) == 0); // overlong
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
return ( (p[1] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
|
||||
|| (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
|
||||
|| (p[1] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
|
||||
|| (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
|
||||
}
|
||||
}
|
||||
else if (known_only == 3)
|
||||
{
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
return ( (p[1] & 0xc0) != 0x80
|
||||
|| (p[0] & 0x1e) == 0); // overlong
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
return ( (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
|
||||
|| (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
|
||||
//|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
|
||||
|| (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
|
||||
|| (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto const needed =
|
||||
[](std::uint8_t const v)
|
||||
{
|
||||
if(v < 128)
|
||||
return 1;
|
||||
if(v < 192)
|
||||
return 0;
|
||||
if(v < 224)
|
||||
return 2;
|
||||
if(v < 240)
|
||||
return 3;
|
||||
if(v < 248)
|
||||
return 4;
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto const end = in + size;
|
||||
|
||||
// Finish up any incomplete code point
|
||||
if(need_ > 0)
|
||||
{
|
||||
// Calculate what we have
|
||||
auto n = (std::min)(size, need_);
|
||||
size -= n;
|
||||
need_ -= n;
|
||||
|
||||
// Add characters to the code point
|
||||
while(n--)
|
||||
*p_++ = *in++;
|
||||
BOOST_ASSERT(p_ <= cp_ + 4);
|
||||
|
||||
// Still incomplete?
|
||||
if(need_ > 0)
|
||||
{
|
||||
// Incomplete code point
|
||||
BOOST_ASSERT(in == end);
|
||||
|
||||
// Do partial validation on the incomplete
|
||||
// code point, this is called "Fail fast"
|
||||
// in Autobahn|Testsuite parlance.
|
||||
return ! fail_fast();
|
||||
}
|
||||
|
||||
// Complete code point, validate it
|
||||
std::uint8_t const* p = &cp_[0];
|
||||
if(! valid(p))
|
||||
return false;
|
||||
p_ = cp_;
|
||||
}
|
||||
|
||||
if(size <= sizeof(std::size_t))
|
||||
goto slow;
|
||||
|
||||
// Align `in` to sizeof(std::size_t) boundary
|
||||
{
|
||||
auto const in0 = in;
|
||||
auto last = reinterpret_cast<std::uint8_t const*>(
|
||||
((reinterpret_cast<std::uintptr_t>(in) + sizeof(std::size_t) - 1) /
|
||||
sizeof(std::size_t)) * sizeof(std::size_t));
|
||||
|
||||
// Check one character at a time for low-ASCII
|
||||
while(in < last)
|
||||
{
|
||||
if(*in & 0x80)
|
||||
{
|
||||
// Not low-ASCII so switch to slow loop
|
||||
size = size - (in - in0);
|
||||
goto slow;
|
||||
}
|
||||
++in;
|
||||
}
|
||||
size = size - (in - in0);
|
||||
}
|
||||
|
||||
// Fast loop: Process 4 or 8 low-ASCII characters at a time
|
||||
{
|
||||
auto const in0 = in;
|
||||
auto last = in + size - 7;
|
||||
auto constexpr mask = static_cast<
|
||||
std::size_t>(0x8080808080808080 & ~std::size_t{0});
|
||||
while(in < last)
|
||||
{
|
||||
#if 0
|
||||
std::size_t temp;
|
||||
std::memcpy(&temp, in, sizeof(temp));
|
||||
if((temp & mask) != 0)
|
||||
#else
|
||||
// Technically UB but works on all known platforms
|
||||
if((*reinterpret_cast<std::size_t const*>(in) & mask) != 0)
|
||||
#endif
|
||||
{
|
||||
size = size - (in - in0);
|
||||
goto slow;
|
||||
}
|
||||
in += sizeof(std::size_t);
|
||||
}
|
||||
// There's at least one more full code point left
|
||||
last += 4;
|
||||
while(in < last)
|
||||
if(! valid(in))
|
||||
return false;
|
||||
goto tail;
|
||||
}
|
||||
|
||||
slow:
|
||||
// Slow loop: Full validation on one code point at a time
|
||||
{
|
||||
auto last = in + size - 3;
|
||||
while(in < last)
|
||||
if(! valid(in))
|
||||
return false;
|
||||
}
|
||||
|
||||
tail:
|
||||
// Handle the remaining bytes. The last
|
||||
// characters could split a code point so
|
||||
// we save the partial code point for later.
|
||||
//
|
||||
// On entry to the loop, `in` points to the
|
||||
// beginning of a code point.
|
||||
//
|
||||
for(;;)
|
||||
{
|
||||
// Number of chars left
|
||||
auto n = end - in;
|
||||
if(! n)
|
||||
break;
|
||||
|
||||
// Chars we need to finish this code point
|
||||
auto const need = needed(*in);
|
||||
if(need == 0)
|
||||
return false;
|
||||
if(need <= n)
|
||||
{
|
||||
// Check a whole code point
|
||||
if(! valid(in))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate how many chars we need
|
||||
// to finish this partial code point
|
||||
need_ = need - n;
|
||||
|
||||
// Save the partial code point
|
||||
while(n--)
|
||||
*p_++ = *in++;
|
||||
BOOST_ASSERT(in == end);
|
||||
BOOST_ASSERT(p_ <= cp_ + 4);
|
||||
|
||||
// Do partial validation on the incomplete
|
||||
// code point, this is called "Fail fast"
|
||||
// in Autobahn|Testsuite parlance.
|
||||
return ! fail_fast();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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(reinterpret_cast<const uint8_t*>(p), n))
|
||||
return false;
|
||||
return c.finish();
|
||||
}
|
||||
check_utf8(char const* p, std::size_t n);
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#if BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/websocket/detail/utf8_checker.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
331
include/boost/beast/websocket/detail/utf8_checker.ipp
Normal file
331
include/boost/beast/websocket/detail/utf8_checker.ipp
Normal file
@@ -0,0 +1,331 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_IPP
|
||||
|
||||
#include <boost/beast/websocket/detail/utf8_checker.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
void
|
||||
utf8_checker::
|
||||
reset()
|
||||
{
|
||||
need_ = 0;
|
||||
p_ = cp_;
|
||||
}
|
||||
|
||||
bool
|
||||
utf8_checker::
|
||||
finish()
|
||||
{
|
||||
auto const success = need_ == 0;
|
||||
reset();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool
|
||||
utf8_checker::
|
||||
write(std::uint8_t const* in, std::size_t size)
|
||||
{
|
||||
auto const valid =
|
||||
[](std::uint8_t const*& p)
|
||||
{
|
||||
if(p[0] < 128)
|
||||
{
|
||||
++p;
|
||||
return true;
|
||||
}
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
if( (p[1] & 0xc0) != 0x80 ||
|
||||
(p[0] & 0x1e) == 0) // overlong
|
||||
return false;
|
||||
p += 2;
|
||||
return true;
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
if( (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
|
||||
|| (p[0] == 0xed && (p[1] & 0x20) == 0x20) // surrogate
|
||||
//|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
|
||||
)
|
||||
return false;
|
||||
p += 3;
|
||||
return true;
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
if( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
|
||||
|| (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[3] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
|
||||
|| (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4 // > U+10FFFF
|
||||
)
|
||||
return false;
|
||||
p += 4;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto const fail_fast =
|
||||
[&]()
|
||||
{
|
||||
if(cp_[0] < 128)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& p = cp_; // alias, only to keep this code similar to valid() above
|
||||
const auto known_only = p_ - cp_;
|
||||
if (known_only == 1)
|
||||
{
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
return ((p[0] & 0x1e) == 0); // overlong
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
return ((p[0] & 0x07) >= 0x05); // invalid F5...FF characters
|
||||
}
|
||||
}
|
||||
else if (known_only == 2)
|
||||
{
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
return ((p[1] & 0xc0) != 0x80 ||
|
||||
(p[0] & 0x1e) == 0); // overlong
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
return ( (p[1] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
|
||||
|| (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
|
||||
|| (p[1] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
|
||||
|| (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
|
||||
}
|
||||
}
|
||||
else if (known_only == 3)
|
||||
{
|
||||
if((p[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
return ( (p[1] & 0xc0) != 0x80
|
||||
|| (p[0] & 0x1e) == 0); // overlong
|
||||
}
|
||||
if((p[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
return ( (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xe0 && (p[1] & 0x20) == 0) // overlong
|
||||
|| (p[0] == 0xed && (p[1] & 0x20) == 0x20)); // surrogate
|
||||
//|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
|
||||
}
|
||||
if((p[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
return ( (p[0] & 0x07) >= 0x05 // invalid F5...FF characters
|
||||
|| (p[1] & 0xc0) != 0x80
|
||||
|| (p[2] & 0xc0) != 0x80
|
||||
|| (p[0] == 0xf0 && (p[1] & 0x30) == 0) // overlong
|
||||
|| (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4); // > U+10FFFF
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto const needed =
|
||||
[](std::uint8_t const v)
|
||||
{
|
||||
if(v < 128)
|
||||
return 1;
|
||||
if(v < 192)
|
||||
return 0;
|
||||
if(v < 224)
|
||||
return 2;
|
||||
if(v < 240)
|
||||
return 3;
|
||||
if(v < 248)
|
||||
return 4;
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto const end = in + size;
|
||||
|
||||
// Finish up any incomplete code point
|
||||
if(need_ > 0)
|
||||
{
|
||||
// Calculate what we have
|
||||
auto n = (std::min)(size, need_);
|
||||
size -= n;
|
||||
need_ -= n;
|
||||
|
||||
// Add characters to the code point
|
||||
while(n--)
|
||||
*p_++ = *in++;
|
||||
BOOST_ASSERT(p_ <= cp_ + 4);
|
||||
|
||||
// Still incomplete?
|
||||
if(need_ > 0)
|
||||
{
|
||||
// Incomplete code point
|
||||
BOOST_ASSERT(in == end);
|
||||
|
||||
// Do partial validation on the incomplete
|
||||
// code point, this is called "Fail fast"
|
||||
// in Autobahn|Testsuite parlance.
|
||||
return ! fail_fast();
|
||||
}
|
||||
|
||||
// Complete code point, validate it
|
||||
std::uint8_t const* p = &cp_[0];
|
||||
if(! valid(p))
|
||||
return false;
|
||||
p_ = cp_;
|
||||
}
|
||||
|
||||
if(size <= sizeof(std::size_t))
|
||||
goto slow;
|
||||
|
||||
// Align `in` to sizeof(std::size_t) boundary
|
||||
{
|
||||
auto const in0 = in;
|
||||
auto last = reinterpret_cast<std::uint8_t const*>(
|
||||
((reinterpret_cast<std::uintptr_t>(in) + sizeof(std::size_t) - 1) /
|
||||
sizeof(std::size_t)) * sizeof(std::size_t));
|
||||
|
||||
// Check one character at a time for low-ASCII
|
||||
while(in < last)
|
||||
{
|
||||
if(*in & 0x80)
|
||||
{
|
||||
// Not low-ASCII so switch to slow loop
|
||||
size = size - (in - in0);
|
||||
goto slow;
|
||||
}
|
||||
++in;
|
||||
}
|
||||
size = size - (in - in0);
|
||||
}
|
||||
|
||||
// Fast loop: Process 4 or 8 low-ASCII characters at a time
|
||||
{
|
||||
auto const in0 = in;
|
||||
auto last = in + size - 7;
|
||||
auto constexpr mask = static_cast<
|
||||
std::size_t>(0x8080808080808080 & ~std::size_t{0});
|
||||
while(in < last)
|
||||
{
|
||||
#if 0
|
||||
std::size_t temp;
|
||||
std::memcpy(&temp, in, sizeof(temp));
|
||||
if((temp & mask) != 0)
|
||||
#else
|
||||
// Technically UB but works on all known platforms
|
||||
if((*reinterpret_cast<std::size_t const*>(in) & mask) != 0)
|
||||
#endif
|
||||
{
|
||||
size = size - (in - in0);
|
||||
goto slow;
|
||||
}
|
||||
in += sizeof(std::size_t);
|
||||
}
|
||||
// There's at least one more full code point left
|
||||
last += 4;
|
||||
while(in < last)
|
||||
if(! valid(in))
|
||||
return false;
|
||||
goto tail;
|
||||
}
|
||||
|
||||
slow:
|
||||
// Slow loop: Full validation on one code point at a time
|
||||
{
|
||||
auto last = in + size - 3;
|
||||
while(in < last)
|
||||
if(! valid(in))
|
||||
return false;
|
||||
}
|
||||
|
||||
tail:
|
||||
// Handle the remaining bytes. The last
|
||||
// characters could split a code point so
|
||||
// we save the partial code point for later.
|
||||
//
|
||||
// On entry to the loop, `in` points to the
|
||||
// beginning of a code point.
|
||||
//
|
||||
for(;;)
|
||||
{
|
||||
// Number of chars left
|
||||
auto n = end - in;
|
||||
if(! n)
|
||||
break;
|
||||
|
||||
// Chars we need to finish this code point
|
||||
auto const need = needed(*in);
|
||||
if(need == 0)
|
||||
return false;
|
||||
if(need <= n)
|
||||
{
|
||||
// Check a whole code point
|
||||
if(! valid(in))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate how many chars we need
|
||||
// to finish this partial code point
|
||||
need_ = need - n;
|
||||
|
||||
// Save the partial code point
|
||||
while(n--)
|
||||
*p_++ = *in++;
|
||||
BOOST_ASSERT(in == end);
|
||||
BOOST_ASSERT(p_ <= cp_ + 4);
|
||||
|
||||
// Do partial validation on the incomplete
|
||||
// code point, this is called "Fail fast"
|
||||
// in Autobahn|Testsuite parlance.
|
||||
return ! fail_fast();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
check_utf8(char const* p, std::size_t n)
|
||||
{
|
||||
utf8_checker c;
|
||||
if(! c.write(reinterpret_cast<const uint8_t*>(p), n))
|
||||
return false;
|
||||
return c.finish();
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_IPP
|
@@ -19,6 +19,7 @@
|
||||
#include <boost/beast/core/buffers_prefix.hpp>
|
||||
#include <boost/beast/core/buffers_suffix.hpp>
|
||||
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||
#include <boost/beast/core/read_size.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/beast/core/detail/bind_continuation.hpp>
|
||||
#include <boost/beast/core/detail/buffer.hpp>
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <boost/beast/websocket/detail/hybi13.hpp>
|
||||
#include <boost/beast/websocket/detail/impl_base.hpp>
|
||||
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
||||
#include <boost/beast/websocket/detail/prng.hpp>
|
||||
#include <boost/beast/core/role.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
@@ -33,6 +34,7 @@
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <random>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
@@ -857,7 +859,7 @@ public:
|
||||
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 `net::post`.
|
||||
|
||||
|
||||
@par Example
|
||||
@code
|
||||
ws.async_handshake("localhost", "/",
|
||||
|
Reference in New Issue
Block a user