Beast.WebSocket:

Beast.WebSocket provides developers with a robust WebSocket
implementation built on Boost.Asio with a consistent asynchronous
model using a modern C++ approach.
This commit is contained in:
Vinnie Falco
2016-02-25 16:17:19 -05:00
parent 5e8d028da2
commit 2cb3834bbb
106 changed files with 14671 additions and 772 deletions

View File

@@ -284,6 +284,8 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\streambuf_readstream.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\temp_buffer.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\type_check.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\ci_char_traits.h">
@@ -312,10 +314,14 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\detail\sha2_context.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\detail\sha_context.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\ripemd.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\secure_erase.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\sha.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\sha2.h">
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\crypto\tests\beast_base64_test.cpp">
@@ -494,6 +500,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\unity\beast_wsproto_unity.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\unit_test.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\unit_test\amount.h">
@@ -527,6 +537,74 @@
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\unit_test\thread.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\decorator.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\error.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\frame.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\hybi13.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\invokable.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\mask.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\socket_base.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\utf8_checker.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\error.h">
</ClInclude>
<None Include="..\..\src\beast\beast\wsproto\impl\accept_op.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\close_op.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\error.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\handshake_op.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\read_frame_op.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\read_op.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\response_op.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\socket.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\ssl.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\teardown.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\write_frame_op.ipp">
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\write_op.ipp">
</None>
<ClInclude Include="..\..\src\beast\beast\wsproto\option.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\rfc6455.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\socket.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\src\test\async_echo_peer.h">
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\wsproto\src\test\beast_wsproto_ws_echo_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\wsproto\src\test\beast_wsproto_ws_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\wsproto\src\test\sync_echo_peer.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\ssl.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\static_string.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\teardown.h">
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\xor_shift_engine.h">
</ClInclude>
<ClInclude Include="..\..\src\ed25519-donna\curve25519-donna-32bit.h">
@@ -3423,6 +3501,10 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\BaseHTTPPeer.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\BasePeer.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\BaseWSPeer.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\server\impl\Door.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -3439,6 +3521,8 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\PlainHTTPPeer.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\PlainWSPeer.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\server\impl\Port.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -3461,6 +3545,8 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\SSLHTTPPeer.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\SSLWSPeer.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\JsonWriter.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\make_Server.h">
@@ -3485,6 +3571,8 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\server\Writer.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\WSSession.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\shamap\Family.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\shamap\FullBelowCache.h">
@@ -3905,10 +3993,6 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\websocket\WebSocket02.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\wsproto\basic_socket.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\wsproto\wsproto.h">
</ClInclude>
<ClCompile Include="..\..\src\rocksdb2\db\builder.cc">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>

View File

@@ -79,6 +79,21 @@
<Filter Include="beast\unit_test">
<UniqueIdentifier>{2762284D-66E5-8B48-1F8E-67116DB1FC6B}</UniqueIdentifier>
</Filter>
<Filter Include="beast\wsproto">
<UniqueIdentifier>{53D8D640-BEB0-1F2C-61D4-5459CDAC7EB4}</UniqueIdentifier>
</Filter>
<Filter Include="beast\wsproto\detail">
<UniqueIdentifier>{E8E91EF5-9155-8F58-4249-3EF221768BCE}</UniqueIdentifier>
</Filter>
<Filter Include="beast\wsproto\impl">
<UniqueIdentifier>{63AB4C85-5B2E-B2A7-9E4F-9F0899402D1D}</UniqueIdentifier>
</Filter>
<Filter Include="beast\wsproto\src">
<UniqueIdentifier>{E5986CF5-37A8-1EE5-1085-645EBEE517B6}</UniqueIdentifier>
</Filter>
<Filter Include="beast\wsproto\src\test">
<UniqueIdentifier>{28A822FF-4532-9678-23F2-D844CB0FE207}</UniqueIdentifier>
</Filter>
<Filter Include="build">
<UniqueIdentifier>{65697F48-7FC6-2A4B-DB6C-56781F3990B5}</UniqueIdentifier>
</Filter>
@@ -376,9 +391,6 @@
<Filter Include="ripple\websocket">
<UniqueIdentifier>{44780F86-42D3-2F2B-0846-5AEE2CA6D7FE}</UniqueIdentifier>
</Filter>
<Filter Include="ripple\wsproto">
<UniqueIdentifier>{1D54E820-ADC9-94FB-19E7-653EFDE4CBE9}</UniqueIdentifier>
</Filter>
<Filter Include="rocksdb2">
<UniqueIdentifier>{15B4B65A-0F03-7BA9-38CD-42A5712392CB}</UniqueIdentifier>
</Filter>
@@ -606,6 +618,9 @@
<ClInclude Include="..\..\src\beast\beast\asio\streambuf_readstream.h">
<Filter>beast\asio</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\temp_buffer.h">
<Filter>beast\asio</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\asio\type_check.h">
<Filter>beast\asio</Filter>
</ClInclude>
@@ -642,12 +657,18 @@
<ClInclude Include="..\..\src\beast\beast\crypto\detail\sha2_context.h">
<Filter>beast\crypto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\detail\sha_context.h">
<Filter>beast\crypto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\ripemd.h">
<Filter>beast\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\secure_erase.h">
<Filter>beast\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\sha.h">
<Filter>beast\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\crypto\sha2.h">
<Filter>beast\crypto</Filter>
</ClInclude>
@@ -864,6 +885,9 @@
<ClCompile Include="..\..\src\beast\beast\unity\beast_test_unity.cpp">
<Filter>beast\unity</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\unity\beast_wsproto_unity.cpp">
<Filter>beast\unity</Filter>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\unit_test.h">
<Filter>beast</Filter>
</ClInclude>
@@ -912,6 +936,102 @@
<ClInclude Include="..\..\src\beast\beast\unit_test\thread.h">
<Filter>beast\unit_test</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto.h">
<Filter>beast</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\decorator.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\error.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\frame.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\hybi13.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\invokable.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\mask.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\socket_base.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\detail\utf8_checker.h">
<Filter>beast\wsproto\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\error.h">
<Filter>beast\wsproto</Filter>
</ClInclude>
<None Include="..\..\src\beast\beast\wsproto\impl\accept_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\close_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\error.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\handshake_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\read_frame_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\read_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\response_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\socket.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\ssl.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\teardown.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\write_frame_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<None Include="..\..\src\beast\beast\wsproto\impl\write_op.ipp">
<Filter>beast\wsproto\impl</Filter>
</None>
<ClInclude Include="..\..\src\beast\beast\wsproto\option.h">
<Filter>beast\wsproto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\rfc6455.h">
<Filter>beast\wsproto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\socket.h">
<Filter>beast\wsproto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\src\test\async_echo_peer.h">
<Filter>beast\wsproto\src\test</Filter>
</ClInclude>
<ClCompile Include="..\..\src\beast\beast\wsproto\src\test\beast_wsproto_ws_echo_test.cpp">
<Filter>beast\wsproto\src\test</Filter>
</ClCompile>
<ClCompile Include="..\..\src\beast\beast\wsproto\src\test\beast_wsproto_ws_test.cpp">
<Filter>beast\wsproto\src\test</Filter>
</ClCompile>
<ClInclude Include="..\..\src\beast\beast\wsproto\src\test\sync_echo_peer.h">
<Filter>beast\wsproto\src\test</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\ssl.h">
<Filter>beast\wsproto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\static_string.h">
<Filter>beast\wsproto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\wsproto\teardown.h">
<Filter>beast\wsproto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\beast\beast\xor_shift_engine.h">
<Filter>beast</Filter>
</ClInclude>
@@ -3834,6 +3954,12 @@
<ClInclude Include="..\..\src\ripple\server\impl\BaseHTTPPeer.h">
<Filter>ripple\server\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\BasePeer.h">
<Filter>ripple\server\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\BaseWSPeer.h">
<Filter>ripple\server\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\server\impl\Door.cpp">
<Filter>ripple\server\impl</Filter>
</ClCompile>
@@ -3852,6 +3978,9 @@
<ClInclude Include="..\..\src\ripple\server\impl\PlainHTTPPeer.h">
<Filter>ripple\server\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\PlainWSPeer.h">
<Filter>ripple\server\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\server\impl\Port.cpp">
<Filter>ripple\server\impl</Filter>
</ClCompile>
@@ -3873,6 +4002,9 @@
<ClInclude Include="..\..\src\ripple\server\impl\SSLHTTPPeer.h">
<Filter>ripple\server\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\impl\SSLWSPeer.h">
<Filter>ripple\server\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\JsonWriter.h">
<Filter>ripple\server</Filter>
</ClInclude>
@@ -3906,6 +4038,9 @@
<ClInclude Include="..\..\src\ripple\server\Writer.h">
<Filter>ripple\server</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\server\WSSession.h">
<Filter>ripple\server</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\shamap\Family.h">
<Filter>ripple\shamap</Filter>
</ClInclude>
@@ -4311,12 +4446,6 @@
<ClInclude Include="..\..\src\ripple\websocket\WebSocket02.h">
<Filter>ripple\websocket</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\wsproto\basic_socket.h">
<Filter>ripple\wsproto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\wsproto\wsproto.h">
<Filter>ripple\wsproto</Filter>
</ClInclude>
<ClCompile Include="..\..\src\rocksdb2\db\builder.cc">
<Filter>rocksdb2\db</Filter>
</ClCompile>

View File

@@ -907,6 +907,7 @@ def get_classic_sources(toolchain):
append_sources(result, *list_sources('src/beast/beast/http/src', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/streams', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/test', '.cpp'))
append_sources(result, *list_sources('src/beast/beast/wsproto/src', '.cpp'))
append_sources(result, *list_sources('src/ripple/beast/container', '.cpp'))
append_sources(result, *list_sources('src/ripple/beast/insight', '.cpp'))
append_sources(result, *list_sources('src/ripple/beast/net', '.cpp'))
@@ -957,6 +958,7 @@ def get_unity_sources(toolchain):
'src/beast/beast/unity/beast_http_unity.cpp',
'src/beast/beast/unity/beast_streams_unity.cpp',
'src/beast/beast/unity/beast_test_unity.cpp',
'src/beast/beast/unity/beast_wsproto_unity.cpp',
'src/ripple/beast/unity/beast_container_unity.cpp',
'src/ripple/beast/unity/beast_insight_unity.cpp',
'src/ripple/beast/unity/beast_net_unity.cpp',

View File

@@ -34,7 +34,16 @@ else if [ os.name ] = HAIKU
lib network ;
}
build-project test/asio ;
if [ os.name ] = NT
{
lib ssl : : <name>ssleay32 ;
lib crypto : : <name>libeay32 ;
}
else
{
lib ssl ;
lib crypto ;
}
project beast
: requirements
@@ -49,6 +58,8 @@ project beast
<threading>multi
<link>static
<runtime-link>static
<toolset>gcc:<cxxflags>-std=c++14
<toolset>clang:<cxxflags>-std=c++14
<os>LINUX:<define>_XOPEN_SOURCE=600
<os>LINUX:<define>_GNU_SOURCE=1
<os>SOLARIS:<define>_XOPEN_SOURCE=500
@@ -68,7 +79,10 @@ project beast
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
: usage-requirements
<include>.
<include>.
:
build-dir bin
;
build-project test ;
build-project examples ;

8
src/beast/TODO.txt Normal file
View File

@@ -0,0 +1,8 @@
* kick out non-beast code
* redo directory structure
* Change build options to C++11 only
* Replace Jamroot with Jamfile

View File

@@ -27,7 +27,6 @@ namespace beast {
namespace debug {
template<class Buffers>
static
std::string
buffers_to_string(Buffers const& bs)
{

View File

@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
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
@@ -17,9 +17,5 @@
*/
//==============================================================================
#ifndef RIPPLE_WSPROTO_H_INCLUDED
#define RIPPLE_WSPROTO_H_INCLUDED
#include <ripple/wsproto/basic_socket.h>
#endif
#include <beast/wsproto/src/test/beast_wsproto_ws_test.cpp>
#include <beast/wsproto/src/test/beast_wsproto_ws_echo_test.cpp>

30
src/beast/beast/wsproto.h Normal file
View File

@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
/*
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_H_INCLUDED
#define BEAST_WSPROTO_H_INCLUDED
#include <beast/wsproto/error.h>
#include <beast/wsproto/option.h>
#include <beast/wsproto/rfc6455.h>
#include <beast/wsproto/socket.h>
#include <beast/wsproto/static_string.h>
#include <beast/wsproto/teardown.h>
#endif

View File

@@ -0,0 +1,232 @@
# Beast.WSProto
--------------------------------------------------------------------------------
Beast.WSProto provides developers with a robust WebSocket implementation
built on Boost.Asio with a consistent asynchronous model using a modern
C++ approach.
## Introduction
Today's web applications increasingly rely on alternatives to standard HTTP
to achieve performance and/or responsiveness. While WebSocket implementations
are widely available in common web development languages such as Javascript,
good implementations in C++ are scarce. A survey of existing C++ WebSocket
solutions reveals interfaces which have performance limitations, place
unecessary restrictions on callers, exhibit excess complexity, and fail to
take advantage of C++ features or the underlying network transport.
Beast.WSProto is built on Boost.Asio, a robust cross platform networking
framework that is part of Boost and also offered as a standalone library.
A proposal to add networking functionality to the C++ standard library,
based on Boost.Asio, is under consideration by the standards committee.
Since the final approved networking interface for the C++ standard library
will likely closely resemble the current interface of Boost.Asio, it is
logical for Beast.WSProto to use Boost.Asio as its network transport.
Beast.WSProto addresses the following goals:
* **Ease of Use.** WSProto offers only one socket object, whose interface
resembles that of Boost.Asio socket as closely as possible. Users familiar
with Boost.Asio will be immediately comfortable using a `wsproto::socket`.
* **Flexibility.** Library interfaces should provide callers with maximum
flexibility in implementation; Important decisions such as how to manage
buffers or be notified of completed asynchronous operations should be made
by callers not the library.
* **Performance.** The implementation should achieve the highest level
of performance possible, with no penalty for using abstractions.
* **Scalability.** The library should facilitate the development of
network applications that scale to thousands of concurrent connections.
* **Efficiency.** The library should support techniques such as
scatter-gather I/O, and allow programs to minimise data copying.
* **Basis for further abstraction.** The library should permit the
development of other libraries that provide higher levels of abstraction.
Beast.WSProto takes advantage of Boost.Asio's universal Asynchronous
model, handler allocation, and handler invocation hooks. Calls to wsproto
asynchronous initiation functions allow callers the choice of using a
completion handler, stackful or stackless coroutines, futures, or user
defined customizations (for example, Boost.Fiber). The implementation
uses handler invocation hooks (`asio_handler_invoke`), providing
execution guarantees on composed operations in a manner identical to
Boost.Asio. The implementation also uses handler allocation hooks
(`asio_handler_allocate`) when allocating memory internally for composed
operations.
There is no need for inheritance or virtual members in `wsproto::socket`.
All operations are templated and transparent to the compiler, allowing for
maximum inlining and optimization.
## Usage
All examples and identifiers mentioned in this document are written as
if the following declarations are in effect:
```C++
#include <beast/wsproto.h>
using namespace beast;
using namespace boost::asio;
```
### Creating a Socket
To participate in a WebSocket connection, callers create an instance
of `wsproto::socket` templated on the `Stream` argument, which must meet
the requirements of `AsyncReadStream`, `AsyncWriteStream`, `SyncReadStream`,
and `SyncWriteStream`. Examples of types that meet these requirements are
`ip::tcp::socket` and `ssl::stream<...>`:
```c++
io_service ios;
wsproto::socket<ip::tcp::socket> ws1(ios); // owns the socket
ssl::context ctx(ssl::context::sslv23);
wsproto::socket<ssl::stream<
ip::tcp::socket>> wss(ios, ctx); // owns the socket
ip::tcp::socket sock(ios);
wsproto::socket<ip::tcp::socket&> ws2(sock); // does not own the socket
```
### Connection Establishment
Callers are responsible for performing tasks such as connection establishment
before attempting websocket activities.
```c++
io_service ios;
wsproto::socket<ip::tcp::socket> ws(ios);
ws.next_layer().connect(ip::tcp::endpoint(
ip::tcp::address::from_string("127.0.0.1"), 80));
```
### WebSocket Handshake
After the connection is established, the socket may be used to initiate
or accept a WebSocket Update request.
```c++
// send a WebSocket Upgrade request.
ws.handshake();
```
### Sending and Receiving Messages
After the WebSocket handshake is accomplished, callers may send and receive
messages using the message oriented interface:
```c++
void echo(wsproto::socket<ip::tcp::socket>& ws)
{
streambuf sb;
wsproto::opcode op;
wsproto::read(ws, op, sb);
wsproto::write(ws, op, sb.data());
sb.consume(sb.size());
}
```
Alternatively, callers may process incoming message data
incrementally:
```c++
void echo(wsproto::socket<ip::tcp::socket>& ws)
{
streambuf sb;
wsproto::msg_info mi{};
for(;;)
{
ws.read_some(mi, sb);
if(mi.fin)
break;
}
wsproto::write(ws, op, sb.data());
}
```
### Asynchronous Completions, Coroutines, and Futures
Asynchronous versions are available for all functions:
```c++
wsproto::async_read(ws, sb, std::bind(
&on_read, beast::asio::placeholders::error));
```
Calls to WSProto asynchronous initiation functions support
asio-style completion handlers, and other completion tokens
such as support for coroutines or futures:
```c++
void echo(wsproto::socket<ip::tcp::socket>& ws,
boost::asio::yield_context yield)
{
wsproto::async_read(ws, sb, yield);
std::future<wsproto::error_code> fut =
wsproto::async_write(ws, sb.data(), boost::use_future);
...
}
```
## Implementation
### Buffers
Because calls to read WebSocket data may return a variable amount of bytes,
the interface to calls that read data require an object that meets the
requirements of `Streambuf`. This concept is modeled on
`boost::asio::basic_streambuf`, which meets the requirements of `Streambuf`
defined below.
The `Streambuf` concept is intended to permit the following implementation
strategies:
* A single contiguous character array, which is reallocated as necessary to
accommodate changes in the size of the byte sequence. This is the
implementation approach currently used in `boost::asio::basic_streambuf`.
* A sequence of one or more byte arrays, where each array is of the same
size. Additional byte array objects are appended to the sequence to
accommodate changes in the size of the byte sequence.
* A sequence of one or more byte arrays of varying sizes. Additional byte
array objects are appended to the sequence to accommodate changes in the
size of the byte sequence. This is the implementation approach currently
used in `beast::basic_streambuf`.
#### `Streambuf` requirements:
In the table below, `X` denotes a class, `a` denotes a value
of type `X`, `n` denotes a value convertible to `std::size_t`,
and `U` and `T` denote unspecified types.
expression | return | type assertion/note/pre/post-condition
------------------------- | ------------- | --------------------------------------
`X::const_buffers_type` | `T` | `T` meets the requirements for `ConstBufferSequence`.
`X::mutable_buffers_type` | `U` | `U` meets the requirements for `MutableBufferSequence`.
`a.commit(n)` | | Moves bytes from the output sequence to the input sequence.
`a.consume(n)` | | Removes bytes from the input sequence.
`a.data()` | `T` | Returns a list of buffers that represents the input sequence.
`a.prepare(n)` | `U` | Returns a list of buffers that represents the output sequence, with the given size.
`a.size()` | `std::size_t` | Returns the size of the input sequence.
`a.max_size()` | `std::size_t` | Returns the maximum size of the `Streambuf`.
### Thread Safety
Like a regular asio socket, a `wsproto::socket` is not thread safe. Callers are
responsible for synchronizing operations on the socket using an implicit or
explicit strand, as per the Asio documentation. A `wsproto::socket` supports
one active read and one active write at the same time (caller initiated close,
ping, and pong operations count as a write).
### Buffering
The implementation does not perform queueing or buffering of messages. If desired,
these features should be implemented by callers. The impact of this design is
that the caller is in full control of the allocation strategy used to store
data and the back-pressure applied on the read and write side of the underlying
TCP/IP connection.
### The `io_service`
The creation and operation of the `boost::asio::io_service` associated with the
Stream object underlying the `wsproto::socket` is completely left up to the
user of the library, permitting any implementation strategy including one that
does not require threads for environments where threads are unavailable.
Beast.WSProto itself does not use or require threads.

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 wsproto {
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

View File

@@ -0,0 +1,112 @@
//------------------------------------------------------------------------------
/*
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_DECORATOR_H_INCLUDED
#define BEAST_WSPROTO_DECORATOR_H_INCLUDED
#include <beast/http/empty_body.h>
#include <beast/http/message.h>
#include <beast/http/string_body.h>
#include <utility>
namespace beast {
namespace wsproto {
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
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,104 @@
//------------------------------------------------------------------------------
/*
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_DETAIL_ERROR_H_INCLUDED
#define BEAST_WSPROTO_DETAIL_ERROR_H_INCLUDED
#include <beast/wsproto/error.h>
namespace boost {
namespace system {
template<>
struct is_error_code_enum<beast::wsproto::error>
{
static bool const value = true;
};
} // system
} // boost
namespace beast {
namespace wsproto {
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
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,375 @@
//------------------------------------------------------------------------------
/*
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_FRAME_H_INCLUDED
#define BEAST_WSPROTO_FRAME_H_INCLUDED
#include <beast/wsproto/rfc6455.h>
#include <beast/wsproto/static_string.h>
#include <beast/wsproto/detail/utf8_checker.h>
#include <beast/asio/consuming_buffers.h>
#include <beast/asio/static_streambuf.h>
#include <boost/asio/buffer.hpp>
#include <boost/endian/buffers.hpp>
#include <cassert>
#include <cstdint>
namespace beast {
namespace wsproto {
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
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,66 @@
//------------------------------------------------------------------------------
/*
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_HYBI13_H_INCLUDED
#define BEAST_WSPROTO_HYBI13_H_INCLUDED
#include <beast/crypto/base64.h>
#include <beast/crypto/sha.h>
#include <boost/utility/string_ref.hpp>
#include <cstdint>
#include <string>
#include <type_traits>
namespace beast {
namespace wsproto {
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 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::sha_hasher h;
h(s.data(), s.size());
auto const digest = static_cast<
beast::sha_hasher::result_type>(h);
return base64_encode(digest.data(), digest.size());
}
} // detail
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,168 @@
//------------------------------------------------------------------------------
/*
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_INVOKABLE_H_INCLUDED
#define BEAST_WSPROTO_INVOKABLE_H_INCLUDED
#include <array>
#include <cassert>
#include <memory>
#include <new>
#include <utility>
namespace beast {
namespace wsproto {
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
} // wsproto
} // beast
#endif

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_WSPROTO_DETAIL_MASKGEN_H_INCLUDED
#define BEAST_WSPROTO_DETAIL_MASKGEN_H_INCLUDED
#include <boost/asio/buffer.hpp>
#include <array>
#include <climits>
#include <cstdint>
#include <random>
#include <type_traits>
namespace beast {
namespace wsproto {
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
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,141 @@
//------------------------------------------------------------------------------
/*
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_SOCKET_BASE_H_INCLUDED
#define BEAST_WSPROTO_SOCKET_BASE_H_INCLUDED
#include <beast/wsproto/error.h>
#include <beast/wsproto/rfc6455.h>
#include <beast/wsproto/detail/decorator.h>
#include <beast/wsproto/detail/frame.h>
#include <beast/wsproto/detail/invokable.h>
#include <beast/wsproto/detail/mask.h>
#include <beast/wsproto/detail/utf8_checker.h>
#include <beast/asio/streambuf.h>
#include <beast/http/empty_body.h>
#include <beast/http/message.h>
#include <beast/http/string_body.h>
#include <boost/asio/error.hpp>
#include <cassert>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
namespace beast {
namespace wsproto {
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 socket_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
socket_base()
: d_(std::make_unique<
decorator<default_decorator>>())
{
}
socket_base(socket_base&&) = default;
socket_base(socket_base const&) = delete;
socket_base& operator=(socket_base&&) = default;
socket_base& operator=(socket_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
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,184 @@
//------------------------------------------------------------------------------
/*
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_UTF8_CHECKER_H_INCLUDED
#define BEAST_WSPROTO_UTF8_CHECKER_H_INCLUDED
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <string> // DEPRECATED
namespace beast {
namespace wsproto {
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
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,72 @@
//------------------------------------------------------------------------------
/*
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_ERROR_H_INCLUDED
#define BEAST_WSPROTO_ERROR_H_INCLUDED
#include <boost/system/error_code.hpp>
namespace beast {
namespace wsproto {
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);
} // wsproto
} // beast
#include <beast/wsproto/impl/error.ipp>
#endif

View File

@@ -0,0 +1,158 @@
//------------------------------------------------------------------------------
/*
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_ACCEPT_OP_H_INCLUDED
#define BEAST_WSPROTO_ACCEPT_OP_H_INCLUDED
#include <beast/wsproto/impl/response_op.ipp>
#include <beast/asio/handler_alloc.h>
#include <beast/asio/prepare_buffers.h>
#include <beast/http/parser.h>
#include <beast/http/read.h>
#include <cassert>
#include <memory>
#include <type_traits>
namespace beast {
namespace wsproto {
// read and respond to an upgrade request
//
template<class Stream>
template<class Handler>
class socket<Stream>::accept_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
socket<Stream>& ws;
http::request<http::empty_body> req;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler, class Buffers>
data(DeducedHandler&& h_, socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Handler>
void
socket<Stream>::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);
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,198 @@
//------------------------------------------------------------------------------
/*
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_CLOSE_OP_H_INCLUDED
#define BEAST_WSPROTO_CLOSE_OP_H_INCLUDED
#include <beast/asio/handler_alloc.h>
#include <beast/asio/static_streambuf.h>
#include <memory>
namespace beast {
namespace wsproto {
// send the close message and wait for the response
//
template<class Stream>
template<class Handler>
class socket<Stream>::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
{
socket<Stream>& ws;
close_reason cr;
Handler h;
fb_type fb;
fmb_type fmb;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Handler>
void
socket<Stream>::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();
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,39 @@
//------------------------------------------------------------------------------
/*
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_ERROR_IPP_H_INCLUDED
#define BEAST_WSPROTO_ERROR_IPP_H_INCLUDED
#include <beast/wsproto/detail/error.h>
namespace beast {
namespace wsproto {
inline
error_code
make_error_code(error e)
{
return error_code(
static_cast<int>(e), detail::get_error_category());
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,170 @@
//------------------------------------------------------------------------------
/*
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_HANDSHAKE_OP_H_INCLUDED
#define BEAST_WSPROTO_HANDSHAKE_OP_H_INCLUDED
#include <beast/asio/handler_alloc.h>
#include <beast/http/empty_body.h>
#include <beast/http/message.h>
#include <beast/http/read.h>
#include <beast/http/write.h>
#include <cassert>
#include <memory>
namespace beast {
namespace wsproto {
// send the upgrade request and process the response
//
template<class Stream>
template<class Handler>
class socket<Stream>::handshake_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
socket<Stream>& 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_, socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Handler>
void
socket<Stream>::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);
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,519 @@
//------------------------------------------------------------------------------
/*
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_READ_FRAME_OP_H_INCLUDED
#define BEAST_WSPROTO_READ_FRAME_OP_H_INCLUDED
#include <beast/wsproto/teardown.h>
#include <beast/asio/handler_alloc.h>
#include <beast/asio/prepare_buffers.h>
#include <beast/asio/static_streambuf.h>
#include <cassert>
#include <memory>
namespace beast {
namespace wsproto {
// Reads a single message frame,
// processes any received control frames.
//
template<class Stream>
template<class Streambuf, class Handler>
class socket<Stream>::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
{
socket<Stream>& 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_, socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Buffers, class Handler>
void
socket<Stream>::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();
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,144 @@
//------------------------------------------------------------------------------
/*
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_READ_OP_H_INCLUDED
#define BEAST_WSPROTO_READ_OP_H_INCLUDED
#include <beast/asio/handler_alloc.h>
#include <memory>
namespace beast {
namespace wsproto {
// read an entire message
//
template<class Stream>
template<class Streambuf, class Handler>
class socket<Stream>::read_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
socket<Stream>& ws;
opcode& op;
Streambuf& sb;
Handler h;
frame_info fi;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Streambuf, class Handler>
void
socket<Stream>::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);
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,147 @@
//------------------------------------------------------------------------------
/*
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_RESPONSE_OP_H_INCLUDED
#define BEAST_WSPROTO_RESPONSE_OP_H_INCLUDED
#include <beast/asio/handler_alloc.h>
#include <beast/http/string_body.h>
#include <beast/http/write.h>
#include <memory>
namespace beast {
namespace wsproto {
// Respond to an upgrade HTTP request
template<class Stream>
template<class Handler>
class socket<Stream>::response_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
socket<Stream>& 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_, socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Handler>
void
socket<Stream>::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);
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,815 @@
//------------------------------------------------------------------------------
/*
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_IMPL_SOCKET_IPP_INCLUDED
#define BEAST_WSPROTO_IMPL_SOCKET_IPP_INCLUDED
#include <beast/wsproto/teardown.h>
#include <beast/wsproto/detail/hybi13.h>
#include <beast/wsproto/impl/accept_op.ipp>
#include <beast/wsproto/impl/close_op.ipp>
#include <beast/wsproto/impl/handshake_op.ipp>
#include <beast/wsproto/impl/read_op.ipp>
#include <beast/wsproto/impl/read_frame_op.ipp>
#include <beast/wsproto/impl/response_op.ipp>
#include <beast/wsproto/impl/write_op.ipp>
#include <beast/wsproto/impl/write_frame_op.ipp>
#include <beast/asio/append_buffers.h>
#include <beast/asio/async_completion.h>
#include <beast/asio/consuming_buffers.h>
#include <beast/asio/prepare_buffers.h>
#include <beast/asio/static_streambuf.h>
#include <beast/asio/streambuf.h>
#include <beast/asio/type_check.h>
#include <beast/http/read.h>
#include <beast/http/write.h>
#include <beast/http/reason.h>
#include <beast/http/rfc2616.h>
#include <boost/endian/buffers.hpp>
#include <algorithm>
#include <cassert>
#include <memory>
#include <utility>
namespace beast {
namespace wsproto {
namespace detail {
template<class _>
void
socket_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
socket_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
socket_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 Stream>
template<class... Args>
socket<Stream>::socket(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 Stream>
void
socket<Stream>::accept(error_code& ec)
{
accept(boost::asio::null_buffers{}, ec);
}
template<class Stream>
template<class AcceptHandler>
auto
socket<Stream>::async_accept(AcceptHandler&& handler)
{
return async_accept(boost::asio::null_buffers{},
std::forward<AcceptHandler>(handler));
}
template<class Stream>
template<class ConstBufferSequence>
void
socket<Stream>::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 Stream>
template<class ConstBufferSequence>
void
socket<Stream>::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 Stream>
template<class ConstBufferSequence, class AcceptHandler>
auto
socket<Stream>::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 Stream>
template<class Body, class Headers>
void
socket<Stream>::accept(
http::message<true, Body, Headers> const& request)
{
error_code ec;
accept(request, ec);
detail::maybe_throw(ec, "accept");
}
template<class Stream>
template<class Body, class Headers>
void
socket<Stream>::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 Stream>
template<class Body, class Headers, class AcceptHandler>
auto
socket<Stream>::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 Stream>
void
socket<Stream>::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 Stream>
template<class HandshakeHandler>
auto
socket<Stream>::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 Stream>
void
socket<Stream>::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 Stream>
template<class CloseHandler>
auto
socket<Stream>::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 Stream>
template<class Streambuf>
void
socket<Stream>::
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 Stream>
template<class Streambuf, class ReadHandler>
auto
socket<Stream>::
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 Stream>
template<class Streambuf>
void
socket<Stream>::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 Stream>
template<class Streambuf, class ReadHandler>
auto
socket<Stream>::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 Stream>
template<class ConstBufferSequence>
void
socket<Stream>::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 Stream>
template<class ConstBufferSequence, class WriteHandler>
auto
socket<Stream>::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 Stream>
template<class ConstBufferSequence>
void
socket<Stream>::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_,
append_buffers(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_,
append_buffers(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 Stream>
template<class ConstBufferSequence, class WriteHandler>
auto
socket<Stream>::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 Stream>
http::request<http::empty_body>
socket<Stream>::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 Stream>
template<class Body, class Headers>
http::response<http::string_body>
socket<Stream>::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 Stream>
template<class Body, class Headers>
void
socket<Stream>::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 Stream>
void
socket<Stream>::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);
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,170 @@
//------------------------------------------------------------------------------
/*
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_SSL_IPP_INCLUDED
#define BEAST_WSPROTO_SSL_IPP_INCLUDED
#include <beast/asio/async_completion.h>
#include <beast/asio/type_check.h>
namespace beast {
namespace wsproto {
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};
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,183 @@
//------------------------------------------------------------------------------
/*
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_TEARDOWN_IPP_INCLUDED
#define BEAST_WSPROTO_TEARDOWN_IPP_INCLUDED
#include <beast/asio/async_completion.h>
#include <beast/asio/type_check.h>
#include <memory>
namespace beast {
namespace wsproto {
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};
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,277 @@
//------------------------------------------------------------------------------
/*
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_WRITE_FRAME_OP_H_INCLUDED
#define BEAST_WSPROTO_WRITE_FRAME_OP_H_INCLUDED
#include <beast/asio/append_buffers.h>
#include <beast/asio/bind_handler.h>
#include <beast/asio/consuming_buffers.h>
#include <beast/asio/handler_alloc.h>
#include <beast/asio/static_streambuf.h>
#include <beast/wsproto/detail/frame.h>
#include <algorithm>
#include <cassert>
#include <memory>
namespace beast {
namespace wsproto {
// write a frame
//
template<class Stream>
template<class Buffers, class Handler>
class socket<Stream>::write_frame_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data : op
{
socket<Stream>& 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_, socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Buffers, class Handler>
void
socket<Stream>::
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_,
append_buffers(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_,
append_buffers(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();
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,150 @@
//------------------------------------------------------------------------------
/*
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_WRITE_OP_H_INCLUDED
#define BEAST_WSPROTO_WRITE_OP_H_INCLUDED
#include <beast/asio/consuming_buffers.h>
#include <beast/asio/prepare_buffers.h>
#include <beast/asio/handler_alloc.h>
#include <beast/asio/temp_buffer.h>
#include <beast/wsproto/detail/frame.h>
#include <algorithm>
#include <cassert>
#include <memory>
namespace beast {
namespace wsproto {
// write a message
//
template<class Stream>
template<class Buffers, class Handler>
class socket<Stream>::write_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data : op
{
socket<Stream>& ws;
consuming_buffers<Buffers> cb;
Handler h;
std::size_t remain;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
socket<Stream>& 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,
socket<Stream>& 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 Stream>
template<class Buffers, class Handler>
void
socket<Stream>::
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);
}
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,305 @@
//------------------------------------------------------------------------------
/*
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_OPTION_H_INCLUDED
#define BEAST_WSPROTO_OPTION_H_INCLUDED
#include <beast/wsproto/detail/socket_base.h>
#include <algorithm>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
namespace beast {
namespace wsproto {
/** 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
...
wsproto::socket<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
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,163 @@
//------------------------------------------------------------------------------
/*
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_RFC6455_H_INCLUDED
#define BEAST_WSPROTO_RFC6455_H_INCLUDED
#include <beast/wsproto/static_string.h>
#include <boost/optional.hpp>
#include <array>
#include <cstdint>
namespace beast {
namespace wsproto {
/** 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
} // wsproto
} // beast
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,285 @@
//------------------------------------------------------------------------------
/*
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_ASYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
#include <beast/unit_test/suite.h>
#include <beast/unit_test/thread.h>
#include <beast/asio/placeholders.h>
#include <beast/asio/streambuf.h>
#include <beast/wsproto.h>
#include <boost/optional.hpp>
#include <functional>
#include <memory>
#include <thread>
namespace beast {
namespace wsproto {
namespace test {
// Asynchronous WebSocket echo client/server
//
class async_echo_peer
{
public:
static std::size_t constexpr autobahnCycles = 520;
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
unit_test::suite& suite_;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::vector<unit_test::thread> thread_;
std::size_t n_ = 0;
public:
async_echo_peer(bool server,
endpoint_type const& ep, unit_test::suite& suite)
: suite_(suite)
, sock_(ios_)
, acceptor_(ios_)
{
if(server)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&async_echo_peer::on_accept, this,
beast::asio::placeholders::error));
}
else
{
Peer{std::move(sock_), ep, suite_};
}
auto const n = 1;
thread_.reserve(n);
for(int i = 0; i < n; ++i)
thread_.emplace_back(suite_,
[&] { ios_.run(); });
}
~async_echo_peer()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
for(auto& t : thread_)
t.join();
}
private:
class Peer
{
struct data
{
int state = 0;
unit_test::suite& suite;
boost::optional<endpoint_type> ep;
wsproto::socket<socket_type> ws;
wsproto::opcode op;
beast::streambuf sb;
int id;
data(socket_type&& sock_,
unit_test::suite& suite_)
: suite(suite_)
, ws(std::move(sock_))
, id([]
{
static int n = 0;
return ++n;
}())
{
}
data(socket_type&& sock_, endpoint_type const& ep_,
unit_test::suite& suite_)
: suite(suite_)
, ep(ep_)
, ws(std::move(sock_))
, id([]
{
static int n = 0;
return ++n;
}())
{
}
};
std::shared_ptr<data> d_;
public:
Peer(Peer&&) = default;
Peer(Peer const&) = default;
Peer& operator=(Peer&&) = delete;
Peer& operator=(Peer const&) = delete;
struct identity
{
template<class Body, class Headers>
void
operator()(http::message<true, Body, Headers>& req)
{
req.headers.replace("User-Agent", "async_echo_client");
}
template<class Body, class Headers>
void
operator()(http::message<false, Body, Headers>& resp)
{
resp.headers.replace("Server", "async_echo_server");
}
};
template<class... Args>
explicit
Peer(socket_type&& sock, Args&&... args)
: d_(std::make_shared<data>(
std::forward<socket_type>(sock),
std::forward<Args>(args)...))
{
auto& d = *d_;
d.ws.set_option(decorate(identity{}));
//d.ws.set_option(auto_fragment_size(0));
d.ws.set_option(read_message_max(64 * 1024 * 1024));
run();
}
void run()
{
auto& d = *d_;
if(! d.ep)
{
d.ws.async_accept(std::move(*this));
}
else
{
d.state = 4;
d.ws.next_layer().async_connect(
*d.ep, std::move(*this));
}
}
void operator()(error_code ec)
{
auto& d = *d_;
switch(d_->state)
{
// did accept
case 0:
if(ec)
return fail(ec, "async_accept");
// start
case 1:
if(ec)
return fail(ec, "async_handshake");
d.sb.consume(d.sb.size());
// read message
d.state = 2;
d.ws.async_read(d.op, d.sb, std::move(*this));
return;
// got message
case 2:
if(ec == wsproto::error::closed)
return;
if(ec)
return fail(ec, "async_read");
// write message
d.state = 1;
d.ws.set_option(wsproto::message_type(d.op));
d.ws.async_write(d.sb.data(), std::move(*this));
return;
// connected
case 4:
if(ec)
return fail(ec, "async_connect");
d.state = 1;
d.ws.async_handshake(
d.ep->address().to_string() + ":" +
std::to_string(d.ep->port()),
"/", std::move(*this));
return;
}
}
private:
void
fail(error_code ec, std::string what)
{
if(ec != wsproto::error::closed)
{
d_->suite.log <<
"#" << std::to_string(d_->id) << " " <<
what << ": " << ec.message();
}
}
};
void
fail(error_code ec, std::string what)
{
suite_.log <<
what << ": " << ec.message();
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
maybe_throw(ec, "accept");
socket_type sock(std::move(sock_));
if(n_ < autobahnCycles)
acceptor_.async_accept(sock_,
std::bind(&async_echo_peer::on_accept, this,
beast::asio::placeholders::error));
Peer{std::move(sock), suite_};
}
};
} // test
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,94 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include <beast/wsproto/src/test/async_echo_peer.h>
#include <beast/wsproto/src/test/sync_echo_peer.h>
#include <boost/asio.hpp>
#include <condition_variable>
#include <mutex>
namespace beast {
namespace wsproto {
namespace test {
class ws_echo_test : public unit_test::suite
{
public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
void
run() override
{
async_echo_peer s1(true, endpoint_type{
address_type::from_string("127.0.0.1"),
6000 }, *this);
sync_echo_peer s2(true, endpoint_type{
address_type::from_string("127.0.0.1"),
6001 }, *this);
boost::asio::io_service ios;
boost::asio::signal_set signals(
ios, SIGINT, SIGTERM);
std::mutex m;
bool stop = false;
std::condition_variable cv;
signals.async_wait(
[&](boost::system::error_code const& ec,
int signal_number)
{
std::lock_guard<std::mutex> lock(m);
stop = true;
cv.notify_one();
});
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return stop; });
}
};
class ws_client_test : public unit_test::suite
{
public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
void
run() override
{
pass();
{
async_echo_peer s1(false, endpoint_type{
address_type::from_string("127.0.0.1"),
9001 }, *this);
}
{
sync_echo_peer s2(false, endpoint_type{
address_type::from_string("127.0.0.1"),
9001 }, *this);
}
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(ws_echo, asio, beast);
BEAST_DEFINE_TESTSUITE_MANUAL(ws_client, asio, beast);
} // test
} // wsproto
} // beast

View File

@@ -0,0 +1,362 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include <beast/wsproto/src/test/async_echo_peer.h>
#include <beast/wsproto/src/test/sync_echo_peer.h>
#include <beast/unit_test/suite.h>
#include <beast/unit_test/thread.h>
#include <beast/http.h>
#include <boost/asio/spawn.hpp>
namespace beast {
namespace wsproto {
class ws_test : public unit_test::suite
{
public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using yield_context = boost::asio::yield_context;
endpoint_type ep_;
//--------------------------------------------------------------------------
// opcodes for creating the test plans
// concurrent read and write
struct case_1{};
// write a bad frame and shut down
struct case_2{};
//--------------------------------------------------------------------------
class coro_peer
{
error_code ec_;
boost::asio::io_service ios_;
boost::asio::ip::tcp::acceptor acceptor_;
socket_type sock_;
socket<socket_type&> ws_;
opcode op_;
beast::streambuf rb_;
beast::streambuf wb_;
yield_context* yield_;
int state_ = 0;
//unit_test::suite& test_;
public:
coro_peer(coro_peer&&) = default;
coro_peer(coro_peer const&) = delete;
coro_peer& operator=(coro_peer&&) = delete;
coro_peer& operator=(coro_peer const&) = delete;
template<class... Ops>
coro_peer(bool server, endpoint_type ep,
unit_test::suite& test, Ops const&... ops)
: acceptor_(ios_)
, sock_(ios_)
, ws_(sock_)
//, test_(test)
{
if(server)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
boost::asio::spawn(ios_,
[=](auto yield)
{
yield_ = &yield;
state_ = 10;
acceptor_.async_accept(sock_, (*yield_)[ec_]);
if(ec_)
return this->fail("accept");
state_ = 20;
ws_.async_accept((*yield_)[ec_]);
if(ec_)
return this->fail("ws.accept");
this->invoke(ops...);
state_ = -1;
});
}
else
{
boost::asio::spawn(ios_,
[=](auto yield)
{
yield_ = &yield;
state_ = 30;
sock_.async_connect(ep, (*yield_)[ec_]);
if(ec_)
return this->fail("connect");
state_ = 40;
ws_.async_handshake(ep.address().to_string() +
std::to_string(ep.port()), "/", (*yield_)[ec_]);
if(ec_)
return this->fail("handshake");
this->invoke(ops...);
state_ = -1;
});
}
}
~coro_peer()
{
}
int
state() const
{
return state_;
}
void
run_one()
{
ios_.run_one();
}
void
step_to(int to = 0)
{
while(state_ != to)
ios_.run_one();
}
private:
template<class String>
void fail(String const& s)
{
}
void invoke_1(case_1)
{
ws_.async_read(op_, rb_,
[&](auto ec)
{
if(ec)
return this->fail(ec);
rb_.consume(rb_.size());
});
state_ = 100;
ws_.async_write(
boost::asio::null_buffers{}, (*yield_)[ec_]);
if(ec_)
return fail("write");
}
void invoke_1(case_2)
{
detail::frame_header fh;
fh.op = opcode::rsv5; // bad opcode
fh.fin = true;
fh.mask = true;
fh.rsv1 = false;
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = 0;
fh.key = 0;
detail::write(wb_, fh);
state_ = 200;
boost::asio::async_write(
ws_.next_layer(), wb_.data(),
(*yield_)[ec_]);
if(ec_)
return fail("write");
ws_.next_layer().shutdown(
socket_type::shutdown_both, ec_);
if(ec_)
return fail("shutdown");
}
inline
void
invoke()
{
}
template<class Op, class... Ops>
inline
void
invoke(Op op, Ops const&... ops)
{
invoke_1(op);
invoke(ops...);
}
};
void
testInvokable()
{
endpoint_type const ep{
address_type::from_string(
"127.0.0.1"), 6000};
coro_peer server(true, ep, *this, case_1{});
coro_peer client(false, ep, *this, case_2{});
server.step_to(10); // async_accept
client.step_to(30); // async_connect
server.step_to(20); // async_accept(ws)
client.step_to(40); // async_handshake
server.step_to(100); // case_1
client.step_to(200); // case_2
client.step_to(-1);
server.step_to(-1);
}
//--------------------------------------------------------------------------
bool
maybe_fail(error_code const& ec, std::string const& what)
{
return expect(! ec, what + ": " + ec.message());
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
maybe_fail(ec, what);
throw ec;
}
}
template<class Buffers>
static
std::string
buffers_to_string(Buffers const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
for(auto i = s.size(); i-- > 0;)
if(s[i] == '\r')
s.replace(i, 1, "\\r");
else if(s[i] == '\n')
s.replace(i, 1, "\\n\n");
return s;
}
int
makeRequest(endpoint_type ep, std::string const& s)
{
using boost::asio::buffer;
boost::asio::io_service ios;
boost::asio::ip::tcp::socket sock(ios);
sock.connect(ep);
write(sock, append_buffers(
buffer(s), buffer("\r\n")));
using namespace http;
response<string_body> resp;
streambuf sb;
read(sock, sb, resp);
return resp.status;
}
void
expectStatus(endpoint_type ep,
int status, std::string const& s)
{
expect(makeRequest(ep, s) == status);
}
void
testHandshake(endpoint_type ep)
{
expectStatus(ep, 400, "GET / HTTP/1.0\r\n");
}
void
syncEchoClient(endpoint_type ep)
{
using boost::asio::buffer;
error_code ec;
boost::asio::io_service ios;
wsproto::socket<socket_type> ws(ios);
ws.next_layer().connect(ep, ec);
if(! maybe_fail(ec, "connect"))
return;
ws.handshake(ep.address().to_string(), "/", ec);
if(! maybe_fail(ec, "upgrade"))
return;
std::string s(65535, '*');
ws.write_frame(true, buffer(s), ec);
if(! maybe_fail(ec, "write"))
return;
boost::asio::streambuf sb;
wsproto::opcode op;
ws.read(op, sb, ec);
if(! maybe_fail(ec, "read"))
return;
if(! ec)
expect(op == wsproto::opcode::text);
expect(buffers_to_string(sb.data()) == s);
sb.consume(sb.size());
ws.close({}, ec);
if(! maybe_fail(ec, "close"))
return;
while(! ec)
{
ws.read(op, sb, ec);
if(! ec)
sb.consume(sb.size());
}
if(ec != error::closed)
maybe_fail(ec, "teardown");
}
void
run() override
{
//testInvokable();
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6000};
testcase("Echo Server");
test::sync_echo_peer s(true, ep, *this);
//testHandshake(ep);
syncEchoClient(ep);
}
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6001};
testcase("Async Echo Server");
test::async_echo_peer s(true, ep, *this);
//testHandshake(ep);
syncEchoClient(ep);
}
}
};
BEAST_DEFINE_TESTSUITE(ws,asio,beast);
} // wsproto
} // beast

View File

@@ -0,0 +1,182 @@
//------------------------------------------------------------------------------
/*
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_SYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
#include <beast/unit_test/suite.h>
#include <beast/asio/streambuf.h>
#include <beast/wsproto.h>
#include <boost/optional.hpp>
#include <functional>
#include <memory>
#include <thread>
namespace beast {
namespace wsproto {
namespace test {
// Synchronous WebSocket echo client/server
//
class sync_echo_peer
{
public:
static std::size_t constexpr autobahnCycles = 520;
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
unit_test::suite& suite_;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
unit_test::thread thread_;
std::size_t n_ = 0;
public:
sync_echo_peer(bool server,
endpoint_type ep, unit_test::suite& suite)
: suite_(suite)
, sock_(ios_)
, acceptor_(ios_)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&sync_echo_peer::on_accept, this,
beast::asio::placeholders::error));
thread_ = unit_test::thread(suite_,
[&]
{
ios_.run();
});
}
~sync_echo_peer()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
private:
void
fail(error_code ec, std::string what)
{
suite_.log <<
what << ": " << ec.message();
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(ec == boost::asio::error::operation_aborted)
return;
maybe_throw(ec, "accept");
++n_;
std::thread{
[
this,
sock = std::move(sock_),
work = boost::asio::io_service::work{ios_}
]() mutable
{
do_peer(std::move(sock));
}}.detach();
if(n_ < autobahnCycles)
acceptor_.async_accept(sock_,
std::bind(&sync_echo_peer::on_accept, this,
beast::asio::placeholders::error));
}
struct identity
{
template<class Body, class Headers>
void
operator()(http::message<true, Body, Headers>& req)
{
req.headers.replace("User-Agent", "sync_echo_client");
}
template<class Body, class Headers>
void
operator()(http::message<false, Body, Headers>& resp)
{
resp.headers.replace("Server", "sync_echo_server");
}
};
void
do_peer(socket_type&& sock)
{
wsproto::socket<socket_type> ws(std::move(sock));
ws.set_option(decorate(identity{}));
ws.set_option(read_message_max(64 * 1024 * 1024));
//ws.set_option(auto_fragment_size(0));
error_code ec;
ws.accept(ec);
if(ec)
{
fail(ec, "accept");
return;
}
for(;;)
{
wsproto::opcode op;
beast::streambuf sb;
ws.read(op, sb, ec);
if(ec)
break;
ws.set_option(wsproto::message_type(op));
ws.write(sb.data(), ec);
if(ec)
break;
}
if(ec && ec != wsproto::error::closed)
{
fail(ec, "read");
}
}
};
} // test
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,86 @@
//------------------------------------------------------------------------------
/*
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_SSL_H_INCLUDED
#define BEAST_WSPROTO_SSL_H_INCLUDED
#include <beast/wsproto/teardown.h>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <memory>
namespace beast {
namespace wsproto {
/** 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);
} // wsproto
} // beast
#include <beast/wsproto/impl/ssl.ipp>
#endif

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_WSPROTO_STATIC_STRING_H_INCLUDED
#define BEAST_WSPROTO_STATIC_STRING_H_INCLUDED
#include <algorithm>
#include <array>
#include <cstdint>
#include <iterator>
#include <stdexcept>
#include <string>
namespace beast {
namespace wsproto {
/** 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());
}
} // wsproto
} // beast
#endif

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_WSPROTO_TEARDOWN_H_INCLUDED
#define BEAST_WSPROTO_TEARDOWN_H_INCLUDED
#include <beast/wsproto/error.h>
#include <boost/asio/ip/tcp.hpp>
#include <type_traits>
namespace beast {
namespace wsproto {
/** 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);
} // wsproto
//------------------------------------------------------------------------------
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, wsproto::error_code& ec)
{
using wsproto::teardown;
teardown(socket, ec);
}
template<class Socket, class TeardownHandler>
inline
void
call_async_teardown(Socket& socket, TeardownHandler&& handler)
{
using wsproto::async_teardown;
async_teardown(socket,
std::forward<TeardownHandler>(handler));
}
} // wsproto_helpers
} // beast
#include <beast/wsproto/impl/teardown.ipp>
#endif

4
src/beast/doc/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
html
temp
reference.qbk
out.txt

83
src/beast/doc/Jamfile Normal file
View File

@@ -0,0 +1,83 @@
#
# 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)
#
import os ;
local broot = [ os.environ BOOST_ROOT ] ;
project beast/doc ;
using boostbook ;
using quickbook ;
using doxygen ;
xml beast_boostbook : beast.qbk ;
path-constant out : . ;
install stylesheets
:
$(broot)/doc/src/boostbook.css
:
<location>$(out)/html
;
explicit stylesheets ;
install images
:
[ glob $(broot)/doc/src/images/*.png ]
images/beast.png
images/body.png
images/message.png
:
<location>$(out)/html/images
;
explicit images ;
install callouts
:
[ glob $(broot)/doc/src/images/callouts/*.png ]
:
<location>$(out)/html/images/callouts
;
explicit callout ;
boostbook doc
:
beast_boostbook
:
<xsl:param>chapter.autolabel=0
<xsl:param>boost.image.src=images/beast.png
<xsl:param>boost.image.alt="Beast Logo"
<xsl:param>boost.image.w=1007
<xsl:param>boost.image.h=107
<xsl:param>boost.root=$(broot)
<xsl:param>chapter.autolabel=0
<xsl:param>chunk.first.sections=1 # Chunk the first top-level section?
<xsl:param>chunk.section.depth=8 # Depth to which sections should be chunked
<xsl:param>generate.section.toc.level=1 # Control depth of TOC generation in sections
<xsl:param>toc.max.depth=2 # How many levels should be created for each TOC?
<xsl:param>toc.section.depth=2 # How deep should recursive sections appear in the TOC?
:
<location>temp
<dependency>stylesheets
<dependency>images
;
#explicit doc ;
# <xsl:param>nav.layout=none
# <format>html:<xsl:param>location=../bin/doc/html
# <xsl:param>generate.toc="chapter nop section nop"
# <xsl:param>root.filename=index
# <xsl:param>output-root="../bin/html"
#[include reference.qbk]
#[xinclude index.xml]

363
src/beast/doc/beast.dox Normal file
View File

@@ -0,0 +1,363 @@
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "Beast"
PROJECT_NUMBER =
PROJECT_BRIEF = C++ Library
PROJECT_LOGO = images/beast.png
OUTPUT_DIRECTORY =
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = YES
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_PACKAGE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = YES
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = NO
SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = NO
SORT_MEMBER_DOCS = NO
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = YES
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = NO
SHOW_FILES = NO
SHOW_NAMESPACES = NO
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = \
../beast/asio/append_buffers.h \
../beast/asio/async_completion.h \
../beast/asio/basic_streambuf.h \
../beast/asio/bind_handler.h \
../beast/asio/buffers_adapter.h \
../beast/asio/consuming_buffers.h \
../beast/asio/handler_alloc.h \
../beast/asio/prepare_buffers.h \
../beast/asio/static_streambuf.h \
../beast/asio/streambuf.h \
../beast/asio/streambuf_readstream.h \
../beast/asio/type_check.h \
../beast/http/basic_headers.h \
../beast/http/basic_parser.h \
../beast/http/chunk_encode.h \
../beast/http/empty_body.h \
../beast/http/error.h \
../beast/http/message.h \
../beast/http/parser.h \
../beast/http/read.h \
../beast/http/resume_context.h \
../beast/http/stream.h \
../beast/http/streambuf_body.h \
../beast/http/string_body.h \
../beast/http/type_check.h \
../beast/http/write.h \
../beast/wsproto/error.h \
../beast/wsproto/option.h \
../beast/wsproto/rfc6455.h \
../beast/wsproto/socket.h \
../beast/wsproto/static_string.h \
../beast/wsproto/teardown.h \
INPUT_ENCODING = UTF-8
FILE_PATTERNS =
RECURSIVE = NO
EXCLUDE = ../beast/http/URL.h
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = NO
HTML_OUTPUT = dhtm
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = NO
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = YES
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = YES
XML_OUTPUT = ../bin/doc/xml
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
SEARCH_INCLUDES = YES
INCLUDE_PATH = ../
INCLUDE_FILE_PATTERNS =
PREDEFINED = DOXYGEN \
GENERATING_DOCS
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
MSCGEN_PATH =
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

177
src/beast/doc/beast.qbk Normal file
View File

@@ -0,0 +1,177 @@
[/
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)
]
[library Beast
[quickbook 1.6]
[copyright 2013 - 2016 Vinnie Falco]
[purpose C++ Library]
[license
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])
]
[authors [Falco, Vinnie]]
[category template]
[category generic]
]
[template mdash[] '''&mdash; ''']
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
[template ticket[number]'''<ulink url="https://svn.boost.org/trac/boost/ticket/'''[number]'''">'''#[number]'''</ulink>''']
[def __POSIX__ /POSIX/]
[def __Windows__ /Windows/]
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
[def __connect__ [@http://www.opengroup.org/onlinepubs/000095399/functions/connect.html `connect()`]]
[def __getpeername__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getpeername.html `getpeername()`]]
[def __getsockname__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockname.html `getsockname()`]]
[def __getsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/getsockopt.html `getsockopt()`]]
[def __ioctl__ [@http://www.opengroup.org/onlinepubs/000095399/functions/ioctl.html `ioctl()`]]
[def __recvfrom__ [@http://www.opengroup.org/onlinepubs/000095399/functions/recvfrom.html `recvfrom()`]]
[def __sendto__ [@http://www.opengroup.org/onlinepubs/000095399/functions/sendto.html `sendto()`]]
[def __setsockopt__ [@http://www.opengroup.org/onlinepubs/000095399/functions/setsockopt.html `setsockopt()`]]
[def __socket__ [@http://www.opengroup.org/onlinepubs/000095399/functions/socket.html `socket()`]]
[section:intro Introduction]
Beast is a cross-platform C++ library built on Boost, containing two modules
implementing widely used network protocols. Beast.HTTP offers a universal
model for describing, sending, and receiving HTTP messages while Beast.WSProto
provides a complete implementation of the WebSocket protocol. Their design
achieves these goals:
* [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be
used to build clients, servers, or both.
* [*Ease of Use.] HTTP messages are modeled using simple, readily
accessible objects. Functions and classes used to send and receive HTTP
or WebSocket messages are designed to resemble Boost.Asio as closely as
possible. Users familiar with Boost.Asio will be immediately comfortable
using this library.
* [*Flexibility.] Interfaces do not mandate specific implementation
strategies; important decisions such as buffer or thread management are
left to users of the library.
* [*Performance.] The implementation performs competitively, making it a
realistic choice for building a high performance network server.
* [*Scalability.] Development of network applications that scale to thousands
of concurrent connections is possible with the implementation.
* [*Basis for further abstraction.] The interfaces facilitate the
development of other libraries that provide higher levels of abstraction.
[section:requirements Requirements]
Beast requires:
* [*C++11.] A minimum of C++11 is needed.
* [*Boost.] Beast is built on Boost, especially Boost.Asio.
* [*OpenSSL.] If using TLS/Secure sockets (optional).
[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
Most of the library is header-only; however, the HTTP parser used is written
in C. To link an application that uses Beast, it is necessary to add a single
.cpp file from beast into your project's build script.
[endsect]
[section:example Examples]
These usage examples are intended to quickly impress upon readers the
flavor of the library.
[note
All examples and identifiers mentioned in this document are written as
if the following statements are in effect:
```
#include <beast/http.h>
#include <beast/wsproto.h>
using namespace beast;
using namespace beast::http;
using namespace boost::asio;
using namespace boost::asio::ip;
```
]
Use HTTP to request the root page from a website and receive the response:
```
std::string const host = "boost.org";
io_service ios;
tcp::resolver r(ios);
tcp::socket sock(ios);
connect(sock, r.resolve(tcp::resolver::query{host, "http"}));
request<empty_body> req(method_t::http_get, "/", 11);
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
write(sock, prepare(req, connection(close)));
parsed_response<streambuf_body> resp;
read(sock, resp);
...
```
Establish a WebSocket connection, send a message and receive the reply:
```
std::string const host = "boost.org";
io_service ios;
tcp::resolver r(ios);
tcp::socket sock(ios);
connect(sock, r.resolve(tcp::resolver::query{host, "ws"}));
wsproto::socket<tcp::socket&> ws(sock);
ws.handshake();
ws.write(ws, buffer("Hello, world!"));
streambuf sb;
wsproto::opcode op;
ws.read(ws, op, sb);
ws.close(); // WebSocket protocol close
```
[endsect]
[section:credits Credits]
Beast would not be possible without the considerable time and patience
contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla,
Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for
supporting its development. Thanks also to Christopher Kohloff, whose Asio
C++ library is the inspiration behind which much of the design and
documentation is based.
[endsect]
[endsect]
[include http.qbk]
[include wsproto.qbk]
[include types.qbk]
[include design.qbk]
[section:quickref Quick Reference]
[xinclude quickref.xml]
[endsect]
[include reference.qbk]
[section:idx Index]
[xinclude index.xml]
[endsect]

439
src/beast/doc/boostbook.dtd Normal file
View File

@@ -0,0 +1,439 @@
<!--
BoostBook DTD - development version
For further information, see: http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Boost_Documentation_Format
Copyright (c) 2002 by Peter Simons <simons@cryp.to>
Copyright (c) 2003-2004 by Douglas Gregor <doug.gregor -at- gmail.com>
Copyright (c) 2007 by Frank Mori Hess <fmhess@users.sourceforge.net>
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)
The latest stable DTD module is identified by the PUBLIC and SYSTEM identifiers:
PUBLIC "-//Boost//DTD BoostBook XML V1.1//EN"
SYSTEM "http://www.boost.org/tools/boostbook/dtd/1.1/boostbook.dtd"
$Revision$
$Date$
-->
<!--========== Define XInclude features. ==========-->
<!-- This is not really integrated into the DTD yet. Needs more
research. -->
<!--
<!ELEMENT xi:include (xi:fallback)?>
<!ATTLIST xi:include
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude"
href CDATA #REQUIRED
parse (xml|text) "xml"
encoding CDATA #IMPLIED>
<!ELEMENT xi:fallback ANY>
<!ATTLIST xi:fallback
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude">
-->
<!ENTITY % local.common.attrib "last-revision CDATA #IMPLIED">
<!--========== Define the BoostBook extensions ==========-->
<!ENTITY % boost.common.attrib "%local.common.attrib;
id CDATA #IMPLIED">
<!ENTITY % boost.namespace.mix
"class|class-specialization|struct|struct-specialization|
union|union-specialization|typedef|enum|
free-function-group|function|overloaded-function|
namespace">
<!ENTITY % boost.template.mix
"template-type-parameter|template-nontype-parameter|template-varargs">
<!ENTITY % boost.class.members
"static-constant|typedef|enum|
copy-assignment|constructor|destructor|method-group|
method|overloaded-method|data-member|class|class-specialization|struct|
struct-specialization|union|union-specialization">
<!ENTITY % boost.class.mix
"%boost.class.members;|free-function-group|function|overloaded-function">
<!ENTITY % boost.class.content
"template?, inherit*, purpose?, description?,
(%boost.class.mix;|access)*">
<!ENTITY % boost.class-specialization.content
"template?, specialization?, inherit?, purpose?, description?,
(%boost.class.mix;|access)*">
<!ENTITY % boost.function.semantics
"purpose?, description?, requires?, effects?, postconditions?,
returns?, throws?, complexity?, notes?, rationale?">
<!ENTITY % library.content
"libraryinfo, (title, ((section|library-reference|testsuite))+)?">
<!ELEMENT library (%library.content;)>
<!ATTLIST library
name CDATA #REQUIRED
dirname CDATA #REQUIRED
html-only CDATA #IMPLIED
url CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT boostbook (title, (chapter|library)*)>
<!ATTLIST boostbook %boost.common.attrib;>
<!ELEMENT libraryinfo (author+, copyright*, legalnotice*, librarypurpose, librarycategory*)>
<!ATTLIST libraryinfo %boost.common.attrib;>
<!ELEMENT librarypurpose (#PCDATA|code|ulink|functionname|methodname|classname|macroname|headername|enumname|globalname)*>
<!ATTLIST librarypurpose %boost.common.attrib;>
<!ELEMENT librarycategory (#PCDATA)>
<!ATTLIST librarycategory
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT libraryname (#PCDATA)>
<!ATTLIST libraryname %boost.common.attrib;>
<!ELEMENT library-reference ANY>
<!ATTLIST library-reference
%boost.common.attrib;>
<!ELEMENT librarylist EMPTY>
<!ATTLIST librarylist %boost.common.attrib;>
<!ELEMENT librarycategorylist (librarycategorydef)*>
<!ATTLIST librarycategorylist %boost.common.attrib;>
<!ELEMENT librarycategorydef (#PCDATA)>
<!ATTLIST librarycategorydef
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT header ANY>
<!ATTLIST header
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT namespace (%boost.namespace.mix;)*>
<!ATTLIST namespace
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT class (%boost.class.content;)>
<!ATTLIST class
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT struct (%boost.class.content;)>
<!ATTLIST struct
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT union (%boost.class.content;)>
<!ATTLIST union
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT class-specialization (%boost.class-specialization.content;)>
<!ATTLIST class-specialization
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT struct-specialization (%boost.class-specialization.content;)>
<!ATTLIST struct-specialization
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT union-specialization (%boost.class-specialization.content;)>
<!ATTLIST union-specialization
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT access (%boost.class.members;)+>
<!ATTLIST access
name CDATA #REQUIRED
%boost.common.attrib;>
<!--========= C++ Templates =========-->
<!ELEMENT template (%boost.template.mix;)*>
<!ATTLIST template %boost.common.attrib;>
<!ELEMENT template-type-parameter (default?, purpose?)>
<!ATTLIST template-type-parameter
name CDATA #REQUIRED
pack CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT template-nontype-parameter (type, default?, purpose?)>
<!ATTLIST template-nontype-parameter
name CDATA #REQUIRED
pack CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT template-varargs EMPTY>
<!ATTLIST template-varargs %boost.common.attrib;>
<!ELEMENT specialization (template-arg)*>
<!ATTLIST specialization %boost.common.attrib;>
<!ELEMENT template-arg ANY>
<!ATTLIST template-arg
pack CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT default ANY>
<!ATTLIST default %boost.common.attrib;>
<!ELEMENT inherit (type, purpose?)>
<!ATTLIST inherit
access CDATA #IMPLIED
pack CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT purpose ANY>
<!ATTLIST purpose %boost.common.attrib;>
<!ELEMENT description ANY>
<!ATTLIST description %boost.common.attrib;>
<!ELEMENT type ANY>
<!ATTLIST type %boost.common.attrib;>
<!ELEMENT typedef (type, purpose?, description?)>
<!ATTLIST typedef
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT enum (enumvalue*, purpose?, description?)>
<!ATTLIST enum
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT enumvalue (default?, purpose?, description?)>
<!ATTLIST enumvalue
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT static-constant (type, default, purpose?, description?)>
<!ATTLIST static-constant
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT data-member (type, purpose?, description?)>
<!ATTLIST data-member
name CDATA #REQUIRED
specifiers CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT paramtype ANY>
<!ATTLIST paramtype %boost.common.attrib;>
<!ELEMENT effects ANY>
<!ATTLIST effects %boost.common.attrib;>
<!ELEMENT postconditions ANY>
<!ATTLIST postconditions %boost.common.attrib;>
<!ELEMENT method-group (method|overloaded-method)*>
<!ATTLIST method-group
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT constructor (template?, parameter*, %boost.function.semantics;)>
<!ATTLIST constructor
specifiers CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT destructor (%boost.function.semantics;)>
<!ATTLIST destructor
specifiers CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT method (template?, type, parameter*, %boost.function.semantics;)>
<!ATTLIST method
name CDATA #REQUIRED
cv CDATA #IMPLIED
specifiers CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT function (template?, type, parameter*, %boost.function.semantics;)>
<!ATTLIST function
name CDATA #REQUIRED
specifiers CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT overloaded-method (signature*, %boost.function.semantics;)>
<!ATTLIST overloaded-method
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT overloaded-function (signature*, %boost.function.semantics;)>
<!ATTLIST overloaded-function
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT signature (template?, type, parameter*)>
<!ATTLIST signature
cv CDATA #IMPLIED
specifiers CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT requires ANY>
<!ATTLIST requires %boost.common.attrib;>
<!ELEMENT returns ANY>
<!ATTLIST returns %boost.common.attrib;>
<!ELEMENT throws ANY>
<!ATTLIST throws %boost.common.attrib;>
<!ELEMENT complexity ANY>
<!ATTLIST complexity %boost.common.attrib;>
<!ELEMENT notes ANY>
<!ATTLIST notes %boost.common.attrib;>
<!ELEMENT rationale ANY>
<!ATTLIST rationale %boost.common.attrib;>
<!ELEMENT functionname (#PCDATA)>
<!ATTLIST functionname
alt CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT enumname (#PCDATA)>
<!ATTLIST enumname
alt CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT macroname (#PCDATA)>
<!ATTLIST macroname
alt CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT headername (#PCDATA)>
<!ATTLIST headername
alt CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT globalname (#PCDATA)>
<!ATTLIST globalname
alt CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT copy-assignment
(template?, type?, parameter*, %boost.function.semantics;)>
<!ATTLIST copy-assignment
cv CDATA #IMPLIED
specifiers CDATA #IMPLIED
%boost.common.attrib;>
<!ELEMENT free-function-group (function|overloaded-function)*>
<!ATTLIST free-function-group
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT precondition ANY>
<!ATTLIST precondition %boost.common.attrib;>
<!ELEMENT code ANY>
<!ATTLIST code %boost.common.attrib;>
<!ELEMENT using-namespace EMPTY>
<!ATTLIST using-namespace
name CDATA #REQUIRED
%boost.common.attrib;>
<!ELEMENT using-class EMPTY>
<!ATTLIST using-class
name CDATA #REQUIRED
%boost.common.attrib;>
<!--========== Boost Testsuite Extensions ==========-->
<!ENTITY % boost.testsuite.tests
"compile-test|link-test|run-test|
compile-fail-test|link-fail-test|run-fail-test">
<!ENTITY % boost.testsuite.test.content
"source*, lib*, requirement*, purpose, if-fails?">
<!ELEMENT testsuite ((%boost.testsuite.tests;)+)>
<!ATTLIST testsuite %boost.common.attrib;>
<!ELEMENT compile-test (%boost.testsuite.test.content;)>
<!ATTLIST compile-test
filename CDATA #REQUIRED
name CDATA #IMPLIED>
<!ELEMENT link-test (%boost.testsuite.test.content;)>
<!ATTLIST link-test
filename CDATA #REQUIRED
name CDATA #IMPLIED>
<!ELEMENT run-test (%boost.testsuite.test.content;)>
<!ATTLIST run-test
filename CDATA #REQUIRED
name CDATA #IMPLIED>
<!ELEMENT compile-fail-test (%boost.testsuite.test.content;)>
<!ATTLIST compile-fail-test
filename CDATA #REQUIRED
name CDATA #IMPLIED>
<!ELEMENT link-fail-test (%boost.testsuite.test.content;)>
<!ATTLIST link-fail-test
filename CDATA #REQUIRED
name CDATA #IMPLIED>
<!ELEMENT run-fail-test (%boost.testsuite.test.content;)>
<!ATTLIST run-fail-test
filename CDATA #REQUIRED
name CDATA #IMPLIED>
<!ELEMENT source (#PCDATA|snippet)*>
<!ELEMENT snippet EMPTY>
<!ATTLIST snippet
name CDATA #REQUIRED>
<!ELEMENT lib (#PCDATA)>
<!ELEMENT requirement (#PCDATA)>
<!ATTLIST requirement
name CDATA #REQUIRED>
<!ELEMENT if-fails ANY>
<!ELEMENT parameter (paramtype, default?, description?)>
<!ATTLIST parameter
name CDATA #IMPLIED
pack CDATA #IMPLIED>
<!ELEMENT programlisting ANY>
<!ATTLIST programlisting
name CDATA #IMPLIED>
<!--========== Customize the DocBook DTD ==========-->
<!ENTITY % local.tech.char.class "|functionname|libraryname|enumname|headername|macroname|code">
<!ENTITY % local.para.class
"|using-namespace|using-class|librarylist|librarycategorylist">
<!ENTITY % local.descobj.class "|libraryinfo">
<!ENTITY % local.classname.attrib "alt CDATA #IMPLIED">
<!ENTITY % local.methodname.attrib "alt CDATA #IMPLIED">
<!ENTITY % local.refentry.class "|library-reference|testsuite">
<!ENTITY % local.title.char.mix "">
<!ENTITY % programlisting.module "IGNORE">
<!ENTITY % parameter.module "IGNORE">
<!ENTITY % function.module "IGNORE">
<!ENTITY % type.module "IGNORE">
<!--========== Import DocBook DTD ==========-->
<!ENTITY % DocBook PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
%DocBook;

213
src/beast/doc/design.qbk Normal file
View File

@@ -0,0 +1,213 @@
[/
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)
]
[section:design Design choices]
The implementations are driven by business needs of cryptocurrency server
applications ([link https://ripple.com Ripple] written in C++. These
needs were not met by existing solutions so new code was written. The new
code tries to avoid design flaws encountered in the already-existing software
libraries:
* Don't sacrifice performance.
* Don't do too much, otherwise interfaces become rigid.
* Symmetric interfaces (client and server the same, or close to it).
* Emulate Boost.Asio interfaces as much as possible, since Asio is
proven and it is familiar to users.
* Let library users make the important decisions such as how to
allocate memory or how to leverage flow control.
Beast formalizes the [link beast.types.Streambuf [*`Streambuf`]] concept,
and relies heavily on the Boost.Asio [*`ConstBufferSequence`] and
[*`MutableBufferSequence`] concepts for passing buffers to functions.
The authors have found the `Streambuf` and buffer sequence interfaces
to be optimal for interacting with Asio, and for other tasks such as
incremental parsing of data in buffers (for example, parsing websocket
frames stored in a [link beast.ref.static_streambuf `static_streambuf`]).
During the development of Beast the authors have studied other software
packages and in particular the comments left during the Boost Review process
of other packages offering similar functionality. In this section we attempt
to address those issues.
[variablelist
[[
"I would also like to see instances of this library being used
in production. That would give some evidence that the design
works in practice.""
][
Beast.HTTP and Beast.WebSocket will be used in [*rippled], an
asynchronous peer to peer server that implements the
[*Ripple Consensus Protocol]. These servers are deployed in multiple
production environments, with banks in many countries running client
applications that connect to [*rippled].
]]
]
[section:http HTTP]
For HTTP we to model the message to maximize flexibility of implementation
strategies while allowing familiar verbs such as [*`read`] and [*`write`].
The HTTP interface is further driven by the needs of the WebSocket module,
as a WebSocket session requires a HTTP Upgrade handshake exchange at the
start. Other design goals:
* Don't try to invent a complete web server or client
* Have simple free functions to send and receive messages.
* Allow the message object to be customized,
[variablelist
[[
"Some more advanced examples, e.g. including TLS with client/server
certificates would help.""
][
The HTTP interface doesn't try to reinvent the wheel, it just uses
the `boost::asio::ip::tcp::socket` or `boost::asio::ssl::stream` that
you set up beforehand. Callers use the interfaces already existing
on those objects to make outgoing connections, accept incoming connections,
or establish TLS sessions with certificates. We find the available Asio
examples for performing these tasks sufficient.
]]
[[
"A built-in router?"
][
We presume this means a facility to match expressions against the URI
in HTTP requests, and dispatch them to calling code. The authors feel
that this is a responsibility of higher level code. Beast.HTTP does
not try to offer a web server.
]]
[[
"Cookies? Forms/File Uploads?""
][
Cookies, or managing these types of HTTP headers in general, is the
responsibility of higher levels. Beast.HTTP just tries to get complete
messages to and from the calling code. It deals in the HTTP headers just
enough to process the message body and leaves the rest to callers. However,
for forms and file uploads the symmetric interface of the message class
allows HTTP requests to include arbitrary body types including those needed
to upload a file or fill out a form.
]]
[[
"...supporting TLS (is this a feature? If not this would be a show-stopper),
etc.
][
Beast.HTTP does not provide direct facilities for implementing TLS
connections; however, the interfaces already existing on the
`boost::asio::ssl::stream` are available and can be used to establish
secure connections. Then, functions like `http::read` or `http::async_write`
can work with those encrypted connections with no problem.
]]
[[
"There should also be more examples of how to integrate the http service
with getting files from the file system, generating responses CGI-style"
][
The design goal for the library is to not try to invent a web server.
We feel that there is a strong need for a basic implementation that
models the HTTP message and provides functions to send and receive them
over Asio. Such an implementation should serve as a building block upon
which higher abstractions such as the aforementioned HTTP service or
cgi-gateway can be built.
]]
[[
"You should send a 100-continue to ask for the rest of the body if required."
][
These behaviors are best left to the calling software. A future library
can build on Beast.HTTP to provide these behaviors.
]]
[[
"What about HTTP/2?""
][
Many reviewers feel that HTTP/2 support is an essential feature of
a HTTP library. The authors agree that HTTP/2 is important but also
feel that the most sensible implementation is one that does not re-use
the same network reading and writing interface for 2 as that for 1.0
and 1.1.
The Beast.HTTP message model is suitable for HTTP/2 and can be re-used.
The IEFT HTTP Working Group adopted message compatiblity with HTTP/1.x
as an explicit goal. A parser can simply emit full headers after
decoding the compressed HTTP/2 headers. The stream ID is not logicaly
part of the message but rather message metadata and should be
communicated out-of-band (see below). HTTP/2 sessions begin with a
traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
upgrade. A HTTP/2 implementation can use existing Beast.HTTP primitives
to perform this handshake.
Free functions for HTTP/2 sessions are not possible because of the
requirement to maintain per-session state. For example, to decode the
compressed headers. Or to remember and respect the remote peer's window
settings. The authors propose that a HTTP/2 implementation be written
as a separate class template, similar to the `websocket::stream` but with
additional interfaces to support version 2 features. We feel that
Beast.HTTP offers enough useful functionality to justify inclusion,
so that developers can take advantage of it right away instead of
waiting.
]]
]
[endsect]
[section:websocket WebSocket]
[variablelist
[[
What about message compression?
][
The feature is not currently present in the library, but the choice
of type requirements for buffers passed to the read functions have been
made with compression in mind. There is the plan to add this feature;
however, we feel that even without compression users can begin taking
advantage of the WebSocket protocol immediately with this library.
]]
[[
Where is the TLS/SSL interface?
][
The `websocket::stream` just wraps the socket or stream that you
provide (for example, a `boost::asio::ip::tcp::socket` or a
`boost::asio::ssl::stream`). You establish your TLS connection
using the interface on `ssl::stream` like shown in all of the Asio
examples, they construct your `websocket::stream` around it.
It works perfectly fine - Beast.WebSocket doesn't try to reinvent the
wheel or put a fresh coat of interface paint on the `ssl::stream`.
The WebSocket implementation [*does] provides support for shutting down
the TLS connection through the use of the ADL compile-time virtual functions
[link beast.ref.wsproto__teardown `teardown`] and
[link beast.ref.wsproto__async_teardown `async_teardown`]. These will
properly close the connection as per rfc6455 and overloads are available
for TLS streams. Callers may provide their own overloads of these functions
for user-defined next layer types.
]]
]
[endsect]
[endsect]

215
src/beast/doc/http.qbk Normal file
View File

@@ -0,0 +1,215 @@
[/
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)
]
[section:http HTTP]
Beast.HTTP offers programmers simple and performant models of HTTP messages and
their associated operations including synchronous and asynchronous reading and
writing using Boost.Asio.
The HTTP protocol is described fully in
[@https://tools.ietf.org/html/rfc2616 rfc2616]
[section:motivation Motivation]
The HTTP protocol is pervasive in network applications. As C++ is a logical
choice for high performance network servers, there is great utility in solid
building blocks for manipulating, sending, and receiving HTTP messages
compliant with the Hypertext Transfer Protocol and the supplements that
follow. Unfortunately reliable implementations or industry standards do not
exist in C++.
Beast.HTTP is built on Boost.Asio and uses HTTP parser from NodeJS, which is
extensively field tested and exceptionally robust. A proposal to add networking
functionality to the C++ standard library, based on Boost.Asio, is under
consideration by the standards committee. Since the final approved networking
interface for the C++ standard library will likely closely resemble the current
interface of Boost.Asio, it is logical for Beast.HTTP to use Boost.Asio as its
network transport.
[note The documentation which follows assumes familiarity with
both Boost.Asio and the HTTP protocol specification described in
[@https://tools.ietf.org/html/rfc2616 rfc2616] ]
[endsect]
[section:example Example]
All examples and identifiers mentioned in this document are written as
if the following declarations are in effect:
```
#include <beast/http.h>
using namespace beast::http;
using namespace boost::asio;
```
Create a HTTP request:
```
request<string_body> req({method_t::http_get, "/", 11});
req.headers.insert("Host", "127.0.0.1:80");
req.headers.insert("User-Agent", "Beast.HTTP");
```
To send a message it must first be prepared through a call to `prepare`. This
customization point transforms the `message` into a `prepared_message`,
filling in some standard HTTP behavior and allowing the Body associated with
the message to perform preparatory steps. For example, a string body may set
the Content-Length and Content-Type appropriately.
```
void send_request(ip::tcp::socket& sock,
request<string_body>&& req)
{
// Send the request on the socket
write(sock, prepare(req, connection(keep_alive));
}
```
Messages can be read from the network and parsed into a `parsed_message` object,
which extends the `message` by adding parse-specific metadata such as the
keep-alive which is context sensitive (depending on the HTTP version for
example). When preparing a response for sending, `prepare` must be called with
an additional parameter, the corresponding parsed request. The implementation
inspects the contents of the request to set dependent fields of the response.
This example reads a message, builds a response, and sends it.
```
void handle_connection(ip::tcp::socket& sock)
{
parsed_request<string_body> req;
read(sock, req);
response<string_body> resp;
...
write(sock, prepare(resp, req));
}
```
[endsect]
[section:message Message model]
A HTTP message (referred to hereafter as "message") contains a request or
response line, a series of zero or more name/value pairs (collectively
termed "headers"), and a series of octets called the message body which may
be zero in length. Applications using HTTP often perform these operations
on messages:
* [*Parse] a new message from a series of octets.
* [*Assemble] a new message from scratch or from an existing message.
* [*Serialize] a message into a series of octets.
* [*Read] a message from a stream. This can be thought of as a compound
operation; a network read, followed by a [*parse].
* [*Write] a message to a stream. This can be thought of as a compound
operation: a [*serialize] followed by a network write.
The spectrum of hardware and software platforms which perform these typical
HTTP operations is vast, ranging from powerful servers in large datacenters
to tiny resource-limited embedded devices. No single concrete implementation
of a class intended to model messages can efficiently serve all needs.
For example, an object that minimizes resources during parsing may not be
able to edit and change headers dynamically. A message that represents the
message body as a disk file may support sending but not parsing. Many efficient
and correct models of messages exist, supporting some or all of the
operations listed above.
To support a variety of implementation strategies, we introduce customization
points for the header and body via class template arguments. This illustration
shows the message class template (boilerplate present in the actual
declaration has been removed for clarity):
[$images/message.png [width 580px] [height 225px]]
The default constructor, move special members, and copy special members are
all defaulted. A message is movable, copyable, or default constructible based
on the capabilities of its template arguments.
Messages modeled in this fashion are ['complete], containing all of the
information required to perform the supported set of operations. They are
['first-class types], returnable from functions and composable. HTTP
requests and responses are distinct types, allowing functions to be
overloaded on the type of message.
[endsect]
[section:body Body parameter]
The `Body` template argument in the `message` class must meet the
[link beast.types.Body [*`Body`] requirements]. It provides customization
of the data member in the message, the algorithm for parsing, and the
algorithm for serialization:
[$images/body.png [width 510px] [height 210px]]
The following types, provided by the library, meet the `Body` requirements:
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
Used in GET requests where there is no message body.
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
`value_type` of `std::string`. Useful for quickly putting together a request
or response with simple text in the message body (such as an error message).
Subject to the performance limit of strings if large amounts of data are
inserted.
* [link beast.ref.http__basic_streambuf_body [*`basic_streambuf_body`:]] A body with a
`value_type` of `streambuf`. A streambuf is an efficient storage object which
uses multiple octet arrays of varying lengths to represent data.
Instances of the optional nested types `writer` and `reader` perform
serialization and deserialization of the message body. If either or
both of these types are present, the message becomes serializable, parsable,
or both. They model [link beast.types.Reader [*`Reader`]] and
[link beast.types.Writer [*`Writer`]] respectively.
For specialized applications, users may implement their own types which
meet the requirements. The examples included with this library provide a
Body implementation used to serve files in a HTTP server.
[endsect]
[section:headers Headers container]
The `Headers` type represents the field/value pairs present in every HTTP
message. These types implement the
[link beast.types.FieldSequence [*`FieldSequence`]]
concept. The value type of a field sequence is an object meeting the
requirements of [link beast.types.Field [*`Field`]]. The implementation can
serialize any instance of `Headers` that meets the field sequence requirements.
This example shows a function which returns `true` if the specified field
sequence has a connect field:
```
template<class FieldSequence>
bool
has_connect(FieldSequence const& fs)
{
return std::find_if(fs.begin(), fs.end(),
[&](auto const& field)
{
return ci_equal(field.name(), "Connect");
});
}
```
[endsect]
[endsect]

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

13
src/beast/doc/index.xml Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "boostbook.dtd">
<!--
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)
-->
<section id="index">
<index/>
</section>

13
src/beast/doc/makeqbk.sh Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/bash
# 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)
mkdir -p ../bin/doc/xml
doxygen beast.dox
cd ../bin/doc/xml
xsltproc combine.xslt index.xml > all.xml
cd ../../../doc
xsltproc reference.xsl ../bin/doc/xml/all.xml > reference.qbk

163
src/beast/doc/quickref.xml Normal file
View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "boostbook.dtd">
<!--
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)
-->
<informaltable frame="all">
<tgroup cols="4">
<colspec colname="a"/>
<colspec colname="b"/>
<colspec colname="c"/>
<colspec colname="d"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="b">
<bridgehead renderas="sect2">HTTP</bridgehead>
</entry>
<entry valign="center" namest="c" nameend="d">
<bridgehead renderas="sect2">WebSocket</bridgehead>
</entry>
</row>
</thead>
<tbody>
<row>
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__basic_headers">basic_headers</link></member>
<member><link linkend="beast.ref.http__basic_parser">basic_parser</link></member>
<member><link linkend="beast.ref.http__basic_streambuf_body">basic_streambuf_body</link></member>
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
<member><link linkend="beast.ref.http__error_code">error_code</link></member>
<member><link linkend="beast.ref.http__message">message</link></member>
<member><link linkend="beast.ref.http__resume_context">resume_context</link></member>
<member><link linkend="beast.ref.http__stream">stream</link></member>
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__chunk_encode">chunk_encode</link></member>
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
<member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__write">write</link></member>
</simplelist>
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.types.Body">Body</link></member>
<member><link linkend="beast.types.Field">Field</link></member>
<member><link linkend="beast.types.FieldSequence">FieldSequence</link></member>
<member><link linkend="beast.types.Reader">Reader</link></member>
<member><link linkend="beast.types.Writer">Writer</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.wsproto__close_reason">close_reason</link></member>
<member><link linkend="beast.ref.wsproto__socket">socket</link></member>
<member><link linkend="beast.ref.wsproto__static_string">static_string</link></member>
</simplelist>
<bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.wsproto__auto_fragment_size">auto_fragment_size</link></member>
<member><link linkend="beast.ref.wsproto__decorate">decorate</link></member>
<member><link linkend="beast.ref.wsproto__keep_alive">keep_alive</link></member>
<member><link linkend="beast.ref.wsproto__read_buffer_size">read_buffer_size</link></member>
<member><link linkend="beast.ref.wsproto__read_message_max">read_message_max</link></member>
<member><link linkend="beast.ref.wsproto__write_buffer_size">write_buffer_size</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.wsproto__async_teardown">async_teardown</link></member>
<member><link linkend="beast.ref.wsproto__teardown">teardown</link></member>
</simplelist>
<bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.wsproto__close_code">close_code</link></member>
<member><link linkend="beast.ref.wsproto__opcode">opcode</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>
<tgroup cols="4">
<colspec colname="a"/>
<colspec colname="b"/>
<colspec colname="c"/>
<colspec colname="d"/>
<thead>
<row>
<entry valign="center" namest="a" nameend="d">
<bridgehead renderas="sect2">Core</bridgehead>
</entry>
</row>
</thead>
<tbody>
<row>
<entry valign="top">
<bridgehead renderas="sect3">Classes</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.async_completion">async_completion</link></member>
<member><link linkend="beast.ref.basic_streambuf">basic_streambuf</link></member>
<member><link linkend="beast.ref.buffers_adapter">buffers_adapter</link></member>
<member><link linkend="beast.ref.consuming_buffers">consuming_buffers</link></member>
<member><link linkend="beast.ref.handler_alloc">handler_alloc</link></member>
<member><link linkend="beast.ref.prepared_buffers">prepared_buffers</link></member>
<member><link linkend="beast.ref.static_streambuf">static_streambuf</link></member>
<member><link linkend="beast.ref.static_streambuf_n">static_streambuf_n</link></member>
<member><link linkend="beast.ref.streambuf">streambuf</link></member>
<member><link linkend="beast.ref.streambuf_readstream">streambuf_readstream</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.append_buffers">append_buffers</link></member>
<member><link linkend="beast.ref.bind_handler">bind_handler</link></member>
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.is_AsyncReadStream">is_AsyncReadStream</link></member>
<member><link linkend="beast.ref.is_AsyncWriteStream">is_AsyncWriteStream</link></member>
<member><link linkend="beast.ref.is_BufferSequence">is_BufferSequence</link></member>
<member><link linkend="beast.ref.is_ConstBufferSequence">is_ConstBufferSequence</link></member>
<member><link linkend="beast.ref.is_Handler">is_Handler</link></member>
<member><link linkend="beast.ref.is_MutableBufferSequence">is_MutableBufferSequence</link></member>
<member><link linkend="beast.ref.is_Stream">is_Stream</link></member>
<member><link linkend="beast.ref.is_Streambuf">is_Streambuf</link></member>
<member><link linkend="beast.ref.is_SyncReadStream">is_SyncReadStream</link></member>
<member><link linkend="beast.ref.is_SyncWriteStream">is_SyncWriteStream</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
<member><link linkend="beast.types.Stream">Stream</link></member>
<member><link linkend="beast.types.Streambuf">Streambuf</link></member>
</simplelist>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>

1725
src/beast/doc/reference.xsl Normal file

File diff suppressed because it is too large Load Diff

446
src/beast/doc/types.qbk Normal file
View File

@@ -0,0 +1,446 @@
[/
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)
]
[section:types Type Requirements]
[section:Body Body]
In this table:
* `X` is a type meeting the requirements of [*`Body`].
[table Body requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::value_type`]
[]
[
The type of the `message::body` member.
If this is not movable or not copyable, the containing message
will be not movable or not copyable.
]
]
[
[`X:value_type{}`]
[]
[`DefaultConstructible`]
]
[
[`Body::reader`]
[]
[
If present, a type meeting the requirements of
[link beast.types.Reader [*`Reader`]].
Provides an implementation to parse the body.
]
]
[
[`Body::writer`]
[]
[
If present, a type meeting the requirements of
[link beast.types.Writer [*`Writer`]].
Provides an implementation to serialize the body.
]
]
]
[endsect]
[section:BufferSequence BufferSequence]
A `BufferSequence` meets [*one of] the following requirements:
* `ConstBufferSequence`
* `MutableBufferSequence`
[endsect]
[section:Field Field]
A [*`Field`] represents a single HTTP header field/value pair.
In this table:
* `X` denotes a type meeting the requirements of [*`Field`].
* `a` denotes a value of type `X`.
[table Field requirements
[[operation][type][semantics, pre/post-conditions]]
[
[`a.name()`]
[`boost::string_ref`]
[
This function returns a value implicitly convertible to
`boost::string_ref` containing the case-insensitve field
name, without leading or trailing white space.
]
]
[
[`a.value()`]
[`boost::string_ref`]
[
This function returns a value implicitly convertible to
`boost::string_ref` containing the value for the field. The
value is considered canonical if there is no leading or
trailing whitespace.
]
]
]
[endsect]
[section:FieldSequence FieldSequence]
A [*`FieldSequence`] is an iterable container whose value type meets
the requirements of [link beast.types.Field [*`Field`]].
In this table:
* `X` denotes a type that meets the requirements of [*`FieldSequence`].
* `a` is a value of type `X`.
[table FieldSequence requirements
[[operation][type][semantics, pre/post-conditions]]
[
[`X::value_type`]
[]
[
A type that meets the requirements of `Field`.
]
]
[
[`X::const_iterator`]
[]
[
A type that meets the requirements of `ForwardIterator`.
]
]
[
[`a.begin()`]
[`X::const_iterator`]
[
Returns an iterator to the beginning of the field sequence.
]
]
[
[`a.end()`]
[`X::const_iterator`]
[
Returns an iterator to the end of the field sequence.
]
]
]
[endsect]
[section:Reader Reader]
Parser implementations will construct the corresponding `reader` object
during the parse. This customization point allows the Body to determine
the strategy for storing incoming message body data.
In this table:
* `X` denotes a type meeting the requirements of [*`Reader`].
* `a` denotes a value of type `X`.
* `p` is any pointer.
* `n` is a value convertible to `std::size_t`.
* `ec` is a value of type `error_code&`.
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`
[table Reader requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X a(m);`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is
guaranteed to end no earlier than after `a` is destroyed.
]
]
[
[`a.write(p, n, ec)`]
[`void`]
[
Deserializes the input sequence into the body.
If `ec` is set, the deserialization is aborted and the error
is returned to the caller.
]
]
]
[note Definitions for required `Reader` member functions should be declared
inline so the generated code becomes part of the implementation. ]
[endsect]
[section:Writer Writer]
A `Writer` serializes the message body. The implementation creates an instance
of this type when serializing a message, and calls into it zero or more times
to provide buffers containing the data. The interface of `Writer` is intended
to allow serialization in these scenarios:
* A body that does not entirely fit in memory.
* A body produced incrementally from coroutine output.
* A body represented by zero or more buffers already in memory.
* A body as a series of buffers when the content size is not known ahead of time.
* Body data generated on demand from other threads.
* Body data computed algorithmically.
In this table:
* `X` denotes a type meeting the requirements of `Writer`.
* `a` denotes a value of type `X`.
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
* `rc` is an object of type [link beast.reference.http__resume_context resume_context].
* `ec` is a value of type `error_code&`.
* `wf` is a [*write function]: a function object of unspecified type provided
by the implementation which accepts any value meeting the requirements
of `ConstBufferSequence` as its single parameter.
[table Writer requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X a(m);`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is
guaranteed to end no earlier than after `a` is destroyed.
]
]
[
[`a.init(ec)`]
[`void`]
[
Called immediately after construction.
If `ec` is set, the serialization is aborted and the error
is propagated to the caller.
]
]
[
[`a.content_length()`]
[`std::size_t`]
[
If this member is present, it is called after initialization
and before calls to provide buffers. The serialized message will
have the Content-Length field set to the value returned from
this function. If this member is absent, the serialized message
body will be chunk-encoded for HTTP versions 1.1 and later, else
the serialized message body will be sent unmodified, with the
error `boost::asio::error::eof` returned to the caller, to notify
they should close the connection to indicate the end of the message.
]
]
[
[`a(rc, ec, wf)`]
[`boost::tribool`]
[
Called repeatedly after `init` succeeds.
`wf` is a function object which takes as its single parameter,
any value meeting the requirements of `ConstBufferSequence`.
Buffers provided by the `writer` to this [*write function] must
remain valid until the next member function of `writer` is
invoked (which may be the destructor). This function returns `true`
to indicate all message body data has been written, or `false`
if there is more body data. If the return value is
`boost::indeterminate`, the implementation will suspend the operation
until the writer invokes `rc`. It is the writers responsibility when
returning `boost::indeterminate`, to acquire ownership of the
`resume_context` via move construction and eventually call it or else
undefined behavior results.
]
]
]
[note Definitions for required `Writer` member functions should be declared
inline so the generated code becomes part of the implementation. ]
Exemplar:
```
struct writer
{
public:
/** Construct the writer.
The msg object is guaranteed to exist for the lifetime of the writer.
Exceptions:
No-throw guarantee.
@param msg The message whose body is to be written.
*/
template<bool isRequest, class Body, class Headers>
explicit
writer(message<isRequest, Body, Headers> const& msg);
/** Initialize the writer.
Called once immediately after construction.
The writer can perform initialization which may fail.
@param ec Contains the error code if any errors occur.
*/
void
init(error_code& ec);
/** Returns the content length.
If this member is present, the implementation will set the
Content-Length field accordingly. If absent, the implementation will
use chunk-encoding or terminate the connection to indicate the end
of the message.
*/
std::size_t
content_length() const;
/** Write zero or one buffer representing the message body.
Postconditions:
If return value is `true`:
* Callee does not take ownership of resume.
* Callee made zero or one calls to `write`.
* There is no more data remaining to write.
If return value is `false`:
* Callee does not take ownership of resume.
* Callee made one call to `write`.
If return value is boost::indeterminate:
* Callee takes ownership of `resume`.
* Caller suspends the write operation
until `resume` is invoked.
When the caller takes ownership of resume, the
asynchronous operation will not complete until the
caller destroys the object.
@param resume A functor to call to resume the write operation
after the writer has returned boost::indeterminate.
@param ec Set to indicate an error. This will cause an
asynchronous write operation to complete with the error.
@param write A functor the writer will call to provide the next
set of buffers. Ownership of the buffers is not transferred,
the writer must guarantee that the buffers remain valid until the
next member function is invoked, which may be the destructor.
@return `true` if there is data, `false` when done,
boost::indeterminate to suspend.
@note Undefined behavior if the callee takes ownership
of resume but does not return boost::indeterminate.
*/
template<class WriteFunction>
boost::tribool
operator()(resume_context&&, error_code&, WriteFunction&& write);
};
```
[endsect]
[section:Stream Stream]
A `Stream` meets the following requirements:
* `SyncReadStream`
* `SyncWriteStream`
* `AsyncReadStream`
* `AsyncWriteStream`
[endsect]
[section:Streambuf Streambuf]
In the table below, `X` denotes a class, `a` denotes a value
of type `X`, `n` denotes a value convertible to `std::size_t`,
and `U` and `T` denote unspecified types.
[table Streambuf requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::const_buffers_type`]
[`T`]
[`T` meets the requirements for `ConstBufferSequence`.]
]
[
[`X::mutable_buffers_type`]
[`U`]
[`U` meets the requirements for `MutableBufferSequence`.]
]
[
[`a.commit(n)`]
[`void`]
[Moves bytes from the output sequence to the input sequence.]
]
[
[`a.consume(n)`]
[`void`]
[Removes bytes from the input sequence.]
]
[
[`a.data()`]
[`T`]
[Returns a list of buffers that represents the input sequence.]
]
[
[`a.prepare(n)`]
[`U`]
[Returns a list of buffers that represents the output sequence, with
the given size.]
]
[
[`a.size()`]
[`std::size_t`]
[Returns the size of the input sequence.]
]
[
[`a.max_size()`]
[`std::size_t`]
[Returns the maximum size of the `Streambuf`.]
]
]
[endsect]
[endsect]

391
src/beast/doc/wsproto.qbk Normal file
View File

@@ -0,0 +1,391 @@
[/
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)
]
[section:wsproto WSProto]
The WebSocket Protocol enables two-way communication between a client
running untrusted code in a controlled environment to a remote host that has
opted-in to communications from that code. The protocol consists of an opening
handshake followed by basic message framing, layered over TCP. The goal of
this technology is to provide a mechanism for browser-based applications that
need two-way communication with servers that does not rely on opening multiple
HTTP connections.
Beast.WSProto provides developers with a robust WebSocket implementation
built on Boost.Asio with a consistent asynchronous model using a modern
C++ approach.
The WebSocket protocol is described fully in
[@https://tools.ietf.org/html/rfc6455 rfc6455]
[section:motivation Motivation]
Today's web applications increasingly rely on alternatives to standard HTTP
to achieve performance and/or responsiveness. While WebSocket implementations
are widely available in common web development languages such as Javascript,
good implementations in C++ are scarce. A survey of existing C++ WebSocket
solutions reveals interfaces which lack symmetry, impose performance penalties,
and needlessly restrict implementation strategies.
Beast.WSProto is built on Boost.Asio, a robust cross platform networking
framework that is part of Boost and also offered as a standalone library.
A proposal to add networking functionality to the C++ standard library,
based on Boost.Asio, is under consideration by the standards committee.
Since the final approved networking interface for the C++ standard library
will likely closely resemble the current interface of Boost.Asio, it is
logical for Beast.WSProto to use Boost.Asio as its network transport.
Beast.WSProto takes advantage of Boost.Asio's extensible asynchronous
model, handler allocation, and handler invocation hooks. Calls to
Beast.WSProto asynchronous initiation functions allow callers the choice
of using a completion handler, stackful or stackless coroutines, futures,
or user defined customizations (for example, Boost.Fiber). The
implementation uses handler invocation hooks (`asio_handler_invoke`),
providing execution guarantees on composed operations in a manner
identical to Boost.Asio. The implementation also uses handler allocation
hooks (`asio_handler_allocate`) when allocating memory internally for
composed operations.
There is no need for inheritance or virtual members in `wsproto::socket`.
All operations are templated and transparent to the compiler, allowing for
maximum inlining and optimization.
[note The documentation which follows assumes familiarity with
both Boost.Asio and the WebSocket protocol specification described in
[@https://tools.ietf.org/html/rfc6455 rfc6455] ]
[endsect]
[section:creating Creating the socket]
The interface to Beast's WebSocket implementation is a single template
class which wraps an object meeting the requirements of `SyncStream` if
synchronous operations are performed, `AsyncStream` if asynchronous
operations are performed, or both. Arguments supplied during construction
are passed to the `Stream` constructor. Here we declare two websockets
which contain the underlying stream:
```
io_service ios;
wsproto::socket<tcp::socket> ws(ios);
ssl::context ctx(ssl::context::sslv23);
wsproto::socket<ssl::stream<tcp::socket>> wss(ios, ctx);
```
For servers that can handshake in multiple protocols, it may be desired
to wrap a socket that already exists. The socket can be moved in:
```
tcp::socket&& sock;
...
wsproto::socket<tcp::socket> ws(std::move(sock));
```
Or, the wrapper can be constructed with a non-owning reference. In
this case, the caller is responsible for managing the lifetime of the
underlying socket being wrapped:
```
tcp::socket sock;
...
wsproto::socket<tcp::socket&> ws(sock);
```
The stream being wrapped can be accessed through the websocket's "next layer",
permitting callers to interact directly with its interface.
```
ssl::context ctx(ssl::context::sslv23);
wsproto::socket<ssl::stream<tcp::socket>> ws(ios, ctx);
...
ws.next_layer().shutdown(); // ssl::stream shutdown
```
[important Initiating read and write operations on the next layer while
websocket operations are being performed can break invariants, and
result in undefined behavior. ]
[endsect]
[section:connecting Making connections]
Connections are established by using the interfaces which already exist for
the wrapped stream. For example, making an outgoing connection:
```
std::string const host = "mywebapp.com";
io_service ios;
tcp::resolver r(ios);
wsproto::socket<tcp::socket> ws(ios);
connect(ws.next_layer(), r.resolve(tcp::resolver::query{host, "ws"}));
```
Accepting an incoming connection:
```
void do_accept(tcp::acceptor& acceptor)
{
wsproto::socket<tcp::socket> ws(acceptor.get_io_service());
acceptor.accept(ws.next_layer());
}
```
[note Examples use synchronous interfaces for clarity of exposition. ]
[endsect]
[section:handshaking Handshaking]
A WebSocket session begins when one side sends the HTTP Upgrade request
for websocket, and the other side sends an appropriate HTTP response
indicating that the request was accepted and that the connection has
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
and the URI of the resource to request. `wsproto::socket::hanshake` is
used to send the request with the required host and resource strings.
```
wsproto::socket<tcp::socket> ws(ios);
...
ws.set_option(wsproto::keep_alive(true));
ws.handshake("ws.mywebapp.com:80", "/cgi-bin/bitcoin-prices");
```
The `wsproto::socket` automatically handles receiving and processing
the HTTP response to the handshake request. The call to handshake is
successful if a HTTP response is received with the 101 "Switching Protocols"
status code. On failure, an error is returned or an exception is thrown.
Depending on the keep alive setting, the socket may remain open for a
subsequent handshake attempt
Performing a handshake for an incoming websocket upgrade request operates
similarly. If the handshake fails, an error is returned or exception thrown:
```
wsproto::socket<tcp::socket> ws(ios);
...
ws.accept();
```
Servers that can handshake in multiple protocols may have already read data
on the connection, or might have already received an entire HTTP request
containing the upgrade request. Overloads of `accept` allow callers to
pass in this additional buffered handshake data.
```
void do_accept(tcp::socket& sock)
{
boost::asio::streambuf sb;
read_until(sock, sb, "\r\n\r\n");
...
wsproto::socket<tcp::socket&> ws(sock);
ws.accept(sb.data());
...
}
```
Alternatively, the caller can pass an entire HTTP request if it was
obtained elsewhere:
```
void do_accept(tcp::socket& sock)
{
boost::asio::streambuf sb;
http::parsed_request<http::empty_body> request;
http::read(sock, request);
...
wsproto::socket<tcp::socket&> ws(sock);
ws.accept(request);
...
}
```
[note Identifiers in the `http` namespace are part of Beast.HTTP. ]
[endsect]
[section:messages Messages]
After the WebSocket handshake is accomplished, callers may send and receive
messages using the message oriented interface. This interface requires that
all of the buffers representing the message are known ahead of time:
```
void echo(wsproto::socket<ip::tcp::socket>& ws)
{
streambuf sb;
wsproto::opcode::value op;
ws.read(sb);
ws.set_option(wsproto::message_type(op));
wsproto::write(ws, sb.data());
sb.consume(sb.size());
}
```
[important Calls to `wsproto::socket::set_option` must be made from the same
implicit or explicit strand as that used to perform other operations. ]
[endsect]
[section:frames Frames]
Some use-cases make it impractical or impossible to buffer the entire
message ahead of time:
* Streaming multimedia to an endpoint.
* Sending a message that does not fit in memory at once.
* Providing incremental results as they become available.
For these cases, the frame oriented interface may be used. This
example reads and echoes a complete message using this interface:
```
void echo(wsproto::socket<ip::tcp::socket>& ws)
{
beast::streambuf sb;
wsproto::frame_info fi;
for(;;)
{
ws.read_frame(fi, sb);
if(fi.fin)
break;
}
ws.set_option(wsproto::message_type(fi.op));
beast::consuming_buffers<streambuf::const_buffers_type> cb(sb.data());
for(;;)
{
using boost::asio::buffer_size;
std::size_t size = std::min(buffer_size(cb));
if(size > 512)
{
ws.write_frame(false, beast::prepare_buffers(512, cb));
cb.consume(512);
}
else
{
ws.write_frame(true, cb);
break;
}
}
}
```
[endsect]
[section:controlframes Control frames]
During read operations, the implementation automatically reads and processes
WebSocket control frames such as ping, pong, and close. Pings are replied
to as soon as possible, pongs are noted. The receipt of a close frame
initiates the WebSocket close procedure, eventually resulting in the
error code `wsproto::error::closed` being delivered to the caller in
a subsequent read operation, assuming no other error takes place.
To ensure timely delivery of control frames, large messages are broken up
into smaller sized frames. The implementation chooses the size and number
of the frames making up the message. The automatic fragment size option
gives callers control over the size of these frames:
```
...
ws.set_option(wsproto::auto_fragment_size(8192));
```
The WebSocket protocol defines a procedure and control message for initiating
a close of the session. Handling of close initiated by the remote end of the
connection is performed automatically. To manually initiate a close, use
`wsproto::socket::close`:
```
ws.close();
```
[note To receive the `wsproto::error::closed` error, a read operation
is required. ]
[endsect]
[section:buffers Buffers]
Because calls to read data may return a variable amount of bytes, the
interface to calls that read data require an object that meets the requirements
of `Streambuf`. This concept is modeled on `boost::asio::basic_streambuf`.
The implementation does not perform queueing or buffering of messages. If
desired, these features should be provided by callers. The impact of this
design is that library users are in full control of the allocation strategy
used to store data and the back-pressure applied on the read and write side
of the underlying TCP/IP connection.
[endsect]
[section:async Asynchronous interface]
Asynchronous versions are available for all functions:
```
wsproto::opcode op;
ws.async_read(op, sb,
[](boost::system::error_code const& ec)
{
...
});
```
Calls to asynchronous initiation functions support the extensible asynchronous
model developed by the Boost.Asio author, allowing for traditional completion
handlers, stackful or stackless coroutines, and even futures:
```
void echo(wsproto::socket<ip::tcp::socket>& ws,
boost::asio::yield_context yield)
{
ws.async_read(sb, yield);
std::future<wsproto::error_code> fut =
ws.async_write, sb.data(), boost::use_future);
...
}
```
[endsect]
[section:io_service io_service]
The creation and operation of the `boost::asio::io_service` associated with
the underlying stream is left to the callers, permitting any implementation
strategy including one that does not require threads for environments where
threads are unavailable. Beast.WSProto itself does not use or require threads.
[endsect]
[section:safety Thread Safety]
Like a regular asio socket, a `wsproto::socket` is not thread safe. Callers
are responsible for synchronizing operations on the socket using an implicit
or explicit strand, as per the Asio documentation. The asynchronous interface
supports one active read and one active write simultaneously. Undefined
behavior results if two or more reads or two or more writes are attempted
concurrently. Caller initiated WebSocket ping, pong, and close operations
each count as an active write.
The implementation uses composed asynchronous operations internally; a high
level read can cause both reads and writes to take place on the underlying
stream. This behavior is transparent to callers.
[endsect]
[endsect]
[include quickref.xml]

View File

@@ -17,3 +17,8 @@ exe http_server :
../beast/http/src/beast_http_nodejs_parser.cpp
http_server.cpp
;
exe wsproto_echo :
../beast/http/src/beast_http_nodejs_parser.cpp
wsproto_echo.cpp
;

View File

@@ -0,0 +1,267 @@
//------------------------------------------------------------------------------
/*
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_ASYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WSPROTO_ASYNC_ECHO_PEER_H_INCLUDED
#include <beast/asio/placeholders.h>
#include <beast/asio/streambuf.h>
#include <beast/wsproto.h>
#include <boost/optional.hpp>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>
namespace beast {
namespace wsproto {
// Asynchronous WebSocket echo client/server
//
class async_echo_peer
{
public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::vector<std::thread> thread_;
public:
async_echo_peer(bool server,
endpoint_type const& ep, std::size_t threads)
: sock_(ios_)
, acceptor_(ios_)
{
if(server)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&async_echo_peer::on_accept, this,
beast::asio::placeholders::error));
}
else
{
Peer{std::move(sock_), ep};
}
thread_.reserve(threads);
for(std::size_t i = 0; i < threads; ++i)
thread_.emplace_back(
[&]{ ios_.run(); });
}
~async_echo_peer()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
for(auto& t : thread_)
t.join();
}
private:
class Peer
{
struct data
{
int state = 0;
boost::optional<endpoint_type> ep;
wsproto::socket<socket_type> ws;
wsproto::opcode op;
beast::streambuf sb;
int id;
data(socket_type&& sock_)
: ws(std::move(sock_))
, id([]
{
static int n = 0;
return ++n;
}())
{
}
data(socket_type&& sock_,
endpoint_type const& ep_)
: ep(ep_)
, ws(std::move(sock_))
, id([]
{
static int n = 0;
return ++n;
}())
{
}
};
std::shared_ptr<data> d_;
public:
Peer(Peer&&) = default;
Peer(Peer const&) = default;
Peer& operator=(Peer&&) = delete;
Peer& operator=(Peer const&) = delete;
struct identity
{
template<class Body, class Headers>
void
operator()(http::message<true, Body, Headers>& req)
{
req.headers.replace("User-Agent", "async_echo_client");
}
template<class Body, class Headers>
void
operator()(http::message<false, Body, Headers>& resp)
{
resp.headers.replace("Server", "async_echo_server");
}
};
template<class... Args>
explicit
Peer(socket_type&& sock, Args&&... args)
: d_(std::make_shared<data>(
std::forward<socket_type>(sock),
std::forward<Args>(args)...))
{
auto& d = *d_;
d.ws.set_option(decorate(identity{}));
d.ws.set_option(read_message_max(64 * 1024 * 1024));
run();
}
void run()
{
auto& d = *d_;
if(! d.ep)
{
d.ws.async_accept(std::move(*this));
}
else
{
d.state = 4;
d.ws.next_layer().async_connect(
*d.ep, std::move(*this));
}
}
void operator()(error_code ec)
{
auto& d = *d_;
switch(d_->state)
{
// did accept
case 0:
if(ec)
return fail(ec, "async_accept");
// start
case 1:
if(ec)
return fail(ec, "async_handshake");
d.sb.consume(d.sb.size());
// read message
d.state = 2;
d.ws.async_read(d.op, d.sb, std::move(*this));
return;
// got message
case 2:
if(ec == wsproto::error::closed)
return;
if(ec)
return fail(ec, "async_read");
// write message
d.state = 1;
d.ws.set_option(wsproto::message_type(d.op));
d.ws.async_write(d.sb.data(), std::move(*this));
return;
// connected
case 4:
if(ec)
return fail(ec, "async_connect");
d.state = 1;
d.ws.async_handshake(
d.ep->address().to_string() + ":" +
std::to_string(d.ep->port()),
"/", std::move(*this));
return;
}
}
private:
void
fail(error_code ec, std::string what)
{
if(ec != wsproto::error::closed)
std::cerr << "#" << d_->id << " " <<
what << ": " << ec.message() << std::endl;
}
};
void
fail(error_code ec, std::string what)
{
std::cerr <<
what << ": " << ec.message() << std::endl;
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
maybe_throw(ec, "accept");
socket_type sock(std::move(sock_));
acceptor_.async_accept(sock_,
std::bind(&async_echo_peer::on_accept, this,
beast::asio::placeholders::error));
Peer{std::move(sock)};
}
};
} // wsproto
} // beast
#endif

View File

@@ -0,0 +1,53 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include "wsproto_async_echo_peer.h"
#include "wsproto_sync_echo_peer.h"
#include <boost/asio.hpp>
#include <condition_variable>
#include <mutex>
int main()
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
beast::wsproto::async_echo_peer s1(true, endpoint_type{
address_type::from_string("127.0.0.1"), 6000 }, 4);
beast::wsproto::sync_echo_peer s2(true, endpoint_type{
address_type::from_string("127.0.0.1"), 6001 });
boost::asio::io_service ios;
boost::asio::signal_set signals(
ios, SIGINT, SIGTERM);
std::mutex m;
bool stop = false;
std::condition_variable cv;
signals.async_wait(
[&](boost::system::error_code const& ec,
int signal_number)
{
std::lock_guard<std::mutex> lock(m);
stop = true;
cv.notify_one();
});
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return stop; });
}

View File

@@ -0,0 +1,179 @@
//------------------------------------------------------------------------------
/*
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_SYNC_ECHO_PEER_H_INCLUDED
#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED
#include <beast/asio/streambuf.h>
#include <beast/wsproto.h>
#include <boost/optional.hpp>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>
namespace beast {
namespace wsproto {
// Synchronous WebSocket echo client/server
//
class sync_echo_peer
{
public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::thread thread_;
public:
sync_echo_peer(bool server, endpoint_type ep)
: sock_(ios_)
, acceptor_(ios_)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&sync_echo_peer::on_accept, this,
beast::asio::placeholders::error));
thread_ = std::thread{[&]{ ios_.run(); }};
}
~sync_echo_peer()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
private:
static
void
fail(error_code ec, std::string what)
{
std::cerr <<
what << ": " << ec.message() << std::endl;
}
static
void
fail(int id, error_code ec, std::string what)
{
std::cerr << "#" << std::to_string(id) << " " <<
what << ": " << ec.message() << std::endl;
}
static
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(ec == boost::asio::error::operation_aborted)
return;
maybe_throw(ec, "accept");
static int id_ = 0;
std::thread{
[
id = ++id_,
this,
sock = std::move(sock_),
work = boost::asio::io_service::work{ios_}
]() mutable
{
do_peer(id, std::move(sock));
}}.detach();
acceptor_.async_accept(sock_,
std::bind(&sync_echo_peer::on_accept, this,
beast::asio::placeholders::error));
}
struct identity
{
template<class Body, class Headers>
void
operator()(http::message<true, Body, Headers>& req)
{
req.headers.replace("User-Agent", "sync_echo_client");
}
template<class Body, class Headers>
void
operator()(http::message<false, Body, Headers>& resp)
{
resp.headers.replace("Server", "sync_echo_server");
}
};
void
do_peer(int id, socket_type&& sock)
{
wsproto::socket<socket_type> ws(std::move(sock));
ws.set_option(decorate(identity{}));
ws.set_option(read_message_max(64 * 1024 * 1024));
error_code ec;
ws.accept(ec);
if(ec)
{
fail(id, ec, "accept");
return;
}
for(;;)
{
wsproto::opcode op;
beast::streambuf sb;
ws.read(op, sb, ec);
if(ec)
break;
ws.set_option(wsproto::message_type(op));
ws.write(sb.data(), ec);
if(ec)
break;
}
if(ec && ec != wsproto::error::closed)
{
fail(id, ec, "read");
}
}
};
} // wsproto
} // beast
#endif

View File

@@ -7,14 +7,14 @@
import os ;
path-constant main : ../../beast/unit_test/src/main.cpp ;
path-constant test_main : ../beast/unit_test/src/main.cpp ;
unit-test all :
append_buffers.cpp
asio.cpp
async_completion.cpp
basic_streambuf.cpp
../basic_headers.cpp
basic_headers.cpp
bind_handler.cpp
buffers_adapter.cpp
buffers_debug.cpp
@@ -26,5 +26,5 @@ unit-test all :
streambuf.cpp
streambuf_readstream.cpp
type_check.cpp
$(main)
$(test_main)
;

View File

@@ -0,0 +1,358 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include <beast/wsproto/src/test/async_echo_peer.h>
#include <beast/wsproto/src/test/sync_echo_peer.h>
#include <beast/unit_test/suite.h>
#include <beast/unit_test/thread.h>
#include <beast/http.h>
#include <boost/asio/spawn.hpp>
namespace beast {
namespace wsproto {
class ws_test : public unit_test::suite
{
public:
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using yield_context = boost::asio::yield_context;
endpoint_type ep_;
//--------------------------------------------------------------------------
// opcodes for creating the test plans
// concurrent read and write
struct case_1{};
// write a bad frame and shut down
struct case_2{};
//--------------------------------------------------------------------------
class coro_peer
{
error_code ec_;
boost::asio::io_service ios_;
boost::asio::ip::tcp::acceptor acceptor_;
socket_type sock_;
socket<socket_type&> ws_;
opcode::value op_;
beast::streambuf rb_;
beast::streambuf wb_;
yield_context* yield_;
int state_ = 0;
//unit_test::suite& test_;
public:
coro_peer(coro_peer&&) = default;
coro_peer(coro_peer const&) = delete;
coro_peer& operator=(coro_peer&&) = delete;
coro_peer& operator=(coro_peer const&) = delete;
template<class... Ops>
coro_peer(bool server, endpoint_type ep,
unit_test::suite& test, Ops const&... ops)
: acceptor_(ios_)
, sock_(ios_)
, ws_(sock_)
//, test_(test)
{
if(server)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
boost::asio::spawn(ios_,
[=](auto yield)
{
yield_ = &yield;
state_ = 10;
acceptor_.async_accept(sock_, (*yield_)[ec_]);
if(ec_)
return this->fail("accept");
state_ = 20;
ws_.async_accept((*yield_)[ec_]);
if(ec_)
return this->fail("ws.accept");
this->invoke(ops...);
state_ = -1;
});
}
else
{
boost::asio::spawn(ios_,
[=](auto yield)
{
yield_ = &yield;
state_ = 30;
sock_.async_connect(ep, (*yield_)[ec_]);
if(ec_)
return this->fail("connect");
state_ = 40;
ws_.async_handshake(ep.address().to_string() +
std::to_string(ep.port()), "/", (*yield_)[ec_]);
if(ec_)
return this->fail("handshake");
this->invoke(ops...);
state_ = -1;
});
}
}
~coro_peer()
{
}
int
state() const
{
return state_;
}
void
run_one()
{
ios_.run_one();
}
void
step_to(int to = 0)
{
while(state_ != to)
ios_.run_one();
}
private:
template<class String>
void fail(String const& s)
{
}
void invoke_1(case_1)
{
async_read(ws_, op_, rb_,
[&](auto ec)
{
if(ec)
return this->fail(ec);
rb_.consume(rb_.size());
});
state_ = 100;
async_write(ws_, opcode::text,
boost::asio::null_buffers{}, (*yield_)[ec_]);
if(ec_)
return fail("write");
}
void invoke_1(case_2)
{
detail::frame_header fh;
fh.op = opcode::rsv5; // bad opcode
fh.fin = true;
fh.mask = true;
fh.rsv1 = false;
fh.rsv2 = false;
fh.rsv3 = false;
fh.len = 0;
fh.key = 0;
detail::write(wb_, fh);
state_ = 200;
boost::asio::async_write(
ws_.next_layer(), wb_.data(),
(*yield_)[ec_]);
if(ec_)
return fail("write");
ws_.next_layer().shutdown(
socket_type::shutdown_both, ec_);
if(ec_)
return fail("shutdown");
}
inline
void
invoke()
{
}
template<class Op, class... Ops>
inline
void
invoke(Op op, Ops const&... ops)
{
invoke_1(op);
invoke(ops...);
}
};
void
testInvokable()
{
endpoint_type const ep{
address_type::from_string(
"127.0.0.1"), 6000};
coro_peer server(true, ep, *this, case_1{});
coro_peer client(false, ep, *this, case_2{});
server.step_to(10); // async_accept
client.step_to(30); // async_connect
server.step_to(20); // async_accept(ws)
client.step_to(40); // async_handshake
server.step_to(100); // case_1
client.step_to(200); // case_2
client.step_to(-1);
server.step_to(-1);
}
//--------------------------------------------------------------------------
void
maybe_fail(error_code const& ec, std::string const& what)
{
expect(! ec, what + ": " + ec.message());
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
maybe_fail(ec, what);
throw ec;
}
}
template<class Buffers>
static
std::string
buffers_to_string(Buffers const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(auto const& b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
for(auto i = s.size(); i-- > 0;)
if(s[i] == '\r')
s.replace(i, 1, "\\r");
else if(s[i] == '\n')
s.replace(i, 1, "\\n\n");
return s;
}
int
makeRequest(endpoint_type ep, std::string const& s)
{
using boost::asio::buffer;
boost::asio::io_service ios;
boost::asio::ip::tcp::socket sock(ios);
sock.connect(ep);
write(sock, append_buffers(
buffer(s), buffer("\r\n")));
using namespace http;
parsed_response<string_body> m;
streambuf sb;
read(sock, sb, m);
return m.status;
}
void
expectStatus(endpoint_type ep,
int status, std::string const& s)
{
expect(makeRequest(ep, s) == status);
}
void
testHandshake(endpoint_type ep)
{
expectStatus(ep, 400, "GET / HTTP/1.0\r\n");
}
void
syncEchoClient(endpoint_type ep)
{
using boost::asio::buffer;
error_code ec;
boost::asio::io_service ios;
wsproto::socket<socket_type> ws(ios);
ws.next_layer().connect(ep, ec);
maybe_fail(ec, "connect");
ws.handshake(ep.address().to_string(), "/", ec);
maybe_fail(ec, "upgrade");
std::string const s = "Hello, world!";
ws.write(wsproto::opcode::text, true, buffer(s), ec);
maybe_fail(ec, "write");
boost::asio::streambuf sb;
wsproto::opcode::value op;
read(ws, op, sb, ec);
maybe_fail(ec, "read");
if(! ec)
expect(op == wsproto::opcode::text);
expect(buffers_to_string(sb.data()) == s);
sb.consume(sb.size());
ws.close({}, ec);
maybe_fail(ec, "close");
while(! ec)
{
read(ws, op, sb, ec);
if(! ec)
sb.consume(sb.size());
}
if(ec != error::closed)
maybe_fail(ec, "teardown");
}
void
run() override
{
//testInvokable();
#if 0
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6000};
testcase("Echo Server");
test::sync_echo_peer s(true, ep, *this);
testHandshake(ep);
syncEchoClient(ep);
}
#endif
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6001};
testcase("Async Echo Server");
test::async_echo_peer s(true, ep, *this);
//testHandshake(ep);
syncEchoClient(ep);
}
}
};
BEAST_DEFINE_TESTSUITE(ws,asio,beast);
} // wsproto
} // beast

View File

@@ -0,0 +1,43 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
#include <beast/asio/ssl_error.h>
#include <beast/unit_test/suite.h>
#include <string>
namespace beast {
class ssl_error_test : public unit_test::suite
{
public:
void run()
{
{
boost::system::error_code ec =
boost::system::error_code (335544539,
boost::asio::error::get_ssl_category ());
std::string const s = beast::error_message_with_ssl(ec);
expect(s == " (20,0,219) error:140000DB:SSL routines:SSL routines:short read");
}
}
};
BEAST_DEFINE_TESTSUITE(ssl_error,asio,beast);
} // beast

View File

@@ -67,7 +67,7 @@ public:
std::integral_constant<bool, Move>;
using propagate_on_container_swap =
std::integral_constant<bool, Swap>;
template<class U>
struct rebind
{
@@ -122,7 +122,7 @@ public:
::operator delete(p);
}
std::size_t
std::size_t
id() const
{
return id_;
@@ -274,11 +274,6 @@ public:
sb_type sb3(sb, alloc_type{});
//expect(sb3.get_allocator().id() == 3);
}
{
using alloc_type =
test_allocator<char, false, false, false, false>;
using sb_type = basic_streambuf<alloc_type>;
}
}
void run() override

View File

@@ -698,7 +698,7 @@ class View_test
auto cfg = std::make_unique<Config>();
setupConfigForUnitTests(*cfg);
for (auto const sectionName : {"port_peer", "port_http", "port_ws"})
for (auto const sectionName : {"port_peer", "port_rpc", "port_ws"})
{
Section& s = (*cfg)[sectionName];
auto const port = s.get<std::int32_t>("port");

View File

@@ -21,6 +21,7 @@
#define RIPPLE_SERVER_HANDLER_H_INCLUDED
#include <ripple/server/Handoff.h>
#include <ripple/server/WSSession.h>
#include <beast/asio/ssl_bundle.h>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
@@ -83,6 +84,20 @@ struct Handler
/** Called when the server has finished its stop. */
virtual void onStopped (Server& server) = 0;
//
// WebSockets
//
/** Called on a WebSocket Upgrade request. */
/** Called for each complete WebSocket message. */
virtual
void
onWSMessage(std::shared_ptr<WSSession> session,
std::vector<boost::asio::const_buffer> const& buffers) = 0;
};
} // ripple

View File

@@ -26,7 +26,12 @@
namespace ripple {
/** Multi-threaded, asynchronous HTTP server. */
/** A multi-protocol server.
This server maintains multiple configured listening ports,
with each listening port allows for multiple protocols including
HTTP, HTTP/S, WebSocket, Secure WebSocket, and the Peer protocol.
*/
class Server
{
public:

View File

@@ -21,6 +21,7 @@
#define RIPPLE_SERVER_SESSION_H_INCLUDED
#include <ripple/server/Writer.h>
#include <ripple/server/WSSession.h>
#include <beast/http/body.h>
#include <beast/http/message.h>
#include <ripple/beast/net/IPEndpoint.h>
@@ -130,6 +131,11 @@ public:
virtual
void
close (bool graceful) = 0;
/** Convert the connection to WebSocket. */
virtual
std::shared_ptr<WSSession>
websocketUpgrade() = 0;
};
} // ripple

View File

@@ -0,0 +1,134 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_SERVER_WSSESSION_H_INCLUDED
#define RIPPLE_SERVER_WSSESSION_H_INCLUDED
#include <ripple/server/Port.h>
#include <ripple/server/Writer.h>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/logic/tribool.hpp>
#include <algorithm>
#include <functional>
#include <memory>
#include <utility>
#include <vector>
namespace ripple {
class WSMsg
{
public:
WSMsg() = default;
WSMsg(WSMsg const&) = delete;
WSMsg& operator=(WSMsg const&) = delete;
virtual ~WSMsg() = default;
/** Retrieve message data.
Returns a tribool indicating whether or not
data is available, and a ConstBufferSequence
representing the data.
tribool values:
maybe: Data is not ready yet
false: Data is available
true: Data is available, and
it is the last chunk of bytes.
Derived classes that do not know when the data
ends (for example, when returning the output of a
paged database query) may return `true` and an
empty vector.
*/
virtual
std::pair<boost::tribool,
std::vector<boost::asio::const_buffer>>
prepare(std::size_t bytes,
std::function<void(void)> resume) = 0;
};
template<class Streambuf>
class StreambufWSMsg : public WSMsg
{
Streambuf sb_;
std::size_t n_ = 0;
public:
StreambufWSMsg(Streambuf&& sb)
: sb_(std::move(sb))
{
}
std::pair<boost::tribool,
std::vector<boost::asio::const_buffer>>
prepare(std::size_t bytes,
std::function<void(void)>) override
{
if(sb_.size() == 0)
return { true, {} };
sb_.consume(n_);
boost::tribool done;
// VFALCO TODO respect `bytes` fully
if(bytes < sb_.size())
{
n_ = bytes;
done = boost::indeterminate;
}
else
{
n_ = sb_.size();
done = true;
}
std::vector<boost::asio::const_buffer> vb;
auto const& data = sb_.data();
vb.reserve(std::distance(
data.begin(), data.end()));
std::copy(data.begin(), data.end(),
std::back_inserter(vb));
return { done, vb };
}
};
struct WSSession
{
std::shared_ptr<void> appDefined;
virtual
Port const&
port() const = 0;
virtual
boost::asio::ip::tcp::endpoint const&
remote_endpoint() const = 0;
/** Send a WebSockets message. */
virtual
void
send(std::shared_ptr<WSMsg> w) = 0;
virtual
void
close() = 0;
};
} // ripple
#endif

View File

@@ -36,7 +36,10 @@ public:
bool
complete() = 0;
/** Removes bytes from the input sequence. */
/** Removes bytes from the input sequence.
Can be called with 0.
*/
virtual
void
consume (std::size_t bytes) = 0;

View File

@@ -0,0 +1,128 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_SERVER_BASEPEER_H_INCLUDED
#define RIPPLE_SERVER_BASEPEER_H_INCLUDED
#include <ripple/server/Handler.h>
#include <ripple/server/Port.h>
#include <ripple/server/impl/io_list.h>
#include <ripple/beast/utility/WrappedSink.h>
#include <boost/asio.hpp>
#include <cassert>
#include <functional>
#include <string>
namespace ripple {
// Common part of all peers
template<class Impl>
class BasePeer
: public io_list::work
{
protected:
using clock_type = std::chrono::system_clock;
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using waitable_timer = boost::asio::basic_waitable_timer <clock_type>;
Port const& port_;
Handler& handler_;
endpoint_type remote_address_;
beast::WrappedSink sink_;
beast::Journal j_;
boost::asio::io_service::work work_;
boost::asio::io_service::strand strand_;
error_code ec_;
public:
BasePeer(Port const& port, Handler& handler,
endpoint_type remote_address,
boost::asio::io_service& io_service,
beast::Journal journal);
void
close() override;
protected:
template<class String>
void
fail(error_code ec, String const& what);
private:
Impl&
impl()
{
return *static_cast<Impl*>(this);
}
};
//------------------------------------------------------------------------------
template<class Impl>
BasePeer<Impl>::BasePeer(Port const& port, Handler& handler,
endpoint_type remote_address,
boost::asio::io_service& io_service,
beast::Journal journal)
: port_(port)
, handler_(handler)
, remote_address_(remote_address)
, sink_(journal.sink(),
[]
{
static int id = 0;
return "##" + std::to_string(++id) + " ";
}())
, j_(sink_)
, work_(io_service)
, strand_(io_service)
{
}
template<class Impl>
void
BasePeer<Impl>::close()
{
if (! strand_.running_in_this_thread())
return strand_.post(std::bind(
&BasePeer::close, impl().shared_from_this()));
error_code ec;
impl().ws_.lowest_layer().close(ec);
}
template<class Impl>
template<class String>
void
BasePeer<Impl>::fail(error_code ec, String const& what)
{
assert(strand_.running_in_this_thread());
if(! ec_ &&
ec != boost::asio::error::operation_aborted)
{
ec_ = ec;
JLOG(j_.trace()) <<
what << ": " << ec.message();
impl().ws_.lowest_layer().close(ec);
}
}
} // ripple
#endif

View File

@@ -0,0 +1,301 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_SERVER_BASEWSPEER_H_INCLUDED
#define RIPPLE_SERVER_BASEWSPEER_H_INCLUDED
#include <ripple/server/impl/BasePeer.h>
#include <ripple/protocol/BuildInfo.h>
#include <beast/wsproto.h>
#include <beast/asio/streambuf.h>
#include <beast/http/message.h>
#include <cassert>
namespace ripple {
/** Represents an active WebSocket connection. */
template <class Impl>
class BaseWSPeer
: public BasePeer<Impl>
, public WSSession
{
protected:
using clock_type = std::chrono::system_clock;
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using waitable_timer = boost::asio::basic_waitable_timer <clock_type>;
using BasePeer<Impl>::fail;
using BasePeer<Impl>::strand_;
private:
friend class BasePeer<Impl>;
http_request_type request_;
beast::wsproto::opcode op_;
beast::streambuf rb_;
beast::streambuf wb_;
std::list<std::shared_ptr<WSMsg>> wq_;
bool do_close_ = false;
public:
template<class Body, class Headers>
BaseWSPeer(
Port const& port,
Handler& handler,
endpoint_type remote_address,
beast::http::message<true, Body, Headers>&& request,
boost::asio::io_service& io_service,
beast::Journal journal);
void
run();
//
// WSSession
//
Port const&
port() const override
{
return this->port_;
}
boost::asio::ip::tcp::endpoint const&
remote_endpoint() const override
{
return this->remote_address_;
}
void
send(std::shared_ptr<WSMsg> w) override;
void
close() override;
protected:
struct identity
{
template<class Body, class Headers>
void
operator()(beast::http::message<true, Body, Headers>& req)
{
req.headers.replace("User-Agent",
BuildInfo::getFullVersionString());
}
template<class Body, class Headers>
void
operator()(beast::http::message<false, Body, Headers>& resp)
{
resp.headers.replace("Server",
BuildInfo::getFullVersionString());
}
};
Impl&
impl()
{
return *static_cast<Impl*>(this);
}
void
on_write_sb(error_code const& ec);
void
do_write();
void
on_write(error_code const& ec);
void
on_write_fin(error_code const& ec);
void
do_read();
void
on_read(error_code const& ec);
void
on_close(error_code const& ec);
virtual
void
do_close() = 0;
};
//------------------------------------------------------------------------------
template<class Impl>
template<class Body, class Headers>
BaseWSPeer<Impl>::BaseWSPeer(
Port const& port,
Handler& handler,
endpoint_type remote_address,
beast::http::message<true, Body, Headers>&& request,
boost::asio::io_service& io_service,
beast::Journal journal)
: BasePeer<Impl>(port, handler, remote_address,
io_service, journal)
, request_(std::move(request))
{
}
template<class Impl>
void
BaseWSPeer<Impl>::run()
{
if(! strand_.running_in_this_thread())
return strand_.post(std::bind(
&BaseWSPeer::run, impl().shared_from_this()));
impl().ws_.set_option(beast::wsproto::decorate(identity{}));
using namespace beast::asio;
impl().ws_.async_accept(request_, strand_.wrap(std::bind(
&BaseWSPeer::on_write_sb, impl().shared_from_this(),
placeholders::error)));
}
template<class Impl>
void
BaseWSPeer<Impl>::send(std::shared_ptr<WSMsg> w)
{
if(! strand_.running_in_this_thread())
return strand_.post(std::bind(
&BaseWSPeer::send, impl().shared_from_this(),
std::move(w)));
wq_.emplace_back(std::move(w));
if(wq_.size() == 1)
on_write({});
}
template<class Impl>
void
BaseWSPeer<Impl>::close()
{
if(! strand_.running_in_this_thread())
return strand_.post(std::bind(
&BaseWSPeer::close, impl().shared_from_this()));
if(wq_.size() > 0)
do_close_ = true;
else
impl().ws_.async_close({}, strand_.wrap(std::bind(
&BaseWSPeer::on_close, impl().shared_from_this(),
beast::asio::placeholders::error)));
}
template<class Impl>
void
BaseWSPeer<Impl>::on_write_sb(error_code const& ec)
{
if(ec)
return fail(ec, "write_resp");
do_read();
}
template<class Impl>
void
BaseWSPeer<Impl>::do_write()
{
if(! strand_.running_in_this_thread())
return strand_.post(std::bind(
&BaseWSPeer::do_write, impl().shared_from_this()));
on_write({});
}
template<class Impl>
void
BaseWSPeer<Impl>::on_write(error_code const& ec)
{
if(ec)
return fail(ec, "write");
auto& w = *wq_.front();
using namespace beast::asio;
auto const result = w.prepare(65536,
std::bind(&BaseWSPeer::do_write,
impl().shared_from_this()));
if(boost::indeterminate(result.first))
return;
if(! result.first)
impl().ws_.async_write_frame(
result.first, result.second, strand_.wrap(std::bind(
&BaseWSPeer::on_write, impl().shared_from_this(),
placeholders::error)));
else
impl().ws_.async_write_frame(
result.first, result.second, strand_.wrap(std::bind(
&BaseWSPeer::on_write_fin, impl().shared_from_this(),
placeholders::error)));
}
template<class Impl>
void
BaseWSPeer<Impl>::on_write_fin(error_code const& ec)
{
if(ec)
return fail(ec, "write_fin");
wq_.pop_front();
if(do_close_)
impl().ws_.async_close({}, strand_.wrap(std::bind(
&BaseWSPeer::on_close, impl().shared_from_this(),
beast::asio::placeholders::error)));
else if(! wq_.empty())
on_write({});
}
template<class Impl>
void
BaseWSPeer<Impl>::do_read()
{
if(! strand_.running_in_this_thread())
return strand_.post(std::bind(
&BaseWSPeer::do_read, impl().shared_from_this()));
using namespace beast::asio;
impl().ws_.async_read(op_, rb_, strand_.wrap(
std::bind(&BaseWSPeer::on_read,
impl().shared_from_this(), placeholders::error)));
}
template<class Impl>
void
BaseWSPeer<Impl>::on_read(error_code const& ec)
{
if(ec == beast::wsproto::error::closed)
return do_close();
if(ec)
return fail(ec, "read");
auto const& data = rb_.data();
std::vector<boost::asio::const_buffer> b;
b.reserve(std::distance(data.begin(), data.end()));
std::copy(data.begin(), data.end(),
std::back_inserter(b));
this->handler_.onWSMessage(impl().shared_from_this(), b);
rb_.consume(rb_.size());
do_read();
}
template<class Impl>
void
BaseWSPeer<Impl>::on_close(error_code const& ec)
{
// great
}
} // ripple
#endif

View File

@@ -170,13 +170,15 @@ Door::Door (Handler& handler, boost::asio::io_service& io_service,
, handler_(handler)
, acceptor_(io_service)
, strand_(io_service)
, ssl_ (
, ssl_(
port_.protocol.count("https") > 0 ||
//port_.protocol.count("wss") > 0 ||
port_.protocol.count("peer") > 0)
, plain_ (
port_.protocol.count("wss2") > 0 ||
port_.protocol.count("peer") > 0)
, plain_(
port_.protocol.count("http") > 0 ||
//port_.protocol.count("ws") > 0 ||
port_.protocol.count("http") > 0)
port_.protocol.count("ws2"))
{
error_code ec;
endpoint_type const local_address =

View File

@@ -21,6 +21,7 @@
#define RIPPLE_SERVER_PLAINHTTPPEER_H_INCLUDED
#include <ripple/server/impl/BaseHTTPPeer.h>
#include <ripple/server/impl/PlainWSPeer.h>
#include <memory>
namespace ripple {
@@ -44,12 +45,15 @@ public:
void
run();
std::shared_ptr<WSSession>
websocketUpgrade() override;
private:
void
do_request();
do_request() override;
void
do_close();
do_close() override;
};
//------------------------------------------------------------------------------
@@ -71,7 +75,7 @@ PlainHTTPPeer::PlainHTTPPeer (Port const& port, Handler& handler,
}
void
PlainHTTPPeer::run ()
PlainHTTPPeer::run()
{
if (!handler_.onAccept (session(), remote_address_))
{
@@ -88,6 +92,17 @@ PlainHTTPPeer::run ()
shared_from_this(), std::placeholders::_1));
}
std::shared_ptr<WSSession>
PlainHTTPPeer::websocketUpgrade()
{
auto ws = ios().emplace<PlainWSPeer>(
port_, handler_, remote_address_,
std::move(message_), std::move(stream_),
journal_);
ws->run();
return ws;
}
void
PlainHTTPPeer::do_request()
{

View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_SERVER_PLAINWSPEER_H_INCLUDED
#define RIPPLE_SERVER_PLAINWSPEER_H_INCLUDED
#include <ripple/server/impl/BaseWSPeer.h>
#include <memory>
namespace ripple {
class PlainWSPeer
: public BaseWSPeer<PlainWSPeer>
, public std::enable_shared_from_this<PlainWSPeer>
{
private:
friend class BasePeer<PlainWSPeer>;
friend class BaseWSPeer<PlainWSPeer>;
using clock_type = std::chrono::system_clock;
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using waitable_timer = boost::asio::basic_waitable_timer <clock_type>;
using socket_type = boost::asio::ip::tcp::socket;
beast::wsproto::socket<socket_type> ws_;
public:
template<class Body, class Headers>
PlainWSPeer(
Port const& port,
Handler& handler,
endpoint_type remote_address,
beast::http::message<true, Body, Headers>&& request,
socket_type&& socket,
beast::Journal journal);
private:
void
do_close() override;
};
//------------------------------------------------------------------------------
template<class Body, class Headers>
PlainWSPeer::PlainWSPeer(
Port const& port,
Handler& handler,
endpoint_type remote_address,
beast::http::message<true, Body, Headers>&& request,
socket_type&& socket,
beast::Journal journal)
: BaseWSPeer(port, handler, remote_address, std::move(request),
socket.get_io_service(), journal)
, ws_(std::move(socket))
{
}
void
PlainWSPeer::do_close()
{
error_code ec;
auto& sock = ws_.next_layer();
sock.shutdown(socket_type::shutdown_both, ec);
if(ec)
return fail(ec, "do_close");
}
} // ripple
#endif

View File

@@ -21,6 +21,7 @@
#define RIPPLE_SERVER_SSLHTTPPEER_H_INCLUDED
#include <ripple/server/impl/BaseHTTPPeer.h>
#include <ripple/server/impl/SSLWSPeer.h>
#include <beast/asio/ssl_bundle.h>
#include <memory>
@@ -47,15 +48,18 @@ public:
void
run();
std::shared_ptr<WSSession>
websocketUpgrade() override;
private:
void
do_handshake (yield_context yield);
void
do_request();
do_request() override;
void
do_close();
do_close() override;
void
on_shutdown (error_code ec);
@@ -93,6 +97,17 @@ SSLHTTPPeer::run()
shared_from_this(), std::placeholders::_1));
}
std::shared_ptr<WSSession>
SSLHTTPPeer::websocketUpgrade()
{
auto ws = ios().emplace<SSLWSPeer>(
port_, handler_, remote_address_,
std::move(message_), std::move(ssl_bundle_),
journal_);
ws->run();
return ws;
}
void
SSLHTTPPeer::do_handshake (yield_context yield)
{

View File

@@ -0,0 +1,106 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
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 RIPPLE_SERVER_SSLWSPEER_H_INCLUDED
#define RIPPLE_SERVER_SSLWSPEER_H_INCLUDED
#include <ripple/server/impl/BaseHTTPPeer.h>
#include <ripple/server/WSSession.h>
#include <beast/asio/ssl_bundle.h>
#include <beast/asio/placeholders.h>
#include <beast/wsproto/ssl.h>
#include <memory>
namespace ripple {
class SSLWSPeer
: public BaseWSPeer<SSLWSPeer>
, public std::enable_shared_from_this<SSLWSPeer>
{
private:
friend class BasePeer<SSLWSPeer>;
friend class BaseWSPeer<SSLWSPeer>;
using clock_type = std::chrono::system_clock;
using error_code = boost::system::error_code;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using waitable_timer =
boost::asio::basic_waitable_timer <clock_type>;
std::unique_ptr<beast::asio::ssl_bundle> ssl_bundle_;
beast::wsproto::socket<
beast::asio::ssl_bundle::stream_type&> ws_;
public:
template<class Body, class Headers>
SSLWSPeer(
Port const& port,
Handler& handler,
endpoint_type remote_endpoint,
beast::http::message<true, Body, Headers>&& request,
std::unique_ptr<
beast::asio::ssl_bundle>&& ssl_bundle,
beast::Journal journal);
private:
void
do_close() override;
void
on_shutdown(error_code ec);
};
//------------------------------------------------------------------------------
template<class Body, class Headers>
SSLWSPeer::SSLWSPeer(
Port const& port,
Handler& handler,
endpoint_type remote_endpoint,
beast::http::message<true, Body, Headers>&& request,
std::unique_ptr<
beast::asio::ssl_bundle>&& ssl_bundle,
beast::Journal journal)
: BaseWSPeer(port, handler, remote_endpoint, std::move(request),
ssl_bundle->socket.get_io_service(), journal)
, ssl_bundle_(std::move(ssl_bundle))
, ws_(ssl_bundle_->stream)
{
}
void
SSLWSPeer::do_close()
{
//start_timer();
using namespace beast::asio;
ws_.next_layer().async_shutdown(
strand_.wrap(std::bind(&SSLWSPeer::on_shutdown,
shared_from_this(), placeholders::error)));
}
void
SSLWSPeer::on_shutdown(error_code ec)
{
//cancel_timer();
ws_.lowest_layer().close(ec);
}
} // ripple
#endif

View File

@@ -19,6 +19,8 @@
#include <BeastConfig.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/beast/net/IPAddressConversion.h>
#include <ripple/json/json_reader.h>
#include <ripple/server/JsonWriter.h>
#include <ripple/server/make_ServerHandler.h>
@@ -29,13 +31,14 @@
#include <ripple/basics/make_SSLContext.h>
#include <ripple/core/JobQueue.h>
#include <ripple/json/to_string.h>
#include <ripple/net/RPCErr.h>
#include <ripple/server/make_Server.h>
#include <ripple/overlay/Overlay.h>
#include <ripple/resource/ResourceManager.h>
#include <ripple/resource/Fees.h>
#include <ripple/rpc/impl/Tuning.h>
#include <beast/crypto/base64.h>
#include <ripple/rpc/RPCHandler.h>
#include <beast/crypto/base64.h>
#include <beast/http/rfc2616.h>
#include <boost/algorithm/string.hpp>
#include <boost/type_traits.hpp>
@@ -120,6 +123,24 @@ ServerHandlerImp::onHandoff (Session& session,
boost::asio::ip::tcp::endpoint remote_address) ->
Handoff
{
if (session.port().protocol.count("wss2") > 0 &&
isWebsocketUpgrade (request))
{
// VFALCO TODO
Resource::Consumer usage;
//if (isUnlimited (role))
// usage = m_resourceManager.newUnlimitedEndpoint (
// remoteIPAddress.to_string());
//else
usage = m_resourceManager.newInboundEndpoint(
beast::IP::from_asio(remote_address));
auto const ws = session.websocketUpgrade();
ws->appDefined = std::make_shared<WSInfoSub>(
m_networkOPs, usage, ws);
Handoff handoff;
handoff.moved = true;
return handoff;
}
if (session.port().protocol.count("wss") > 0 &&
isWebsocketUpgrade (request))
{
@@ -142,6 +163,24 @@ ServerHandlerImp::onHandoff (Session& session,
boost::asio::ip::tcp::endpoint remote_address) ->
Handoff
{
if (session.port().protocol.count("ws2") > 0 &&
isWebsocketUpgrade (request))
{
// VFALCO TODO
Resource::Consumer usage;
//if (isUnlimited (role))
// usage = m_resourceManager.newUnlimitedEndpoint (
// remoteIPAddress.to_string());
//else
usage = m_resourceManager.newInboundEndpoint(
beast::IP::from_asio(remote_address));
auto const ws = session.websocketUpgrade();
ws->appDefined = std::make_shared<WSInfoSub>(
m_networkOPs, usage, ws);
Handoff handoff;
handoff.moved = true;
return handoff;
}
if (session.port().protocol.count("ws") > 0 &&
isWebsocketUpgrade (request))
{
@@ -208,6 +247,45 @@ ServerHandlerImp::onRequest (Session& session)
});
}
void
ServerHandlerImp::onWSMessage(
std::shared_ptr<WSSession> session,
std::vector<boost::asio::const_buffer> const& buffers)
{
// VFALCO This is inefficient, the JSON
// should be parsed from the buffer sequence.
std::string s;
s.reserve(boost::asio::buffer_size(buffers));
std::copy(boost::asio::buffers_begin(buffers),
boost::asio::buffers_end(buffers),
std::back_inserter(s));
//m_journal.error << "Recv: " << s;
Json::Value jv;
// VFALCO should we parse a coroutine instead?
if(! Json::Reader{}.parse(s, jv))
{
// TODO Send error
return;
}
m_jobQueue.postCoro(jtCLIENT, "WS-Client",
[this, session = std::move(session),
jv = std::move(jv)](auto const& coro)
{
auto const jr =
this->processSession(session, coro, jv);
beast::streambuf sb;
//m_journal.error << "Send: " << to_string(jr);
Json::stream(jr,
[&sb](auto const p, auto const n)
{
sb.commit(boost::asio::buffer_copy(
sb.prepare(n), boost::asio::buffer(p, n)));
});
session->send(std::make_shared<
StreambufWSMsg<decltype(sb)>>(std::move(sb)));
});
}
void
ServerHandlerImp::onClose (Session& session,
boost::system::error_code const&)
@@ -224,6 +302,122 @@ ServerHandlerImp::onStopped (Server&)
//------------------------------------------------------------------------------
Json::Value
ServerHandlerImp::processSession(
std::shared_ptr<WSSession> const& session,
std::shared_ptr<JobCoro> const& coro,
Json::Value const& jv)
{
auto is = std::static_pointer_cast<InfoSub> (session->appDefined);
/*
if (getConsumer().disconnect ())
{
disconnect ();
return rpcError (rpcSLOW_DOWN);
}
*/
// Requests without "command" are invalid.
//
if (!jv.isMember (jss::command))
{
Json::Value jr (Json::objectValue);
jr[jss::type] = jss::response;
jr[jss::status] = jss::error;
jr[jss::error] = jss::missingCommand;
jr[jss::request] = jv;
if (jv.isMember (jss::id))
{
jr[jss::id] = jv[jss::id];
}
/*
getConsumer().charge (Resource::feeInvalidRPC);
*/
return jr;
}
Resource::Charge loadType = Resource::feeReferenceRPC;
Json::Value jr (Json::objectValue);
auto required = RPC::roleRequired (jv[jss::command].asString());
// VFALCO TODO Get identity/credentials from HTTP headers
std::string const user = "";
std::string const fwdfor = "";
auto role = requestRole (required, session->port(), jv,
beast::IP::from_asio(session->remote_endpoint().address()),
user);
if (Role::FORBID == role)
{
jr[jss::result] = rpcError (rpcFORBIDDEN);
}
else
{
// VFALCO TODO InfoSub parameter in context
RPC::Context context{
app_.journal ("RPCHandler"),
jv,
app_,
loadType,
app_.getOPs(),
app_.getLedgerMaster(),
is->getConsumer(),
role,
coro,
is,
{ user, fwdfor }
};
RPC::doCommand (context, jr[jss::result]);
}
/*
getConsumer().charge (loadType);
if (getConsumer().warn ())
{
jr[jss::warning] = jss::load;
}
*/
// Currently we will simply unwrap errors returned by the RPC
// API, in the future maybe we can make the responses
// consistent.
//
// Regularize result. This is duplicate code.
if (jr[jss::result].isMember (jss::error))
{
jr = jr[jss::result];
jr[jss::status] = jss::error;
jr[jss::request] = jv;
}
else
{
jr[jss::status] = jss::success;
// For testing resource limits on this connection.
if (jv[jss::command].asString() == "ping")
{
/*
if (getConsumer().isUnlimited())
jr[jss::unlimited] = true;
*/
}
}
if (jv.isMember (jss::id))
{
jr[jss::id] = jv[jss::id];
}
jr[jss::type] = jss::response;
return jr;
}
template<class ConstBufferSequence>
static
std::string

View File

@@ -23,9 +23,13 @@
#include <ripple/core/Job.h>
#include <ripple/core/JobCoro.h>
#include <ripple/json/Output.h>
#include <ripple/json/to_string.h>
#include <ripple/net/InfoSub.h>
#include <ripple/server/Handler.h>
#include <ripple/server/JsonWriter.h>
#include <ripple/server/ServerHandler.h>
#include <ripple/server/Session.h>
#include <ripple/server/WSSession.h>
#include <ripple/rpc/RPCHandler.h>
#include <ripple/app/main/CollectorManager.h>
#include <map>
@@ -33,17 +37,46 @@
namespace ripple {
inline
bool operator< (Port const& lhs, Port const& rhs)
{
return lhs.name < rhs.name;
}
class WSInfoSub : public InfoSub
{
std::weak_ptr<WSSession> ws_;
public:
WSInfoSub(Source& source, Consumer consumer,
std::shared_ptr<WSSession> const& ws)
: InfoSub(source, consumer)
, ws_(ws)
{
}
void
send(Json::Value const& jv, bool)
{
auto sp = ws_.lock();
if(! sp)
return;
beast::streambuf sb;
write(sb, jv);
auto m = std::make_shared<
StreambufWSMsg<decltype(sb)>>(
std::move(sb));
sp->send(m);
}
};
// Private implementation
class ServerHandlerImp
: public ServerHandler
, public Handler
{
private:
Application& app_;
Resource::Manager& m_resourceManager;
beast::Journal m_journal;
@@ -85,7 +118,7 @@ private:
onStop() override;
//
// HTTP::Handler
// Handler
//
bool
@@ -105,6 +138,10 @@ private:
void
onRequest (Session& session) override;
void
onWSMessage(std::shared_ptr<WSSession> session,
std::vector<boost::asio::const_buffer> const& buffers) override;
void
onClose (Session& session,
boost::system::error_code const&) override;
@@ -114,6 +151,12 @@ private:
//--------------------------------------------------------------------------
Json::Value
processSession(
std::shared_ptr<WSSession> const& session,
std::shared_ptr<JobCoro> const& coro,
Json::Value const& jv);
void
processSession (std::shared_ptr<Session> const&,
std::shared_ptr<JobCoro> jobCoro);

View File

@@ -131,6 +131,12 @@ public:
session.close (true);
}
void
onWSMessage(std::shared_ptr<WSSession> session,
std::vector<boost::asio::const_buffer> const&) override
{
}
void
onClose (Session& session,
boost::system::error_code const&) override
@@ -328,6 +334,12 @@ public:
{
}
void
onWSMessage(std::shared_ptr<WSSession> session,
std::vector<boost::asio::const_buffer> const& buffers) override
{
}
void
onClose (Session& session,
boost::system::error_code const&) override

View File

@@ -49,6 +49,9 @@ public:
std::unique_ptr<WSClient>
makeWSClient(Config const& cfg);
std::unique_ptr<WSClient>
makeWS2Client(Config const& cfg);
} // test
} // ripple

Some files were not shown because too many files have changed in this diff Show More