Use static_string for WebSocket handshakes:

fix #328

This replaces usages of std::string with
static_string when performing WebSocket handshakes.
This commit is contained in:
Vinnie Falco
2017-04-29 15:50:05 -07:00
parent c5184bed47
commit 707f5c867e
5 changed files with 48 additions and 28 deletions

View File

@@ -2,6 +2,7 @@
* Refactor static_string * Refactor static_string
* Refactor base64 * Refactor base64
* Use static_string for WebSocket handshakes
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@@ -8,8 +8,10 @@
#ifndef BEAST_WEBSOCKET_DETAIL_HYBI13_HPP #ifndef BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
#define BEAST_WEBSOCKET_DETAIL_HYBI13_HPP #define BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
#include <beast/core/static_string.hpp>
#include <beast/core/detail/base64.hpp> #include <beast/core/detail/base64.hpp>
#include <beast/core/detail/sha1.hpp> #include <beast/core/detail/sha1.hpp>
#include <boost/assert.hpp>
#include <boost/utility/string_ref.hpp> #include <boost/utility/string_ref.hpp>
#include <array> #include <array>
#include <cstdint> #include <cstdint>
@@ -20,11 +22,17 @@ namespace beast {
namespace websocket { namespace websocket {
namespace detail { namespace detail {
using sec_ws_key_type = static_string<
beast::detail::base64::encoded_size(16)>;
using sec_ws_accept_type = static_string<
beast::detail::base64::encoded_size(20)>;
template<class Gen> template<class Gen>
std::string void
make_sec_ws_key(Gen& g) make_sec_ws_key(sec_ws_key_type& key, Gen& g)
{ {
std::array<std::uint8_t, 16> a; char a[16];
for(int i = 0; i < 16; i += 4) for(int i = 0; i < 16; i += 4)
{ {
auto const v = g(); auto const v = g();
@@ -33,24 +41,27 @@ make_sec_ws_key(Gen& g)
a[i+2] = (v >> 16) & 0xff; a[i+2] = (v >> 16) & 0xff;
a[i+3] = (v >> 24) & 0xff; a[i+3] = (v >> 24) & 0xff;
} }
return beast::detail::base64_encode( key.resize(key.max_size());
a.data(), a.size()); key.resize(beast::detail::base64::encode(
key.data(), &a[0], 16));
} }
template<class = void> template<class = void>
std::string void
make_sec_ws_accept(boost::string_ref const& key) make_sec_ws_accept(sec_ws_accept_type& accept,
boost::string_ref key)
{ {
std::string s(key.data(), key.size()); BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
s += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 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::sha1_context ctx;
beast::detail::init(ctx); beast::detail::init(ctx);
beast::detail::update(ctx, s.data(), s.size()); beast::detail::update(ctx, m.data(), m.size());
std::array<std::uint8_t, char digest[beast::detail::sha1_context::digest_size];
beast::detail::sha1_context::digest_size> digest; beast::detail::finish(ctx, &digest[0]);
beast::detail::finish(ctx, digest.data()); accept.resize(accept.max_size());
return beast::detail::base64_encode( accept.resize(beast::detail::base64::encode(
digest.data(), digest.size()); accept.data(), &digest[0], sizeof(digest)));
} }
} // detail } // detail

View File

@@ -34,7 +34,7 @@ class stream<NextLayer>::handshake_op
bool cont; bool cont;
stream<NextLayer>& ws; stream<NextLayer>& ws;
response_type* res_p; response_type* res_p;
std::string key; detail::sec_ws_key_type key;
request_type req; request_type req;
response_type res; response_type res;
int state = 0; int state = 0;

View File

@@ -134,7 +134,7 @@ do_handshake(response_type* res_p,
{ {
response_type res; response_type res;
reset(); reset();
std::string key; detail::sec_ws_key_type key;
{ {
auto const req = build_request( auto const req = build_request(
key, host, resource, decorator); key, host, resource, decorator);
@@ -155,7 +155,7 @@ template<class NextLayer>
template<class Decorator> template<class Decorator>
request_type request_type
stream<NextLayer>:: stream<NextLayer>::
build_request(std::string& key, build_request(detail::sec_ws_key_type& key,
boost::string_ref const& host, boost::string_ref const& host,
boost::string_ref const& resource, boost::string_ref const& resource,
Decorator const& decorator) Decorator const& decorator)
@@ -167,7 +167,7 @@ build_request(std::string& key,
req.fields.insert("Host", host); req.fields.insert("Host", host);
req.fields.insert("Upgrade", "websocket"); req.fields.insert("Upgrade", "websocket");
req.fields.insert("Connection", "upgrade"); req.fields.insert("Connection", "upgrade");
key = detail::make_sec_ws_key(maskgen_); detail::make_sec_ws_key(key, maskgen_);
req.fields.insert("Sec-WebSocket-Key", key); req.fields.insert("Sec-WebSocket-Key", key);
req.fields.insert("Sec-WebSocket-Version", "13"); req.fields.insert("Sec-WebSocket-Version", "13");
if(pmd_opts_.client_enable) if(pmd_opts_.client_enable)
@@ -186,6 +186,7 @@ build_request(std::string& key,
req.fields, config); req.fields, config);
} }
decorator(req); decorator(req);
// VFALCO Use static_string here
if(! req.fields.exists("User-Agent")) if(! req.fields.exists("User-Agent"))
req.fields.insert("User-Agent", req.fields.insert("User-Agent",
std::string("Beast/") + BEAST_VERSION_STRING); std::string("Beast/") + BEAST_VERSION_STRING);
@@ -203,6 +204,7 @@ build_response(request_type const& req,
[&decorator](response_type& res) [&decorator](response_type& res)
{ {
decorator(res); decorator(res);
// VFALCO Use static_string here
if(! res.fields.exists("Server")) if(! res.fields.exists("Server"))
res.fields.insert("Server", res.fields.insert("Server",
std::string("Beast/") + std::string("Beast/") +
@@ -235,6 +237,9 @@ build_response(request_type const& req,
return err("Missing Sec-WebSocket-Key"); return err("Missing Sec-WebSocket-Key");
if(! http::token_list{req.fields["Upgrade"]}.exists("websocket")) if(! http::token_list{req.fields["Upgrade"]}.exists("websocket"))
return err("Missing websocket Upgrade token"); return err("Missing websocket Upgrade token");
auto const key = req.fields["Sec-WebSocket-Key"];
if(key.size() > detail::sec_ws_key_type::max_size_n)
return err("Invalid Sec-WebSocket-Key");
{ {
auto const version = auto const version =
req.fields["Sec-WebSocket-Version"]; req.fields["Sec-WebSocket-Version"];
@@ -255,6 +260,7 @@ build_response(request_type const& req,
return res; return res;
} }
} }
response_type res; response_type res;
{ {
detail::pmd_offer offer; detail::pmd_offer offer;
@@ -269,10 +275,9 @@ build_response(request_type const& req,
res.fields.insert("Upgrade", "websocket"); res.fields.insert("Upgrade", "websocket");
res.fields.insert("Connection", "upgrade"); res.fields.insert("Connection", "upgrade");
{ {
auto const key = detail::sec_ws_accept_type accept;
req.fields["Sec-WebSocket-Key"]; detail::make_sec_ws_accept(accept, key);
res.fields.insert("Sec-WebSocket-Accept", res.fields.insert("Sec-WebSocket-Accept", accept);
detail::make_sec_ws_accept(key));
} }
decorate(res); decorate(res);
return res; return res;
@@ -282,7 +287,7 @@ template<class NextLayer>
void void
stream<NextLayer>:: stream<NextLayer>::
do_response(http::response_header const& res, do_response(http::response_header const& res,
boost::string_ref const& key, error_code& ec) detail::sec_ws_key_type const& key, error_code& ec)
{ {
// VFALCO Review these error codes // VFALCO Review these error codes
auto fail = [&]{ ec = error::response_failed; }; auto fail = [&]{ ec = error::response_failed; };
@@ -296,8 +301,10 @@ do_response(http::response_header const& res,
return fail(); return fail();
if(! res.fields.exists("Sec-WebSocket-Accept")) if(! res.fields.exists("Sec-WebSocket-Accept"))
return fail(); return fail();
if(res.fields["Sec-WebSocket-Accept"] != detail::sec_ws_accept_type accept;
detail::make_sec_ws_accept(key)) detail::make_sec_ws_accept(accept, key);
if(accept.compare(
res.fields["Sec-WebSocket-Accept"]) != 0)
return fail(); return fail();
detail::pmd_offer offer; detail::pmd_offer offer;
pmd_read(offer, res.fields); pmd_read(offer, res.fields);

View File

@@ -10,6 +10,7 @@
#include <beast/config.hpp> #include <beast/config.hpp>
#include <beast/websocket/option.hpp> #include <beast/websocket/option.hpp>
#include <beast/websocket/detail/hybi13.hpp>
#include <beast/websocket/detail/stream_base.hpp> #include <beast/websocket/detail/stream_base.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>
@@ -2981,7 +2982,7 @@ private:
template<class Decorator> template<class Decorator>
request_type request_type
build_request(std::string& key, build_request(detail::sec_ws_key_type& key,
boost::string_ref const& host, boost::string_ref const& host,
boost::string_ref const& resource, boost::string_ref const& resource,
Decorator const& decorator); Decorator const& decorator);
@@ -2993,7 +2994,7 @@ private:
void void
do_response(http::response_header const& resp, do_response(http::response_header const& resp,
boost::string_ref const& key, error_code& ec); detail::sec_ws_key_type const& key, error_code& ec);
}; };
} // websocket } // websocket