mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
Merge subtree Beast 1.0.0-b5
Merge commit 'a9e507da9b636d394bb43d6bf8002d013530f57a' into develop
This commit is contained in:
@@ -236,26 +236,14 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\BeastConfig.h">
|
<ClInclude Include="..\..\src\BeastConfig.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\abstract_ostream.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\amount.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\amount.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\basic_abstract_ostream.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\basic_scoped_ostream.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\basic_std_ostream.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\debug_ostream.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\detail\const_container.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\detail\const_container.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\global_suites.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\global_suites.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\match.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\match.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\print.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\recorder.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\recorder.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\reporter.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\reporter.hpp">
|
||||||
@@ -306,8 +294,6 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\temp_dir.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\write_streambuf.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\core\detail\write_streambuf.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\core\error.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\core\error.hpp">
|
||||||
@@ -352,6 +338,8 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\body_type.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\body_type.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\beast\include\beast\http\concepts.hpp">
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\detail\basic_parser_v1.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\detail\basic_parser_v1.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\detail\chunk_encode.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\detail\chunk_encode.hpp">
|
||||||
@@ -394,8 +382,6 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\string_body.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\string_body.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\type_check.hpp">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\write.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\write.hpp">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\websocket.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\websocket.hpp">
|
||||||
@@ -428,6 +414,8 @@
|
|||||||
</None>
|
</None>
|
||||||
<None Include="..\..\src\beast\include\beast\websocket\impl\handshake_op.ipp">
|
<None Include="..\..\src\beast\include\beast\websocket\impl\handshake_op.ipp">
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="..\..\src\beast\include\beast\websocket\impl\ping_op.ipp">
|
||||||
|
</None>
|
||||||
<None Include="..\..\src\beast\include\beast\websocket\impl\read_frame_op.ipp">
|
<None Include="..\..\src\beast\include\beast\websocket\impl\read_frame_op.ipp">
|
||||||
</None>
|
</None>
|
||||||
<None Include="..\..\src\beast\include\beast\websocket\impl\read_op.ipp">
|
<None Include="..\..\src\beast\include\beast\websocket\impl\read_op.ipp">
|
||||||
|
|||||||
@@ -537,24 +537,9 @@
|
|||||||
<ClInclude Include="..\..\src\BeastConfig.h">
|
<ClInclude Include="..\..\src\BeastConfig.h">
|
||||||
<Filter>.</Filter>
|
<Filter>.</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\abstract_ostream.hpp">
|
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\amount.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\amount.hpp">
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
<Filter>extras\beast\unit_test</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\basic_abstract_ostream.hpp">
|
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\basic_scoped_ostream.hpp">
|
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\basic_std_ostream.hpp">
|
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\debug_ostream.hpp">
|
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\detail\const_container.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\detail\const_container.hpp">
|
||||||
<Filter>extras\beast\unit_test\detail</Filter>
|
<Filter>extras\beast\unit_test\detail</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -564,9 +549,6 @@
|
|||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\match.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\match.hpp">
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
<Filter>extras\beast\unit_test</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\print.hpp">
|
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\recorder.hpp">
|
<ClInclude Include="..\..\src\beast\extras\beast\unit_test\recorder.hpp">
|
||||||
<Filter>extras\beast\unit_test</Filter>
|
<Filter>extras\beast\unit_test</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -642,9 +624,6 @@
|
|||||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\core\detail\stream_concepts.hpp">
|
||||||
<Filter>include\beast\core\detail</Filter>
|
<Filter>include\beast\core\detail</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\temp_dir.hpp">
|
|
||||||
<Filter>include\beast\core\detail</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\core\detail\write_streambuf.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\core\detail\write_streambuf.hpp">
|
||||||
<Filter>include\beast\core\detail</Filter>
|
<Filter>include\beast\core\detail</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -711,6 +690,9 @@
|
|||||||
<ClInclude Include="..\..\src\beast\include\beast\http\body_type.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\body_type.hpp">
|
||||||
<Filter>include\beast\http</Filter>
|
<Filter>include\beast\http</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\beast\include\beast\http\concepts.hpp">
|
||||||
|
<Filter>include\beast\http</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\detail\basic_parser_v1.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\detail\basic_parser_v1.hpp">
|
||||||
<Filter>include\beast\http\detail</Filter>
|
<Filter>include\beast\http\detail</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -774,9 +756,6 @@
|
|||||||
<ClInclude Include="..\..\src\beast\include\beast\http\string_body.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\string_body.hpp">
|
||||||
<Filter>include\beast\http</Filter>
|
<Filter>include\beast\http</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\type_check.hpp">
|
|
||||||
<Filter>include\beast\http</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\beast\include\beast\http\write.hpp">
|
<ClInclude Include="..\..\src\beast\include\beast\http\write.hpp">
|
||||||
<Filter>include\beast\http</Filter>
|
<Filter>include\beast\http</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -825,6 +804,9 @@
|
|||||||
<None Include="..\..\src\beast\include\beast\websocket\impl\handshake_op.ipp">
|
<None Include="..\..\src\beast\include\beast\websocket\impl\handshake_op.ipp">
|
||||||
<Filter>include\beast\websocket\impl</Filter>
|
<Filter>include\beast\websocket\impl</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="..\..\src\beast\include\beast\websocket\impl\ping_op.ipp">
|
||||||
|
<Filter>include\beast\websocket\impl</Filter>
|
||||||
|
</None>
|
||||||
<None Include="..\..\src\beast\include\beast\websocket\impl\read_frame_op.ipp">
|
<None Include="..\..\src\beast\include\beast\websocket\impl\read_frame_op.ipp">
|
||||||
<Filter>include\beast\websocket\impl</Filter>
|
<Filter>include\beast\websocket\impl</Filter>
|
||||||
</None>
|
</None>
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ packages: &gcc5_pkgs
|
|||||||
- autotools-dev
|
- autotools-dev
|
||||||
- libc6-dbg
|
- libc6-dbg
|
||||||
|
|
||||||
packages: &clang36_pkgs
|
packages: &clang38_pkgs
|
||||||
- clang-3.6
|
- clang-3.8
|
||||||
- g++-5
|
- g++-5
|
||||||
- python-software-properties
|
- python-software-properties
|
||||||
- libssl-dev
|
- libssl-dev
|
||||||
@@ -53,56 +53,57 @@ packages: &clang36_pkgs
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# GCC/Debug
|
# GCC/Debug
|
||||||
|
# - compiler: gcc
|
||||||
|
# env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
|
||||||
|
# addons: &ao_gcc5
|
||||||
|
# apt:
|
||||||
|
# sources: ['ubuntu-toolchain-r-test']
|
||||||
|
# packages: *gcc5_pkgs
|
||||||
|
|
||||||
|
# # GCC/Release
|
||||||
|
# - compiler: gcc
|
||||||
|
# env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=64
|
||||||
|
# addons: *ao_gcc5
|
||||||
|
|
||||||
|
# Coverage
|
||||||
- compiler: gcc
|
- compiler: gcc
|
||||||
env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64
|
env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64
|
||||||
addons: &ao_gcc5
|
addons: &ao_gcc5
|
||||||
apt:
|
apt:
|
||||||
sources: ['ubuntu-toolchain-r-test']
|
sources: ['ubuntu-toolchain-r-test']
|
||||||
packages: *gcc5_pkgs
|
packages: *gcc5_pkgs
|
||||||
|
|
||||||
# - compiler: gcc
|
# # Clang/Debug
|
||||||
# env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=32
|
# - compiler: clang
|
||||||
# addons: *ao_gcc5
|
# env: GCC_VER=5 VARIANT=debug CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||||
|
# addons: &ao_clang38
|
||||||
|
# apt:
|
||||||
|
# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
|
||||||
|
# packages: *clang38_pkgs
|
||||||
|
|
||||||
# GCC/Release
|
# # Clang/Release
|
||||||
- compiler: gcc
|
# - compiler: clang
|
||||||
env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=64
|
# env: GCC_VER=5 VARIANT=release CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||||
addons: *ao_gcc5
|
# addons: *ao_clang38
|
||||||
|
|
||||||
# # - compiler: gcc
|
# Clang/AddressSanitizer
|
||||||
# # env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=32
|
|
||||||
# # addons: *ao_gcc5
|
|
||||||
|
|
||||||
# Clang/Debug
|
|
||||||
- compiler: clang
|
- compiler: clang
|
||||||
env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=64
|
env: GCC_VER=5 VARIANT=asan CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||||
addons: &ao_clang36
|
addons: &ao_clang38
|
||||||
apt:
|
apt:
|
||||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
|
||||||
packages: *clang36_pkgs
|
packages: *clang38_pkgs
|
||||||
|
|
||||||
# # - compiler: clang
|
# Clang/MemorySanitizer
|
||||||
# # env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=32
|
# VFALCO Generates false positives unless libc++ is compiled with msan turned on
|
||||||
# # addons: *ao_clang36
|
#- compiler: clang
|
||||||
|
# env: GCC_VER=5 VARIANT=msan CLANG_VER=3.8 ADDRESS_MODEL=64 MSAN_OPTIONS=poison_in_dtor=1,sanitize-memory-track-origins=2
|
||||||
|
# addons: *ao_clang38
|
||||||
|
|
||||||
# Clang/Release
|
# Clang/UndefinedBehaviourSanitizer
|
||||||
- compiler: clang
|
- compiler: clang
|
||||||
env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 ADDRESS_MODEL=64
|
env: GCC_VER=5 VARIANT=usan CLANG_VER=3.8 ADDRESS_MODEL=64
|
||||||
addons: *ao_clang36
|
addons: *ao_clang38
|
||||||
|
|
||||||
# # - compiler: clang
|
|
||||||
# # env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 ADDRESS_MODEL=32
|
|
||||||
# # addons: *ao_clang36
|
|
||||||
|
|
||||||
# Coverage
|
|
||||||
- compiler: gcc
|
|
||||||
env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64
|
|
||||||
addons: *ao_gcc5
|
|
||||||
|
|
||||||
# ASAN
|
|
||||||
- compiler: gcc
|
|
||||||
env: GCC_VER=5 VARIANT=asan ADDRESS_MODEL=64
|
|
||||||
addons: *ao_gcc5
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
|
|||||||
@@ -61,6 +61,22 @@ variant asan
|
|||||||
<linkflags>"-fsanitize=address"
|
<linkflags>"-fsanitize=address"
|
||||||
;
|
;
|
||||||
|
|
||||||
|
variant msan
|
||||||
|
:
|
||||||
|
debug
|
||||||
|
:
|
||||||
|
<cxxflags>"-fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 -fsanitize-memory-use-after-dtor"
|
||||||
|
<linkflags>"-fsanitize=memory"
|
||||||
|
;
|
||||||
|
|
||||||
|
variant usan
|
||||||
|
:
|
||||||
|
debug
|
||||||
|
:
|
||||||
|
<cxxflags>"-fsanitize=undefined -fno-omit-frame-pointer"
|
||||||
|
<linkflags>"-fsanitize=undefined"
|
||||||
|
;
|
||||||
|
|
||||||
project beast
|
project beast
|
||||||
: requirements
|
: requirements
|
||||||
<include>.
|
<include>.
|
||||||
@@ -90,7 +106,7 @@ project beast
|
|||||||
<os>SOLARIS:<define>__EXTENSIONS__
|
<os>SOLARIS:<define>__EXTENSIONS__
|
||||||
<os>SOLARIS:<library>socket
|
<os>SOLARIS:<library>socket
|
||||||
<os>SOLARIS:<library>nsl
|
<os>SOLARIS:<library>nsl
|
||||||
<os>NT:<define>_WIN32_WINNT=0x0501
|
<os>NT:<define>_WIN32_WINNT=0x0601
|
||||||
<os>NT,<toolset>cw:<library>ws2_32
|
<os>NT,<toolset>cw:<library>ws2_32
|
||||||
<os>NT,<toolset>cw:<library>mswsock
|
<os>NT,<toolset>cw:<library>mswsock
|
||||||
<os>NT,<toolset>gcc:<library>ws2_32
|
<os>NT,<toolset>gcc:<library>ws2_32
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
[](https://travis-ci.org/vinniefalco/Beast) [![codecov]
|
[](https://travis-ci.org/vinniefalco/Beast) [![codecov]
|
||||||
(https://codecov.io/gh/vinniefalco/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/vinniefalco/Beast) [![Documentation]
|
(https://codecov.io/gh/vinniefalco/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/vinniefalco/Beast) [![Documentation]
|
||||||
(https://img.shields.io/badge/documentation-master-brightgreen.svg)](http://vinniefalco.github.io/beast/)
|
(https://img.shields.io/badge/documentation-master-brightgreen.svg)](http://vinniefalco.github.io/beast/) [![License]
|
||||||
|
(https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt)
|
||||||
|
|
||||||
Beast provides implementations of the HTTP and WebSocket protocols
|
Beast provides implementations of the HTTP and WebSocket protocols
|
||||||
built on top of Boost.Asio and other parts of boost.
|
built on top of Boost.Asio and other parts of boost.
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ WebSocket:
|
|||||||
* Don't try to read requests into empty_body
|
* Don't try to read requests into empty_body
|
||||||
* Give callers control over the http request/response used during handshake
|
* Give callers control over the http request/response used during handshake
|
||||||
* Investigate poor autobahn results in Debug builds
|
* Investigate poor autobahn results in Debug builds
|
||||||
|
* Fall through composed operation switch cases
|
||||||
|
* Replace stream::error_ with stream::state_, example states: ok, error, abort_io
|
||||||
|
Need a cancel state so waking up a ping stored in invokable knows to call the
|
||||||
|
final handler with operation_aborted
|
||||||
|
* Use close_code::no_code instead of close_code::none
|
||||||
|
* Make request_type, response_type public APIs,
|
||||||
|
use in stream member function signatures
|
||||||
|
|
||||||
HTTP:
|
HTTP:
|
||||||
* Define Parser concept in HTTP
|
* Define Parser concept in HTTP
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
[template mdash[] '''— ''']
|
[template mdash[] '''— ''']
|
||||||
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
|
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
|
||||||
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></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 __POSIX__ /POSIX/]
|
||||||
[def __Windows__ /Windows/]
|
[def __Windows__ /Windows/]
|
||||||
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
|
[def __accept__ [@http://www.opengroup.org/onlinepubs/000095399/functions/accept.html `accept()`]]
|
||||||
@@ -191,8 +190,15 @@ supporting its development.
|
|||||||
[include websocket.qbk]
|
[include websocket.qbk]
|
||||||
|
|
||||||
[section:types Type Requirements]
|
[section:types Type Requirements]
|
||||||
[include core_types.qbk]
|
[include types/Body.qbk]
|
||||||
[include http_types.qbk]
|
[include types/BufferSequence.qbk]
|
||||||
|
[include types/Field.qbk]
|
||||||
|
[include types/FieldSequence.qbk]
|
||||||
|
[include types/Parser.qbk]
|
||||||
|
[include types/Reader.qbk]
|
||||||
|
[include types/Streambuf.qbk]
|
||||||
|
[include types/Streams.qbk]
|
||||||
|
[include types/Writer.qbk]
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[include design.qbk]
|
[include design.qbk]
|
||||||
|
|||||||
@@ -41,14 +41,19 @@
|
|||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
|
||||||
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
|
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__is_ReadableBody">is_ReadableBody</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
<entry valign="top">
|
<entry valign="top">
|
||||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
|
<member><link linkend="beast.ref.http__async_parse">async_parse</link></member>
|
||||||
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
|
<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__async_write">async_write</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__parse">parse</link></member>
|
||||||
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
|
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
|
||||||
<member><link linkend="beast.ref.http__read">read</link></member>
|
<member><link linkend="beast.ref.http__read">read</link></member>
|
||||||
<member><link linkend="beast.ref.http__write">write</link></member>
|
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||||
@@ -62,6 +67,7 @@
|
|||||||
<member><link linkend="beast.types.Body">Body</link></member>
|
<member><link linkend="beast.types.Body">Body</link></member>
|
||||||
<member><link linkend="beast.types.Field">Field</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.FieldSequence">FieldSequence</link></member>
|
||||||
|
<member><link linkend="beast.types.Parser">Parser</link></member>
|
||||||
<member><link linkend="beast.types.Reader">Reader</link></member>
|
<member><link linkend="beast.types.Reader">Reader</link></member>
|
||||||
<member><link linkend="beast.types.Writer">Writer</link></member>
|
<member><link linkend="beast.types.Writer">Writer</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
@@ -70,16 +76,20 @@
|
|||||||
<bridgehead renderas="sect3">Classes</bridgehead>
|
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.websocket__close_reason">close_reason</link></member>
|
<member><link linkend="beast.ref.websocket__close_reason">close_reason</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__ping_data">ping_data</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__stream">stream</link></member>
|
<member><link linkend="beast.ref.websocket__stream">stream</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__reason_string">reason_string</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Options</bridgehead>
|
<bridgehead renderas="sect3">Options</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.websocket__auto_fragment_size">auto_fragment_size</link></member>
|
<member><link linkend="beast.ref.websocket__auto_fragment_size">auto_fragment_size</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__decorate">decorate</link></member>
|
<member><link linkend="beast.ref.websocket__decorate">decorate</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__keep_alive">keep_alive</link></member>
|
<member><link linkend="beast.ref.websocket__keep_alive">keep_alive</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__mask_buffer_size">mask_buffer_size</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__message_type">message_type</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__pong_callback">pong_callback</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__read_buffer_size">read_buffer_size</link></member>
|
<member><link linkend="beast.ref.websocket__read_buffer_size">read_buffer_size</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__read_message_max">read_message_max</link></member>
|
<member><link linkend="beast.ref.websocket__read_message_max">read_message_max</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__write_buffer_size">write_buffer_size</link></member>
|
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
<entry valign="top">
|
<entry valign="top">
|
||||||
@@ -91,6 +101,7 @@
|
|||||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.websocket__close_code">close_code</link></member>
|
<member><link linkend="beast.ref.websocket__close_code">close_code</link></member>
|
||||||
|
<member><link linkend="beast.ref.websocket__error">error</link></member>
|
||||||
<member><link linkend="beast.ref.websocket__opcode">opcode</link></member>
|
<member><link linkend="beast.ref.websocket__opcode">opcode</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
@@ -163,10 +174,10 @@
|
|||||||
<bridgehead renderas="sect3">Concepts</bridgehead>
|
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
|
<member><link linkend="beast.types.BufferSequence">BufferSequence</link></member>
|
||||||
<member><link linkend="beast.types.stream.AsyncStream">AsyncStream</link></member>
|
<member><link linkend="beast.types.streams.AsyncStream">AsyncStream</link></member>
|
||||||
<member><link linkend="beast.types.stream.Stream">Stream</link></member>
|
<member><link linkend="beast.types.streams.Stream">Stream</link></member>
|
||||||
<member><link linkend="beast.types.Streambuf">Streambuf</link></member>
|
<member><link linkend="beast.types.Streambuf">Streambuf</link></member>
|
||||||
<member><link linkend="beast.types.stream.SyncStream">SyncStream</link></member>
|
<member><link linkend="beast.types.streams.SyncStream">SyncStream</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|||||||
@@ -1528,7 +1528,7 @@
|
|||||||
<xsl:text> </xsl:text>
|
<xsl:text> </xsl:text>
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test="type = 'class AsyncStream'">
|
<xsl:when test="type = 'class AsyncStream'">
|
||||||
<xsl:text>class ``[link beast.types.stream.AsyncStream [*AsyncStream]]``</xsl:text>
|
<xsl:text>class ``[link beast.types.streams.AsyncStream [*AsyncStream]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class AsyncReadStream'">
|
<xsl:when test="type = 'class AsyncReadStream'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]``</xsl:text>
|
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]``</xsl:text>
|
||||||
@@ -1556,13 +1556,13 @@
|
|||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]``</xsl:text>
|
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'Stream' or type = 'class Stream'">
|
<xsl:when test="declname = 'Stream' or type = 'class Stream'">
|
||||||
<xsl:text>class ``[link beast.types.stream.Stream [*Stream]]``</xsl:text>
|
<xsl:text>class ``[link beast.types.streams.Stream [*Stream]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'Streambuf' or type = 'class Streambuf'">
|
<xsl:when test="declname = 'Streambuf' or type = 'class Streambuf'">
|
||||||
<xsl:text>class ``[link beast.types.Streambuf [*Streambuf]]``</xsl:text>
|
<xsl:text>class ``[link beast.types.Streambuf [*Streambuf]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="type = 'class SyncStream'">
|
<xsl:when test="type = 'class SyncStream'">
|
||||||
<xsl:text>class ``[link beast.types.stream.SyncStream [*SyncStream]]``</xsl:text>
|
<xsl:text>class ``[link beast.types.streams.SyncStream [*SyncStream]]``</xsl:text>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:when test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
|
<xsl:when test="declname = 'SyncReadStream' or type = 'class SyncReadStream'">
|
||||||
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]``</xsl:text>
|
<xsl:text>class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]``</xsl:text>
|
||||||
|
|||||||
50
src/beast/doc/types/Body.qbk
Normal file
50
src/beast/doc/types/Body.qbk
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
[/
|
||||||
|
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: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]
|
||||||
15
src/beast/doc/types/BufferSequence.qbk
Normal file
15
src/beast/doc/types/BufferSequence.qbk
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[/
|
||||||
|
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:BufferSequence BufferSequence]
|
||||||
|
|
||||||
|
A `BufferSequence` is a type meeting either of the following requirements:
|
||||||
|
|
||||||
|
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]]
|
||||||
|
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
41
src/beast/doc/types/Field.qbk
Normal file
41
src/beast/doc/types/Field.qbk
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
[/
|
||||||
|
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: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-insensitive 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]
|
||||||
51
src/beast/doc/types/FieldSequence.qbk
Normal file
51
src/beast/doc/types/FieldSequence.qbk
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
[/
|
||||||
|
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: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]
|
||||||
58
src/beast/doc/types/Parser.qbk
Normal file
58
src/beast/doc/types/Parser.qbk
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
[/
|
||||||
|
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:Parser Parser]
|
||||||
|
|
||||||
|
A [*`Parser`] is used to deserialize HTTP/1 messages from [link beast.types.streams streams].
|
||||||
|
Objects of this type are used with [link beast.ref.http__parse http::parse] and
|
||||||
|
[link beast.ref.http__async_parse http::async_parse].
|
||||||
|
|
||||||
|
In this table:
|
||||||
|
|
||||||
|
* `X` denotes a type meeting the requirements of [*`Parser`].
|
||||||
|
|
||||||
|
* `a` denotes a value of type `X`.
|
||||||
|
|
||||||
|
* `b` is a value meeting the requirements of [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConvertibleToConstBuffer.html [*`ConvertibleToConstBuffer`]].
|
||||||
|
|
||||||
|
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
|
||||||
|
|
||||||
|
[table Parser requirements
|
||||||
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
[
|
||||||
|
[`a.complete()`]
|
||||||
|
[`bool`]
|
||||||
|
[
|
||||||
|
Returns `true` when a complete HTTP/1 message has been parsed.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.write(b, ec)`]
|
||||||
|
[`std::size_t`]
|
||||||
|
[
|
||||||
|
Parses the octets in the specified input buffer sequentially until
|
||||||
|
an error occurs, the end of the buffer is reached, or a complete
|
||||||
|
HTTP/1 message has been parsed. If an error occurs, `ec` is set
|
||||||
|
to the error code and parsing stops. This function returns the
|
||||||
|
number of bytes consumed from the input buffer.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[
|
||||||
|
[`a.write_eof(ec)`]
|
||||||
|
[`void`]
|
||||||
|
[
|
||||||
|
Indicates to the parser that no more octets will be available.
|
||||||
|
Typically this function is called when the end of stream is reached.
|
||||||
|
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
|
||||||
|
generates a `boost::asio::error::eof` error. Some HTTP/1 messages
|
||||||
|
determine the end of the message body by an end of file marker or
|
||||||
|
closing of the connection.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
54
src/beast/doc/types/Reader.qbk
Normal file
54
src/beast/doc/types/Reader.qbk
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
[/
|
||||||
|
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: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]
|
||||||
@@ -5,54 +5,32 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:BufferSequence BufferSequence]
|
|
||||||
|
|
||||||
A `BufferSequence` is a type meeting either of the following requirements:
|
|
||||||
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]]
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]]
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:stream Streams]
|
|
||||||
|
|
||||||
Stream types represent objects capable of performing synchronous or
|
|
||||||
asynchronous I/O. They are based on concepts from `boost::asio`.
|
|
||||||
|
|
||||||
[heading:Stream Stream]
|
|
||||||
|
|
||||||
A type modeling [*`Stream`] meets either or both of the following requirements:
|
|
||||||
|
|
||||||
* [link beast.types.stream.AsyncStream [*`AsyncStream`]]
|
|
||||||
* [link beast.types.stream.SyncStream [*`SyncStream`]]
|
|
||||||
|
|
||||||
[heading:AsyncStream AsyncStream]
|
|
||||||
|
|
||||||
A type modeling [*`AsyncStream`] meets the following requirements:
|
|
||||||
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*`AsyncReadStream`]]
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*`AsyncWriteStream`]]
|
|
||||||
|
|
||||||
[heading:SyncStream SyncStream]
|
|
||||||
|
|
||||||
A type modeling [*`SyncStream`] meets the following requirements:
|
|
||||||
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*`SyncReadStream`]]
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html [*`SyncWriteStream`]]
|
|
||||||
|
|
||||||
[endsect]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:Streambuf Streambuf]
|
[section:Streambuf Streambuf]
|
||||||
|
|
||||||
|
A [*`Streambuf`] represents a logical octet sequence divided in two sections,
|
||||||
|
the input sequence and the output sequence. Octets are written to the output
|
||||||
|
sequence, then moved to the input sequence where they are available for
|
||||||
|
reading. When some or all of the input sequence is no longer needed, it may
|
||||||
|
be consumed.
|
||||||
|
|
||||||
|
The interface to this concept is intended to permit the following
|
||||||
|
implementation strategies:
|
||||||
|
|
||||||
|
* A single contiguous octet array, which is reallocated as necessary to
|
||||||
|
accommodate changes in the size of the octet sequence.
|
||||||
|
|
||||||
|
* A sequence of one or more octet arrays, where each array is of the same
|
||||||
|
size. Additional octet array objects are appended to the sequence to
|
||||||
|
accommodate changes in the size of the octet sequence.
|
||||||
|
|
||||||
|
* A sequence of one or more octet arrays of varying sizes. Additional octet
|
||||||
|
array objects are appended to the sequence to accommodate changes in the
|
||||||
|
size of the character sequence. This is the implementation approached
|
||||||
|
currently offered by [link beast.ref.basic_streambuf `basic_streambuf`].
|
||||||
|
|
||||||
In the table below:
|
In the table below:
|
||||||
|
|
||||||
* `X` denotes a class
|
* `X` denotes a class meeting the requirements of [*`Streambuf`]
|
||||||
* `a` denotes a value of type `X`
|
* `a` denotes a value of type `X`
|
||||||
* `n` denotes a value convertible to `std::size_t`
|
* `n` denotes a value convertible to `std::size_t`
|
||||||
* `U`, `T` denote unspecified types.
|
* `U`, `T` denote unspecified types.
|
||||||
@@ -62,12 +40,12 @@ In the table below:
|
|||||||
[
|
[
|
||||||
[`X::const_buffers_type`]
|
[`X::const_buffers_type`]
|
||||||
[`T`]
|
[`T`]
|
||||||
[`T` meets the requirements for `ConstBufferSequence`.]
|
[`T` meets the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html `ConstBufferSequence`].]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`X::mutable_buffers_type`]
|
[`X::mutable_buffers_type`]
|
||||||
[`U`]
|
[`U`]
|
||||||
[`U` meets the requirements for `MutableBufferSequence`.]
|
[`U` meets the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html `MutableBufferSequence`].]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.commit(n)`]
|
[`a.commit(n)`]
|
||||||
@@ -98,7 +76,7 @@ In the table below:
|
|||||||
[
|
[
|
||||||
[`a.max_size()`]
|
[`a.max_size()`]
|
||||||
[`std::size_t`]
|
[`std::size_t`]
|
||||||
[Returns the maximum size of the `Streambuf`.]
|
[Returns the maximum size of the stream buffer.]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`read_size_helper(a, n)`]
|
[`read_size_helper(a, n)`]
|
||||||
34
src/beast/doc/types/Streams.qbk
Normal file
34
src/beast/doc/types/Streams.qbk
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[/
|
||||||
|
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:streams Streams]
|
||||||
|
|
||||||
|
Stream types represent objects capable of performing synchronous or
|
||||||
|
asynchronous I/O. They are based on concepts from `boost::asio`.
|
||||||
|
|
||||||
|
[heading:Stream Stream]
|
||||||
|
|
||||||
|
A type modeling [*`Stream`] meets either or both of the following requirements:
|
||||||
|
|
||||||
|
* [*`AsyncStream`]
|
||||||
|
* [*`SyncStream`]
|
||||||
|
|
||||||
|
[heading:AsyncStream AsyncStream]
|
||||||
|
|
||||||
|
A type modeling [*`AsyncStream`] meets the following requirements:
|
||||||
|
|
||||||
|
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncReadStream.html [*`AsyncReadStream`]]
|
||||||
|
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*`AsyncWriteStream`]]
|
||||||
|
|
||||||
|
[heading:SyncStream SyncStream]
|
||||||
|
|
||||||
|
A type modeling [*`SyncStream`] meets the following requirements:
|
||||||
|
|
||||||
|
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncReadStream.html [*`SyncReadStream`]]
|
||||||
|
* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncWriteStream.html [*`SyncWriteStream`]]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
@@ -5,199 +5,6 @@
|
|||||||
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[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` is a type meeting either of the following requirements:
|
|
||||||
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]]
|
|
||||||
* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`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]
|
[section:Writer Writer]
|
||||||
|
|
||||||
A `Writer` serializes the message body. The implementation creates an instance
|
A `Writer` serializes the message body. The implementation creates an instance
|
||||||
@@ -370,4 +177,3 @@ public:
|
|||||||
```
|
```
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -46,13 +46,15 @@ model, handler allocation, and handler invocation hooks. Calls to
|
|||||||
Beast.WebSocket asynchronous initiation functions allow callers the choice
|
Beast.WebSocket asynchronous initiation functions allow callers the choice
|
||||||
of using a completion handler, stackful or stackless coroutines, futures,
|
of using a completion handler, stackful or stackless coroutines, futures,
|
||||||
or user defined customizations (for example, Boost.Fiber). The
|
or user defined customizations (for example, Boost.Fiber). The
|
||||||
implementation uses handler invocation hooks (`asio_handler_invoke`),
|
implementation uses handler invocation hooks
|
||||||
|
([@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]),
|
||||||
providing execution guarantees on composed operations in a manner
|
providing execution guarantees on composed operations in a manner
|
||||||
identical to Boost.Asio. The implementation also uses handler allocation
|
identical to Boost.Asio. The implementation also uses handler allocation hooks
|
||||||
hooks (`asio_handler_allocate`) when allocating memory internally for
|
([@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`])
|
||||||
composed operations.
|
when allocating memory internally for composed operations.
|
||||||
|
|
||||||
There is no need for inheritance or virtual members in `websocket::stream`.
|
There is no need for inheritance or virtual members in a
|
||||||
|
[link beast.ref.websocket__stream `beast::websocket::stream`].
|
||||||
All operations are templated and transparent to the compiler, allowing for
|
All operations are templated and transparent to the compiler, allowing for
|
||||||
maximum inlining and optimization.
|
maximum inlining and optimization.
|
||||||
|
|
||||||
@@ -67,43 +69,47 @@ both Boost.Asio and the WebSocket protocol specification described in
|
|||||||
[section:creating Creating the socket]
|
[section:creating Creating the socket]
|
||||||
|
|
||||||
The interface to Beast's WebSocket implementation is a single template
|
The interface to Beast's WebSocket implementation is a single template
|
||||||
class [link beast.ref.websocket__stream `websocket::stream`] which wraps a
|
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
|
||||||
"next layer" object. The next layer object must meet the requirements of
|
wraps a "next layer" object. The next layer object must meet the requirements
|
||||||
`SyncReadStream` and `SyncWriteStream` if synchronous operations are performed,
|
of [link beast.types.streams.SyncStream [*`SyncReadStream`]] if synchronous
|
||||||
`AsyncReadStream` and `AsyncWriteStream` is asynchronous operations are
|
operations are performed, or
|
||||||
performed, or both. Arguments supplied during construction are passed to
|
[link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous
|
||||||
next layer's constructor. Here we declare two websockets which have ownership
|
operations are performed, or both. Arguments supplied during construction are
|
||||||
of the next layer:
|
passed to next layer's constructor. Here we declare two websockets which have
|
||||||
|
ownership of the next layer:
|
||||||
```
|
```
|
||||||
io_service ios;
|
boost::asio::io_service ios;
|
||||||
websocket::stream<ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||||
|
|
||||||
ssl::context ctx(ssl::context::sslv23);
|
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
||||||
websocket::stream<ssl::stream<ip::tcp::socket>> wss(ios, ctx);
|
beast::websocket::stream<
|
||||||
|
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> wss(ios, ctx);
|
||||||
```
|
```
|
||||||
|
|
||||||
For servers that can handshake in multiple protocols, it may be desired
|
For servers that can handshake in multiple protocols, it may be desired
|
||||||
to wrap an object that already exists. This socket can be moved in:
|
to wrap an object that already exists. This socket can be moved in:
|
||||||
```
|
```
|
||||||
tcp::socket&& sock;
|
boost::asio::ip::tcp::socket&& sock;
|
||||||
...
|
...
|
||||||
websocket::stream<ip::tcp::socket> ws(std::move(sock));
|
beast::websocket::stream<
|
||||||
|
boost::asio::ip::tcp::socket> ws(std::move(sock));
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, the wrapper can be constructed with a non-owning reference. In
|
Or, the wrapper can be constructed with a non-owning reference. In
|
||||||
this case, the caller is responsible for managing the lifetime of the
|
this case, the caller is responsible for managing the lifetime of the
|
||||||
underlying socket being wrapped:
|
underlying socket being wrapped:
|
||||||
```
|
```
|
||||||
tcp::socket sock;
|
boost::asio::ip::tcp::socket sock;
|
||||||
...
|
...
|
||||||
websocket::stream<ip::tcp::socket&> ws(sock);
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
|
||||||
```
|
```
|
||||||
|
|
||||||
The layer being wrapped can be accessed through the websocket's "next layer",
|
The layer being wrapped can be accessed through the websocket's "next layer",
|
||||||
permitting callers to interact directly with its interface.
|
permitting callers to interact directly with its interface.
|
||||||
```
|
```
|
||||||
ssl::context ctx(ssl::context::sslv23);
|
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
||||||
websocket::stream<ssl::stream<ip::tcp::socket>> ws(ios, ctx);
|
beast::websocket::stream<
|
||||||
|
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws(ios, ctx);
|
||||||
...
|
...
|
||||||
ws.next_layer().shutdown(); // ssl::stream shutdown
|
ws.next_layer().shutdown(); // ssl::stream shutdown
|
||||||
```
|
```
|
||||||
@@ -122,17 +128,18 @@ Connections are established by using the interfaces which already exist
|
|||||||
for the next layer. For example, making an outgoing connection:
|
for the next layer. For example, making an outgoing connection:
|
||||||
```
|
```
|
||||||
std::string const host = "mywebapp.com";
|
std::string const host = "mywebapp.com";
|
||||||
io_service ios;
|
boost::asio::io_service ios;
|
||||||
tcp::resolver r(ios);
|
boost::asio::ip::tcp::resolver r(ios);
|
||||||
websocket::stream<ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||||
connect(ws.next_layer(), r.resolve(tcp::resolver::query{host, "ws"}));
|
boost::asio::connect(ws.next_layer(),
|
||||||
|
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
|
||||||
```
|
```
|
||||||
|
|
||||||
Accepting an incoming connection:
|
Accepting an incoming connection:
|
||||||
```
|
```
|
||||||
void do_accept(tcp::acceptor& acceptor)
|
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
|
||||||
{
|
{
|
||||||
websocket::stream<ip::tcp::socket> ws(acceptor.get_io_service());
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(acceptor.get_io_service());
|
||||||
acceptor.accept(ws.next_layer());
|
acceptor.accept(ws.next_layer());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -149,26 +156,26 @@ A WebSocket session begins when one side sends the HTTP Upgrade request
|
|||||||
for websocket, and the other side sends an appropriate HTTP response
|
for websocket, and the other side sends an appropriate HTTP response
|
||||||
indicating that the request was accepted and that the connection has
|
indicating that the request was accepted and that the connection has
|
||||||
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
|
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
|
||||||
and the URI of the resource to request. `hanshake` is used to send the
|
and the URI of the resource to request. `handshake` is used to send the
|
||||||
request with the required host and resource strings.
|
request with the required host and resource strings.
|
||||||
```
|
```
|
||||||
websocket::stream<ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||||
...
|
...
|
||||||
ws.set_option(websocket::keep_alive(true));
|
ws.set_option(beast::websocket::keep_alive(true));
|
||||||
ws.handshake("ws.mywebapp.com:80", "/cgi-bin/bitcoin-prices");
|
ws.handshake("ws.example.com:80", "/cgi-bin/bitcoin-prices");
|
||||||
```
|
```
|
||||||
|
|
||||||
The `websocket::stream` automatically handles receiving and processing
|
The [link beast.ref.websocket__stream `beast::websocket::stream`] automatically
|
||||||
the HTTP response to the handshake request. The call to handshake is
|
handles receiving and processing the HTTP response to the handshake request.
|
||||||
successful if a HTTP response is received with the 101 "Switching Protocols"
|
The call to handshake is successful if a HTTP response is received with the
|
||||||
status code. On failure, an error is returned or an exception is thrown.
|
101 "Switching Protocols" status code. On failure, an error is returned or an
|
||||||
Depending on the keep alive setting, the socket may remain open for a
|
exception is thrown. Depending on the keep alive setting, the socket may remain
|
||||||
subsequent handshake attempt
|
open for a subsequent handshake attempt
|
||||||
|
|
||||||
Performing a handshake for an incoming websocket upgrade request operates
|
Performing a handshake for an incoming websocket upgrade request operates
|
||||||
similarly. If the handshake fails, an error is returned or exception thrown:
|
similarly. If the handshake fails, an error is returned or exception thrown:
|
||||||
```
|
```
|
||||||
websocket::stream<ip::tcp::socket> ws(ios);
|
beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ios);
|
||||||
...
|
...
|
||||||
ws.accept();
|
ws.accept();
|
||||||
```
|
```
|
||||||
@@ -178,12 +185,12 @@ on the connection, or might have already received an entire HTTP request
|
|||||||
containing the upgrade request. Overloads of `accept` allow callers to
|
containing the upgrade request. Overloads of `accept` allow callers to
|
||||||
pass in this additional buffered handshake data.
|
pass in this additional buffered handshake data.
|
||||||
```
|
```
|
||||||
void do_accept(tcp::socket& sock)
|
void do_accept(boost::asio::ip::tcp::socket& sock)
|
||||||
{
|
{
|
||||||
boost::asio::streambuf sb;
|
boost::asio::streambuf sb;
|
||||||
read_until(sock, sb, "\r\n\r\n");
|
boost::asio::read_until(sock, sb, "\r\n\r\n");
|
||||||
...
|
...
|
||||||
websocket::stream<ip::tcp::socket&> ws(sock);
|
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
|
||||||
ws.accept(sb.data());
|
ws.accept(sb.data());
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
@@ -192,12 +199,12 @@ void do_accept(tcp::socket& sock)
|
|||||||
Alternatively, the caller can pass an entire HTTP request if it was
|
Alternatively, the caller can pass an entire HTTP request if it was
|
||||||
obtained elsewhere:
|
obtained elsewhere:
|
||||||
```
|
```
|
||||||
void do_accept(tcp::socket& sock)
|
void do_accept(boost::asio::ip::tcp::socket& sock)
|
||||||
{
|
{
|
||||||
boost::asio::streambuf sb;
|
boost::asio::streambuf sb;
|
||||||
http::request<http::empty_body> request;
|
beast::http::request<http::empty_body> request;
|
||||||
http::read(sock, request);
|
beast::http::read(sock, request);
|
||||||
if(http::is_upgrade(request))
|
if(beast::http::is_upgrade(request))
|
||||||
{
|
{
|
||||||
websocket::stream<ip::tcp::socket&> ws(sock);
|
websocket::stream<ip::tcp::socket&> ws(sock);
|
||||||
ws.accept(request);
|
ws.accept(request);
|
||||||
@@ -206,8 +213,6 @@ void do_accept(tcp::socket& sock)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
[note Identifiers in the `http` namespace are part of Beast.HTTP. ]
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
@@ -218,20 +223,21 @@ After the WebSocket handshake is accomplished, callers may send and receive
|
|||||||
messages using the message oriented interface. This interface requires that
|
messages using the message oriented interface. This interface requires that
|
||||||
all of the buffers representing the message are known ahead of time:
|
all of the buffers representing the message are known ahead of time:
|
||||||
```
|
```
|
||||||
void echo(websocket::stream<ip::tcp::socket>& ws)
|
void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
||||||
{
|
{
|
||||||
streambuf sb;
|
beast::streambuf sb;
|
||||||
websocket::opcode::value op;
|
beast::websocket::opcode::value op;
|
||||||
ws.read(sb);
|
ws.read(sb);
|
||||||
|
|
||||||
ws.set_option(websocket::message_type(op));
|
ws.set_option(beast::websocket::message_type(op));
|
||||||
websocket::write(ws, sb.data());
|
ws.write(sb.data());
|
||||||
sb.consume(sb.size());
|
sb.consume(sb.size());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
[important Calls to `set_option` must be made from the same implicit
|
[important Calls to [link beast.ref.websocket__stream.set_option `set_option`]
|
||||||
or explicit strand as that used to perform other operations. ]
|
must be made from the same implicit or explicit strand as that used to perform
|
||||||
|
other operations. ]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -249,18 +255,19 @@ message ahead of time:
|
|||||||
For these cases, the frame oriented interface may be used. This
|
For these cases, the frame oriented interface may be used. This
|
||||||
example reads and echoes a complete message using this interface:
|
example reads and echoes a complete message using this interface:
|
||||||
```
|
```
|
||||||
void echo(websocket::stream<ip::tcp::socket>& ws)
|
void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
|
||||||
{
|
{
|
||||||
streambuf sb;
|
beast::streambuf sb;
|
||||||
websocket::frame_info fi;
|
beast::websocket::frame_info fi;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
ws.read_frame(fi, sb);
|
ws.read_frame(fi, sb);
|
||||||
if(fi.fin)
|
if(fi.fin)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ws.set_option(websocket::message_type(fi.op));
|
ws.set_option(beast::websocket::message_type(fi.op));
|
||||||
consuming_buffers<streambuf::const_buffers_type> cb(sb.data());
|
beast::consuming_buffers<
|
||||||
|
beast::streambuf::const_buffers_type> cb(sb.data());
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
using boost::asio::buffer_size;
|
using boost::asio::buffer_size;
|
||||||
@@ -287,10 +294,11 @@ void echo(websocket::stream<ip::tcp::socket>& ws)
|
|||||||
|
|
||||||
During read operations, the implementation automatically reads and processes
|
During read operations, the implementation automatically reads and processes
|
||||||
WebSocket control frames such as ping, pong, and close. Pings are replied
|
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
|
to as soon as possible, pongs are delivered to the pong callback. The receipt
|
||||||
initiates the WebSocket close procedure, eventually resulting in the
|
of a close frame initiates the WebSocket close procedure, eventually resulting
|
||||||
error code `websocket::error::closed` being delivered to the caller in
|
in the error code [link beast.ref.websocket__error `error::closed`] being
|
||||||
a subsequent read operation, assuming no other error takes place.
|
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
|
To ensure timely delivery of control frames, large messages are broken up
|
||||||
into smaller sized frames. The implementation chooses the size and number
|
into smaller sized frames. The implementation chooses the size and number
|
||||||
@@ -298,19 +306,49 @@ of the frames making up the message. The automatic fragment size option
|
|||||||
gives callers control over the size of these frames:
|
gives callers control over the size of these frames:
|
||||||
```
|
```
|
||||||
...
|
...
|
||||||
ws.set_option(websocket::auto_fragment_size(8192));
|
ws.set_option(beast::websocket::auto_fragment_size(8192));
|
||||||
```
|
```
|
||||||
|
|
||||||
The WebSocket protocol defines a procedure and control message for initiating
|
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
|
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
|
connection is performed automatically. To manually initiate a close, use
|
||||||
`websocket::stream::close`:
|
[link beast.ref.websocket__stream.close `close`]:
|
||||||
```
|
```
|
||||||
ws.close();
|
ws.close();
|
||||||
```
|
```
|
||||||
|
|
||||||
[note To receive the `websocket::error::closed` error, a read operation
|
[note To receive the [link beast.ref.websocket__error `error::closed`]
|
||||||
is required. ]
|
error, a read operation is required. ]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section:pongs Pong messages]
|
||||||
|
|
||||||
|
To receive pong control frames, callers may register a "pong callback" using
|
||||||
|
[link beast.ref.websocket__stream.set_option `set_option`]:
|
||||||
|
|
||||||
|
the following signature:
|
||||||
|
```
|
||||||
|
void on_pong(ping_data const& payload);
|
||||||
|
...
|
||||||
|
ws.set_option(pong_callback{&on_pong});
|
||||||
|
```
|
||||||
|
|
||||||
|
When a pong callback is registered, any pongs received through either
|
||||||
|
synchronous read functions or asynchronous read functions will invoke the
|
||||||
|
pong callback, passing the payload in the pong message as the argument.
|
||||||
|
|
||||||
|
Unlike regular completion handlers used in calls to asynchronous initiation
|
||||||
|
functions, the pong callback only needs to be set once. The callback is not
|
||||||
|
reset when a pong is received. The same callback is used for both synchronous
|
||||||
|
and asynchronous reads. The pong callback is passive; in order to receive
|
||||||
|
pongs, a synchronous or asynchronous stream read function must be active.
|
||||||
|
|
||||||
|
[note When an asynchronous read function receives a pong, the the pong callback
|
||||||
|
is invoked in the same manner as that used to invoke the final completion
|
||||||
|
handler of the corresponding read function.]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -320,7 +358,8 @@ is required. ]
|
|||||||
|
|
||||||
Because calls to read data may return a variable amount of bytes, the
|
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
|
interface to calls that read data require an object that meets the requirements
|
||||||
of `Streambuf`. This concept is modeled on `boost::asio::basic_streambuf`.
|
of [link beast.types.Streambuf [*`Streambuf`]]. This concept is modeled on
|
||||||
|
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
|
||||||
|
|
||||||
The implementation does not perform queueing or buffering of messages. If
|
The implementation does not perform queueing or buffering of messages. If
|
||||||
desired, these features should be provided by callers. The impact of this
|
desired, these features should be provided by callers. The impact of this
|
||||||
@@ -331,6 +370,7 @@ of the underlying TCP/IP connection.
|
|||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:async Asynchronous interface]
|
[section:async Asynchronous interface]
|
||||||
|
|
||||||
Asynchronous versions are available for all functions:
|
Asynchronous versions are available for all functions:
|
||||||
@@ -361,12 +401,14 @@ void echo(websocket::stream<ip::tcp::socket>& ws,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section:io_service io_service]
|
[section:io_service The io_service]
|
||||||
|
|
||||||
The creation and operation of the `boost::asio::io_service` associated with
|
The creation and operation of the
|
||||||
the underlying stream is left to the callers, permitting any implementation
|
[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
|
||||||
strategy including one that does not require threads for environments where
|
associated with the underlying stream is left to the callers, permitting any
|
||||||
threads are unavailable. Beast.WSProto itself does not use or require threads.
|
implementation strategy including one that does not require threads for
|
||||||
|
environments where threads are unavailable. Beast.WebSocket itself does not
|
||||||
|
use or require threads.
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -374,13 +416,13 @@ threads are unavailable. Beast.WSProto itself does not use or require threads.
|
|||||||
|
|
||||||
[section:safety Thread Safety]
|
[section:safety Thread Safety]
|
||||||
|
|
||||||
Like a regular asio socket, a `websocket::stream` is not thread safe. Callers
|
Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
|
||||||
are responsible for synchronizing operations on the socket using an implicit
|
not thread safe. Callers are responsible for synchronizing operations on the
|
||||||
or explicit strand, as per the Asio documentation. The asynchronous interface
|
socket using an implicit or explicit strand, as per the Asio documentation.
|
||||||
supports one active read and one active write simultaneously. Undefined
|
The asynchronous interface supports one active read and one active write
|
||||||
behavior results if two or more reads or two or more writes are attempted
|
simultaneously. Undefined behavior results if two or more reads or two or
|
||||||
concurrently. Caller initiated WebSocket ping, pong, and close operations
|
more writes are attempted concurrently. Caller initiated WebSocket ping, pong,
|
||||||
each count as an active write.
|
and close operations each count as an active write.
|
||||||
|
|
||||||
The implementation uses composed asynchronous operations internally; a high
|
The implementation uses composed asynchronous operations internally; a high
|
||||||
level read can cause both reads and writes to take place on the underlying
|
level read can cause both reads and writes to take place on the underlying
|
||||||
|
|||||||
@@ -157,25 +157,35 @@ public:
|
|||||||
return next_layer_.async_write_some(buffers,
|
return next_layer_.async_write_some(buffers,
|
||||||
std::forward<WriteHandler>(handler));
|
std::forward<WriteHandler>(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void
|
||||||
|
teardown(fail_stream<NextLayer>& stream,
|
||||||
|
boost::system::error_code& ec)
|
||||||
|
{
|
||||||
|
if(stream.pfc_->fail(ec))
|
||||||
|
return;
|
||||||
|
websocket_helpers::call_teardown(stream.next_layer(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TeardownHandler>
|
||||||
|
friend
|
||||||
|
void
|
||||||
|
async_teardown(fail_stream<NextLayer>& stream,
|
||||||
|
TeardownHandler&& handler)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
if(stream.pfc_->fail(ec))
|
||||||
|
{
|
||||||
|
stream.get_io_service().post(
|
||||||
|
bind_handler(std::move(handler), ec));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
websocket_helpers::call_async_teardown(
|
||||||
|
stream.next_layer(), std::forward<TeardownHandler>(handler));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
teardown(fail_stream<NextLayer>& stream,
|
|
||||||
boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
websocket_helpers::call_teardown(stream.next_layer(), ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer, class TeardownHandler>
|
|
||||||
void
|
|
||||||
async_teardown(fail_stream<NextLayer>& stream,
|
|
||||||
TeardownHandler&& handler)
|
|
||||||
{
|
|
||||||
websocket_helpers::call_async_teardown(
|
|
||||||
stream.next_layer(), std::forward<TeardownHandler>(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // test
|
} // test
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_ABSTRACT_OSTREAM_HPP
|
|
||||||
#define BEAST_UNIT_TEST_ABSTRACT_OSTREAM_HPP
|
|
||||||
|
|
||||||
#include <beast/unit_test/basic_abstract_ostream.hpp>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
/** An abstract ostream for `char`. */
|
|
||||||
using abstract_ostream = basic_abstract_ostream <char>;
|
|
||||||
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_BASIC_ABSTRACT_OSTREAM_HPP
|
|
||||||
#define BEAST_UNIT_TEST_BASIC_ABSTRACT_OSTREAM_HPP
|
|
||||||
|
|
||||||
#include <beast/unit_test/basic_scoped_ostream.hpp>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
/** Abstraction for an output stream similar to std::basic_ostream. */
|
|
||||||
template <
|
|
||||||
class CharT,
|
|
||||||
class Traits = std::char_traits <CharT>
|
|
||||||
>
|
|
||||||
class basic_abstract_ostream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using string_type = std::basic_string <CharT, Traits>;
|
|
||||||
using scoped_stream_type = basic_scoped_ostream <CharT, Traits>;
|
|
||||||
|
|
||||||
basic_abstract_ostream() = default;
|
|
||||||
|
|
||||||
virtual
|
|
||||||
~basic_abstract_ostream() = default;
|
|
||||||
|
|
||||||
basic_abstract_ostream (basic_abstract_ostream const&) = default;
|
|
||||||
basic_abstract_ostream& operator= (
|
|
||||||
basic_abstract_ostream const&) = default;
|
|
||||||
|
|
||||||
/** Returns `true` if the stream is active.
|
|
||||||
Inactive streams do not produce output.
|
|
||||||
*/
|
|
||||||
/** @{ */
|
|
||||||
virtual
|
|
||||||
bool
|
|
||||||
active() const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return active();
|
|
||||||
}
|
|
||||||
/** @} */
|
|
||||||
|
|
||||||
/** Called to output each string. */
|
|
||||||
virtual
|
|
||||||
void
|
|
||||||
write (string_type const& s) = 0;
|
|
||||||
|
|
||||||
scoped_stream_type
|
|
||||||
operator<< (std::ostream& manip (std::ostream&))
|
|
||||||
{
|
|
||||||
return scoped_stream_type (manip,
|
|
||||||
[this](string_type const& s)
|
|
||||||
{
|
|
||||||
this->write (s);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
scoped_stream_type
|
|
||||||
operator<< (T const& t)
|
|
||||||
{
|
|
||||||
return scoped_stream_type (t,
|
|
||||||
[this](string_type const& s)
|
|
||||||
{
|
|
||||||
this->write (s);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_BASIC_SCOPED_OSTREAM_HPP
|
|
||||||
#define BEAST_UNIT_TEST_BASIC_SCOPED_OSTREAM_HPP
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
// gcc libstd++ doesn't have move constructors for basic_ostringstream
|
|
||||||
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
|
|
||||||
//
|
|
||||||
#ifndef BEAST_NO_STDLIB_STREAM_MOVE
|
|
||||||
# ifdef __GLIBCXX__
|
|
||||||
# define BEAST_NO_STDLIB_STREAM_MOVE 1
|
|
||||||
# else
|
|
||||||
# define BEAST_NO_STDLIB_STREAM_MOVE 0
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
template <
|
|
||||||
class CharT,
|
|
||||||
class Traits
|
|
||||||
>
|
|
||||||
class basic_abstract_ostream;
|
|
||||||
|
|
||||||
/** Scoped output stream that forwards to a functor upon destruction. */
|
|
||||||
template <
|
|
||||||
class CharT,
|
|
||||||
class Traits = std::char_traits <CharT>,
|
|
||||||
class Allocator = std::allocator <CharT>
|
|
||||||
>
|
|
||||||
class basic_scoped_ostream
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
using handler_t = std::function <void (
|
|
||||||
std::basic_string <CharT, Traits, Allocator> const&)>;
|
|
||||||
|
|
||||||
using stream_type = std::basic_ostringstream <
|
|
||||||
CharT, Traits, Allocator>;
|
|
||||||
|
|
||||||
handler_t m_handler;
|
|
||||||
|
|
||||||
#if BEAST_NO_STDLIB_STREAM_MOVE
|
|
||||||
std::unique_ptr <stream_type> m_ss;
|
|
||||||
|
|
||||||
stream_type& stream()
|
|
||||||
{
|
|
||||||
return *m_ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
stream_type m_ss;
|
|
||||||
|
|
||||||
stream_type& stream()
|
|
||||||
{
|
|
||||||
return m_ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
using string_type = std::basic_string <CharT, Traits>;
|
|
||||||
|
|
||||||
// Disallow copy since that would duplicate the output
|
|
||||||
basic_scoped_ostream (basic_scoped_ostream const&) = delete;
|
|
||||||
basic_scoped_ostream& operator= (basic_scoped_ostream const) = delete;
|
|
||||||
|
|
||||||
template <class Handler>
|
|
||||||
explicit basic_scoped_ostream (Handler&& handler)
|
|
||||||
: m_handler (std::forward <Handler> (handler))
|
|
||||||
#if BEAST_NO_STDLIB_STREAM_MOVE
|
|
||||||
, m_ss (new stream_type())
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, class Handler>
|
|
||||||
basic_scoped_ostream (T const& t, Handler&& handler)
|
|
||||||
: m_handler (std::forward <Handler> (handler))
|
|
||||||
#if BEAST_NO_STDLIB_STREAM_MOVE
|
|
||||||
, m_ss (new stream_type())
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
stream() << t;
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_scoped_ostream (basic_abstract_ostream <
|
|
||||||
CharT, Traits>& ostream)
|
|
||||||
: m_handler (
|
|
||||||
[&](string_type const& s)
|
|
||||||
{
|
|
||||||
ostream.write (s);
|
|
||||||
})
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_scoped_ostream (basic_scoped_ostream&& other)
|
|
||||||
: m_handler (std::move (other.m_handler))
|
|
||||||
, m_ss (std::move (other.m_ss))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~basic_scoped_ostream()
|
|
||||||
{
|
|
||||||
auto const& s (stream().str());
|
|
||||||
if (! s.empty())
|
|
||||||
m_handler (s);
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_scoped_ostream&
|
|
||||||
operator<< (std::ostream& manip (std::ostream&))
|
|
||||||
{
|
|
||||||
stream() << manip;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
basic_scoped_ostream&
|
|
||||||
operator<< (T const& t)
|
|
||||||
{
|
|
||||||
stream() << t;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_BASIC_STD_OSTREAM_HPP
|
|
||||||
#define BEAST_UNIT_TEST_BASIC_STD_OSTREAM_HPP
|
|
||||||
|
|
||||||
#include <beast/unit_test/basic_abstract_ostream.hpp>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
/** Wraps an existing std::basic_ostream as an abstract_ostream. */
|
|
||||||
template <
|
|
||||||
class CharT,
|
|
||||||
class Traits = std::char_traits <CharT>
|
|
||||||
>
|
|
||||||
class basic_std_ostream
|
|
||||||
: public basic_abstract_ostream <CharT, Traits>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
using typename basic_abstract_ostream <CharT, Traits>::string_type;
|
|
||||||
|
|
||||||
std::reference_wrapper <std::ostream> m_stream;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit basic_std_ostream (
|
|
||||||
std::basic_ostream <CharT, Traits>& stream)
|
|
||||||
: m_stream (stream)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
write (string_type const& s) override
|
|
||||||
{
|
|
||||||
m_stream.get() << s << std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using std_ostream = basic_std_ostream <char>;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Returns a basic_std_ostream using template argument deduction. */
|
|
||||||
template <
|
|
||||||
class CharT,
|
|
||||||
class Traits = std::char_traits <CharT>
|
|
||||||
>
|
|
||||||
basic_std_ostream <CharT, Traits>
|
|
||||||
make_std_ostream (std::basic_ostream <CharT, Traits>& stream)
|
|
||||||
{
|
|
||||||
return basic_std_ostream <CharT, Traits> (stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_DEBUG_OSTREAM_HPP
|
|
||||||
#define BEAST_UNIT_TEST_DEBUG_OSTREAM_HPP
|
|
||||||
|
|
||||||
#include <beast/unit_test/abstract_ostream.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN
|
|
||||||
# define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
# undef WIN32_LEAN_AND_MEAN
|
|
||||||
# else
|
|
||||||
#include <windows.h>
|
|
||||||
# endif
|
|
||||||
# ifdef min
|
|
||||||
# undef min
|
|
||||||
# endif
|
|
||||||
# ifdef max
|
|
||||||
# undef max
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
/** A basic_abstract_ostream that redirects output to an attached debugger. */
|
|
||||||
class debug_ostream
|
|
||||||
: public abstract_ostream
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
bool m_debugger;
|
|
||||||
|
|
||||||
public:
|
|
||||||
debug_ostream()
|
|
||||||
: m_debugger (IsDebuggerPresent() != FALSE)
|
|
||||||
{
|
|
||||||
// Note that the check for an attached debugger is made only
|
|
||||||
// during construction time, for efficiency. A stream created before
|
|
||||||
// the debugger is attached will not have output redirected.
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
write (string_type const& s) override
|
|
||||||
{
|
|
||||||
if (m_debugger)
|
|
||||||
{
|
|
||||||
OutputDebugStringA ((s + "\n").c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << s << std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#else
|
|
||||||
class debug_ostream
|
|
||||||
: public abstract_ostream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void
|
|
||||||
write (string_type const& s) override
|
|
||||||
{
|
|
||||||
std::cout << s << std::endl;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
144
src/beast/extras/beast/unit_test/dstream.hpp
Normal file
144
src/beast/extras/beast/unit_test/dstream.hpp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_UNIT_TEST_DSTREAM_HPP
|
||||||
|
#define BEAST_UNIT_TEST_DSTREAM_HPP
|
||||||
|
|
||||||
|
#include <boost/utility/base_from_member.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# ifndef NOMINMAX
|
||||||
|
# define NOMINMAX 1
|
||||||
|
# endif
|
||||||
|
# ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif
|
||||||
|
# include <windows.h>
|
||||||
|
# undef WIN32_LEAN_AND_MEAN
|
||||||
|
# undef NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace unit_test {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
template<class CharT, class Traits, class Allocator>
|
||||||
|
class dstream_buf
|
||||||
|
: public std::basic_stringbuf<CharT, Traits, Allocator>
|
||||||
|
{
|
||||||
|
bool dbg_;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void write(T const*) = delete;
|
||||||
|
|
||||||
|
void write(char const* s)
|
||||||
|
{
|
||||||
|
if(dbg_)
|
||||||
|
OutputDebugStringA(s);
|
||||||
|
else
|
||||||
|
std::cout << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(wchar_t const* s)
|
||||||
|
{
|
||||||
|
if(dbg_)
|
||||||
|
OutputDebugStringW(s);
|
||||||
|
else
|
||||||
|
std::wcout << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
dstream_buf()
|
||||||
|
: dbg_(IsDebuggerPresent() != FALSE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~dstream_buf()
|
||||||
|
{
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sync() override
|
||||||
|
{
|
||||||
|
write(this->str().c_str());
|
||||||
|
this->str("");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template<class CharT, class Traits, class Allocator>
|
||||||
|
class dstream_buf
|
||||||
|
: public std::basic_stringbuf<CharT, Traits, Allocator>
|
||||||
|
{
|
||||||
|
template<class T>
|
||||||
|
void write(T const*) = delete;
|
||||||
|
|
||||||
|
void write(char const* s)
|
||||||
|
{
|
||||||
|
std::cout << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(wchar_t const* s)
|
||||||
|
{
|
||||||
|
std::wcout << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
~dstream_buf()
|
||||||
|
{
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sync() override
|
||||||
|
{
|
||||||
|
write(this->str().c_str());
|
||||||
|
this->str("");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/// A std::ostream that redirects output to the debugger if attached.
|
||||||
|
template<
|
||||||
|
class CharT,
|
||||||
|
class Traits = std::char_traits<CharT>,
|
||||||
|
class Allocator = std::allocator<CharT>
|
||||||
|
>
|
||||||
|
class basic_dstream
|
||||||
|
: private boost::base_from_member<
|
||||||
|
detail::dstream_buf<CharT, Traits, Allocator>>
|
||||||
|
, public std::basic_ostream<CharT, Traits>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
basic_dstream()
|
||||||
|
: std::basic_ostream<CharT, Traits>(&this->member)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using dstream = basic_dstream<char>;
|
||||||
|
using dwstream = basic_dstream<wchar_t>;
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -15,7 +15,8 @@ namespace unit_test {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <class = void>
|
/// Holds test suites registered during static initialization.
|
||||||
|
inline
|
||||||
suite_list&
|
suite_list&
|
||||||
global_suites()
|
global_suites()
|
||||||
{
|
{
|
||||||
@@ -23,26 +24,20 @@ global_suites()
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Suite>
|
template<class Suite>
|
||||||
struct insert_suite
|
struct insert_suite
|
||||||
{
|
{
|
||||||
template <class = void>
|
insert_suite(char const* name, char const* module,
|
||||||
insert_suite (char const* name, char const* module,
|
char const* library, bool manual)
|
||||||
char const* library, bool manual);
|
{
|
||||||
|
global_suites().insert<Suite>(
|
||||||
|
name, module, library, manual);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class Suite>
|
|
||||||
template <class>
|
|
||||||
insert_suite<Suite>::insert_suite (char const* name,
|
|
||||||
char const* module, char const* library, bool manual)
|
|
||||||
{
|
|
||||||
global_suites().insert <Suite> (
|
|
||||||
name, module, library, manual);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|
||||||
/** Holds suites registered during static initialization. */
|
/// Holds test suites registered during static initialization.
|
||||||
inline
|
inline
|
||||||
suite_list const&
|
suite_list const&
|
||||||
global_suites()
|
global_suites()
|
||||||
|
|||||||
@@ -6,12 +6,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <beast/unit_test/amount.hpp>
|
#include <beast/unit_test/amount.hpp>
|
||||||
|
#include <beast/unit_test/dstream.hpp>
|
||||||
#include <beast/unit_test/global_suites.hpp>
|
#include <beast/unit_test/global_suites.hpp>
|
||||||
#include <beast/unit_test/match.hpp>
|
#include <beast/unit_test/match.hpp>
|
||||||
#include <beast/unit_test/reporter.hpp>
|
#include <beast/unit_test/reporter.hpp>
|
||||||
#include <beast/unit_test/suite.hpp>
|
#include <beast/unit_test/suite.hpp>
|
||||||
#include <beast/unit_test/debug_ostream.hpp>
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -25,11 +26,10 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace unit_test {
|
namespace unit_test {
|
||||||
|
|
||||||
|
static
|
||||||
std::string
|
std::string
|
||||||
prefix(suite_info const& s)
|
prefix(suite_info const& s)
|
||||||
{
|
{
|
||||||
@@ -38,32 +38,34 @@ prefix(suite_info const& s)
|
|||||||
return " ";
|
return " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Log>
|
static
|
||||||
void
|
void
|
||||||
print(Log& log, suite_list const& c)
|
print(std::ostream& os, suite_list const& c)
|
||||||
{
|
{
|
||||||
std::size_t manual = 0;
|
std::size_t manual = 0;
|
||||||
for(auto const& s : c)
|
for(auto const& s : c)
|
||||||
{
|
{
|
||||||
log <<
|
os << prefix (s) << s.full_name() << '\n';
|
||||||
prefix (s) <<
|
|
||||||
s.full_name();
|
|
||||||
if(s.manual())
|
if(s.manual())
|
||||||
++manual;
|
++manual;
|
||||||
}
|
}
|
||||||
log <<
|
os <<
|
||||||
amount(c.size(), "suite") << " total, " <<
|
amount(c.size(), "suite") << " total, " <<
|
||||||
amount(manual, "manual suite")
|
amount(manual, "manual suite") <<
|
||||||
|
'\n'
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Log>
|
// Print the list of suites
|
||||||
|
// Used with the --print command line option
|
||||||
|
static
|
||||||
void
|
void
|
||||||
print(Log& log)
|
print(std::ostream& os)
|
||||||
{
|
{
|
||||||
log << "------------------------------------------";
|
os << "------------------------------------------\n";
|
||||||
print(log, global_suites());
|
print(os, global_suites());
|
||||||
log << "------------------------------------------";
|
os << "------------------------------------------" <<
|
||||||
|
std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // unit_test
|
} // unit_test
|
||||||
@@ -97,11 +99,11 @@ int main(int ac, char const* av[])
|
|||||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||||
po::notify(vm);
|
po::notify(vm);
|
||||||
|
|
||||||
beast::debug_ostream log;
|
dstream log;
|
||||||
|
|
||||||
if(vm.count("help"))
|
if(vm.count("help"))
|
||||||
{
|
{
|
||||||
log << desc;
|
log << desc << std::endl;
|
||||||
}
|
}
|
||||||
else if(vm.count("print"))
|
else if(vm.count("print"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_UNIT_TEST_PRINT_H_INCLUDED
|
|
||||||
#define BEAST_UNIT_TEST_PRINT_H_INCLUDED
|
|
||||||
|
|
||||||
#include <beast/unit_test/amount.hpp>
|
|
||||||
#include <beast/unit_test/results.hpp>
|
|
||||||
#include <beast/unit_test/abstract_ostream.hpp>
|
|
||||||
#include <beast/unit_test/basic_std_ostream.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace unit_test {
|
|
||||||
|
|
||||||
/** Write test results to the specified output stream. */
|
|
||||||
/** @{ */
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
print (results const& r, abstract_ostream& stream)
|
|
||||||
{
|
|
||||||
for (auto const& s : r)
|
|
||||||
{
|
|
||||||
for (auto const& c : s)
|
|
||||||
{
|
|
||||||
stream <<
|
|
||||||
s.name() <<
|
|
||||||
(c.name().empty() ? "" : ("." + c.name()));
|
|
||||||
|
|
||||||
std::size_t i (1);
|
|
||||||
for (auto const& t : c.tests)
|
|
||||||
{
|
|
||||||
if (! t.pass)
|
|
||||||
stream <<
|
|
||||||
"#" << i <<
|
|
||||||
" failed: " << t.reason;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream <<
|
|
||||||
amount (r.size(), "suite") << ", " <<
|
|
||||||
amount (r.cases(), "case") << ", " <<
|
|
||||||
amount (r.total(), "test") << " total, " <<
|
|
||||||
amount (r.failed(), "failure")
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
print (results const& r, std::ostream& stream = std::cout)
|
|
||||||
{
|
|
||||||
auto s (make_std_ostream (stream));
|
|
||||||
print (r, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // unit_test
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include <beast/unit_test/amount.hpp>
|
#include <beast/unit_test/amount.hpp>
|
||||||
#include <beast/unit_test/recorder.hpp>
|
#include <beast/unit_test/recorder.hpp>
|
||||||
#include <beast/unit_test/abstract_ostream.hpp>
|
|
||||||
#include <beast/unit_test/basic_std_ostream.hpp>
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -30,7 +28,7 @@ namespace detail {
|
|||||||
/** A simple test runner that writes everything to a stream in real time.
|
/** A simple test runner that writes everything to a stream in real time.
|
||||||
The totals are output when the object is destroyed.
|
The totals are output when the object is destroyed.
|
||||||
*/
|
*/
|
||||||
template <class = void>
|
template<class = void>
|
||||||
class reporter : public runner
|
class reporter : public runner
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -42,7 +40,11 @@ private:
|
|||||||
std::size_t total = 0;
|
std::size_t total = 0;
|
||||||
std::size_t failed = 0;
|
std::size_t failed = 0;
|
||||||
|
|
||||||
case_results (std::string const& name_ = "");
|
explicit
|
||||||
|
case_results(std::string name_ = "")
|
||||||
|
: name(std::move(name_))
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct suite_results
|
struct suite_results
|
||||||
@@ -51,14 +53,16 @@ private:
|
|||||||
std::size_t cases = 0;
|
std::size_t cases = 0;
|
||||||
std::size_t total = 0;
|
std::size_t total = 0;
|
||||||
std::size_t failed = 0;
|
std::size_t failed = 0;
|
||||||
typename clock_type::time_point start =
|
typename clock_type::time_point start = clock_type::now();
|
||||||
clock_type::now();
|
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
suite_results (std::string const& name_ = "");
|
suite_results(std::string const& name_ = "")
|
||||||
|
: name(std::move(name_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
add (case_results const& r);
|
add(case_results const& r);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct results
|
struct results
|
||||||
@@ -76,35 +80,30 @@ private:
|
|||||||
std::size_t total = 0;
|
std::size_t total = 0;
|
||||||
std::size_t failed = 0;
|
std::size_t failed = 0;
|
||||||
std::vector<run_time> top;
|
std::vector<run_time> top;
|
||||||
typename clock_type::time_point start =
|
typename clock_type::time_point start = clock_type::now();
|
||||||
clock_type::now();
|
|
||||||
|
|
||||||
void
|
void
|
||||||
add (suite_results const& r);
|
add (suite_results const& r);
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::optional <std_ostream> std_ostream_;
|
std::ostream& os_;
|
||||||
std::reference_wrapper <beast::abstract_ostream> stream_;
|
|
||||||
results results_;
|
results results_;
|
||||||
suite_results suite_results_;
|
suite_results suite_results_;
|
||||||
case_results case_results_;
|
case_results case_results_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
reporter (reporter const&) = delete;
|
reporter(reporter const&) = delete;
|
||||||
reporter& operator= (reporter const&) = delete;
|
reporter& operator=(reporter const&) = delete;
|
||||||
|
|
||||||
~reporter();
|
~reporter();
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
reporter (std::ostream& stream = std::cout);
|
reporter(std::ostream& os = std::cout);
|
||||||
|
|
||||||
explicit
|
|
||||||
reporter (beast::abstract_ostream& stream);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static
|
static
|
||||||
std::string
|
std::string
|
||||||
fmtdur (typename clock_type::duration const& d);
|
fmtdur(typename clock_type::duration const& d);
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
@@ -137,42 +136,27 @@ private:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
reporter<_>::case_results::case_results (
|
|
||||||
std::string const& name_)
|
|
||||||
: name (name_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class _>
|
|
||||||
reporter<_>::suite_results::suite_results (
|
|
||||||
std::string const& name_)
|
|
||||||
: name (name_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class _>
|
|
||||||
void
|
void
|
||||||
reporter<_>::suite_results::add (case_results const& r)
|
reporter<_>::
|
||||||
|
suite_results::add(case_results const& r)
|
||||||
{
|
{
|
||||||
++cases;
|
++cases;
|
||||||
total += r.total;
|
total += r.total;
|
||||||
failed += r.failed;
|
failed += r.failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::results::add (
|
reporter<_>::
|
||||||
suite_results const& r)
|
results::add(suite_results const& r)
|
||||||
{
|
{
|
||||||
++suites;
|
++suites;
|
||||||
total += r.total;
|
total += r.total;
|
||||||
cases += r.cases;
|
cases += r.cases;
|
||||||
failed += r.failed;
|
failed += r.failed;
|
||||||
|
auto const elapsed = clock_type::now() - r.start;
|
||||||
auto const elapsed =
|
if (elapsed >= std::chrono::seconds{1})
|
||||||
clock_type::now() - r.start;
|
|
||||||
if (elapsed >= std::chrono::seconds(1))
|
|
||||||
{
|
{
|
||||||
auto const iter = std::lower_bound(top.begin(),
|
auto const iter = std::lower_bound(top.begin(),
|
||||||
top.end(), elapsed,
|
top.end(), elapsed,
|
||||||
@@ -196,50 +180,40 @@ reporter<_>::results::add (
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
reporter<_>::reporter (
|
reporter<_>::
|
||||||
std::ostream& stream)
|
reporter(std::ostream& os)
|
||||||
: std_ostream_ (std::ref (stream))
|
: os_(os)
|
||||||
, stream_ (*std_ostream_)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
reporter<_>::~reporter()
|
reporter<_>::~reporter()
|
||||||
{
|
{
|
||||||
if (results_.top.size() > 0)
|
if(results_.top.size() > 0)
|
||||||
{
|
{
|
||||||
stream_.get() << "Longest suite times:";
|
os_ << "Longest suite times:\n";
|
||||||
for (auto const& i : results_.top)
|
for(auto const& i : results_.top)
|
||||||
stream_.get() << std::setw(8) <<
|
os_ << std::setw(8) <<
|
||||||
fmtdur(i.second) << " " << i.first;
|
fmtdur(i.second) << " " << i.first << '\n';
|
||||||
}
|
}
|
||||||
auto const elapsed =
|
auto const elapsed = clock_type::now() - results_.start;
|
||||||
clock_type::now() - results_.start;
|
os_ <<
|
||||||
stream_.get() <<
|
|
||||||
fmtdur(elapsed) << ", " <<
|
fmtdur(elapsed) << ", " <<
|
||||||
amount (results_.suites, "suite") << ", " <<
|
amount{results_.suites, "suite"} << ", " <<
|
||||||
amount (results_.cases, "case") << ", " <<
|
amount{results_.cases, "case"} << ", " <<
|
||||||
amount (results_.total, "test") << " total, " <<
|
amount{results_.total, "test"} << " total, " <<
|
||||||
amount (results_.failed, "failure");
|
amount{results_.failed, "failure"} <<
|
||||||
|
std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
reporter<_>::reporter (
|
|
||||||
abstract_ostream& stream)
|
|
||||||
: stream_ (stream)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class _>
|
|
||||||
std::string
|
std::string
|
||||||
reporter<_>::fmtdur (
|
reporter<_>::fmtdur(typename clock_type::duration const& d)
|
||||||
typename clock_type::duration const& d)
|
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
auto const ms =
|
auto const ms = duration_cast<milliseconds>(d);
|
||||||
duration_cast<milliseconds>(d);
|
if (ms < seconds{1})
|
||||||
if (ms < seconds(1))
|
|
||||||
return std::to_string(ms.count()) + "ms";
|
return std::to_string(ms.count()) + "ms";
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::fixed << std::setprecision(1) <<
|
ss << std::fixed << std::setprecision(1) <<
|
||||||
@@ -247,67 +221,68 @@ reporter<_>::fmtdur (
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::on_suite_begin (
|
reporter<_>::
|
||||||
suite_info const& info)
|
on_suite_begin(suite_info const& info)
|
||||||
{
|
{
|
||||||
suite_results_ = suite_results (info.full_name());
|
suite_results_ = suite_results{info.full_name()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::on_suite_end()
|
reporter<_>::on_suite_end()
|
||||||
{
|
{
|
||||||
results_.add (suite_results_);
|
results_.add(suite_results_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::on_case_begin (
|
reporter<_>::
|
||||||
std::string const& name)
|
on_case_begin(std::string const& name)
|
||||||
{
|
{
|
||||||
case_results_ = case_results (name);
|
case_results_ = case_results (name);
|
||||||
|
os_ <<
|
||||||
stream_.get() <<
|
|
||||||
suite_results_.name <<
|
suite_results_.name <<
|
||||||
(case_results_.name.empty() ?
|
(case_results_.name.empty() ?
|
||||||
"" : (" " + case_results_.name));
|
"" : (" " + case_results_.name)) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::on_case_end()
|
reporter<_>::
|
||||||
|
on_case_end()
|
||||||
{
|
{
|
||||||
suite_results_.add (case_results_);
|
suite_results_.add(case_results_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::on_pass()
|
reporter<_>::
|
||||||
|
on_pass()
|
||||||
{
|
{
|
||||||
++case_results_.total;
|
++case_results_.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::on_fail (
|
reporter<_>::
|
||||||
std::string const& reason)
|
on_fail(std::string const& reason)
|
||||||
{
|
{
|
||||||
++case_results_.failed;
|
++case_results_.failed;
|
||||||
++case_results_.total;
|
++case_results_.total;
|
||||||
stream_.get() <<
|
os_ <<
|
||||||
"#" << case_results_.total <<
|
"#" << case_results_.total << " failed" <<
|
||||||
" failed" <<
|
(reason.empty() ? "" : ": ") << reason << std::endl;
|
||||||
(reason.empty() ? "" : ": ") << reason;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
reporter<_>::on_log (
|
reporter<_>::
|
||||||
std::string const& s)
|
on_log(std::string const& s)
|
||||||
{
|
{
|
||||||
stream_.get() << s;
|
os_ << s;
|
||||||
|
os_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
|
#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/unit_test/suite_info.hpp>
|
#include <beast/unit_test/suite_info.hpp>
|
||||||
#include <beast/unit_test/abstract_ostream.hpp>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
@@ -23,28 +23,6 @@ namespace unit_test {
|
|||||||
*/
|
*/
|
||||||
class runner
|
class runner
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
// Reroutes log output to the runner
|
|
||||||
class stream_t : public abstract_ostream
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
runner& owner_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
stream_t() = delete;
|
|
||||||
stream_t& operator= (stream_t const&) = delete;
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
stream_t (runner& owner);
|
|
||||||
|
|
||||||
void
|
|
||||||
write (string_type const& s) override
|
|
||||||
{
|
|
||||||
owner_.log (s);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
stream_t stream_;
|
|
||||||
std::string arg_;
|
std::string arg_;
|
||||||
bool default_ = false;
|
bool default_ = false;
|
||||||
bool failed_ = false;
|
bool failed_ = false;
|
||||||
@@ -52,21 +30,20 @@ private:
|
|||||||
std::recursive_mutex mutex_;
|
std::recursive_mutex mutex_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
runner() = default;
|
||||||
virtual ~runner() = default;
|
virtual ~runner() = default;
|
||||||
runner (runner const&) = default;
|
runner(runner const&) = delete;
|
||||||
runner& operator= (runner const&) = default;
|
runner& operator=(runner const&) = delete;
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
runner();
|
|
||||||
|
|
||||||
/** Set the argument string.
|
/** Set the argument string.
|
||||||
|
|
||||||
The argument string is available to suites and
|
The argument string is available to suites and
|
||||||
allows for customization of the test. Each suite
|
allows for customization of the test. Each suite
|
||||||
defines its own syntax for the argumnet string.
|
defines its own syntax for the argumnet string.
|
||||||
The same argument is passed to all suites.
|
The same argument is passed to all suites.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
arg (std::string const& s)
|
arg(std::string const& s)
|
||||||
{
|
{
|
||||||
arg_ = s;
|
arg_ = s;
|
||||||
}
|
}
|
||||||
@@ -81,7 +58,7 @@ public:
|
|||||||
/** Run the specified suite.
|
/** Run the specified suite.
|
||||||
@return `true` if any conditions failed.
|
@return `true` if any conditions failed.
|
||||||
*/
|
*/
|
||||||
template <class = void>
|
template<class = void>
|
||||||
bool
|
bool
|
||||||
run (suite_info const& s);
|
run (suite_info const& s);
|
||||||
|
|
||||||
@@ -124,69 +101,63 @@ public:
|
|||||||
bool
|
bool
|
||||||
run_each_if (SequenceContainer const& c, Pred pred = Pred{});
|
run_each_if (SequenceContainer const& c, Pred pred = Pred{});
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
//
|
//
|
||||||
// Overrides
|
// Overrides
|
||||||
//
|
//
|
||||||
|
|
||||||
/** Called when a new suite starts. */
|
/// Called when a new suite starts.
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
on_suite_begin (suite_info const&)
|
on_suite_begin(suite_info const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when a suite ends. */
|
/// Called when a suite ends.
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
on_suite_end()
|
on_suite_end()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when a new case starts. */
|
/// Called when a new case starts.
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
on_case_begin (std::string const&)
|
on_case_begin(std::string const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when a new case ends. */
|
/// Called when a new case ends.
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
on_case_end()
|
on_case_end()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called for each passing condition. */
|
/// Called for each passing condition.
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
on_pass ()
|
on_pass()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called for each failing condition. */
|
/// Called for each failing condition.
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
on_fail (std::string const&)
|
on_fail(std::string const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when a test logs output. */
|
/// Called when a test logs output.
|
||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
on_log (std::string const&)
|
on_log(std::string const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class suite;
|
friend class suite;
|
||||||
|
|
||||||
abstract_ostream&
|
|
||||||
stream()
|
|
||||||
{
|
|
||||||
return stream_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a new testcase.
|
// Start a new testcase.
|
||||||
template <class = void>
|
template <class = void>
|
||||||
void
|
void
|
||||||
@@ -207,20 +178,6 @@ private:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template <class>
|
|
||||||
runner::stream_t::stream_t (runner& owner)
|
|
||||||
: owner_ (owner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
runner::runner()
|
|
||||||
: stream_ (*this)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
template <class>
|
||||||
bool
|
bool
|
||||||
runner::run (suite_info const& s)
|
runner::run (suite_info const& s)
|
||||||
|
|||||||
@@ -9,15 +9,23 @@
|
|||||||
#define BEAST_UNIT_TEST_SUITE_HPP
|
#define BEAST_UNIT_TEST_SUITE_HPP
|
||||||
|
|
||||||
#include <beast/unit_test/runner.hpp>
|
#include <beast/unit_test/runner.hpp>
|
||||||
#include <string>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace unit_test {
|
namespace unit_test {
|
||||||
|
|
||||||
class thread;
|
class thread;
|
||||||
|
|
||||||
|
enum abort_t
|
||||||
|
{
|
||||||
|
no_abort_on_fail,
|
||||||
|
abort_on_fail
|
||||||
|
};
|
||||||
|
|
||||||
/** A testsuite class.
|
/** A testsuite class.
|
||||||
|
|
||||||
Derived classes execute a series of testcases, where each testcase is
|
Derived classes execute a series of testcases, where each testcase is
|
||||||
a series of pass/fail tests. To provide a unit test using this class,
|
a series of pass/fail tests. To provide a unit test using this class,
|
||||||
derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
|
derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
|
||||||
@@ -25,13 +33,6 @@ class thread;
|
|||||||
*/
|
*/
|
||||||
class suite
|
class suite
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
enum abort_t
|
|
||||||
{
|
|
||||||
no_abort_on_fail,
|
|
||||||
abort_on_fail
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool abort_ = false;
|
bool abort_ = false;
|
||||||
bool aborted_ = false;
|
bool aborted_ = false;
|
||||||
@@ -44,95 +45,100 @@ private:
|
|||||||
char const*
|
char const*
|
||||||
what() const noexcept override
|
what() const noexcept override
|
||||||
{
|
{
|
||||||
return "suite aborted";
|
return "test suite aborted";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
template<class CharT, class Traits, class Allocator>
|
||||||
// Memberspace
|
class log_buf
|
||||||
class log_t
|
: public std::basic_stringbuf<CharT, Traits, Allocator>
|
||||||
{
|
{
|
||||||
private:
|
suite& suite_;
|
||||||
friend class suite;
|
|
||||||
suite* suite_ = nullptr;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
log_t () = default;
|
explicit
|
||||||
|
log_buf(suite& self)
|
||||||
|
: suite_(self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
~log_buf()
|
||||||
abstract_ostream::scoped_stream_type
|
{
|
||||||
operator<< (T const& t);
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the raw stream used for output. */
|
int
|
||||||
abstract_ostream&
|
sync() override
|
||||||
stream();
|
{
|
||||||
|
auto const& s = this->str();
|
||||||
|
if(s.size() > 0)
|
||||||
|
suite_.runner_->on_log(s);
|
||||||
|
this->str("");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<
|
||||||
|
class CharT,
|
||||||
|
class Traits = std::char_traits<CharT>,
|
||||||
|
class Allocator = std::allocator<CharT>
|
||||||
|
>
|
||||||
|
class log_os : public std::basic_ostream<CharT, Traits>
|
||||||
|
{
|
||||||
|
log_buf<CharT, Traits, Allocator> buf_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
log_os(suite& self)
|
||||||
|
: std::basic_ostream<CharT, Traits>(&buf_)
|
||||||
|
, buf_(self)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
private:
|
|
||||||
|
|
||||||
class scoped_testcase;
|
class scoped_testcase;
|
||||||
|
|
||||||
// Memberspace
|
|
||||||
class testcase_t
|
class testcase_t
|
||||||
{
|
{
|
||||||
private:
|
suite& suite_;
|
||||||
friend class suite;
|
|
||||||
suite* suite_ = nullptr;
|
|
||||||
std::stringstream ss_;
|
std::stringstream ss_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
testcase_t() = default;
|
explicit
|
||||||
|
testcase_t(suite& self)
|
||||||
|
: suite_(self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/** Open a new testcase.
|
/** Open a new testcase.
|
||||||
A testcase is a series of evaluated test conditions. A test suite
|
|
||||||
may have multiple test cases. A test is associated with the last
|
A testcase is a series of evaluated test conditions. A test
|
||||||
opened testcase. When the test first runs, a default unnamed
|
suite may have multiple test cases. A test is associated with
|
||||||
case is opened. Tests with only one case may omit the call
|
the last opened testcase. When the test first runs, a default
|
||||||
to testcase.
|
unnamed case is opened. Tests with only one case may omit the
|
||||||
@param abort If `true`, the suite will be stopped on first failure.
|
call to testcase.
|
||||||
|
|
||||||
|
@param abort Determines if suite continues running after a failure.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
operator() (std::string const& name,
|
operator()(std::string const& name,
|
||||||
abort_t abort = no_abort_on_fail);
|
abort_t abort = no_abort_on_fail);
|
||||||
|
|
||||||
/** Stream style composition of testcase names. */
|
|
||||||
/** @{ */
|
|
||||||
scoped_testcase
|
scoped_testcase
|
||||||
operator() (abort_t abort);
|
operator()(abort_t abort);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
scoped_testcase
|
scoped_testcase
|
||||||
operator<< (T const& t);
|
operator<<(T const& t);
|
||||||
/** @} */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Type for scoped stream logging.
|
/** Logging output stream.
|
||||||
To use this type, declare a local variable of the type
|
|
||||||
on the stack in derived class member function and construct
|
|
||||||
it from log.stream();
|
|
||||||
|
|
||||||
@code
|
Text sent to the log output stream will be forwarded to
|
||||||
|
the output stream associated with the runner.
|
||||||
scoped_stream ss (log.stream();
|
|
||||||
|
|
||||||
ss << "Hello" << std::endl;
|
|
||||||
ss << "world" << std::endl;
|
|
||||||
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
Streams constructed in this fashion will not have the line
|
|
||||||
ending automatically appended.
|
|
||||||
|
|
||||||
Thread safety:
|
|
||||||
|
|
||||||
The scoped_stream may only be used by one thread.
|
|
||||||
Multiline output sent to the stream will be atomically
|
|
||||||
written to the underlying abstract_Ostream
|
|
||||||
*/
|
*/
|
||||||
using scoped_stream = abstract_ostream::scoped_stream_type;
|
log_os<char> log;
|
||||||
|
|
||||||
/** Memberspace for logging. */
|
|
||||||
log_t log;
|
|
||||||
|
|
||||||
/** Memberspace for declaring test cases. */
|
/** Memberspace for declaring test cases. */
|
||||||
testcase_t testcase;
|
testcase_t testcase;
|
||||||
@@ -145,6 +151,12 @@ public:
|
|||||||
return *p_this_suite();
|
return *p_this_suite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suite()
|
||||||
|
: log(*this)
|
||||||
|
, testcase(*this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/** Invokes the test using the specified runner.
|
/** Invokes the test using the specified runner.
|
||||||
Data members are set up here instead of the constructor as a
|
Data members are set up here instead of the constructor as a
|
||||||
convenience to writing the derived class to avoid repetition of
|
convenience to writing the derived class to avoid repetition of
|
||||||
@@ -272,84 +284,51 @@ private:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline
|
|
||||||
abstract_ostream::scoped_stream_type
|
|
||||||
suite::log_t::operator<< (T const& t)
|
|
||||||
{
|
|
||||||
return suite_->runner_->stream() << t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the raw stream used for output. */
|
|
||||||
inline
|
|
||||||
abstract_ostream&
|
|
||||||
suite::log_t::stream()
|
|
||||||
{
|
|
||||||
return suite_->runner_->stream();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Helper for streaming testcase names
|
// Helper for streaming testcase names
|
||||||
class suite::scoped_testcase
|
class suite::scoped_testcase
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
suite* suite_;
|
suite& suite_;
|
||||||
std::stringstream* ss_;
|
std::stringstream& ss_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~scoped_testcase();
|
scoped_testcase& operator=(scoped_testcase const&) = delete;
|
||||||
|
|
||||||
scoped_testcase (suite* s, std::stringstream* ss);
|
~scoped_testcase()
|
||||||
|
{
|
||||||
|
auto const& name = ss_.str();
|
||||||
|
if(! name.empty())
|
||||||
|
suite_.runner_->testcase (name);
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
scoped_testcase(suite& self, std::stringstream& ss)
|
||||||
scoped_testcase (suite* s, std::stringstream* ss, T const& t);
|
: suite_(self)
|
||||||
|
, ss_(ss)
|
||||||
|
{
|
||||||
|
ss_.clear();
|
||||||
|
ss_.str({});
|
||||||
|
}
|
||||||
|
|
||||||
scoped_testcase& operator= (scoped_testcase const&) = delete;
|
template<class T>
|
||||||
|
scoped_testcase(suite& self,
|
||||||
|
std::stringstream& ss, T const& t)
|
||||||
|
: suite_(self)
|
||||||
|
, ss_(ss)
|
||||||
|
{
|
||||||
|
ss_.clear();
|
||||||
|
ss_.str({});
|
||||||
|
ss_ << t;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template<class T>
|
||||||
scoped_testcase&
|
scoped_testcase&
|
||||||
operator<< (T const& t);
|
operator<<(T const& t)
|
||||||
|
{
|
||||||
|
ss_ << t;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline
|
|
||||||
suite::scoped_testcase::~scoped_testcase()
|
|
||||||
{
|
|
||||||
auto const& name (ss_->str());
|
|
||||||
if (! name.empty())
|
|
||||||
suite_->runner_->testcase (name);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss)
|
|
||||||
: suite_ (s)
|
|
||||||
, ss_ (ss)
|
|
||||||
{
|
|
||||||
ss_->clear();
|
|
||||||
ss_->str({});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline
|
|
||||||
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss, T const& t)
|
|
||||||
: suite_ (s)
|
|
||||||
, ss_ (ss)
|
|
||||||
{
|
|
||||||
ss_->clear();
|
|
||||||
ss_->str({});
|
|
||||||
*ss_ << t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline
|
|
||||||
suite::scoped_testcase&
|
|
||||||
suite::scoped_testcase::operator<< (T const& t)
|
|
||||||
{
|
|
||||||
*ss_ << t;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
inline
|
inline
|
||||||
@@ -357,16 +336,16 @@ void
|
|||||||
suite::testcase_t::operator() (std::string const& name,
|
suite::testcase_t::operator() (std::string const& name,
|
||||||
abort_t abort)
|
abort_t abort)
|
||||||
{
|
{
|
||||||
suite_->abort_ = abort == abort_on_fail;
|
suite_.abort_ = abort == abort_on_fail;
|
||||||
suite_->runner_->testcase (name);
|
suite_.runner_->testcase (name);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
suite::scoped_testcase
|
suite::scoped_testcase
|
||||||
suite::testcase_t::operator() (abort_t abort)
|
suite::testcase_t::operator() (abort_t abort)
|
||||||
{
|
{
|
||||||
suite_->abort_ = abort == abort_on_fail;
|
suite_.abort_ = abort == abort_on_fail;
|
||||||
return { suite_, &ss_ };
|
return { suite_, ss_ };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
@@ -374,7 +353,7 @@ inline
|
|||||||
suite::scoped_testcase
|
suite::scoped_testcase
|
||||||
suite::testcase_t::operator<< (T const& t)
|
suite::testcase_t::operator<< (T const& t)
|
||||||
{
|
{
|
||||||
return { suite_, &ss_, t };
|
return { suite_, ss_, t };
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -511,8 +490,6 @@ void
|
|||||||
suite::run (runner& r)
|
suite::run (runner& r)
|
||||||
{
|
{
|
||||||
runner_ = &r;
|
runner_ = &r;
|
||||||
log.suite_ = this;
|
|
||||||
testcase.suite_ = this;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef BEAST_UNIT_TEST_SUITE_INFO_HPP
|
#ifndef BEAST_UNIT_TEST_SUITE_INFO_HPP
|
||||||
#define BEAST_UNIT_TEST_SUITE_INFO_HPP
|
#define BEAST_UNIT_TEST_SUITE_INFO_HPP
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -20,19 +21,28 @@ class runner;
|
|||||||
/** Associates a unit test type with metadata. */
|
/** Associates a unit test type with metadata. */
|
||||||
class suite_info
|
class suite_info
|
||||||
{
|
{
|
||||||
private:
|
using run_type = std::function<void(runner&)>;
|
||||||
using run_type = std::function <void (runner&)>;
|
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
std::string module_;
|
std::string module_;
|
||||||
std::string library_;
|
std::string library_;
|
||||||
bool m_manual;
|
bool manual_;
|
||||||
run_type m_run;
|
run_type run_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <class = void>
|
suite_info(
|
||||||
suite_info (std::string const& name, std::string const& module,
|
std::string name,
|
||||||
std::string const& library, bool manual, run_type run);
|
std::string module,
|
||||||
|
std::string library,
|
||||||
|
bool manual,
|
||||||
|
run_type run)
|
||||||
|
: name_(std::move(name))
|
||||||
|
, module_(std::move(module))
|
||||||
|
, library_(std::move(library))
|
||||||
|
, manual_(manual)
|
||||||
|
, run_(std::move(run))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::string const&
|
std::string const&
|
||||||
name() const
|
name() const
|
||||||
@@ -52,61 +62,58 @@ public:
|
|||||||
return library_;
|
return library_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if this suite only runs manually. */
|
/// Returns `true` if this suite only runs manually.
|
||||||
bool
|
bool
|
||||||
manual() const
|
manual() const
|
||||||
{
|
{
|
||||||
return m_manual;
|
return manual_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the canonical suite name as a string. */
|
/// Return the canonical suite name as a string.
|
||||||
template <class = void>
|
|
||||||
std::string
|
std::string
|
||||||
full_name() const;
|
full_name() const
|
||||||
|
|
||||||
/** Run a new instance of the associated test suite. */
|
|
||||||
void
|
|
||||||
run (runner& r) const
|
|
||||||
{
|
{
|
||||||
m_run (r);
|
return library_ + "." + module_ + "." + name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a new instance of the associated test suite.
|
||||||
|
void
|
||||||
|
run(runner& r) const
|
||||||
|
{
|
||||||
|
run_ (r);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool
|
||||||
|
operator<(suite_info const& lhs, suite_info const& rhs)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
std::tie(lhs.library_, lhs.module_, lhs.name_) <
|
||||||
|
std::tie(rhs.library_, rhs.module_, rhs.name_);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template <class>
|
/// Convenience for producing suite_info for a given test type.
|
||||||
suite_info::suite_info (std::string const& name, std::string const& module,
|
template<class Suite>
|
||||||
std::string const& library, bool manual, run_type run)
|
|
||||||
: name_ (name)
|
|
||||||
, module_ (module)
|
|
||||||
, library_ (library)
|
|
||||||
, m_manual (manual)
|
|
||||||
, m_run (std::move (run))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
std::string
|
|
||||||
suite_info::full_name() const
|
|
||||||
{
|
|
||||||
return library_ + "." + module_ + "." + name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
operator< (suite_info const& lhs, suite_info const& rhs)
|
|
||||||
{
|
|
||||||
return lhs.full_name() < rhs.full_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience for producing suite_info for a given test type. */
|
|
||||||
template <class Suite>
|
|
||||||
suite_info
|
suite_info
|
||||||
make_suite_info (std::string const& name, std::string const& module,
|
make_suite_info(
|
||||||
std::string const& library, bool manual)
|
std::string name,
|
||||||
|
std::string module,
|
||||||
|
std::string library,
|
||||||
|
bool manual)
|
||||||
{
|
{
|
||||||
return suite_info(name, module, library, manual,
|
return suite_info(
|
||||||
[](runner& r) { return Suite{}(r); });
|
std::move(name),
|
||||||
|
std::move(module),
|
||||||
|
std::move(library),
|
||||||
|
manual,
|
||||||
|
[](runner& r)
|
||||||
|
{
|
||||||
|
Suite{}(r);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // unit_test
|
} // unit_test
|
||||||
|
|||||||
@@ -18,23 +18,27 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace unit_test {
|
namespace unit_test {
|
||||||
|
|
||||||
/** A container of test suites. */
|
/// A container of test suites.
|
||||||
class suite_list
|
class suite_list
|
||||||
: public detail::const_container <std::set <suite_info>>
|
: public detail::const_container <std::set <suite_info>>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::unordered_set <std::string> names_;
|
std::unordered_set<std::string> names_;
|
||||||
std::unordered_set <std::type_index> classes_;
|
std::unordered_set<std::type_index> classes_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Insert a suite into the set.
|
/** Insert a suite into the set.
|
||||||
|
|
||||||
The suite must not already exist.
|
The suite must not already exist.
|
||||||
*/
|
*/
|
||||||
template <class Suite>
|
template <class Suite>
|
||||||
void
|
void
|
||||||
insert (char const* name, char const* module, char const* library,
|
insert(
|
||||||
|
char const* name,
|
||||||
|
char const* module,
|
||||||
|
char const* library,
|
||||||
bool manual);
|
bool manual);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,7 +46,10 @@ public:
|
|||||||
|
|
||||||
template <class Suite>
|
template <class Suite>
|
||||||
void
|
void
|
||||||
suite_list::insert (char const* name, char const* module, char const* library,
|
suite_list::insert(
|
||||||
|
char const* name,
|
||||||
|
char const* module,
|
||||||
|
char const* library,
|
||||||
bool manual)
|
bool manual)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@@ -59,9 +66,8 @@ suite_list::insert (char const* name, char const* module, char const* library,
|
|||||||
assert (result.second); // Duplicate type
|
assert (result.second); // Duplicate type
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
cont().emplace(make_suite_info<Suite>(
|
||||||
cont().emplace (std::move (make_suite_info <Suite> (
|
name, module, library, manual));
|
||||||
name, module, library, manual)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // unit_test
|
} // unit_test
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_TEMP_DIR_H_INCLUDED
|
|
||||||
#define BEAST_DETAIL_TEMP_DIR_H_INCLUDED
|
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
/** RAII temporary directory.
|
|
||||||
|
|
||||||
The directory and all its contents are deleted when
|
|
||||||
the instance of `temp_dir` is destroyed.
|
|
||||||
*/
|
|
||||||
class temp_dir
|
|
||||||
{
|
|
||||||
boost::filesystem::path path_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
#if ! GENERATING_DOCS
|
|
||||||
temp_dir(const temp_dir&) = delete;
|
|
||||||
temp_dir& operator=(const temp_dir&) = delete;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Construct a temporary directory.
|
|
||||||
temp_dir()
|
|
||||||
{
|
|
||||||
auto const dir =
|
|
||||||
boost::filesystem::temp_directory_path();
|
|
||||||
do
|
|
||||||
{
|
|
||||||
path_ =
|
|
||||||
dir / boost::filesystem::unique_path();
|
|
||||||
}
|
|
||||||
while(boost::filesystem::exists(path_));
|
|
||||||
boost::filesystem::create_directory (path_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Destroy a temporary directory.
|
|
||||||
~temp_dir()
|
|
||||||
{
|
|
||||||
boost::filesystem::remove_all (path_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the native path for the temporary directory
|
|
||||||
std::string
|
|
||||||
path() const
|
|
||||||
{
|
|
||||||
return path_.string();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the native path for the a file.
|
|
||||||
|
|
||||||
The file does not need to exist.
|
|
||||||
*/
|
|
||||||
std::string
|
|
||||||
file(std::string const& name) const
|
|
||||||
{
|
|
||||||
return (path_ / name).string();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // detail
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_DETAIL_UNIT_TEST_H_INCLUDED
|
|
||||||
#define BEAST_DETAIL_UNIT_TEST_H_INCLUDED
|
|
||||||
|
|
||||||
#include <beast/detail/unit_test/amount.h>
|
|
||||||
#include <beast/detail/unit_test/print.h>
|
|
||||||
#include <beast/detail/unit_test/global_suites.h>
|
|
||||||
#include <beast/detail/unit_test/match.h>
|
|
||||||
#include <beast/detail/unit_test/recorder.h>
|
|
||||||
#include <beast/detail/unit_test/reporter.h>
|
|
||||||
#include <beast/detail/unit_test/results.h>
|
|
||||||
#include <beast/detail/unit_test/runner.h>
|
|
||||||
#include <beast/detail/unit_test/suite.h>
|
|
||||||
#include <beast/detail/unit_test/suite_info.h>
|
|
||||||
#include <beast/detail/unit_test/suite_list.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
156
src/beast/include/beast/http/concepts.hpp
Normal file
156
src/beast/include/beast/http/concepts.hpp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
|
||||||
|
#define BEAST_HTTP_TYPE_CHECK_HPP
|
||||||
|
|
||||||
|
#include <beast/core/error.hpp>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class has_value_type
|
||||||
|
{
|
||||||
|
template<class U, class R =
|
||||||
|
typename U::value_type>
|
||||||
|
static std::true_type check(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<T>(0));
|
||||||
|
public:
|
||||||
|
static bool constexpr value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, bool B = has_value_type<T>::value>
|
||||||
|
struct extract_value_type
|
||||||
|
{
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct extract_value_type<T, true>
|
||||||
|
{
|
||||||
|
using type = typename T::value_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class has_reader
|
||||||
|
{
|
||||||
|
template<class U, class R =
|
||||||
|
typename U::reader>
|
||||||
|
static std::true_type check(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
public:
|
||||||
|
using type = decltype(check<T>(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class has_writer
|
||||||
|
{
|
||||||
|
template<class U, class R =
|
||||||
|
typename U::writer>
|
||||||
|
static std::true_type check(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
public:
|
||||||
|
using type = decltype(check<T>(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct is_Body
|
||||||
|
{
|
||||||
|
using type = std::integral_constant<bool,
|
||||||
|
has_value_type<T>::value &&
|
||||||
|
std::is_default_constructible<
|
||||||
|
typename extract_value_type<T>::type>::value
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class is_Parser
|
||||||
|
{
|
||||||
|
template<class U, class R =
|
||||||
|
std::is_convertible<decltype(
|
||||||
|
std::declval<U>().complete()),
|
||||||
|
bool>>
|
||||||
|
static R check1(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check1(...);
|
||||||
|
using type1 = decltype(check1<T>(0));
|
||||||
|
|
||||||
|
template<class U, class R =
|
||||||
|
std::is_convertible<decltype(
|
||||||
|
std::declval<U>().write(
|
||||||
|
std::declval<boost::asio::const_buffer const&>(),
|
||||||
|
std::declval<error_code&>())),
|
||||||
|
std::size_t>>
|
||||||
|
static R check2(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check2(...);
|
||||||
|
using type2 = decltype(check2<T>(0));
|
||||||
|
|
||||||
|
template<class U, class R = decltype(
|
||||||
|
std::declval<U>().write_eof(std::declval<error_code&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check3(int);
|
||||||
|
template<class>
|
||||||
|
static std::false_type check3(...);
|
||||||
|
using type3 = decltype(check3<T>(0));
|
||||||
|
|
||||||
|
public:
|
||||||
|
using type = std::integral_constant<bool,
|
||||||
|
type1::value &&
|
||||||
|
type2::value &&
|
||||||
|
type3::value
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/// Determine if `T` meets the requirements of `Body`.
|
||||||
|
template<class T>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
struct is_Body : std::integral_constant<bool, ...>{};
|
||||||
|
#else
|
||||||
|
using is_Body = typename detail::is_Body<T>::type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Determine if `T` meets the requirements of `ReadableBody`.
|
||||||
|
template<class T>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
struct is_ReadableBody : std::integral_constant<bool, ...>{};
|
||||||
|
#else
|
||||||
|
using is_ReadableBody = typename detail::has_reader<T>::type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Determine if `T` meets the requirements of `WritableBody`.
|
||||||
|
template<class T>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
struct is_WritableBody : std::integral_constant<bool, ...>{};
|
||||||
|
#else
|
||||||
|
using is_WritableBody = typename detail::has_writer<T>::type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Determine if `T` meets the requirements of `Parser`.
|
||||||
|
template<class T>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
struct is_Parser : std::integral_constant<bool, ...>{};
|
||||||
|
#else
|
||||||
|
using is_Parser = typename detail::is_Parser<T>::type;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -34,20 +34,6 @@ struct empty_body
|
|||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct reader
|
|
||||||
{
|
|
||||||
template<bool isRequest, class Headers>
|
|
||||||
explicit
|
|
||||||
reader(message<isRequest, empty_body, Headers>&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
write(void const*, std::size_t, error_code&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct writer
|
struct writer
|
||||||
{
|
{
|
||||||
writer(writer const&) = delete;
|
writer(writer const&) = delete;
|
||||||
|
|||||||
@@ -463,8 +463,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
|
|
||||||
case s_header_field:
|
case s_header_field:
|
||||||
{
|
{
|
||||||
for(; p != end; ch = *++p)
|
for(; p != end; ++p)
|
||||||
{
|
{
|
||||||
|
ch = *p;
|
||||||
auto c = to_field_char(ch);
|
auto c = to_field_char(ch);
|
||||||
if(! c)
|
if(! c)
|
||||||
break;
|
break;
|
||||||
@@ -660,8 +661,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
|
|
||||||
case s_header_value_text:
|
case s_header_value_text:
|
||||||
{
|
{
|
||||||
for(; p != end; ch = *++p)
|
for(; p != end; ++p)
|
||||||
{
|
{
|
||||||
|
ch = *p;
|
||||||
if(ch == '\r')
|
if(ch == '\r')
|
||||||
{
|
{
|
||||||
if(cb(nullptr))
|
if(cb(nullptr))
|
||||||
|
|||||||
@@ -105,33 +105,6 @@ prepare_content_length(prepare_info& pi,
|
|||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Headers>
|
|
||||||
void
|
|
||||||
prepare_connection(
|
|
||||||
message_v1<isRequest, Body, Headers>& msg)
|
|
||||||
{
|
|
||||||
if(msg.version >= 11)
|
|
||||||
{
|
|
||||||
if(! msg.headers.exists("Content-Length") &&
|
|
||||||
! rfc2616::token_in_list(
|
|
||||||
msg.headers["Transfer-Encoding"], "chunked"))
|
|
||||||
if(! rfc2616::token_in_list(
|
|
||||||
msg.headers["Connection"], "close"))
|
|
||||||
msg.headers.insert("Connection", "close");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(! msg.headers.exists("Content-Length"))
|
|
||||||
{
|
|
||||||
// VFALCO We are erasing the whole header when we
|
|
||||||
// should be removing just the keep-alive.
|
|
||||||
if(rfc2616::token_in_list(
|
|
||||||
msg.headers["Connection"], "keep-alive"))
|
|
||||||
msg.headers.erase("Connection");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<
|
template<
|
||||||
bool isRequest, class Body, class Headers,
|
bool isRequest, class Body, class Headers,
|
||||||
class... Options>
|
class... Options>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
|
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||||
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||||
|
|
||||||
|
#include <beast/http/concepts.hpp>
|
||||||
#include <beast/http/parser_v1.hpp>
|
#include <beast/http/parser_v1.hpp>
|
||||||
#include <beast/http/type_check.hpp>
|
|
||||||
#include <beast/core/bind_handler.hpp>
|
#include <beast/core/bind_handler.hpp>
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_alloc.hpp>
|
||||||
#include <beast/core/stream_concepts.hpp>
|
#include <beast/core/stream_concepts.hpp>
|
||||||
@@ -323,6 +323,12 @@ void
|
|||||||
parse(SyncReadStream& stream,
|
parse(SyncReadStream& stream,
|
||||||
Streambuf& streambuf, Parser& parser)
|
Streambuf& streambuf, Parser& parser)
|
||||||
{
|
{
|
||||||
|
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||||
|
"SyncReadStream requirements not met");
|
||||||
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
static_assert(is_Parser<Parser>::value,
|
||||||
|
"Parser requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
parse(stream, streambuf, parser, ec);
|
parse(stream, streambuf, parser, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
@@ -403,6 +409,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
|||||||
"SyncReadStream requirements not met");
|
"SyncReadStream requirements not met");
|
||||||
static_assert(is_Streambuf<Streambuf>::value,
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
"Streambuf requirements not met");
|
"Streambuf requirements not met");
|
||||||
|
static_assert(is_ReadableBody<Body>::value,
|
||||||
|
"ReadableBody requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
read(stream, streambuf, msg, ec);
|
read(stream, streambuf, msg, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
@@ -420,6 +428,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
|||||||
"SyncReadStream requirements not met");
|
"SyncReadStream requirements not met");
|
||||||
static_assert(is_Streambuf<Streambuf>::value,
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
"Streambuf requirements not met");
|
"Streambuf requirements not met");
|
||||||
|
static_assert(is_ReadableBody<Body>::value,
|
||||||
|
"ReadableBody requirements not met");
|
||||||
parser_v1<isRequest, Body, Headers> p;
|
parser_v1<isRequest, Body, Headers> p;
|
||||||
parse(stream, streambuf, p, ec);
|
parse(stream, streambuf, p, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
@@ -441,6 +451,8 @@ async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
|||||||
"AsyncReadStream requirements not met");
|
"AsyncReadStream requirements not met");
|
||||||
static_assert(is_Streambuf<Streambuf>::value,
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
"Streambuf requirements not met");
|
"Streambuf requirements not met");
|
||||||
|
static_assert(is_ReadableBody<Body>::value,
|
||||||
|
"ReadableBody requirements not met");
|
||||||
beast::async_completion<ReadHandler,
|
beast::async_completion<ReadHandler,
|
||||||
void(error_code)> completion(handler);
|
void(error_code)> completion(handler);
|
||||||
detail::read_op<AsyncReadStream, Streambuf,
|
detail::read_op<AsyncReadStream, Streambuf,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef BEAST_HTTP_IMPL_WRITE_IPP
|
#ifndef BEAST_HTTP_IMPL_WRITE_IPP
|
||||||
#define BEAST_HTTP_IMPL_WRITE_IPP
|
#define BEAST_HTTP_IMPL_WRITE_IPP
|
||||||
|
|
||||||
|
#include <beast/http/concepts.hpp>
|
||||||
#include <beast/http/resume_context.hpp>
|
#include <beast/http/resume_context.hpp>
|
||||||
#include <beast/http/detail/chunk_encode.hpp>
|
#include <beast/http/detail/chunk_encode.hpp>
|
||||||
#include <beast/http/detail/has_content_length.hpp>
|
#include <beast/http/detail/has_content_length.hpp>
|
||||||
@@ -448,6 +449,8 @@ write(SyncWriteStream& stream,
|
|||||||
{
|
{
|
||||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||||
"SyncWriteStream requirements not met");
|
"SyncWriteStream requirements not met");
|
||||||
|
static_assert(is_WritableBody<Body>::value,
|
||||||
|
"WritableBody requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
write(stream, msg, ec);
|
write(stream, msg, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
@@ -463,6 +466,8 @@ write(SyncWriteStream& stream,
|
|||||||
{
|
{
|
||||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||||
"SyncWriteStream requirements not met");
|
"SyncWriteStream requirements not met");
|
||||||
|
static_assert(is_WritableBody<Body>::value,
|
||||||
|
"WritableBody requirements not met");
|
||||||
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||||
wp.init(ec);
|
wp.init(ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
@@ -546,6 +551,8 @@ async_write(AsyncWriteStream& stream,
|
|||||||
static_assert(
|
static_assert(
|
||||||
is_AsyncWriteStream<AsyncWriteStream>::value,
|
is_AsyncWriteStream<AsyncWriteStream>::value,
|
||||||
"AsyncWriteStream requirements not met");
|
"AsyncWriteStream requirements not met");
|
||||||
|
static_assert(is_WritableBody<Body>::value,
|
||||||
|
"WritableBody requirements not met");
|
||||||
beast::async_completion<WriteHandler,
|
beast::async_completion<WriteHandler,
|
||||||
void(error_code)> completion(handler);
|
void(error_code)> completion(handler);
|
||||||
detail::write_op<AsyncWriteStream, decltype(completion.handler),
|
detail::write_op<AsyncWriteStream, decltype(completion.handler),
|
||||||
@@ -607,6 +614,8 @@ std::ostream&
|
|||||||
operator<<(std::ostream& os,
|
operator<<(std::ostream& os,
|
||||||
message_v1<isRequest, Body, Headers> const& msg)
|
message_v1<isRequest, Body, Headers> const& msg)
|
||||||
{
|
{
|
||||||
|
static_assert(is_WritableBody<Body>::value,
|
||||||
|
"WritableBody requirements not met");
|
||||||
detail::ostream_SyncStream oss(os);
|
detail::ostream_SyncStream oss(os);
|
||||||
error_code ec;
|
error_code ec;
|
||||||
write(oss, msg, ec);
|
write(oss, msg, ec);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#define BEAST_HTTP_PARSER_V1_HPP
|
#define BEAST_HTTP_PARSER_V1_HPP
|
||||||
|
|
||||||
#include <beast/http/basic_parser_v1.hpp>
|
#include <beast/http/basic_parser_v1.hpp>
|
||||||
|
#include <beast/http/concepts.hpp>
|
||||||
#include <beast/http/message_v1.hpp>
|
#include <beast/http/message_v1.hpp>
|
||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -54,6 +55,9 @@ public:
|
|||||||
message_v1<isRequest, Body, Headers>;
|
message_v1<isRequest, Body, Headers>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static_assert(is_ReadableBody<Body>::value,
|
||||||
|
"ReadableBody requirements not met");
|
||||||
|
|
||||||
std::string field_;
|
std::string field_;
|
||||||
std::string value_;
|
std::string value_;
|
||||||
message_type m_;
|
message_type m_;
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
|
|
||||||
#define BEAST_HTTP_TYPE_CHECK_HPP
|
|
||||||
|
|
||||||
#include <beast/core/error.hpp>
|
|
||||||
#include <boost/asio/buffer.hpp>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace http {
|
|
||||||
|
|
||||||
/// Determine if `T` meets the requirements of `Parser`.
|
|
||||||
template<class T>
|
|
||||||
class is_Parser
|
|
||||||
{
|
|
||||||
template<class U, class R =
|
|
||||||
std::is_convertible<decltype(
|
|
||||||
std::declval<U>().complete()),
|
|
||||||
bool>>
|
|
||||||
static R check1(int);
|
|
||||||
template<class>
|
|
||||||
static std::false_type check1(...);
|
|
||||||
using type1 = decltype(check1<T>(0));
|
|
||||||
|
|
||||||
template<class U, class R =
|
|
||||||
std::is_convertible<decltype(
|
|
||||||
std::declval<U>().write(
|
|
||||||
std::declval<boost::asio::const_buffer const&>(),
|
|
||||||
std::declval<error_code&>())),
|
|
||||||
std::size_t>>
|
|
||||||
static R check2(int);
|
|
||||||
template<class>
|
|
||||||
static std::false_type check2(...);
|
|
||||||
using type2 = decltype(check2<T>(0));
|
|
||||||
|
|
||||||
template<class U, class R = decltype(
|
|
||||||
std::declval<U>().write_eof(std::declval<error_code&>()),
|
|
||||||
std::true_type{})>
|
|
||||||
static R check3(int);
|
|
||||||
template<class>
|
|
||||||
static std::false_type check3(...);
|
|
||||||
using type3 = decltype(check3<T>(0));
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// `true` if `T` meets the requirements.
|
|
||||||
static bool const value =
|
|
||||||
type1::value && type2::value && type3::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // http
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -16,6 +16,6 @@
|
|||||||
//
|
//
|
||||||
#define BEAST_VERSION 100000
|
#define BEAST_VERSION 100000
|
||||||
|
|
||||||
#define BEAST_VERSION_STRING "1.0.0-b4"
|
#define BEAST_VERSION_STRING "1.0.0-b5"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ namespace beast {
|
|||||||
namespace websocket {
|
namespace websocket {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
};
|
||||||
|
|
||||||
// Contents of a WebSocket frame header
|
// Contents of a WebSocket frame header
|
||||||
struct frame_header
|
struct frame_header
|
||||||
{
|
{
|
||||||
@@ -286,8 +296,7 @@ read_fh2(frame_header& fh, Streambuf& sb,
|
|||||||
//
|
//
|
||||||
template<class Buffers>
|
template<class Buffers>
|
||||||
void
|
void
|
||||||
read(ping_payload_type& data,
|
read(ping_data& data, Buffers const& bs)
|
||||||
Buffers const& bs, close_code::value& code)
|
|
||||||
{
|
{
|
||||||
using boost::asio::buffer_copy;
|
using boost::asio::buffer_copy;
|
||||||
using boost::asio::buffer_size;
|
using boost::asio::buffer_size;
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ using maskgen = maskgen_t<std::mt19937>;
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
//using prepared_key_type = std::size_t;
|
using prepared_key_type =
|
||||||
using prepared_key_type = std::uint32_t;
|
std::conditional<sizeof(void*) == 8,
|
||||||
//using prepared_key_type = std::uint64_t;
|
std::uint64_t, std::uint32_t>::type;
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
@@ -93,19 +93,6 @@ prepare_key(std::uint64_t& prepared, std::uint32_t key)
|
|||||||
template<class T>
|
template<class T>
|
||||||
inline
|
inline
|
||||||
typename std::enable_if<std::is_integral<T>::value, T>::type
|
typename std::enable_if<std::is_integral<T>::value, T>::type
|
||||||
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<typename std::make_unsigned<T>::type>(t) >> (bits - n)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
inline
|
|
||||||
typename std::enable_if<std::is_integral<T>::value, T>::type
|
|
||||||
ror(T t, unsigned n = 1)
|
ror(T t, unsigned n = 1)
|
||||||
{
|
{
|
||||||
auto constexpr bits =
|
auto constexpr bits =
|
||||||
@@ -120,7 +107,7 @@ ror(T t, unsigned n = 1)
|
|||||||
//
|
//
|
||||||
template<class = void>
|
template<class = void>
|
||||||
void
|
void
|
||||||
mask_inplace_safe(
|
mask_inplace_general(
|
||||||
boost::asio::mutable_buffer const& b,
|
boost::asio::mutable_buffer const& b,
|
||||||
std::uint32_t& key)
|
std::uint32_t& key)
|
||||||
{
|
{
|
||||||
@@ -151,7 +138,7 @@ mask_inplace_safe(
|
|||||||
//
|
//
|
||||||
template<class = void>
|
template<class = void>
|
||||||
void
|
void
|
||||||
mask_inplace_safe(
|
mask_inplace_general(
|
||||||
boost::asio::mutable_buffer const& b,
|
boost::asio::mutable_buffer const& b,
|
||||||
std::uint64_t& key)
|
std::uint64_t& key)
|
||||||
{
|
{
|
||||||
@@ -186,164 +173,13 @@ mask_inplace_safe(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
inline
|
||||||
void
|
void
|
||||||
mask_inplace(
|
mask_inplace(
|
||||||
boost::asio::mutable_buffer const& b,
|
boost::asio::mutable_buffer const& b,
|
||||||
std::uint32_t& key)
|
std::uint32_t& key)
|
||||||
{
|
{
|
||||||
mask_inplace_safe(b, key);
|
mask_inplace_general(b, key);
|
||||||
//mask_inplace_32(b, key);
|
|
||||||
//mask_inplace_x86(b, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
@@ -352,9 +188,7 @@ mask_inplace(
|
|||||||
boost::asio::mutable_buffer const& b,
|
boost::asio::mutable_buffer const& b,
|
||||||
std::uint64_t& key)
|
std::uint64_t& key)
|
||||||
{
|
{
|
||||||
mask_inplace_safe(b, key);
|
mask_inplace_general(b, key);
|
||||||
//mask_inplace_64(b, key);
|
|
||||||
//mask_inplace_amd(b, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply mask in place
|
// Apply mask in place
|
||||||
|
|||||||
@@ -30,15 +30,6 @@ namespace beast {
|
|||||||
namespace websocket {
|
namespace websocket {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<class String>
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
maybe_throw(error_code const& ec, String const&)
|
|
||||||
{
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class UInt>
|
template<class UInt>
|
||||||
static
|
static
|
||||||
std::size_t
|
std::size_t
|
||||||
@@ -59,6 +50,8 @@ clamp(UInt x, std::size_t limit)
|
|||||||
return static_cast<std::size_t>(x);
|
return static_cast<std::size_t>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using pong_cb = std::function<void(ping_data const&)>;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
struct stream_base
|
struct stream_base
|
||||||
@@ -69,42 +62,46 @@ protected:
|
|||||||
detail::maskgen maskgen_; // source of mask keys
|
detail::maskgen maskgen_; // source of mask keys
|
||||||
decorator_type d_; // adorns http messages
|
decorator_type d_; // adorns http messages
|
||||||
bool keep_alive_ = false; // close on failed upgrade
|
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_ =
|
std::size_t rd_msg_max_ =
|
||||||
16 * 1024 * 1024; // max message size
|
16 * 1024 * 1024; // max message size
|
||||||
|
std::size_t
|
||||||
|
wr_frag_size_ = 16 * 1024; // size of auto-fragments
|
||||||
|
std::size_t mask_buf_size_ = 4096; // mask buffer size
|
||||||
|
opcode wr_opcode_ = opcode::text; // outgoing message type
|
||||||
|
pong_cb pong_cb_; // pong callback
|
||||||
|
role_type role_; // server or client
|
||||||
|
bool failed_; // the connection failed
|
||||||
|
|
||||||
detail::frame_header rd_fh_; // current frame header
|
detail::frame_header rd_fh_; // current frame header
|
||||||
detail::prepared_key_type rd_key_; // prepared masking key
|
detail::prepared_key_type rd_key_; // prepared masking key
|
||||||
detail::utf8_checker rd_utf8_check_;// for current text msg
|
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_size_; // size of the current message so far
|
||||||
std::uint64_t rd_need_ = 0; // bytes left in msg frame payload
|
std::uint64_t rd_need_ = 0; // bytes left in msg frame payload
|
||||||
opcode rd_opcode_; // opcode of current msg
|
opcode rd_opcode_; // opcode of current msg
|
||||||
bool rd_cont_ = false; // expecting a continuation frame
|
bool rd_cont_; // expecting a continuation frame
|
||||||
bool rd_close_ = false; // got close frame
|
|
||||||
op* rd_block_ = nullptr; // op currently reading
|
|
||||||
|
|
||||||
std::size_t
|
bool wr_close_; // sent close frame
|
||||||
wr_frag_size_ = 16 * 1024; // size of auto-fragments
|
bool wr_cont_; // next write is continuation frame
|
||||||
std::size_t wr_buf_size_ = 4096; // write buffer size
|
op* wr_block_; // op currenly writing
|
||||||
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
|
|
||||||
|
|
||||||
|
ping_data* pong_data_; // where to put pong payload
|
||||||
invokable rd_op_; // invoked after write completes
|
invokable rd_op_; // invoked after write completes
|
||||||
invokable wr_op_; // invoked after read completes
|
invokable wr_op_; // invoked after read completes
|
||||||
close_reason cr_; // set from received close frame
|
close_reason cr_; // set from received close frame
|
||||||
|
|
||||||
|
stream_base(stream_base&&) = default;
|
||||||
|
stream_base(stream_base const&) = delete;
|
||||||
|
stream_base& operator=(stream_base&&) = default;
|
||||||
|
stream_base& operator=(stream_base const&) = delete;
|
||||||
|
|
||||||
stream_base()
|
stream_base()
|
||||||
: d_(new decorator<default_decorator>{})
|
: d_(new decorator<default_decorator>{})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_base(stream_base&&) = default;
|
template<class = void>
|
||||||
stream_base(stream_base const&) = delete;
|
void
|
||||||
stream_base& operator=(stream_base&&) = default;
|
open(role_type role);
|
||||||
stream_base& operator=(stream_base const&) = delete;
|
|
||||||
|
|
||||||
template<class = void>
|
template<class = void>
|
||||||
void
|
void
|
||||||
@@ -118,7 +115,7 @@ protected:
|
|||||||
template<class Streambuf>
|
template<class Streambuf>
|
||||||
void
|
void
|
||||||
write_ping(Streambuf& sb, opcode op,
|
write_ping(Streambuf& sb, opcode op,
|
||||||
ping_payload_type const& data);
|
ping_data const& data);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class stream<NextLayer>::accept_op
|
|||||||
struct data
|
struct data
|
||||||
{
|
{
|
||||||
stream<NextLayer>& ws;
|
stream<NextLayer>& ws;
|
||||||
http::request_v1<http::empty_body> req;
|
http::request_v1<http::string_body> req;
|
||||||
Handler h;
|
Handler h;
|
||||||
bool cont;
|
bool cont;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
@@ -48,6 +48,7 @@ class stream<NextLayer>::accept_op
|
|||||||
{
|
{
|
||||||
using boost::asio::buffer_copy;
|
using boost::asio::buffer_copy;
|
||||||
using boost::asio::buffer_size;
|
using boost::asio::buffer_size;
|
||||||
|
ws.reset();
|
||||||
ws.stream_.buffer().commit(buffer_copy(
|
ws.stream_.buffer().commit(buffer_copy(
|
||||||
ws.stream_.buffer().prepare(
|
ws.stream_.buffer().prepare(
|
||||||
buffer_size(buffers)), buffers));
|
buffer_size(buffers)), buffers));
|
||||||
@@ -133,8 +134,14 @@ operator()(error_code const& ec,
|
|||||||
// got message
|
// got message
|
||||||
case 1:
|
case 1:
|
||||||
// respond to request
|
// respond to request
|
||||||
|
#if 1
|
||||||
|
// VFALCO I have no idea why passing std::move(*this) crashes
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.async_accept(d.req, *this);
|
||||||
|
#else
|
||||||
response_op<Handler>{
|
response_op<Handler>{
|
||||||
std::move(d.h), d.ws, d.req, true};
|
std::move(d.h), d.ws, d.req, true};
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,9 @@ template<class NextLayer>
|
|||||||
template<class Handler>
|
template<class Handler>
|
||||||
class stream<NextLayer>::close_op
|
class stream<NextLayer>::close_op
|
||||||
{
|
{
|
||||||
using alloc_type =
|
using alloc_type = handler_alloc<char, Handler>;
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
using fb_type =
|
using fb_type = detail::frame_streambuf;
|
||||||
detail::frame_streambuf;
|
|
||||||
using fmb_type =
|
|
||||||
typename fb_type::mutable_buffers_type;
|
|
||||||
|
|
||||||
struct data : op
|
struct data : op
|
||||||
{
|
{
|
||||||
@@ -64,24 +61,19 @@ public:
|
|||||||
std::forward<DeducedHandler>(h), ws,
|
std::forward<DeducedHandler>(h), ws,
|
||||||
std::forward<Args>(args)...))
|
std::forward<Args>(args)...))
|
||||||
{
|
{
|
||||||
(*this)(error_code{}, 0, false);
|
(*this)(error_code{}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
auto& d = *d_;
|
(*this)(error_code{});
|
||||||
d.cont = false;
|
|
||||||
(*this)(error_code{}, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(error_code const& ec)
|
|
||||||
{
|
|
||||||
(*this)(ec, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operator()(error_code ec,
|
operator()(error_code ec, std::size_t);
|
||||||
std::size_t bytes_transferred, bool again = true);
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec, bool again = true);
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void* asio_handler_allocate(
|
void* asio_handler_allocate(
|
||||||
@@ -117,12 +109,26 @@ public:
|
|||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
template<class Handler>
|
template<class Handler>
|
||||||
void
|
void
|
||||||
stream<NextLayer>::close_op<Handler>::operator()(
|
stream<NextLayer>::close_op<Handler>::
|
||||||
error_code ec, std::size_t bytes_transferred, bool again)
|
operator()(error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
if(ec)
|
||||||
|
d.ws.failed_ = true;
|
||||||
|
(*this)(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::close_op<Handler>::
|
||||||
|
operator()(error_code ec, bool again)
|
||||||
{
|
{
|
||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
d.cont = d.cont || again;
|
d.cont = d.cont || again;
|
||||||
while(! ec && d.state != 99)
|
if(ec)
|
||||||
|
goto upcall;
|
||||||
|
for(;;)
|
||||||
{
|
{
|
||||||
switch(d.state)
|
switch(d.state)
|
||||||
{
|
{
|
||||||
@@ -130,49 +136,52 @@ stream<NextLayer>::close_op<Handler>::operator()(
|
|||||||
if(d.ws.wr_block_)
|
if(d.ws.wr_block_)
|
||||||
{
|
{
|
||||||
// suspend
|
// suspend
|
||||||
d.state = 1;
|
d.state = 2;
|
||||||
d.ws.rd_op_.template emplace<
|
d.ws.wr_op_.template emplace<
|
||||||
close_op>(std::move(*this));
|
close_op>(std::move(*this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(d.ws.error_)
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
{
|
{
|
||||||
// call handler
|
// call handler
|
||||||
d.state = 99;
|
|
||||||
d.ws.get_io_service().post(
|
d.ws.get_io_service().post(
|
||||||
bind_handler(std::move(*this),
|
bind_handler(std::move(*this),
|
||||||
boost::asio::error::operation_aborted, 0));
|
boost::asio::error::operation_aborted));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
d.state = 2;
|
// fall through
|
||||||
break;
|
|
||||||
|
|
||||||
// resume
|
|
||||||
case 1:
|
case 1:
|
||||||
if(d.ws.error_)
|
// send close frame
|
||||||
{
|
|
||||||
// call handler
|
|
||||||
d.state = 99;
|
|
||||||
ec = boost::asio::error::operation_aborted;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
d.state = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
// send close
|
|
||||||
d.state = 99;
|
d.state = 99;
|
||||||
assert(! d.ws.wr_close_);
|
|
||||||
d.ws.wr_close_ = true;
|
d.ws.wr_close_ = true;
|
||||||
assert(! d.ws.wr_block_);
|
assert(! d.ws.wr_block_);
|
||||||
d.ws.wr_block_ = &d;
|
d.ws.wr_block_ = &d;
|
||||||
boost::asio::async_write(d.ws.stream_,
|
boost::asio::async_write(d.ws.stream_,
|
||||||
d.fb.data(), std::move(*this));
|
d.fb.data(), std::move(*this));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
d.state = 3;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this), ec));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 99:
|
||||||
|
goto upcall;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ec)
|
upcall:
|
||||||
d.ws.error_ = true;
|
|
||||||
if(d.ws.wr_block_ == &d)
|
if(d.ws.wr_block_ == &d)
|
||||||
d.ws.wr_block_ = nullptr;
|
d.ws.wr_block_ = nullptr;
|
||||||
d.ws.rd_op_.maybe_invoke();
|
d.ws.rd_op_.maybe_invoke();
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class stream<NextLayer>::handshake_op
|
|||||||
, cont(boost_asio_handler_cont_helpers::
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
is_continuation(h))
|
is_continuation(h))
|
||||||
{
|
{
|
||||||
|
ws.reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,16 +65,11 @@ public:
|
|||||||
std::forward<DeducedHandler>(h), ws,
|
std::forward<DeducedHandler>(h), ws,
|
||||||
std::forward<Args>(args)...))
|
std::forward<Args>(args)...))
|
||||||
{
|
{
|
||||||
(*this)(error_code{}, 0, false);
|
(*this)(error_code{}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(error_code const& ec)
|
void
|
||||||
{
|
operator()(error_code ec, bool again = true);
|
||||||
(*this)(ec, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(error_code ec,
|
|
||||||
std::size_t bytes_transferred, bool again = true);
|
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void* asio_handler_allocate(
|
void* asio_handler_allocate(
|
||||||
@@ -109,9 +105,8 @@ public:
|
|||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
template<class Handler>
|
template<class Handler>
|
||||||
void
|
void
|
||||||
stream<NextLayer>::handshake_op<
|
stream<NextLayer>::handshake_op<Handler>::
|
||||||
Handler>::operator()(error_code ec,
|
operator()(error_code ec, bool again)
|
||||||
std::size_t bytes_transferred, bool again)
|
|
||||||
{
|
{
|
||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
d.cont = d.cont || again;
|
d.cont = d.cont || again;
|
||||||
|
|||||||
193
src/beast/include/beast/websocket/impl/ping_op.ipp
Normal file
193
src/beast/include/beast/websocket/impl/ping_op.ipp
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_WEBSOCKET_IMPL_PING_OP_HPP
|
||||||
|
#define BEAST_WEBSOCKET_IMPL_PING_OP_HPP
|
||||||
|
|
||||||
|
#include <beast/core/bind_handler.hpp>
|
||||||
|
#include <beast/core/handler_alloc.hpp>
|
||||||
|
#include <beast/websocket/detail/frame.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
// write a ping frame
|
||||||
|
//
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Handler>
|
||||||
|
class stream<NextLayer>::ping_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data : op
|
||||||
|
{
|
||||||
|
stream<NextLayer>& ws;
|
||||||
|
Handler h;
|
||||||
|
detail::frame_streambuf fb;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||||
|
ping_data const& payload)
|
||||||
|
: ws(ws_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
ws.template write_ping<static_streambuf>(
|
||||||
|
fb, opcode::ping, payload);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ping_op(ping_op&&) = default;
|
||||||
|
ping_op(ping_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
ping_op(DeducedHandler&& h,
|
||||||
|
stream<NextLayer>& ws, Args&&... args)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
(*this)(error_code{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec, std::size_t);
|
||||||
|
|
||||||
|
void operator()(error_code ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, ping_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, ping_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(ping_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, ping_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::ping_op<Handler>::
|
||||||
|
operator()(error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
if(ec)
|
||||||
|
d.ws.failed_ = true;
|
||||||
|
(*this)(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
ping_op<Handler>::
|
||||||
|
operator()(error_code ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
if(ec)
|
||||||
|
goto upcall;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if(d.ws.wr_block_)
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.state = 2;
|
||||||
|
d.ws.wr_op_.template emplace<
|
||||||
|
ping_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// send ping frame
|
||||||
|
d.state = 99;
|
||||||
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
d.fb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
d.state = 3;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this), ec));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 99:
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upcall:
|
||||||
|
if(d.ws.wr_block_ == &d)
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
|
d.ws.rd_op_.maybe_invoke();
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -81,18 +81,19 @@ public:
|
|||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
auto& d = *d_;
|
(*this)(error_code{}, 0, true);
|
||||||
d.cont = false;
|
|
||||||
(*this)(error_code{}, 0, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(error_code const& ec)
|
void operator()(error_code const& ec)
|
||||||
{
|
{
|
||||||
(*this)(ec, 0);
|
(*this)(ec, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(error_code ec,
|
void operator()(error_code ec,
|
||||||
std::size_t bytes_transferred, bool again = true);
|
std::size_t bytes_transferred);
|
||||||
|
|
||||||
|
void operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again);
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void* asio_handler_allocate(
|
void* asio_handler_allocate(
|
||||||
@@ -129,373 +130,415 @@ template<class NextLayer>
|
|||||||
template<class Buffers, class Handler>
|
template<class Buffers, class Handler>
|
||||||
void
|
void
|
||||||
stream<NextLayer>::read_frame_op<Buffers, Handler>::
|
stream<NextLayer>::read_frame_op<Buffers, Handler>::
|
||||||
operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
operator()(error_code ec, std::size_t bytes_transferred)
|
||||||
{
|
{
|
||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
d.cont = d.cont || again;
|
if(ec)
|
||||||
close_code::value code = close_code::none;
|
d.ws.failed_ = true;
|
||||||
while(! ec && d.state != 99)
|
(*this)(ec, bytes_transferred, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::read_frame_op<Buffers, Handler>::
|
||||||
|
operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
enum
|
||||||
{
|
{
|
||||||
switch(d.state)
|
do_start = 0,
|
||||||
|
do_read_payload = 1,
|
||||||
|
do_frame_done = 3,
|
||||||
|
do_read_fh = 4,
|
||||||
|
do_control_payload = 7,
|
||||||
|
do_control = 8,
|
||||||
|
do_pong_resume = 9,
|
||||||
|
do_pong = 11,
|
||||||
|
do_close_resume = 13,
|
||||||
|
do_close = 15,
|
||||||
|
do_fail = 18,
|
||||||
|
|
||||||
|
do_call_handler = 99
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& d = *d_;
|
||||||
|
if(! ec)
|
||||||
|
{
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
close_code::value code = close_code::none;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
case 0:
|
switch(d.state)
|
||||||
if(d.ws.error_)
|
|
||||||
{
|
{
|
||||||
|
case do_start:
|
||||||
|
if(d.ws.failed_)
|
||||||
|
{
|
||||||
|
d.state = do_call_handler;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = d.ws.rd_need_ > 0 ?
|
||||||
|
do_read_payload : do_read_fh;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
case do_read_payload:
|
||||||
|
d.state = do_read_payload + 1;
|
||||||
|
d.smb = d.sb.prepare(
|
||||||
|
detail::clamp(d.ws.rd_need_));
|
||||||
|
// receive payload data
|
||||||
|
d.ws.stream_.async_read_some(
|
||||||
|
*d.smb, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case do_read_payload + 1:
|
||||||
|
{
|
||||||
|
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
|
||||||
|
code = close_code::bad_payload;
|
||||||
|
d.state = do_fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.sb.commit(bytes_transferred);
|
||||||
|
if(d.ws.rd_need_ > 0)
|
||||||
|
{
|
||||||
|
d.state = do_read_payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
case do_frame_done:
|
||||||
// call handler
|
// call handler
|
||||||
d.state = 99;
|
d.fi.op = d.ws.rd_opcode_;
|
||||||
d.ws.get_io_service().post(
|
d.fi.fin = d.ws.rd_fh_.fin &&
|
||||||
bind_handler(std::move(*this),
|
d.ws.rd_need_ == 0;
|
||||||
boost::asio::error::operation_aborted, 0));
|
goto upcall;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
case do_read_fh:
|
||||||
|
d.state = do_read_fh + 1;
|
||||||
|
boost::asio::async_read(d.ws.stream_,
|
||||||
|
d.fb.prepare(2), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case do_read_fh + 1:
|
||||||
|
{
|
||||||
|
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 = do_fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = do_read_fh + 2;
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if(d.ws.rd_need_ > 0)
|
|
||||||
{
|
|
||||||
d.state = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
d.state = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
case do_read_fh + 2:
|
||||||
// read payload
|
d.fb.commit(bytes_transferred);
|
||||||
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;
|
code = close_code::none;
|
||||||
ping_payload_type data;
|
detail::read_fh2(d.ws.rd_fh_,
|
||||||
detail::read(data, d.fb.data(), code);
|
d.fb, d.ws.role_, code);
|
||||||
|
if(code == close_code::none)
|
||||||
|
d.ws.prepare_fh(code);
|
||||||
if(code != close_code::none)
|
if(code != close_code::none)
|
||||||
{
|
{
|
||||||
// protocol error
|
// protocol error
|
||||||
d.state = 16;
|
d.state = do_fail;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
d.fb.reset();
|
if(detail::is_control(d.ws.rd_fh_.op))
|
||||||
if(d.ws.wr_close_)
|
|
||||||
{
|
{
|
||||||
d.state = 2;
|
if(d.ws.rd_fh_.len > 0)
|
||||||
|
{
|
||||||
|
// read control payload
|
||||||
|
d.state = do_control_payload;
|
||||||
|
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 = do_control;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
d.ws.template write_ping<static_streambuf>(
|
if(d.ws.rd_need_ > 0)
|
||||||
d.fb, opcode::pong, data);
|
|
||||||
if(d.ws.wr_block_)
|
|
||||||
{
|
{
|
||||||
assert(d.ws.wr_block_ != &d);
|
d.state = do_read_payload;
|
||||||
// suspend
|
break;
|
||||||
d.state = 13;
|
|
||||||
d.ws.rd_op_.template emplace<
|
|
||||||
read_frame_op>(std::move(*this));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
d.state = 14;
|
// empty frame
|
||||||
|
d.state = do_frame_done;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
else if(d.ws.rd_fh_.op == opcode::pong)
|
//------------------------------------------------------------------
|
||||||
{
|
|
||||||
code = close_code::none;
|
case do_control_payload:
|
||||||
ping_payload_type data;
|
if(d.ws.rd_fh_.mask)
|
||||||
detail::read(data, d.fb.data(), code);
|
detail::mask_inplace(
|
||||||
if(code != close_code::none)
|
*d.fmb, d.ws.rd_key_);
|
||||||
{
|
d.fb.commit(bytes_transferred);
|
||||||
// protocol error
|
d.state = do_control; // VFALCO fall through?
|
||||||
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;
|
break;
|
||||||
}
|
|
||||||
assert(d.ws.rd_fh_.op == opcode::close);
|
//------------------------------------------------------------------
|
||||||
{
|
|
||||||
detail::read(d.ws.cr_, d.fb.data(), code);
|
case do_control:
|
||||||
if(code != close_code::none)
|
if(d.ws.rd_fh_.op == opcode::ping)
|
||||||
{
|
{
|
||||||
d.state = 16;
|
ping_data data;
|
||||||
break;
|
detail::read(data, d.fb.data());
|
||||||
}
|
|
||||||
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.fb.reset();
|
||||||
d.ws.template write_close<
|
if(d.ws.wr_close_)
|
||||||
static_streambuf>(d.fb, cr);
|
{
|
||||||
|
// ignore ping when closing
|
||||||
|
d.state = do_read_fh;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.ws.template write_ping<static_streambuf>(
|
||||||
|
d.fb, opcode::pong, data);
|
||||||
if(d.ws.wr_block_)
|
if(d.ws.wr_block_)
|
||||||
{
|
{
|
||||||
// suspend
|
// suspend
|
||||||
d.state = 9;
|
d.state = do_pong_resume;
|
||||||
|
assert(d.ws.wr_block_ != &d);
|
||||||
d.ws.rd_op_.template emplace<
|
d.ws.rd_op_.template emplace<
|
||||||
read_frame_op>(std::move(*this));
|
read_frame_op>(std::move(*this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
d.state = 10;
|
d.state = do_pong;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// call handler;
|
else if(d.ws.rd_fh_.op == opcode::pong)
|
||||||
d.state = 99;
|
{
|
||||||
ec = error::closed;
|
code = close_code::none;
|
||||||
break;
|
ping_data payload;
|
||||||
}
|
detail::read(payload, d.fb.data());
|
||||||
|
if(d.ws.pong_cb_)
|
||||||
|
d.ws.pong_cb_(payload);
|
||||||
|
d.fb.reset();
|
||||||
|
d.state = do_read_fh;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(d.ws.rd_fh_.op == opcode::close);
|
||||||
|
{
|
||||||
|
detail::read(d.ws.cr_, d.fb.data(), code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
// protocol error
|
||||||
|
d.state = do_fail;
|
||||||
|
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 = do_close_resume;
|
||||||
|
d.ws.rd_op_.template emplace<
|
||||||
|
read_frame_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = do_close;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// call handler;
|
||||||
|
ec = error::closed;
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
|
||||||
// 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 do_pong_resume:
|
||||||
case 10:
|
d.state = do_pong_resume + 1;
|
||||||
d.state = 11;
|
d.ws.get_io_service().post(bind_handler(
|
||||||
assert(! d.ws.wr_block_);
|
std::move(*this), ec, bytes_transferred));
|
||||||
d.ws.wr_block_ = &d;
|
return;
|
||||||
boost::asio::async_write(d.ws.stream_,
|
|
||||||
d.fb.data(), std::move(*this));
|
|
||||||
return;;
|
|
||||||
|
|
||||||
// teardown
|
case do_pong_resume + 1:
|
||||||
case 11:
|
if(d.ws.failed_)
|
||||||
d.state = 12;
|
{
|
||||||
websocket_helpers::call_async_teardown(
|
// call handler
|
||||||
d.ws.next_layer(), std::move(*this));
|
ec = boost::asio::error::operation_aborted;
|
||||||
return;
|
goto upcall;
|
||||||
|
}
|
||||||
|
d.state = do_pong;
|
||||||
|
break; // VFALCO fall through?
|
||||||
|
|
||||||
case 12:
|
//------------------------------------------------------------------
|
||||||
// call handler
|
|
||||||
d.state = 99;
|
|
||||||
ec = error::closed;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// resume
|
case do_pong:
|
||||||
case 13:
|
if(d.ws.wr_close_)
|
||||||
if(d.ws.error_)
|
{
|
||||||
{
|
// ignore ping when closing
|
||||||
// call handler
|
d.fb.reset();
|
||||||
d.state = 99;
|
d.state = do_read_fh;
|
||||||
ec = boost::asio::error::operation_aborted;
|
break;
|
||||||
break;
|
}
|
||||||
}
|
// send pong
|
||||||
if(d.ws.wr_close_)
|
d.state = do_pong + 1;
|
||||||
{
|
assert(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
d.fb.data(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case do_pong + 1:
|
||||||
d.fb.reset();
|
d.fb.reset();
|
||||||
d.state = 2;
|
d.state = do_read_fh;
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
break;
|
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 do_close_resume:
|
||||||
case 15:
|
d.state = do_close_resume + 1;
|
||||||
d.fb.reset();
|
d.ws.get_io_service().post(bind_handler(
|
||||||
d.state = 2;
|
std::move(*this), ec, bytes_transferred));
|
||||||
d.ws.wr_block_ = nullptr;
|
return;
|
||||||
break;
|
|
||||||
|
|
||||||
// fail the connection
|
case do_close_resume + 1:
|
||||||
case 16:
|
if(d.ws.failed_)
|
||||||
if(! d.ws.wr_close_)
|
{
|
||||||
{
|
// call handler
|
||||||
|
d.state = do_call_handler;
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
ec = error::closed;
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
d.state = do_close;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
case do_close:
|
||||||
|
d.state = do_close + 1;
|
||||||
|
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;
|
||||||
|
|
||||||
|
case do_close + 1:
|
||||||
|
d.state = do_close + 2;
|
||||||
|
websocket_helpers::call_async_teardown(
|
||||||
|
d.ws.next_layer(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case do_close + 2:
|
||||||
|
// call handler
|
||||||
|
ec = error::closed;
|
||||||
|
goto upcall;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
case do_fail:
|
||||||
|
if(d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
d.state = do_fail + 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
d.fb.reset();
|
d.fb.reset();
|
||||||
d.ws.template write_close<
|
d.ws.template write_close<
|
||||||
static_streambuf>(d.fb, code);
|
static_streambuf>(d.fb, code);
|
||||||
if(d.ws.wr_block_)
|
if(d.ws.wr_block_)
|
||||||
{
|
{
|
||||||
// suspend
|
// suspend
|
||||||
d.state = 17;
|
d.state = do_fail + 2;
|
||||||
d.ws.rd_op_.template emplace<
|
d.ws.rd_op_.template emplace<
|
||||||
read_frame_op>(std::move(*this));
|
read_frame_op>(std::move(*this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
d.state = 18;
|
// fall through
|
||||||
|
|
||||||
|
case do_fail + 1:
|
||||||
|
d.ws.failed_ = true;
|
||||||
|
// send close frame
|
||||||
|
d.state = do_fail + 4;
|
||||||
|
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;
|
||||||
|
|
||||||
|
case do_fail + 2:
|
||||||
|
d.state = do_fail + 3;
|
||||||
|
d.ws.get_io_service().post(bind_handler(
|
||||||
|
std::move(*this), ec, bytes_transferred));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case do_fail + 3:
|
||||||
|
if(d.ws.failed_)
|
||||||
|
{
|
||||||
|
d.state = do_fail + 5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = do_fail + 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case do_fail + 4:
|
||||||
|
d.state = do_fail + 5;
|
||||||
|
websocket_helpers::call_async_teardown(
|
||||||
|
d.ws.next_layer(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case do_fail + 5:
|
||||||
|
// call handler
|
||||||
|
ec = error::failed;
|
||||||
|
goto upcall;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
case do_call_handler:
|
||||||
|
goto upcall;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
|
||||||
websocket_helpers::call_async_teardown(
|
|
||||||
d.ws.next_layer(), std::move(*this));
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 20:
|
|
||||||
// call handler
|
|
||||||
d.state = 99;
|
|
||||||
ec = error::failed;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
while(! ec);
|
||||||
}
|
}
|
||||||
if(ec)
|
upcall:
|
||||||
d.ws.error_ = true;
|
|
||||||
if(d.ws.wr_block_ == &d)
|
if(d.ws.wr_block_ == &d)
|
||||||
d.ws.wr_block_ = nullptr;
|
d.ws.wr_block_ = nullptr;
|
||||||
d.ws.wr_op_.maybe_invoke();
|
d.ws.wr_op_.maybe_invoke();
|
||||||
|
|||||||
@@ -105,24 +105,34 @@ operator()(error_code const& ec, bool again)
|
|||||||
{
|
{
|
||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
d.cont = d.cont || again;
|
d.cont = d.cont || again;
|
||||||
while(! ec && d.state != 99)
|
while(! ec)
|
||||||
{
|
{
|
||||||
switch(d.state)
|
switch(d.state)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
// read payload
|
// read payload
|
||||||
d.state = 1;
|
d.state = 1;
|
||||||
|
#if 0
|
||||||
|
// VFALCO This causes dereference of null, because
|
||||||
|
// the handler is moved from the data block
|
||||||
|
// before asio_handler_deallocate is called.
|
||||||
d.ws.async_read_frame(
|
d.ws.async_read_frame(
|
||||||
d.fi, d.sb, std::move(*this));
|
d.fi, d.sb, std::move(*this));
|
||||||
|
#else
|
||||||
|
d.ws.async_read_frame(d.fi, d.sb, *this);
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// got payload
|
// got payload
|
||||||
case 1:
|
case 1:
|
||||||
d.op = d.fi.op;
|
d.op = d.fi.op;
|
||||||
d.state = d.fi.fin ? 99 : 0;
|
if(d.fi.fin)
|
||||||
|
goto upcall;
|
||||||
|
d.state = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
upcall:
|
||||||
d.h(ec);
|
d.h(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ class stream<NextLayer>::response_op
|
|||||||
, h(std::forward<DeducedHandler>(h_))
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
, cont(cont_)
|
, cont(cont_)
|
||||||
{
|
{
|
||||||
|
// can't call stream::reset() here
|
||||||
|
// otherwise accept_op will malfunction
|
||||||
|
//
|
||||||
if(resp.status != 101)
|
if(resp.status != 101)
|
||||||
final_ec = error::handshake_failed;
|
final_ec = error::handshake_failed;
|
||||||
}
|
}
|
||||||
@@ -123,7 +126,7 @@ operator()(error_code ec, bool again)
|
|||||||
d.state = 99;
|
d.state = 99;
|
||||||
ec = d.final_ec;
|
ec = d.final_ec;
|
||||||
if(! ec)
|
if(! ec)
|
||||||
d.ws.role_ = role_type::server;
|
d.ws.open(detail::role_type::server);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <beast/websocket/impl/accept_op.ipp>
|
#include <beast/websocket/impl/accept_op.ipp>
|
||||||
#include <beast/websocket/impl/close_op.ipp>
|
#include <beast/websocket/impl/close_op.ipp>
|
||||||
#include <beast/websocket/impl/handshake_op.ipp>
|
#include <beast/websocket/impl/handshake_op.ipp>
|
||||||
|
#include <beast/websocket/impl/ping_op.ipp>
|
||||||
#include <beast/websocket/impl/read_op.ipp>
|
#include <beast/websocket/impl/read_op.ipp>
|
||||||
#include <beast/websocket/impl/read_frame_op.ipp>
|
#include <beast/websocket/impl/read_frame_op.ipp>
|
||||||
#include <beast/websocket/impl/response_op.ipp>
|
#include <beast/websocket/impl/response_op.ipp>
|
||||||
@@ -40,6 +41,13 @@ namespace websocket {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
void
|
||||||
|
stream_base::open(role_type role)
|
||||||
|
{
|
||||||
|
role_ = role;
|
||||||
|
}
|
||||||
|
|
||||||
template<class _>
|
template<class _>
|
||||||
void
|
void
|
||||||
stream_base::prepare_fh(close_code::value& code)
|
stream_base::prepare_fh(close_code::value& code)
|
||||||
@@ -76,7 +84,7 @@ stream_base::prepare_fh(close_code::value& code)
|
|||||||
}
|
}
|
||||||
rd_size_ += rd_fh_.len;
|
rd_size_ += rd_fh_.len;
|
||||||
}
|
}
|
||||||
if(rd_size_ > rd_msg_max_)
|
if(rd_msg_max_ && rd_size_ > rd_msg_max_)
|
||||||
{
|
{
|
||||||
code = close_code::too_big;
|
code = close_code::too_big;
|
||||||
return;
|
return;
|
||||||
@@ -100,7 +108,7 @@ stream_base::write_close(
|
|||||||
fh.rsv3 = false;
|
fh.rsv3 = false;
|
||||||
fh.len = cr.code == close_code::none ?
|
fh.len = cr.code == close_code::none ?
|
||||||
0 : 2 + cr.reason.size();
|
0 : 2 + cr.reason.size();
|
||||||
fh.mask = role_ == role_type::client;
|
fh.mask = role_ == detail::role_type::client;
|
||||||
if(fh.mask)
|
if(fh.mask)
|
||||||
fh.key = maskgen_();
|
fh.key = maskgen_();
|
||||||
detail::write(sb, fh);
|
detail::write(sb, fh);
|
||||||
@@ -136,7 +144,7 @@ stream_base::write_close(
|
|||||||
template<class Streambuf>
|
template<class Streambuf>
|
||||||
void
|
void
|
||||||
stream_base::write_ping(Streambuf& sb,
|
stream_base::write_ping(Streambuf& sb,
|
||||||
opcode op, ping_payload_type const& data)
|
opcode op, ping_data const& data)
|
||||||
{
|
{
|
||||||
frame_header fh;
|
frame_header fh;
|
||||||
fh.op = op;
|
fh.op = op;
|
||||||
@@ -184,7 +192,8 @@ accept()
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
accept(boost::asio::null_buffers{}, ec);
|
accept(boost::asio::null_buffers{}, ec);
|
||||||
detail::maybe_throw(ec, "accept");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -223,7 +232,8 @@ accept(ConstBufferSequence const& buffers)
|
|||||||
"ConstBufferSequence requirements not met");
|
"ConstBufferSequence requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
accept(buffers, ec);
|
accept(buffers, ec);
|
||||||
detail::maybe_throw(ec, "accept");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -239,10 +249,11 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
|
|||||||
"ConstBufferSequence requirements not met");
|
"ConstBufferSequence requirements not met");
|
||||||
using boost::asio::buffer_copy;
|
using boost::asio::buffer_copy;
|
||||||
using boost::asio::buffer_size;
|
using boost::asio::buffer_size;
|
||||||
|
reset();
|
||||||
stream_.buffer().commit(buffer_copy(
|
stream_.buffer().commit(buffer_copy(
|
||||||
stream_.buffer().prepare(
|
stream_.buffer().prepare(
|
||||||
buffer_size(buffers)), buffers));
|
buffer_size(buffers)), buffers));
|
||||||
http::request_v1<http::empty_body> m;
|
http::request_v1<http::string_body> m;
|
||||||
http::read(next_layer(), stream_.buffer(), m, ec);
|
http::read(next_layer(), stream_.buffer(), m, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
return;
|
return;
|
||||||
@@ -279,7 +290,8 @@ accept(http::request_v1<Body, Headers> const& request)
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
accept(request, ec);
|
accept(request, ec);
|
||||||
detail::maybe_throw(ec, "accept");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -291,8 +303,11 @@ accept(http::request_v1<Body, Headers> const& req,
|
|||||||
{
|
{
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
|
reset();
|
||||||
auto const res = build_response(req);
|
auto const res = build_response(req);
|
||||||
http::write(stream_, res, ec);
|
http::write(stream_, res, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
if(res.status != 101)
|
if(res.status != 101)
|
||||||
{
|
{
|
||||||
ec = error::handshake_failed;
|
ec = error::handshake_failed;
|
||||||
@@ -300,7 +315,7 @@ accept(http::request_v1<Body, Headers> const& req,
|
|||||||
// teardown if Connection: close.
|
// teardown if Connection: close.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
role_ = role_type::server;
|
open(detail::role_type::server);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -316,6 +331,7 @@ async_accept(http::request_v1<Body, Headers> const& req,
|
|||||||
beast::async_completion<
|
beast::async_completion<
|
||||||
AcceptHandler, void(error_code)
|
AcceptHandler, void(error_code)
|
||||||
> completion(handler);
|
> completion(handler);
|
||||||
|
reset();
|
||||||
response_op<decltype(completion.handler)>{
|
response_op<decltype(completion.handler)>{
|
||||||
completion.handler, *this, req,
|
completion.handler, *this, req,
|
||||||
boost_asio_handler_cont_helpers::
|
boost_asio_handler_cont_helpers::
|
||||||
@@ -333,7 +349,8 @@ handshake(boost::string_ref const& host,
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
handshake(host, resource, ec);
|
handshake(host, resource, ec);
|
||||||
detail::maybe_throw(ec, "upgrade");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -344,6 +361,7 @@ handshake(boost::string_ref const& host,
|
|||||||
{
|
{
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
|
reset();
|
||||||
std::string key;
|
std::string key;
|
||||||
http::write(stream_,
|
http::write(stream_,
|
||||||
build_request(host, resource, key), ec);
|
build_request(host, resource, key), ec);
|
||||||
@@ -383,7 +401,8 @@ close(close_reason const& cr)
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
close(cr, ec);
|
close(cr, ec);
|
||||||
detail::maybe_throw(ec, "close");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -398,7 +417,7 @@ close(close_reason const& cr, error_code& ec)
|
|||||||
detail::frame_streambuf fb;
|
detail::frame_streambuf fb;
|
||||||
write_close<static_streambuf>(fb, cr);
|
write_close<static_streambuf>(fb, cr);
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -418,6 +437,45 @@ async_close(close_reason const& cr, CloseHandler&& handler)
|
|||||||
return completion.result.get();
|
return completion.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
ping(ping_data const& payload)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ping(payload, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
ping(ping_data const& payload, error_code& ec)
|
||||||
|
{
|
||||||
|
detail::frame_streambuf sb;
|
||||||
|
write_ping<static_streambuf>(
|
||||||
|
sb, opcode::ping, payload);
|
||||||
|
boost::asio::write(stream_, sb.data(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class PingHandler>
|
||||||
|
typename async_completion<
|
||||||
|
PingHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_ping(ping_data const& payload, PingHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
PingHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
ping_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, payload};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
template<class Streambuf>
|
template<class Streambuf>
|
||||||
void
|
void
|
||||||
@@ -428,7 +486,8 @@ read(opcode& op, Streambuf& streambuf)
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
read(op, streambuf, ec);
|
read(op, streambuf, ec);
|
||||||
detail::maybe_throw(ec, "read");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -481,7 +540,8 @@ read_frame(frame_info& fi, Streambuf& streambuf)
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
read_frame(fi, streambuf, ec);
|
read_frame(fi, streambuf, ec);
|
||||||
detail::maybe_throw(ec, "read_some");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -500,8 +560,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
|||||||
// read header
|
// read header
|
||||||
detail::frame_streambuf fb;
|
detail::frame_streambuf fb;
|
||||||
do_read_fh(fb, code, ec);
|
do_read_fh(fb, code, ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
if(error_)
|
if(failed_)
|
||||||
return;
|
return;
|
||||||
if(code != close_code::none)
|
if(code != close_code::none)
|
||||||
break;
|
break;
|
||||||
@@ -513,8 +573,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
|||||||
auto const mb = fb.prepare(
|
auto const mb = fb.prepare(
|
||||||
static_cast<std::size_t>(rd_fh_.len));
|
static_cast<std::size_t>(rd_fh_.len));
|
||||||
fb.commit(boost::asio::read(stream_, mb, ec));
|
fb.commit(boost::asio::read(stream_, mb, ec));
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
if(error_)
|
if(failed_)
|
||||||
return;
|
return;
|
||||||
if(rd_fh_.mask)
|
if(rd_fh_.mask)
|
||||||
detail::mask_inplace(mb, rd_key_);
|
detail::mask_inplace(mb, rd_key_);
|
||||||
@@ -522,27 +582,23 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
|||||||
}
|
}
|
||||||
if(rd_fh_.op == opcode::ping)
|
if(rd_fh_.op == opcode::ping)
|
||||||
{
|
{
|
||||||
ping_payload_type data;
|
ping_data data;
|
||||||
detail::read(data, fb.data(), code);
|
detail::read(data, fb.data());
|
||||||
if(code != close_code::none)
|
|
||||||
break;
|
|
||||||
fb.reset();
|
fb.reset();
|
||||||
write_ping<static_streambuf>(
|
write_ping<static_streambuf>(
|
||||||
fb, opcode::pong, data);
|
fb, opcode::pong, data);
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
if(error_)
|
if(failed_)
|
||||||
return;
|
return;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if(rd_fh_.op == opcode::pong)
|
else if(rd_fh_.op == opcode::pong)
|
||||||
{
|
{
|
||||||
ping_payload_type data;
|
ping_data payload;
|
||||||
detail::read(data, fb.data(), code);
|
detail::read(payload, fb.data());
|
||||||
if(code != close_code::none)
|
if(pong_cb_)
|
||||||
break;
|
pong_cb_(payload);
|
||||||
// VFALCO How to notify callers using
|
|
||||||
// the synchronous interface?
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
assert(rd_fh_.op == opcode::close);
|
assert(rd_fh_.op == opcode::close);
|
||||||
@@ -560,8 +616,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
|||||||
wr_close_ = true;
|
wr_close_ = true;
|
||||||
write_close<static_streambuf>(fb, cr);
|
write_close<static_streambuf>(fb, cr);
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
if(error_)
|
if(failed_)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -578,8 +634,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
|||||||
detail::clamp(rd_need_));
|
detail::clamp(rd_need_));
|
||||||
auto const bytes_transferred =
|
auto const bytes_transferred =
|
||||||
stream_.read_some(smb, ec);
|
stream_.read_some(smb, ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
if(error_)
|
if(failed_)
|
||||||
return;
|
return;
|
||||||
rd_need_ -= bytes_transferred;
|
rd_need_ -= bytes_transferred;
|
||||||
auto const pb = prepare_buffers(
|
auto const pb = prepare_buffers(
|
||||||
@@ -610,23 +666,23 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
|||||||
detail::frame_streambuf fb;
|
detail::frame_streambuf fb;
|
||||||
write_close<static_streambuf>(fb, code);
|
write_close<static_streambuf>(fb, code);
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
if(error_)
|
if(failed_)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
websocket_helpers::call_teardown(next_layer(), ec);
|
websocket_helpers::call_teardown(next_layer(), ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
if(error_)
|
if(failed_)
|
||||||
return;
|
return;
|
||||||
ec = error::failed;
|
ec = error::failed;
|
||||||
error_ = true;
|
failed_ = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(! ec)
|
if(! ec)
|
||||||
websocket_helpers::call_teardown(next_layer(), ec);
|
websocket_helpers::call_teardown(next_layer(), ec);
|
||||||
if(! ec)
|
if(! ec)
|
||||||
ec = error::closed;
|
ec = error::closed;
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -658,7 +714,8 @@ write(ConstBufferSequence const& buffers)
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
write(buffers, ec);
|
write(buffers, ec);
|
||||||
detail::maybe_throw(ec, "write");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -719,7 +776,8 @@ write_frame(bool fin, ConstBufferSequence const& buffers)
|
|||||||
"SyncStream requirements not met");
|
"SyncStream requirements not met");
|
||||||
error_code ec;
|
error_code ec;
|
||||||
write_frame(fin, buffers, ec);
|
write_frame(fin, buffers, ec);
|
||||||
detail::maybe_throw(ec, "write");
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -744,7 +802,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
|||||||
fh.rsv2 = false;
|
fh.rsv2 = false;
|
||||||
fh.rsv3 = false;
|
fh.rsv3 = false;
|
||||||
fh.len = buffer_size(bs);
|
fh.len = buffer_size(bs);
|
||||||
fh.mask = role_ == role_type::client;
|
fh.mask = role_ == detail::role_type::client;
|
||||||
if(fh.mask)
|
if(fh.mask)
|
||||||
fh.key = maskgen_();
|
fh.key = maskgen_();
|
||||||
detail::fh_streambuf fh_buf;
|
detail::fh_streambuf fh_buf;
|
||||||
@@ -754,22 +812,21 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
|||||||
// send header and payload
|
// send header and payload
|
||||||
boost::asio::write(stream_,
|
boost::asio::write(stream_,
|
||||||
buffer_cat(fh_buf.data(), bs), ec);
|
buffer_cat(fh_buf.data(), bs), ec);
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
detail::prepared_key_type key;
|
detail::prepared_key_type key;
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
auto const tmp_size = detail::clamp(
|
auto const tmp_size =
|
||||||
fh.len, wr_buf_size_);
|
detail::clamp(fh.len, mask_buf_size_);
|
||||||
std::unique_ptr<std::uint8_t[]> up(
|
std::unique_ptr<std::uint8_t[]> up(
|
||||||
new std::uint8_t[tmp_size]);
|
new std::uint8_t[tmp_size]);
|
||||||
auto const tmp = up.get();
|
|
||||||
std::uint64_t remain = fh.len;
|
std::uint64_t remain = fh.len;
|
||||||
consuming_buffers<ConstBufferSequence> cb(bs);
|
consuming_buffers<ConstBufferSequence> cb(bs);
|
||||||
{
|
{
|
||||||
auto const n =
|
auto const n =
|
||||||
detail::clamp(remain, tmp_size);
|
detail::clamp(remain, tmp_size);
|
||||||
mutable_buffers_1 mb{tmp, n};
|
mutable_buffers_1 mb{up.get(), n};
|
||||||
buffer_copy(mb, cb);
|
buffer_copy(mb, cb);
|
||||||
cb.consume(n);
|
cb.consume(n);
|
||||||
remain -= n;
|
remain -= n;
|
||||||
@@ -779,7 +836,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
|||||||
buffer_cat(fh_buf.data(), mb), ec);
|
buffer_cat(fh_buf.data(), mb), ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
{
|
{
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -787,7 +844,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
|||||||
{
|
{
|
||||||
auto const n =
|
auto const n =
|
||||||
detail::clamp(remain, tmp_size);
|
detail::clamp(remain, tmp_size);
|
||||||
mutable_buffers_1 mb{tmp, n};
|
mutable_buffers_1 mb{up.get(), n};
|
||||||
buffer_copy(mb, cb);
|
buffer_copy(mb, cb);
|
||||||
cb.consume(n);
|
cb.consume(n);
|
||||||
remain -= n;
|
remain -= n;
|
||||||
@@ -796,7 +853,7 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
|||||||
boost::asio::write(stream_, mb, ec);
|
boost::asio::write(stream_, mb, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
{
|
{
|
||||||
error_ = ec != 0;
|
failed_ = ec != 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -826,6 +883,23 @@ async_write_frame(bool fin,
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
reset()
|
||||||
|
{
|
||||||
|
failed_ = false;
|
||||||
|
rd_need_ = 0;
|
||||||
|
rd_cont_ = false;
|
||||||
|
wr_close_ = false;
|
||||||
|
wr_cont_ = false;
|
||||||
|
wr_block_ = nullptr; // should be nullptr on close anyway
|
||||||
|
pong_data_ = nullptr; // should be nullptr on close anyway
|
||||||
|
|
||||||
|
stream_.buffer().consume(
|
||||||
|
stream_.buffer().size());
|
||||||
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
http::request_v1<http::empty_body>
|
http::request_v1<http::empty_body>
|
||||||
stream<NextLayer>::
|
stream<NextLayer>::
|
||||||
@@ -860,8 +934,11 @@ build_response(http::request_v1<Body, Headers> const& req)
|
|||||||
res.reason = http::reason_string(res.status);
|
res.reason = http::reason_string(res.status);
|
||||||
res.version = req.version;
|
res.version = req.version;
|
||||||
res.body = text;
|
res.body = text;
|
||||||
// VFALCO TODO respect keep-alive here
|
(*d_)(res);
|
||||||
prepare(res);
|
prepare(res,
|
||||||
|
(is_keep_alive(req) && keep_alive_) ?
|
||||||
|
http::connection::keep_alive :
|
||||||
|
http::connection::close);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
if(req.version < 11)
|
if(req.version < 11)
|
||||||
@@ -874,17 +951,28 @@ build_response(http::request_v1<Body, Headers> const& req)
|
|||||||
return err("Missing Host");
|
return err("Missing Host");
|
||||||
if(! req.headers.exists("Sec-WebSocket-Key"))
|
if(! req.headers.exists("Sec-WebSocket-Key"))
|
||||||
return err("Missing Sec-WebSocket-Key");
|
return err("Missing Sec-WebSocket-Key");
|
||||||
|
if(! rfc2616::token_in_list(
|
||||||
|
req.headers["Upgrade"], "websocket"))
|
||||||
|
return err("Missing websocket Upgrade token");
|
||||||
{
|
{
|
||||||
auto const version =
|
auto const version =
|
||||||
req.headers["Sec-WebSocket-Version"];
|
req.headers["Sec-WebSocket-Version"];
|
||||||
if(version.empty())
|
if(version.empty())
|
||||||
return err("Missing Sec-WebSocket-Version");
|
return err("Missing Sec-WebSocket-Version");
|
||||||
if(version != "13")
|
if(version != "13")
|
||||||
return err("Unsupported Sec-WebSocket-Version");
|
{
|
||||||
|
http::response_v1<http::string_body> res;
|
||||||
|
res.status = 426;
|
||||||
|
res.reason = http::reason_string(res.status);
|
||||||
|
res.version = req.version;
|
||||||
|
res.headers.insert("Sec-WebSocket-Version", "13");
|
||||||
|
prepare(res,
|
||||||
|
(is_keep_alive(req) && keep_alive_) ?
|
||||||
|
http::connection::keep_alive :
|
||||||
|
http::connection::close);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(! rfc2616::token_in_list(
|
|
||||||
req.headers["Upgrade"], "websocket"))
|
|
||||||
return err("Missing websocket Upgrade token");
|
|
||||||
http::response_v1<http::string_body> res;
|
http::response_v1<http::string_body> res;
|
||||||
res.status = 101;
|
res.status = 101;
|
||||||
res.reason = http::reason_string(res.status);
|
res.reason = http::reason_string(res.status);
|
||||||
@@ -893,7 +981,6 @@ build_response(http::request_v1<Body, Headers> const& req)
|
|||||||
{
|
{
|
||||||
auto const key =
|
auto const key =
|
||||||
req.headers["Sec-WebSocket-Key"];
|
req.headers["Sec-WebSocket-Key"];
|
||||||
res.headers.insert("Sec-WebSocket-Key", key);
|
|
||||||
res.headers.insert("Sec-WebSocket-Accept",
|
res.headers.insert("Sec-WebSocket-Accept",
|
||||||
detail::make_sec_ws_accept(key));
|
detail::make_sec_ws_accept(key));
|
||||||
}
|
}
|
||||||
@@ -912,6 +999,8 @@ do_response(http::response_v1<Body, Headers> const& res,
|
|||||||
{
|
{
|
||||||
// VFALCO Review these error codes
|
// VFALCO Review these error codes
|
||||||
auto fail = [&]{ ec = error::response_failed; };
|
auto fail = [&]{ ec = error::response_failed; };
|
||||||
|
if(res.version < 11)
|
||||||
|
return fail();
|
||||||
if(res.status != 101)
|
if(res.status != 101)
|
||||||
return fail();
|
return fail();
|
||||||
if(! is_upgrade(res))
|
if(! is_upgrade(res))
|
||||||
@@ -924,7 +1013,7 @@ do_response(http::response_v1<Body, Headers> const& res,
|
|||||||
if(res.headers["Sec-WebSocket-Accept"] !=
|
if(res.headers["Sec-WebSocket-Accept"] !=
|
||||||
detail::make_sec_ws_accept(key))
|
detail::make_sec_ws_accept(key))
|
||||||
return fail();
|
return fail();
|
||||||
role_ = role_type::client;
|
open(detail::role_type::client);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operator()(
|
operator()(error_code ec, std::size_t, bool again = true);
|
||||||
error_code ec, std::size_t, bool again = true);
|
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void* asio_handler_allocate(std::size_t size,
|
void* asio_handler_allocate(std::size_t size,
|
||||||
|
|||||||
@@ -57,17 +57,17 @@ class stream<NextLayer>::write_frame_op
|
|||||||
opcode::cont : ws.wr_opcode_;
|
opcode::cont : ws.wr_opcode_;
|
||||||
ws.wr_cont_ = ! fin;
|
ws.wr_cont_ = ! fin;
|
||||||
fh.fin = fin;
|
fh.fin = fin;
|
||||||
fh.rsv1 = 0;
|
fh.rsv1 = false;
|
||||||
fh.rsv2 = 0;
|
fh.rsv2 = false;
|
||||||
fh.rsv3 = 0;
|
fh.rsv3 = false;
|
||||||
fh.len = boost::asio::buffer_size(cb);
|
fh.len = boost::asio::buffer_size(cb);
|
||||||
fh.mask = ws.role_ == role_type::client;
|
fh.mask = ws.role_ == detail::role_type::client;
|
||||||
if(fh.mask)
|
if(fh.mask)
|
||||||
{
|
{
|
||||||
fh.key = ws.maskgen_();
|
fh.key = ws.maskgen_();
|
||||||
detail::prepare_key(key, fh.key);
|
detail::prepare_key(key, fh.key);
|
||||||
tmp_size = detail::clamp(
|
tmp_size = detail::clamp(
|
||||||
fh.len, ws.wr_buf_size_);
|
fh.len, ws.mask_buf_size_);
|
||||||
tmp = boost_asio_handler_alloc_helpers::
|
tmp = boost_asio_handler_alloc_helpers::
|
||||||
allocate(tmp_size, h);
|
allocate(tmp_size, h);
|
||||||
remain = fh.len;
|
remain = fh.len;
|
||||||
@@ -100,18 +100,17 @@ public:
|
|||||||
std::forward<DeducedHandler>(h), ws,
|
std::forward<DeducedHandler>(h), ws,
|
||||||
std::forward<Args>(args)...))
|
std::forward<Args>(args)...))
|
||||||
{
|
{
|
||||||
(*this)(error_code{}, 0, false);
|
(*this)(error_code{}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
auto& d = *d_;
|
(*this)(error_code{});
|
||||||
d.cont = false;
|
|
||||||
(*this)(error_code{}, 0, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(error_code ec,
|
void operator()(error_code ec, std::size_t);
|
||||||
std::size_t bytes_transferred, bool again = true);
|
|
||||||
|
void operator()(error_code ec, bool again = true);
|
||||||
|
|
||||||
friend
|
friend
|
||||||
void* asio_handler_allocate(
|
void* asio_handler_allocate(
|
||||||
@@ -144,19 +143,33 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write_frame_op<Buffers, Handler>::
|
||||||
|
operator()(error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
if(ec)
|
||||||
|
d.ws.failed_ = true;
|
||||||
|
(*this)(ec);
|
||||||
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
template<class Buffers, class Handler>
|
template<class Buffers, class Handler>
|
||||||
void
|
void
|
||||||
stream<NextLayer>::
|
stream<NextLayer>::
|
||||||
write_frame_op<Buffers, Handler>::
|
write_frame_op<Buffers, Handler>::
|
||||||
operator()(
|
operator()(error_code ec, bool again)
|
||||||
error_code ec, std::size_t bytes_transferred, bool again)
|
|
||||||
{
|
{
|
||||||
using boost::asio::buffer_copy;
|
using boost::asio::buffer_copy;
|
||||||
using boost::asio::mutable_buffers_1;
|
using boost::asio::mutable_buffers_1;
|
||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
d.cont = d.cont || again;
|
d.cont = d.cont || again;
|
||||||
while(! ec && d.state != 99)
|
if(ec)
|
||||||
|
goto upcall;
|
||||||
|
for(;;)
|
||||||
{
|
{
|
||||||
switch(d.state)
|
switch(d.state)
|
||||||
{
|
{
|
||||||
@@ -164,41 +177,27 @@ operator()(
|
|||||||
if(d.ws.wr_block_)
|
if(d.ws.wr_block_)
|
||||||
{
|
{
|
||||||
// suspend
|
// suspend
|
||||||
d.state = 1;
|
d.state = 3;
|
||||||
d.ws.wr_op_.template emplace<
|
d.ws.wr_op_.template emplace<
|
||||||
write_frame_op>(std::move(*this));
|
write_frame_op>(std::move(*this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(d.ws.error_)
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
{
|
{
|
||||||
// call handler
|
// call handler
|
||||||
d.state = 99;
|
d.state = 99;
|
||||||
d.ws.get_io_service().post(
|
d.ws.get_io_service().post(
|
||||||
bind_handler(std::move(*this),
|
bind_handler(std::move(*this),
|
||||||
boost::asio::error::operation_aborted, 0));
|
boost::asio::error::operation_aborted));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(! d.ws.wr_close_);
|
// fall through
|
||||||
d.state = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// resume
|
|
||||||
case 1:
|
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)
|
if(! d.fh.mask)
|
||||||
{
|
{
|
||||||
// send header and payload
|
// send header and entire payload
|
||||||
d.state = 99;
|
d.state = 99;
|
||||||
assert(! d.ws.wr_block_);
|
assert(! d.ws.wr_block_);
|
||||||
d.ws.wr_block_ = &d;
|
d.ws.wr_block_ = &d;
|
||||||
@@ -215,7 +214,7 @@ operator()(
|
|||||||
d.remain -= n;
|
d.remain -= n;
|
||||||
detail::mask_inplace(mb, d.key);
|
detail::mask_inplace(mb, d.key);
|
||||||
// send header and payload
|
// send header and payload
|
||||||
d.state = d.remain > 0 ? 3 : 99;
|
d.state = d.remain > 0 ? 2 : 99;
|
||||||
assert(! d.ws.wr_block_);
|
assert(! d.ws.wr_block_);
|
||||||
d.ws.wr_block_ = &d;
|
d.ws.wr_block_ = &d;
|
||||||
boost::asio::async_write(d.ws.stream_,
|
boost::asio::async_write(d.ws.stream_,
|
||||||
@@ -225,7 +224,7 @@ operator()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sent masked payload
|
// sent masked payload
|
||||||
case 3:
|
case 2:
|
||||||
{
|
{
|
||||||
auto const n =
|
auto const n =
|
||||||
detail::clamp(d.remain, d.tmp_size);
|
detail::clamp(d.remain, d.tmp_size);
|
||||||
@@ -238,24 +237,41 @@ operator()(
|
|||||||
// send payload
|
// send payload
|
||||||
if(d.remain == 0)
|
if(d.remain == 0)
|
||||||
d.state = 99;
|
d.state = 99;
|
||||||
assert(! d.ws.wr_block_);
|
assert(d.ws.wr_block_ == &d);
|
||||||
d.ws.wr_block_ = &d;
|
|
||||||
boost::asio::async_write(
|
boost::asio::async_write(
|
||||||
d.ws.stream_, mb, std::move(*this));
|
d.ws.stream_, mb, std::move(*this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
d.state = 4;
|
||||||
|
d.ws.get_io_service().post(bind_handler(
|
||||||
|
std::move(*this), ec));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 99:
|
||||||
|
goto upcall;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ec)
|
upcall:
|
||||||
d.ws.error_ = true;
|
|
||||||
if(d.ws.wr_block_ == &d)
|
|
||||||
d.ws.wr_block_ = nullptr;
|
|
||||||
if(d.tmp)
|
if(d.tmp)
|
||||||
{
|
{
|
||||||
boost_asio_handler_alloc_helpers::
|
boost_asio_handler_alloc_helpers::
|
||||||
deallocate(d.tmp, d.tmp_size, d.h);
|
deallocate(d.tmp, d.tmp_size, d.h);
|
||||||
d.tmp = nullptr;
|
d.tmp = nullptr;
|
||||||
}
|
}
|
||||||
|
if(d.ws.wr_block_ == &d)
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
d.ws.rd_op_.maybe_invoke();
|
d.ws.rd_op_.maybe_invoke();
|
||||||
d.h(ec);
|
d.h(ec);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ operator()(error_code ec, bool again)
|
|||||||
{
|
{
|
||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
d.cont = d.cont || again;
|
d.cont = d.cont || again;
|
||||||
while(! ec && d.state != 99)
|
if(! ec)
|
||||||
{
|
{
|
||||||
switch(d.state)
|
switch(d.state)
|
||||||
{
|
{
|
||||||
@@ -126,6 +126,9 @@ operator()(error_code ec, bool again)
|
|||||||
d.ws.async_write_frame(fin, pb, std::move(*this));
|
d.ws.async_write_frame(fin, pb, std::move(*this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 99:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.h(ec);
|
d.h(ec);
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
#ifndef BEAST_WEBSOCKET_OPTION_HPP
|
#ifndef BEAST_WEBSOCKET_OPTION_HPP
|
||||||
#define BEAST_WEBSOCKET_OPTION_HPP
|
#define BEAST_WEBSOCKET_OPTION_HPP
|
||||||
|
|
||||||
|
#include <beast/websocket/rfc6455.hpp>
|
||||||
#include <beast/websocket/detail/stream_base.hpp>
|
#include <beast/websocket/detail/stream_base.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace websocket {
|
namespace websocket {
|
||||||
@@ -147,6 +149,45 @@ struct keep_alive
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** Mask buffer size option.
|
||||||
|
|
||||||
|
Sets the size of the buffer allocated when the implementation
|
||||||
|
must allocate memory to apply the mask to a payload. Only affects
|
||||||
|
streams operating in the client role, since only clients send
|
||||||
|
masked frames. Lowering the size of the buffer can decrease the
|
||||||
|
memory requirements for each connection, while increasing the size
|
||||||
|
of the buffer can reduce the number of calls made to the next
|
||||||
|
layer to write masked data.
|
||||||
|
|
||||||
|
The default setting is 4096. The minimum value is 1.
|
||||||
|
|
||||||
|
@note Objects of this type are passed to @ref stream::set_option.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
Setting the write buffer size.
|
||||||
|
@code
|
||||||
|
...
|
||||||
|
websocket::stream<ip::tcp::socket> ws(ios);
|
||||||
|
ws.set_option(mask_buffer_size{8192});
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using mask_buffer_size = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct mask_buffer_size
|
||||||
|
{
|
||||||
|
std::size_t value;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
mask_buffer_size(std::size_t n)
|
||||||
|
: value(n)
|
||||||
|
{
|
||||||
|
if(n == 0)
|
||||||
|
throw std::domain_error("invalid mask buffer size");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Message type option.
|
/** Message type option.
|
||||||
|
|
||||||
This controls the opcode set for outgoing messages. Valid
|
This controls the opcode set for outgoing messages. Valid
|
||||||
@@ -184,6 +225,50 @@ struct message_type
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** Pong callback option.
|
||||||
|
|
||||||
|
Sets the callback to be invoked whenever a pong is received
|
||||||
|
during a call to @ref read, @ref read_frame, @ref async_read,
|
||||||
|
or @ref async_read_frame.
|
||||||
|
|
||||||
|
Unlike completion handlers, the callback will be invoked for
|
||||||
|
each received pong during a call to any synchronous or
|
||||||
|
asynchronous read function. The operation is passive, with
|
||||||
|
no associated error code, and triggered by reads.
|
||||||
|
|
||||||
|
The signature of the callback must be:
|
||||||
|
@code
|
||||||
|
void callback(
|
||||||
|
ping_data const& payload // Payload of the pong frame
|
||||||
|
);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
If the read operation receiving a pong frame is an asynchronous
|
||||||
|
operation, the callback will be invoked using the same method as
|
||||||
|
that used to invoke the final handler.
|
||||||
|
|
||||||
|
@note To remove the pong callback, construct the option with
|
||||||
|
no parameters: `set_option(pong_callback{})`
|
||||||
|
*/
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using pong_callback = implementation_defined;
|
||||||
|
#else
|
||||||
|
struct pong_callback
|
||||||
|
{
|
||||||
|
detail::pong_cb value;
|
||||||
|
|
||||||
|
pong_callback() = default;
|
||||||
|
pong_callback(pong_callback&&) = default;
|
||||||
|
pong_callback(pong_callback const&) = default;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
pong_callback(detail::pong_cb f)
|
||||||
|
: value(std::move(f))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Read buffer size option.
|
/** Read buffer size option.
|
||||||
|
|
||||||
Sets the number of bytes allocated to the socket's read buffer.
|
Sets the number of bytes allocated to the socket's read buffer.
|
||||||
@@ -224,7 +309,8 @@ struct read_buffer_size
|
|||||||
frame headers indicating a size that would bring the total
|
frame headers indicating a size that would bring the total
|
||||||
message size over this limit will cause a protocol failure.
|
message size over this limit will cause a protocol failure.
|
||||||
|
|
||||||
The default setting is 16 megabytes.
|
The default setting is 16 megabytes. A value of zero indicates
|
||||||
|
a limit of `std::numeric_limits<std::uint64_t>::max()`.
|
||||||
|
|
||||||
@note Objects of this type are passed to @ref stream::set_option.
|
@note Objects of this type are passed to @ref stream::set_option.
|
||||||
|
|
||||||
@@ -251,44 +337,6 @@ struct read_message_max
|
|||||||
};
|
};
|
||||||
#endif
|
#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 @ref stream::set_option.
|
|
||||||
|
|
||||||
@par Example
|
|
||||||
Setting the write buffer size.
|
|
||||||
@code
|
|
||||||
...
|
|
||||||
websocket::stream<ip::tcp::socket> ws(ios);
|
|
||||||
ws.set_option(write_buffer_size{8192});
|
|
||||||
@endcode
|
|
||||||
*/
|
|
||||||
#if GENERATING_DOCS
|
|
||||||
using write_buffer_size = implementation_defined;
|
|
||||||
#else
|
|
||||||
struct write_buffer_size
|
|
||||||
{
|
|
||||||
std::size_t value;
|
|
||||||
|
|
||||||
explicit
|
|
||||||
write_buffer_size(std::size_t n)
|
|
||||||
: value(n)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,13 @@ enum
|
|||||||
protocol_error = 1002,
|
protocol_error = 1002,
|
||||||
|
|
||||||
unknown_data = 1003,
|
unknown_data = 1003,
|
||||||
|
|
||||||
|
/// Indicates a received close frame has no close code
|
||||||
|
//no_code = 1005, // TODO
|
||||||
|
|
||||||
|
/// Indicates the connection was closed without receiving a close frame
|
||||||
|
no_close = 1006,
|
||||||
|
|
||||||
bad_payload = 1007,
|
bad_payload = 1007,
|
||||||
policy_error = 1008,
|
policy_error = 1008,
|
||||||
too_big = 1009,
|
too_big = 1009,
|
||||||
@@ -80,12 +87,11 @@ enum
|
|||||||
} // close_code
|
} // close_code
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ! GENERATING_DOCS
|
/// The type representing the reason string in a close frame.
|
||||||
using reason_string_type =
|
using reason_string = static_string<123, char>;
|
||||||
static_string<123, char>;
|
|
||||||
using ping_payload_type =
|
/// The type representing the payload of ping and pong messages.
|
||||||
static_string<125, char>;
|
using ping_data = static_string<125, char>;
|
||||||
#endif
|
|
||||||
|
|
||||||
/** Description of the close reason.
|
/** Description of the close reason.
|
||||||
|
|
||||||
@@ -98,7 +104,7 @@ struct close_reason
|
|||||||
close_code::value code = close_code::none;
|
close_code::value code = close_code::none;
|
||||||
|
|
||||||
/// The optional utf8-encoded reason string.
|
/// The optional utf8-encoded reason string.
|
||||||
reason_string_type reason;
|
reason_string reason;
|
||||||
|
|
||||||
/** Default constructor.
|
/** Default constructor.
|
||||||
|
|
||||||
@@ -114,17 +120,17 @@ struct close_reason
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Construct from a reason. code is close_code::normal.
|
/// Construct from a reason. code is close_code::normal.
|
||||||
template<class CharT>
|
template<std::size_t N>
|
||||||
close_reason(CharT const* reason_)
|
close_reason(char const (&reason_)[N])
|
||||||
: code(close_code::normal)
|
: code(close_code::normal)
|
||||||
, reason(reason_)
|
, reason(reason_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct from a code and reason.
|
/// Construct from a code and reason.
|
||||||
template<class CharT>
|
template<std::size_t N>
|
||||||
close_reason(close_code::value code_,
|
close_reason(close_code::value code_,
|
||||||
CharT const* reason_)
|
char const (&reason_)[N])
|
||||||
: code(code_)
|
: code(code_)
|
||||||
, reason(reason_)
|
, reason(reason_)
|
||||||
{
|
{
|
||||||
@@ -137,20 +143,6 @@ struct close_reason
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if ! GENERATING_DOCS
|
|
||||||
|
|
||||||
/// Identifies the role of a WebSockets stream.
|
|
||||||
enum class role_type
|
|
||||||
{
|
|
||||||
/// Stream is operating as a client.
|
|
||||||
client,
|
|
||||||
|
|
||||||
/// Stream is operating as a server.
|
|
||||||
server
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ struct frame_info
|
|||||||
@tparam NextLayer The type representing the next layer, to which
|
@tparam NextLayer The type representing the next layer, to which
|
||||||
data will be read and written during operations. For synchronous
|
data will be read and written during operations. For synchronous
|
||||||
operations, the type must support the @b `SyncStream` concept.
|
operations, the type must support the @b `SyncStream` concept.
|
||||||
For asynchronous operations, the type must support the @b `AsyncStream`
|
For asynchronous operations, the type must support the
|
||||||
concept.
|
@b `AsyncStream` concept.
|
||||||
|
|
||||||
@note A stream object must not be destroyed while there are
|
@note A stream object must not be destroyed while there are
|
||||||
pending asynchronous operations associated with it.
|
pending asynchronous operations associated with it.
|
||||||
@@ -84,7 +84,7 @@ struct frame_info
|
|||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
class stream : public detail::stream_base
|
class stream : public detail::stream_base
|
||||||
{
|
{
|
||||||
friend class ws_test;
|
friend class stream_test;
|
||||||
|
|
||||||
streambuf_readstream<NextLayer, streambuf> stream_;
|
streambuf_readstream<NextLayer, streambuf> stream_;
|
||||||
|
|
||||||
@@ -124,12 +124,12 @@ public:
|
|||||||
|
|
||||||
/** Construct a WebSocket stream.
|
/** Construct a WebSocket stream.
|
||||||
|
|
||||||
This constructor creates a websocket stream and initialises
|
This constructor creates a websocket stream and initializes
|
||||||
the next layer object.
|
the next layer object.
|
||||||
|
|
||||||
@throws Any exceptions thrown by the NextLayer constructor.
|
@throws Any exceptions thrown by the NextLayer constructor.
|
||||||
|
|
||||||
@param args The arguments to be passed to initialise the
|
@param args The arguments to be passed to initialize the
|
||||||
next layer object. The arguments are forwarded to the next
|
next layer object. The arguments are forwarded to the next
|
||||||
layer's constructor.
|
layer's constructor.
|
||||||
*/
|
*/
|
||||||
@@ -198,6 +198,13 @@ public:
|
|||||||
wr_opcode_ = o.value;
|
wr_opcode_ = o.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the pong callback
|
||||||
|
void
|
||||||
|
set_option(pong_callback o)
|
||||||
|
{
|
||||||
|
pong_cb_ = std::move(o.value);
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the read buffer size
|
/// Set the read buffer size
|
||||||
void
|
void
|
||||||
set_option(read_buffer_size const& o)
|
set_option(read_buffer_size const& o)
|
||||||
@@ -212,11 +219,11 @@ public:
|
|||||||
rd_msg_max_ = o.value;
|
rd_msg_max_ = o.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the size of the write buffer
|
/// Set the size of the mask buffer
|
||||||
void
|
void
|
||||||
set_option(write_buffer_size const& o)
|
set_option(mask_buffer_size const& o)
|
||||||
{
|
{
|
||||||
wr_buf_size_ = std::max<std::size_t>(o.value, 1024);
|
mask_buf_size_ = o.value;
|
||||||
stream_.capacity(o.value);
|
stream_.capacity(o.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,7 +795,7 @@ public:
|
|||||||
|
|
||||||
/** Send a WebSocket close frame.
|
/** Send a WebSocket close frame.
|
||||||
|
|
||||||
This function is used to sycnhronously send a close frame on
|
This function is used to synchronously send a close frame on
|
||||||
the stream. The call blocks until one of the following is true:
|
the stream. The call blocks until one of the following is true:
|
||||||
|
|
||||||
@li The close frame finishes sending.
|
@li The close frame finishes sending.
|
||||||
@@ -817,7 +824,7 @@ public:
|
|||||||
|
|
||||||
/** Send a WebSocket close frame.
|
/** Send a WebSocket close frame.
|
||||||
|
|
||||||
This function is used to sycnhronously send a close frame on
|
This function is used to synchronously send a close frame on
|
||||||
the stream. The call blocks until one of the following is true:
|
the stream. The call blocks until one of the following is true:
|
||||||
|
|
||||||
@li The close frame finishes sending.
|
@li The close frame finishes sending.
|
||||||
@@ -844,7 +851,7 @@ public:
|
|||||||
void
|
void
|
||||||
close(close_reason const& cr, error_code& ec);
|
close(close_reason const& cr, error_code& ec);
|
||||||
|
|
||||||
/** Start an asycnhronous operation to send a WebSocket close frame.
|
/** Start an asynchronous operation to send a WebSocket close frame.
|
||||||
|
|
||||||
This function is used to asynchronously send a close frame on
|
This function is used to asynchronously send a close frame on
|
||||||
the stream. This function call always returns immediately. The
|
the stream. This function call always returns immediately. The
|
||||||
@@ -858,7 +865,7 @@ public:
|
|||||||
This operation is implemented in terms of one or more calls to the
|
This operation is implemented in terms of one or more calls to the
|
||||||
next layer's `async_write_some` functions, and is known as a
|
next layer's `async_write_some` functions, and is known as a
|
||||||
<em>composed operation</em>. The program must ensure that the
|
<em>composed operation</em>. The program must ensure that the
|
||||||
stream performs no other write operations (such as
|
stream performs no other write operations (such as @ref async_ping,
|
||||||
@ref stream::async_write, @ref stream::async_write_frame, or
|
@ref stream::async_write, @ref stream::async_write_frame, or
|
||||||
@ref stream::async_close) until this operation completes.
|
@ref stream::async_close) until this operation completes.
|
||||||
|
|
||||||
@@ -896,6 +903,84 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
async_close(close_reason const& cr, CloseHandler&& handler);
|
async_close(close_reason const& cr, CloseHandler&& handler);
|
||||||
|
|
||||||
|
/** Send a WebSocket ping frame.
|
||||||
|
|
||||||
|
This function is used to synchronously send a ping frame on
|
||||||
|
the stream. The call blocks until one of the following is true:
|
||||||
|
|
||||||
|
@li The ping frame finishes sending.
|
||||||
|
|
||||||
|
@li An error occurs on the stream.
|
||||||
|
|
||||||
|
This function is implemented in terms of one or more calls to the
|
||||||
|
next layer's `write_some` functions.
|
||||||
|
|
||||||
|
@param payload The payload of the ping message, which may be empty.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ping(ping_data const& payload);
|
||||||
|
|
||||||
|
/** Send a WebSocket ping frame.
|
||||||
|
|
||||||
|
This function is used to synchronously send a ping frame on
|
||||||
|
the stream. The call blocks until one of the following is true:
|
||||||
|
|
||||||
|
@li The ping frame finishes sending.
|
||||||
|
|
||||||
|
@li An error occurs on the stream.
|
||||||
|
|
||||||
|
This function is implemented in terms of one or more calls to the
|
||||||
|
next layer's `write_some` functions.
|
||||||
|
|
||||||
|
@param payload The payload of the ping message, which may be empty.
|
||||||
|
|
||||||
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ping(ping_data const& payload, error_code& ec);
|
||||||
|
|
||||||
|
/** Start an asynchronous operation to send a WebSocket ping frame.
|
||||||
|
|
||||||
|
This function is used to asynchronously send a ping frame to
|
||||||
|
the stream. The function call always returns immediately. The
|
||||||
|
asynchronous operation will continue until one of the following
|
||||||
|
is true:
|
||||||
|
|
||||||
|
@li The entire ping frame is sent.
|
||||||
|
|
||||||
|
@li An error occurs on the stream.
|
||||||
|
|
||||||
|
This operation is implemented in terms of one or more calls to the
|
||||||
|
next layer's `async_write_some` functions, and is known as a
|
||||||
|
<em>composed operation</em>. The program must ensure that the
|
||||||
|
stream performs no other writes until this operation completes.
|
||||||
|
|
||||||
|
@param payload The payload of the ping message, which may be empty.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the read operation
|
||||||
|
completes. Copies will be made of the handler as required. The
|
||||||
|
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 PingHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
typename async_completion<
|
||||||
|
PingHandler, void(error_code)>::result_type
|
||||||
|
#endif
|
||||||
|
async_ping(ping_data const& payload, PingHandler&& handler);
|
||||||
|
|
||||||
/** Read a message from the stream.
|
/** Read a message from the stream.
|
||||||
|
|
||||||
This function is used to synchronously read a message from
|
This function is used to synchronously read a message from
|
||||||
@@ -969,7 +1054,7 @@ public:
|
|||||||
|
|
||||||
/** Start an asynchronous operation to read a message from the stream.
|
/** Start an asynchronous operation to read a message from the stream.
|
||||||
|
|
||||||
This function is used to asychronously read a message from
|
This function is used to asynchronously read a message from
|
||||||
the stream. The function call always returns immediately. The
|
the stream. The function call always returns immediately. The
|
||||||
asynchronous operation will continue until one of the following
|
asynchronous operation will continue until one of the following
|
||||||
is true:
|
is true:
|
||||||
@@ -981,7 +1066,7 @@ public:
|
|||||||
This operation is implemented in terms of one or more calls to the
|
This operation is implemented in terms of one or more calls to the
|
||||||
next layer's `async_read_some` and `async_write_some` functions,
|
next layer's `async_read_some` and `async_write_some` functions,
|
||||||
and is known as a <em>composed operation</em>. The program must
|
and is known as a <em>composed operation</em>. The program must
|
||||||
ensure that the stream performs no other until this operation
|
ensure that the stream performs no other reads until this operation
|
||||||
completes.
|
completes.
|
||||||
|
|
||||||
Upon a success, op is set to either binary or text depending on
|
Upon a success, op is set to either binary or text depending on
|
||||||
@@ -989,15 +1074,14 @@ public:
|
|||||||
hold all the message payload bytes (which may be zero in length).
|
hold all the message payload bytes (which may be zero in length).
|
||||||
|
|
||||||
Control frames encountered while reading frame or message data
|
Control frames encountered while reading frame or message data
|
||||||
are handled automatically. Pings are replied to, pongs are noted,
|
are handled automatically. Pings are replied to, pongs cause
|
||||||
and close frames initiate the WebSocket close procedure. When a
|
an outstanding call to `async_ping` to complete, and close
|
||||||
close frame is received, this call will eventually return
|
frames initiate the WebSocket close procedure. When a close
|
||||||
@ref error::closed. Because of the need to handle control frames,
|
frame is received, this call will eventually return
|
||||||
read operations can cause writes to take place. These writes are
|
@ref error::closed. Because of the need to handle control
|
||||||
managed transparently; callers can still have one active
|
frames, these read operations can cause writes to take place.
|
||||||
asynchronous read and asynchronous write operation pending
|
Despite this, calls to `async_read` and `async_read_frame`
|
||||||
simultaneously (a user initiated call to @ref async_close
|
only count as read operations.
|
||||||
counts as a write).
|
|
||||||
|
|
||||||
@param op A value to receive the message type.
|
@param op A value to receive the message type.
|
||||||
This object must remain valid until the handler is called.
|
This object must remain valid until the handler is called.
|
||||||
@@ -1104,7 +1188,7 @@ public:
|
|||||||
|
|
||||||
/** Start an asynchronous operation to read a message frame from the stream.
|
/** Start an asynchronous operation to read a message frame from the stream.
|
||||||
|
|
||||||
This function is used to asychronously read a single message
|
This function is used to asynchronously read a single message
|
||||||
frame from the websocket. The function call always returns
|
frame from the websocket. The function call always returns
|
||||||
immediately. The asynchronous operation will continue until
|
immediately. The asynchronous operation will continue until
|
||||||
one of the following conditions is true:
|
one of the following conditions is true:
|
||||||
@@ -1116,7 +1200,7 @@ public:
|
|||||||
This operation is implemented in terms of one or more calls to the
|
This operation is implemented in terms of one or more calls to the
|
||||||
next layer's `async_read_some` and `async_write_some` functions,
|
next layer's `async_read_some` and `async_write_some` functions,
|
||||||
and is known as a <em>composed operation</em>. The program must
|
and is known as a <em>composed operation</em>. The program must
|
||||||
ensure that the stream performs no other until this operation
|
ensure that the stream performs no other reads until this operation
|
||||||
completes.
|
completes.
|
||||||
|
|
||||||
Upon a successful completion, fi is filled out to reflect the
|
Upon a successful completion, fi is filled out to reflect the
|
||||||
@@ -1242,7 +1326,7 @@ public:
|
|||||||
|
|
||||||
/** Start an asynchronous operation to write a message to the stream.
|
/** Start an asynchronous operation to write a message to the stream.
|
||||||
|
|
||||||
This function is used to asychronously write a message to
|
This function is used to asynchronously write a message to
|
||||||
the stream. The function call always returns immediately.
|
the stream. The function call always returns immediately.
|
||||||
The asynchronous operation will continue until one of the
|
The asynchronous operation will continue until one of the
|
||||||
following conditions is true:
|
following conditions is true:
|
||||||
@@ -1407,11 +1491,15 @@ private:
|
|||||||
template<class Handler> class accept_op;
|
template<class Handler> class accept_op;
|
||||||
template<class Handler> class close_op;
|
template<class Handler> class close_op;
|
||||||
template<class Handler> class handshake_op;
|
template<class Handler> class handshake_op;
|
||||||
|
template<class Handler> class ping_op;
|
||||||
template<class Handler> class response_op;
|
template<class Handler> class response_op;
|
||||||
template<class Streambuf, class Handler> class read_op;
|
|
||||||
template<class Streambuf, class Handler> class read_frame_op;
|
|
||||||
template<class Buffers, class Handler> class write_op;
|
template<class Buffers, class Handler> class write_op;
|
||||||
template<class Buffers, class Handler> class write_frame_op;
|
template<class Buffers, class Handler> class write_frame_op;
|
||||||
|
template<class Streambuf, class Handler> class read_op;
|
||||||
|
template<class Streambuf, class Handler> class read_frame_op;
|
||||||
|
|
||||||
|
void
|
||||||
|
reset();
|
||||||
|
|
||||||
http::request_v1<http::empty_body>
|
http::request_v1<http::empty_body>
|
||||||
build_request(boost::string_ref const& host,
|
build_request(boost::string_ref const& host,
|
||||||
|
|||||||
@@ -6,7 +6,16 @@ set -ex
|
|||||||
|
|
||||||
################################## ENVIRONMENT #################################
|
################################## ENVIRONMENT #################################
|
||||||
|
|
||||||
export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH
|
# If not CI, then set some defaults
|
||||||
|
if [[ "${CI:-}" == "" ]]; then
|
||||||
|
TRAVIS_BRANCH=${TRAVIS_BRANCH:-feature}
|
||||||
|
CC=${CC:-gcc}
|
||||||
|
ADDRESS_MODEL=${ADDRESS_MODEL:-64}
|
||||||
|
VARIANT=${VARIANT:-debug}
|
||||||
|
# If running locally we assume we have lcov/valgrind on PATH
|
||||||
|
else
|
||||||
|
export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH
|
||||||
|
fi
|
||||||
|
|
||||||
echo "using toolset: $CC"
|
echo "using toolset: $CC"
|
||||||
echo "using variant: $VARIANT"
|
echo "using variant: $VARIANT"
|
||||||
@@ -19,10 +28,25 @@ function run_tests_with_gdb {
|
|||||||
for x in bin/**/*-tests; do scripts/run-with-gdb.sh "$x"; done
|
for x in bin/**/*-tests; do scripts/run-with-gdb.sh "$x"; done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function run_tests {
|
||||||
|
for x in bin/**/*-tests; do "$x"; done
|
||||||
|
}
|
||||||
|
|
||||||
|
num_procs=1
|
||||||
|
if [[ $(uname) == "Darwin" ]]; then
|
||||||
|
num_procs=$(sysctl -n hw.ncpu)
|
||||||
|
elif [[ $(uname -s) == "Linux" ]]; then
|
||||||
|
num_procs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l) # physical cores
|
||||||
|
virt_num_procs=$(nproc) # CircleCI returns 32 phys procs, but 1 virt proc
|
||||||
|
if (("$virt_num_procs" < "$num_procs")); then
|
||||||
|
num_procs=$virt_num_procs
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
function build_beast {
|
function build_beast {
|
||||||
$BOOST_ROOT/bjam toolset=$CC \
|
$BOOST_ROOT/bjam toolset=$CC \
|
||||||
variant=$VARIANT \
|
variant=$VARIANT \
|
||||||
address-model=$ADDRESS_MODEL
|
address-model=$ADDRESS_MODEL -j${num_procs}
|
||||||
}
|
}
|
||||||
|
|
||||||
##################################### BUILD ####################################
|
##################################### BUILD ####################################
|
||||||
@@ -38,24 +62,24 @@ if [[ $VARIANT == "coverage" ]]; then
|
|||||||
lcov --no-external -c -i -d . -o baseline.info > /dev/null
|
lcov --no-external -c -i -d . -o baseline.info > /dev/null
|
||||||
|
|
||||||
# Perform test
|
# Perform test
|
||||||
run_tests_with_gdb
|
run_tests
|
||||||
|
|
||||||
# Run autobahn tests
|
# Run autobahn tests
|
||||||
export SERVER=`find . -name "websocket-echo"`
|
export SERVER=$(find . -name "websocket-echo")
|
||||||
nohup scripts/run-with-gdb.sh $SERVER&
|
nohup $SERVER&
|
||||||
|
|
||||||
# We need to wait a while so wstest can connect!
|
# We need to wait a while so wstest can connect!
|
||||||
sleep 5
|
sleep 5
|
||||||
# cd scripts && wstest -m fuzzingclient
|
cd scripts && wstest -m fuzzingclient
|
||||||
# cd ..
|
cd ..
|
||||||
# Show the output
|
# Show the output
|
||||||
cat nohup.out
|
cat nohup.out
|
||||||
rm nohup.out
|
rm nohup.out
|
||||||
jobs
|
jobs
|
||||||
|
sleep 5
|
||||||
# Kill it gracefully
|
# Kill it gracefully
|
||||||
kill -INT %1
|
kill -INT %1
|
||||||
sleep 1
|
wait
|
||||||
kill -INT %1 || echo "Dead already"
|
|
||||||
|
|
||||||
# Create test coverage data file
|
# Create test coverage data file
|
||||||
lcov --no-external -c -d . -o testrun.info > /dev/null
|
lcov --no-external -c -d . -o testrun.info > /dev/null
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ do
|
|||||||
test -x $( type -p ${g}-$GCC_VER )
|
test -x $( type -p ${g}-$GCC_VER )
|
||||||
ln -sv $(type -p ${g}-$GCC_VER) $HOME/bin/${g}
|
ln -sv $(type -p ${g}-$GCC_VER) $HOME/bin/${g}
|
||||||
done
|
done
|
||||||
for c in clang clang++
|
for c in clang clang++ llvm-symbolizer
|
||||||
do
|
do
|
||||||
test -x $( type -p ${c}-$CLANG_VER )
|
test -x $( type -p ${c}-$CLANG_VER )
|
||||||
ln -sv $(type -p ${c}-$CLANG_VER) $HOME/bin/${c}
|
ln -sv $(type -p ${c}-$CLANG_VER) $HOME/bin/${c}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ unit-test core-tests :
|
|||||||
core/write_streambuf.cpp
|
core/write_streambuf.cpp
|
||||||
core/detail/base64.cpp
|
core/detail/base64.cpp
|
||||||
core/detail/empty_base_optimization.cpp
|
core/detail/empty_base_optimization.cpp
|
||||||
|
core/detail/get_lowest_layer.cpp
|
||||||
core/detail/sha1.cpp
|
core/detail/sha1.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ unit-test http-tests :
|
|||||||
http/basic_headers.cpp
|
http/basic_headers.cpp
|
||||||
http/basic_parser_v1.cpp
|
http/basic_parser_v1.cpp
|
||||||
http/body_type.cpp
|
http/body_type.cpp
|
||||||
|
http/concepts.cpp
|
||||||
http/empty_body.cpp
|
http/empty_body.cpp
|
||||||
http/headers.cpp
|
http/headers.cpp
|
||||||
http/message.cpp
|
http/message.cpp
|
||||||
@@ -57,7 +59,6 @@ unit-test http-tests :
|
|||||||
http/status.cpp
|
http/status.cpp
|
||||||
http/streambuf_body.cpp
|
http/streambuf_body.cpp
|
||||||
http/string_body.cpp
|
http/string_body.cpp
|
||||||
http/type_check.cpp
|
|
||||||
http/write.cpp
|
http/write.cpp
|
||||||
http/detail/chunk_encode.cpp
|
http/detail/chunk_encode.cpp
|
||||||
;
|
;
|
||||||
@@ -77,6 +78,7 @@ unit-test websocket-tests :
|
|||||||
websocket/teardown.cpp
|
websocket/teardown.cpp
|
||||||
websocket/detail/frame.cpp
|
websocket/detail/frame.cpp
|
||||||
websocket/detail/mask.cpp
|
websocket/detail/mask.cpp
|
||||||
|
websocket/detail/stream_base.cpp
|
||||||
websocket/detail/utf8_checker.cpp
|
websocket/detail/utf8_checker.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ add_executable (core-tests
|
|||||||
write_streambuf.cpp
|
write_streambuf.cpp
|
||||||
detail/base64.cpp
|
detail/base64.cpp
|
||||||
detail/empty_base_optimization.cpp
|
detail/empty_base_optimization.cpp
|
||||||
|
detail/get_lowest_layer.cpp
|
||||||
detail/sha1.cpp
|
detail/sha1.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ public:
|
|||||||
expect(buffer_size(buffer_cat(
|
expect(buffer_size(buffer_cat(
|
||||||
sb1.data(), sb2.data())) == 12);
|
sb1.data(), sb2.data())) == 12);
|
||||||
}
|
}
|
||||||
|
for(auto it = bs.begin(); it != bs.end(); ++it)
|
||||||
|
{
|
||||||
|
decltype(bs)::const_iterator copy;
|
||||||
|
copy = it;
|
||||||
|
expect(copy == it);
|
||||||
|
copy = copy;
|
||||||
|
expect(copy == it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testIterators()
|
void testIterators()
|
||||||
|
|||||||
88
src/beast/test/core/detail/get_lowest_layer.cpp
Normal file
88
src/beast/test/core/detail/get_lowest_layer.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
|
||||||
|
// Test that header file is self-contained.
|
||||||
|
#include <beast/core/detail/get_lowest_layer.hpp>
|
||||||
|
|
||||||
|
#include <beast/unit_test/suite.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class get_lowest_layer_test
|
||||||
|
: public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct F1
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct F2
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
struct F3
|
||||||
|
{
|
||||||
|
using next_layer_type =
|
||||||
|
typename std::remove_reference<F>::type;
|
||||||
|
|
||||||
|
using lowest_layer_type = typename
|
||||||
|
get_lowest_layer<next_layer_type>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
struct F4
|
||||||
|
{
|
||||||
|
using next_layer_type =
|
||||||
|
typename std::remove_reference<F>::type;
|
||||||
|
|
||||||
|
using lowest_layer_type = typename
|
||||||
|
get_lowest_layer<next_layer_type>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
static_assert(! has_lowest_layer<F1>::value, "");
|
||||||
|
static_assert(! has_lowest_layer<F2>::value, "");
|
||||||
|
static_assert(has_lowest_layer<F3<F1>>::value, "");
|
||||||
|
static_assert(has_lowest_layer<F4<F3<F2>>>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F1>::type, F1>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F2>::type, F2>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F3<F1>>::type, F1>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F3<F2>>::type, F2>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F4<F1>>::type, F1>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F4<F2>>::type, F2>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F4<F3<F1>>>::type, F1>::value, "");
|
||||||
|
|
||||||
|
static_assert(std::is_same<
|
||||||
|
get_lowest_layer<F4<F3<F2>>>::type, F2>::value, "");
|
||||||
|
|
||||||
|
pass();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(get_lowest_layer,core,beast);
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // beast
|
||||||
@@ -89,6 +89,7 @@ public:
|
|||||||
|
|
||||||
void testIterator()
|
void testIterator()
|
||||||
{
|
{
|
||||||
|
using boost::asio::buffer_size;
|
||||||
using boost::asio::const_buffer;
|
using boost::asio::const_buffer;
|
||||||
char b[3];
|
char b[3];
|
||||||
std::array<const_buffer, 3> bs{{
|
std::array<const_buffer, 3> bs{{
|
||||||
@@ -98,7 +99,12 @@ public:
|
|||||||
auto pb = prepare_buffers(2, bs);
|
auto pb = prepare_buffers(2, bs);
|
||||||
std::size_t n = 0;
|
std::size_t n = 0;
|
||||||
for(auto it = pb.end(); it != pb.begin(); --it)
|
for(auto it = pb.end(); it != pb.begin(); --it)
|
||||||
|
{
|
||||||
|
decltype(pb)::const_iterator it2(std::move(it));
|
||||||
|
expect(buffer_size(*it2) == 1);
|
||||||
|
it = std::move(it2);
|
||||||
++n;
|
++n;
|
||||||
|
}
|
||||||
expect(n == 2);
|
expect(n == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ add_executable (http-tests
|
|||||||
basic_headers.cpp
|
basic_headers.cpp
|
||||||
basic_parser_v1.cpp
|
basic_parser_v1.cpp
|
||||||
body_type.cpp
|
body_type.cpp
|
||||||
|
concepts.cpp
|
||||||
empty_body.cpp
|
empty_body.cpp
|
||||||
headers.cpp
|
headers.cpp
|
||||||
message.cpp
|
message.cpp
|
||||||
@@ -24,7 +25,6 @@ add_executable (http-tests
|
|||||||
status.cpp
|
status.cpp
|
||||||
streambuf_body.cpp
|
streambuf_body.cpp
|
||||||
string_body.cpp
|
string_body.cpp
|
||||||
type_check.cpp
|
|
||||||
write.cpp
|
write.cpp
|
||||||
detail/chunk_encode.cpp
|
detail/chunk_encode.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -532,6 +532,7 @@ public:
|
|||||||
void testInvalidMatrix()
|
void testInvalidMatrix()
|
||||||
{
|
{
|
||||||
using boost::asio::buffer;
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
std::string s;
|
std::string s;
|
||||||
|
|
||||||
for(std::size_t n = 0;; ++n)
|
for(std::size_t n = 0;; ++n)
|
||||||
@@ -549,15 +550,24 @@ public:
|
|||||||
s[n] = 0;
|
s[n] = 0;
|
||||||
for(std::size_t m = 1; m < len - 1; ++m)
|
for(std::size_t m = 1; m < len - 1; ++m)
|
||||||
{
|
{
|
||||||
|
// Use separately allocated buffers so
|
||||||
|
// address sanitizer has something to chew on.
|
||||||
|
//
|
||||||
|
std::unique_ptr<char[]> p1(new char[m]);
|
||||||
|
std::unique_ptr<char[]> p2(new char[len - m]);
|
||||||
|
auto const b1 = buffer(p1.get(), m);
|
||||||
|
auto const b2 = buffer(p2.get(), len - m);
|
||||||
|
buffer_copy(b1, buffer(s.data(), m));
|
||||||
|
buffer_copy(b2, buffer(s.data() + m, len - m));
|
||||||
null_parser<true> p;
|
null_parser<true> p;
|
||||||
error_code ec;
|
error_code ec;
|
||||||
p.write(buffer(s.data(), m), ec);
|
p.write(b1, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
{
|
{
|
||||||
pass();
|
pass();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
p.write(buffer(s.data() + m, len - m), ec);
|
p.write(b2, ec);
|
||||||
expect(ec);
|
expect(ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,4 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <beast/http/type_check.hpp>
|
#include <beast/http/concepts.hpp>
|
||||||
@@ -59,6 +59,7 @@ public:
|
|||||||
}
|
}
|
||||||
catch(std::exception const&)
|
catch(std::exception const&)
|
||||||
{
|
{
|
||||||
|
pass();
|
||||||
}
|
}
|
||||||
m.headers.erase("Content-Length");
|
m.headers.erase("Content-Length");
|
||||||
m.headers.insert("Connection", "keep-alive");
|
m.headers.insert("Connection", "keep-alive");
|
||||||
@@ -69,7 +70,12 @@ public:
|
|||||||
}
|
}
|
||||||
catch(std::exception const&)
|
catch(std::exception const&)
|
||||||
{
|
{
|
||||||
|
pass();
|
||||||
}
|
}
|
||||||
|
m.version = 11;
|
||||||
|
m.headers.erase("Connection");
|
||||||
|
m.headers.insert("Connection", "close");
|
||||||
|
expect(! is_keep_alive(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public:
|
|||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using clock_type = std::chrono::high_resolution_clock;
|
using clock_type = std::chrono::high_resolution_clock;
|
||||||
log << name;
|
log << name << std::endl;
|
||||||
for(std::size_t trial = 1; trial <= repeat; ++trial)
|
for(std::size_t trial = 1; trial <= repeat; ++trial)
|
||||||
{
|
{
|
||||||
auto const t0 = clock_type::now();
|
auto const t0 = clock_type::now();
|
||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
auto const elapsed = clock_type::now() - t0;
|
auto const elapsed = clock_type::now() - t0;
|
||||||
log <<
|
log <<
|
||||||
"Trial " << trial << ": " <<
|
"Trial " << trial << ": " <<
|
||||||
duration_cast<milliseconds>(elapsed).count() << " ms";
|
duration_cast<milliseconds>(elapsed).count() << " ms" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,10 +109,10 @@ public:
|
|||||||
static std::size_t constexpr Repeat = 50;
|
static std::size_t constexpr Repeat = 50;
|
||||||
|
|
||||||
log << "sizeof(request parser) == " <<
|
log << "sizeof(request parser) == " <<
|
||||||
sizeof(basic_parser_v1<true, null_parser<true>>);
|
sizeof(basic_parser_v1<true, null_parser<true>>) << '\n';
|
||||||
|
|
||||||
log << "sizeof(response parser) == " <<
|
log << "sizeof(response parser) == " <<
|
||||||
sizeof(basic_parser_v1<false, null_parser<true>>);
|
sizeof(basic_parser_v1<false, null_parser<true>>)<< '\n';
|
||||||
|
|
||||||
testcase << "Parser speed test, " <<
|
testcase << "Parser speed test, " <<
|
||||||
((Repeat * size_ + 512) / 1024) << "KB in " <<
|
((Repeat * size_ + 512) / 1024) << "KB in " <<
|
||||||
|
|||||||
@@ -12,8 +12,10 @@
|
|||||||
#include <beast/http/headers.hpp>
|
#include <beast/http/headers.hpp>
|
||||||
#include <beast/http/parser_v1.hpp>
|
#include <beast/http/parser_v1.hpp>
|
||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
|
#include <beast/http/write.hpp>
|
||||||
#include <beast/test/string_stream.hpp>
|
#include <beast/test/string_stream.hpp>
|
||||||
#include <beast/unit_test/suite.hpp>
|
#include <beast/unit_test/suite.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
@@ -25,16 +27,18 @@ class streambuf_body_test : public beast::unit_test::suite
|
|||||||
public:
|
public:
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
test::string_stream ss(ios_,
|
std::string const s =
|
||||||
"HTTP/1.1 200 OK\r\n"
|
"HTTP/1.1 200 OK\r\n"
|
||||||
"Server: test\r\n"
|
"Server: test\r\n"
|
||||||
"Content-Length: 3\r\n"
|
"Content-Length: 3\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"xyz");
|
"xyz";
|
||||||
|
test::string_stream ss(ios_, s);
|
||||||
parser_v1<false, streambuf_body, headers> p;
|
parser_v1<false, streambuf_body, headers> p;
|
||||||
streambuf sb;
|
streambuf sb;
|
||||||
parse(ss, sb, p);
|
parse(ss, sb, p);
|
||||||
expect(to_string(p.get().body.data()) == "xyz");
|
expect(to_string(p.get().body.data()) == "xyz");
|
||||||
|
expect(boost::lexical_cast<std::string>(p.get()) == s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ add_executable (websocket-tests
|
|||||||
teardown.cpp
|
teardown.cpp
|
||||||
detail/frame.cpp
|
detail/frame.cpp
|
||||||
detail/mask.cpp
|
detail/mask.cpp
|
||||||
|
detail/stream_base.cpp
|
||||||
detail/utf8_checker.cpp
|
detail/utf8_checker.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -225,7 +225,6 @@ public:
|
|||||||
testCloseCodes();
|
testCloseCodes();
|
||||||
testFrameHeader();
|
testFrameHeader();
|
||||||
testBadFrameHeaders();
|
testBadFrameHeaders();
|
||||||
pass();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// Test that header file is self-contained.
|
||||||
#include <beast/websocket/detail/mask.hpp>
|
#include <beast/websocket/detail/mask.hpp>
|
||||||
|
|
||||||
#include <beast/unit_test/suite.hpp>
|
#include <beast/unit_test/suite.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|||||||
40
src/beast/test/websocket/detail/stream_base.cpp
Normal file
40
src/beast/test/websocket/detail/stream_base.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
|
||||||
|
// Test that header file is self-contained.
|
||||||
|
#include <beast/websocket/detail/stream_base.hpp>
|
||||||
|
|
||||||
|
#include <beast/unit_test/suite.hpp>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class stream_base_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void testClamp()
|
||||||
|
{
|
||||||
|
expect(detail::clamp(
|
||||||
|
std::numeric_limits<std::uint64_t>::max()) ==
|
||||||
|
std::numeric_limits<std::size_t>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
testClamp();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(stream_base,websocket,beast);
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -91,14 +91,16 @@ private:
|
|||||||
bool log;
|
bool log;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
boost::optional<endpoint_type> ep;
|
boost::optional<endpoint_type> ep;
|
||||||
websocket::stream<socket_type> ws;
|
stream<socket_type> ws;
|
||||||
websocket::opcode op;
|
boost::asio::io_service::strand strand;
|
||||||
|
opcode op;
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
data(bool log_, socket_type&& sock_)
|
data(bool log_, socket_type&& sock_)
|
||||||
: log(log_)
|
: log(log_)
|
||||||
, ws(std::move(sock_))
|
, ws(std::move(sock_))
|
||||||
|
, strand(ws.get_io_service())
|
||||||
, id([]
|
, id([]
|
||||||
{
|
{
|
||||||
static int n = 0;
|
static int n = 0;
|
||||||
@@ -112,6 +114,7 @@ private:
|
|||||||
: log(log_)
|
: log(log_)
|
||||||
, ep(ep_)
|
, ep(ep_)
|
||||||
, ws(std::move(sock_))
|
, ws(std::move(sock_))
|
||||||
|
, strand(ws.get_io_service())
|
||||||
, id([]
|
, id([]
|
||||||
{
|
{
|
||||||
static int n = 0;
|
static int n = 0;
|
||||||
@@ -174,8 +177,34 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, std::size_t N>
|
||||||
|
static
|
||||||
|
bool
|
||||||
|
match(Streambuf& sb, char const(&s)[N])
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
if(sb.size() < N-1)
|
||||||
|
return false;
|
||||||
|
static_string<N-1> t;
|
||||||
|
t.resize(N-1);
|
||||||
|
buffer_copy(buffer(t.data(), t.size()),
|
||||||
|
sb.data());
|
||||||
|
if(t != s)
|
||||||
|
return false;
|
||||||
|
sb.consume(N-1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
(*this)(ec);
|
||||||
|
}
|
||||||
|
|
||||||
void operator()(error_code ec)
|
void operator()(error_code ec)
|
||||||
{
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
switch(d.state)
|
switch(d.state)
|
||||||
{
|
{
|
||||||
@@ -191,19 +220,54 @@ private:
|
|||||||
d.sb.consume(d.sb.size());
|
d.sb.consume(d.sb.size());
|
||||||
// read message
|
// read message
|
||||||
d.state = 2;
|
d.state = 2;
|
||||||
d.ws.async_read(d.op, d.sb, std::move(*this));
|
d.ws.async_read(d.op, d.sb,
|
||||||
|
d.strand.wrap(std::move(*this)));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// got message
|
// got message
|
||||||
case 2:
|
case 2:
|
||||||
if(ec == websocket::error::closed)
|
if(ec == error::closed)
|
||||||
return;
|
return;
|
||||||
if(ec)
|
if(ec)
|
||||||
return fail(ec, "async_read");
|
return fail(ec, "async_read");
|
||||||
|
if(match(d.sb, "RAW"))
|
||||||
|
{
|
||||||
|
d.state = 1;
|
||||||
|
boost::asio::async_write(d.ws.next_layer(),
|
||||||
|
d.sb.data(), d.strand.wrap(std::move(*this)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(match(d.sb, "TEXT"))
|
||||||
|
{
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.set_option(message_type{opcode::text});
|
||||||
|
d.ws.async_write(
|
||||||
|
d.sb.data(), d.strand.wrap(std::move(*this)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(match(d.sb, "PING"))
|
||||||
|
{
|
||||||
|
ping_data payload;
|
||||||
|
d.sb.consume(buffer_copy(
|
||||||
|
buffer(payload.data(), payload.size()),
|
||||||
|
d.sb.data()));
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.async_ping(payload,
|
||||||
|
d.strand.wrap(std::move(*this)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(match(d.sb, "CLOSE"))
|
||||||
|
{
|
||||||
|
d.state = 1;
|
||||||
|
d.ws.async_close({},
|
||||||
|
d.strand.wrap(std::move(*this)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
// write message
|
// write message
|
||||||
d.state = 1;
|
d.state = 1;
|
||||||
d.ws.set_option(websocket::message_type(d.op));
|
d.ws.set_option(message_type(d.op));
|
||||||
d.ws.async_write(d.sb.data(), std::move(*this));
|
d.ws.async_write(d.sb.data(),
|
||||||
|
d.strand.wrap(std::move(*this)));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// connected
|
// connected
|
||||||
@@ -214,7 +278,7 @@ private:
|
|||||||
d.ws.async_handshake(
|
d.ws.async_handshake(
|
||||||
d.ep->address().to_string() + ":" +
|
d.ep->address().to_string() + ":" +
|
||||||
std::to_string(d.ep->port()),
|
std::to_string(d.ep->port()),
|
||||||
"/", std::move(*this));
|
"/", d.strand.wrap(std::move(*this)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +290,7 @@ private:
|
|||||||
auto& d = *d_;
|
auto& d = *d_;
|
||||||
if(d.log)
|
if(d.log)
|
||||||
{
|
{
|
||||||
if(ec != websocket::error::closed)
|
if(ec != error::closed)
|
||||||
std::cerr << "#" << d_->id << " " <<
|
std::cerr << "#" << d_->id << " " <<
|
||||||
what << ": " << ec.message() << std::endl;
|
what << ": " << ec.message() << std::endl;
|
||||||
}
|
}
|
||||||
@@ -256,6 +320,8 @@ private:
|
|||||||
{
|
{
|
||||||
if(! acceptor_.is_open())
|
if(! acceptor_.is_open())
|
||||||
return;
|
return;
|
||||||
|
if(ec == boost::asio::error::operation_aborted)
|
||||||
|
return;
|
||||||
maybe_throw(ec, "accept");
|
maybe_throw(ec, "accept");
|
||||||
socket_type sock(std::move(sock_));
|
socket_type sock(std::move(sock_));
|
||||||
acceptor_.async_accept(sock_,
|
acceptor_.async_accept(sock_,
|
||||||
|
|||||||
@@ -101,15 +101,17 @@ private:
|
|||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
sync_echo_peer& self;
|
sync_echo_peer& self;
|
||||||
socket_type sock;
|
|
||||||
boost::asio::io_service::work work;
|
boost::asio::io_service::work work;
|
||||||
|
// Must be destroyed before work otherwise the
|
||||||
|
// io_service could be destroyed before the socket.
|
||||||
|
socket_type sock;
|
||||||
|
|
||||||
lambda(int id_, sync_echo_peer& self_,
|
lambda(int id_, sync_echo_peer& self_,
|
||||||
socket_type&& sock_)
|
socket_type&& sock_)
|
||||||
: id(id_)
|
: id(id_)
|
||||||
, self(self_)
|
, self(self_)
|
||||||
|
, work(sock_.get_io_service())
|
||||||
, sock(std::move(sock_))
|
, sock(std::move(sock_))
|
||||||
, work(sock.get_io_service())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,10 +151,31 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class Streambuf, std::size_t N>
|
||||||
|
static
|
||||||
|
bool
|
||||||
|
match(Streambuf& sb, char const(&s)[N])
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
if(sb.size() < N-1)
|
||||||
|
return false;
|
||||||
|
static_string<N-1> t;
|
||||||
|
t.resize(N-1);
|
||||||
|
buffer_copy(buffer(t.data(), t.size()),
|
||||||
|
sb.data());
|
||||||
|
if(t != s)
|
||||||
|
return false;
|
||||||
|
sb.consume(N-1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
do_peer(int id, socket_type&& sock)
|
do_peer(int id, socket_type&& sock)
|
||||||
{
|
{
|
||||||
websocket::stream<socket_type> ws(std::move(sock));
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
stream<socket_type> ws(std::move(sock));
|
||||||
ws.set_option(decorate(identity{}));
|
ws.set_option(decorate(identity{}));
|
||||||
ws.set_option(read_message_max(64 * 1024 * 1024));
|
ws.set_option(read_message_max(64 * 1024 * 1024));
|
||||||
error_code ec;
|
error_code ec;
|
||||||
@@ -164,17 +187,45 @@ private:
|
|||||||
}
|
}
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
websocket::opcode op;
|
opcode op;
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
ws.read(op, sb, ec);
|
ws.read(op, sb, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
|
{
|
||||||
|
auto const s = ec.message();
|
||||||
break;
|
break;
|
||||||
ws.set_option(websocket::message_type(op));
|
}
|
||||||
ws.write(sb.data(), ec);
|
ws.set_option(message_type(op));
|
||||||
|
if(match(sb, "RAW"))
|
||||||
|
{
|
||||||
|
boost::asio::write(
|
||||||
|
ws.next_layer(), sb.data(), ec);
|
||||||
|
}
|
||||||
|
else if(match(sb, "TEXT"))
|
||||||
|
{
|
||||||
|
ws.set_option(message_type{opcode::text});
|
||||||
|
ws.write(sb.data(), ec);
|
||||||
|
}
|
||||||
|
else if(match(sb, "PING"))
|
||||||
|
{
|
||||||
|
ping_data payload;
|
||||||
|
sb.consume(buffer_copy(
|
||||||
|
buffer(payload.data(), payload.size()),
|
||||||
|
sb.data()));
|
||||||
|
ws.ping(payload, ec);
|
||||||
|
}
|
||||||
|
else if(match(sb, "CLOSE"))
|
||||||
|
{
|
||||||
|
ws.close({}, ec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ws.write(sb.data(), ec);
|
||||||
|
}
|
||||||
if(ec)
|
if(ec)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(ec && ec != websocket::error::closed)
|
if(ec && ec != error::closed)
|
||||||
{
|
{
|
||||||
fail(id, ec, "read");
|
fail(id, ec, "read");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user